mumble-voip_mumble/overlay/overlay_exe/overlay_exe.cpp

195 lines
5.5 KiB
C++

// Copyright 2015-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include <windows.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <stdio.h>
#include <string>
#include <vector>
#include "../overlay.h"
#include "overlay_exe.h"
#define UNUSED(x) ((void) x)
typedef int (*OverlayHelperProcessMain)(unsigned int magic, HANDLE parent);
// Signal to the overlay DLL that it should not inject
// into this process.
extern "C" __declspec(dllexport) void mumbleSelfDetection(){};
// Alert shows a fatal error dialog and waits for the user to click OK.
static void Alert(LPCWSTR title, LPCWSTR msg) {
MessageBox(nullptr, msg, title, MB_OK | MB_ICONERROR);
}
// GetExecutableDirPath returns the directory that
// mumble_ol_helper.exe resides in.
static std::wstring GetExecutableDirPath() {
wchar_t path[MAX_PATH];
if (GetModuleFileNameW(nullptr, path, MAX_PATH) == 0)
return std::wstring();
if (!PathRemoveFileSpecW(path))
return std::wstring();
std::wstring exePath(path);
return exePath.append(L"\\");
}
// ConfigureEnvironment prepares mumble_ol_helper.exe's environment to
// run mumble_ol.dll's OverlayHelperProcessMain() function.
static bool ConfigureEnvironment() {
std::wstring exePath = GetExecutableDirPath();
// Remove the current directory from the DLL search path.
if (!SetDllDirectoryW(L""))
return false;
// Set mumble_ol_helper.exe's directory as the current working directory.
if (!SetCurrentDirectoryW(exePath.c_str()))
return false;
return true;
}
// GetAbsoluteMumbleOverlayDllPath returns the absolute path to
// mumble_ol.dll - the DLL containing the Mumble overlay code.
static std::wstring GetAbsoluteMumbleOverlayDllPath() {
std::wstring exePath = GetExecutableDirPath();
if (exePath.empty())
return std::wstring();
std::wstring absDLLPath(exePath);
absDLLPath.append(L"\\");
absDLLPath.append(MUMBLE_OVERLAY_DLL_NAME);
return absDLLPath;
}
// GetCommandLineArgs returns the command line arguments
// passed to the process.
// If the returned vector has a length of 0, an unknown
// error occurred.
static std::vector< std::wstring > GetCommandLineArgs() {
std::vector< std::wstring > args;
LPWSTR cmdLine = GetCommandLine();
if (!cmdLine) {
return args;
}
int argc = 0;
LPWSTR *argv = CommandLineToArgvW(cmdLine, &argc);
if (!argv) {
return args;
}
for (int i = 0; i < argc; i++) {
args.push_back(std::wstring(argv[i]));
}
return args;
}
int main(int argc, char **argv) {
UNUSED(argc);
UNUSED(argv);
// Mumble passes the OVERLAY_MAGIC_NUMBER it was built
// with the first argument to this helper process.
//
// This is done for two reasons.
//
// The first reason is that we need to ensure
// compatibility between Mumble, the overlay helper,
// and the overlay itself.
//
// The second reason is that it is an easy way to catch
// users accidentally double-clicking on the overlay
// helper's EXE file. We can detect these non-Mumble
// initiated launches by checking if we were passed any
// arguments at all. If no parameters are passed, we
// display a nice alert dialog directing users to
// 'mumble.exe' instead.
unsigned int magic = 0;
HANDLE parent = 0;
{
std::vector< std::wstring > args = GetCommandLineArgs();
// If there is only a single argument, it's the program name.
// That probably means that a user has double-clicked
// the executable. Tell them to run 'mumble.exe' instead.
//
// This also handles the case where GetCommandLineArgs returns
// an empty vector (the error case).
if (args.size() <= 1) {
Alert(L"Mumble Overlay", L"This program is not meant to be run by itself. Run 'mumble.exe' instead.");
return OVERLAY_HELPER_ERROR_EXE_NO_ARGUMENTS;
}
// The Mumble process passes the overlay magic number,
// and a HANDLE in numeric form as its only two arguments.
// We must have both of them to continue.
if (args.size() < 3) {
return OVERLAY_HELPER_ERROR_TOO_FEW_ARGUMENTS;
}
std::wstring magicNumberStr = args[1];
std::wstring handleStr = args[2];
try {
unsigned long passedInMagic = std::stoul(magicNumberStr);
magic = static_cast< unsigned int >(passedInMagic);
} catch (std::exception &) {
return OVERLAY_HELPER_ERROR_EXE_INVALID_MAGIC_ARGUMENT;
}
try {
unsigned long long passedInHandle = std::stoull(handleStr);
parent = reinterpret_cast< HANDLE >(passedInHandle & 0xFFFFFFFFULL);
} catch (std::exception &) {
return OVERLAY_HELPER_ERROR_EXE_INVALID_HANDLE_ARGUMENT;
}
}
if (magic != OVERLAY_MAGIC_NUMBER) {
return OVERLAY_HELPER_ERROR_EXE_MAGIC_MISMATCH;
}
if (!ConfigureEnvironment()) {
return OVERLAY_HELPER_ERROR_EXE_CONFIGURE_ENVIRONMENT;
}
std::wstring absDLLPath = GetAbsoluteMumbleOverlayDllPath();
if (absDLLPath.empty()) {
return OVERLAY_HELPER_ERROR_EXE_GET_DLL_PATH;
}
HMODULE dll = LoadLibraryExW(absDLLPath.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!dll) {
return OVERLAY_HELPER_ERROR_EXE_LOAD_DLL;
}
OverlayHelperProcessMain entryPoint =
reinterpret_cast< OverlayHelperProcessMain >(GetProcAddress(dll, "OverlayHelperProcessMain"));
if (!entryPoint) {
return OVERLAY_HELPER_ERROR_EXE_LOOKUP_ENTRY_POINT;
}
return entryPoint(magic, parent);
}
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, wchar_t *cmdArg, int cmdShow) {
UNUSED(instance);
UNUSED(prevInstance);
UNUSED(cmdArg);
UNUSED(cmdShow);
return main(0, nullptr);
}