diff options
author | Mikkel Krautz <mikkel@krautz.dk> | 2015-02-27 20:34:44 +0300 |
---|---|---|
committer | Mikkel Krautz <mikkel@krautz.dk> | 2015-04-22 22:03:53 +0300 |
commit | 6c446e4ead44a66b5706a95b410f5429e7890b9f (patch) | |
tree | f429fc5a3d33bbe7c57e5b608148dc143f499623 | |
parent | b1880294df43d16ff03e927fba1572a027dac6aa (diff) |
Ensure overlay helpers exit when the Mumble process terminates.
Normally, Mumble itself will terminate the helper processes.
But if Mumble crashes, or is manually killed by the user, it
will not be able to terminate the helper processes itself.
In order to fix this, we create a way for the helpers to know
when their parent process has terminated.
This is implemented by creating an inheritable process handle
in Mumble, and passing its value to the helpers.
The helpers then WaitForSingleObject() on the parent handle, and
exits with status code 0 if it the WaitForSingleObject() call
returns successfully.
-rw-r--r-- | overlay/lib.cpp | 18 | ||||
-rw-r--r-- | overlay/overlay_exe/overlay_exe.cpp | 32 | ||||
-rw-r--r-- | overlay/overlay_exe/overlay_exe.h | 19 | ||||
-rw-r--r-- | src/mumble/Overlay_win.cpp | 32 | ||||
-rw-r--r-- | src/mumble/Overlay_win.h | 1 |
5 files changed, 93 insertions, 9 deletions
diff --git a/overlay/lib.cpp b/overlay/lib.cpp index e9eeb4370..9358212c0 100644 --- a/overlay/lib.cpp +++ b/overlay/lib.cpp @@ -448,13 +448,29 @@ extern "C" __declspec(dllexport) void __cdecl PrepareD3D9(); // Via dxgi.cpp extern "C" __declspec(dllexport) void __cdecl PrepareDXGI(); -extern "C" __declspec(dllexport) int __cdecl OverlayHelperProcessMain(unsigned int magic) { +void __stdcall OverlayHelperProcessParentDeathThread(void *udata) { + HANDLE parent = reinterpret_cast<HANDLE>(udata); + DWORD status = WaitForSingleObject(parent, INFINITE); + if (status != WAIT_OBJECT_0) { + ExitProcess(OVERLAY_HELPER_ERROR_DLL_PDEATH_WAIT_FAIL); + } + + ExitProcess(0); +} + +extern "C" __declspec(dllexport) int __cdecl OverlayHelperProcessMain(unsigned int magic, HANDLE parent) { int retval = 0; if (GetOverlayMagicVersion() != magic) { return OVERLAY_HELPER_ERROR_DLL_MAGIC_MISMATCH; } + HANDLE pcheckHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) OverlayHelperProcessParentDeathThread, + reinterpret_cast<void *>(parent), 0, NULL); + if (pcheckHandle == 0) { + return OVERLAY_HELPER_ERROR_DLL_PDEATH_THREAD_ERROR; + } + PrepareD3D9(); PrepareDXGI(); diff --git a/overlay/overlay_exe/overlay_exe.cpp b/overlay/overlay_exe/overlay_exe.cpp index be9dbc653..bee53bdf7 100644 --- a/overlay/overlay_exe/overlay_exe.cpp +++ b/overlay/overlay_exe/overlay_exe.cpp @@ -40,7 +40,7 @@ #define UNUSED(x) ((void)x) -typedef int (*OverlayHelperProcessMain)(unsigned int magic); +typedef int (*OverlayHelperProcessMain)(unsigned int magic, HANDLE parent); // Signal to the overlay DLL that it should not inject // into this process. @@ -116,6 +116,7 @@ int main(int argc, char **argv) { // display a nice alert dialog directing users to // 'mumble.exe' instead. unsigned int magic = 0; + HANDLE parent = 0; { std::wstring commandLine(GetCommandLine()); @@ -123,21 +124,40 @@ int main(int argc, char **argv) { // if the program was passed any arguments. If we don't // find them, it probably means that a user has double-clicked // the executable. Tell them to run 'mumble.exe' instead. - size_t sep = commandLine.find(std::wstring(L" ")); + std::wstring doubleSpace = std::wstring(L" "); + size_t sep = commandLine.find(doubleSpace); if (sep == std::string::npos) { Alert(L"Mumble Overlay", L"This program is not meant to be run by itself. Run 'mumble.exe' instead."); return OVERLAY_HELPER_ERROR_EXE_MISSING_MAGIC_ARGUMENT; } - // We expect that the Mumble process passes the overlay - // magic number that it is built with to us. - std::wstring magicNumberStr = commandLine.substr(sep); + // The Mumble process passes the overlay magic number, + // and a HANDLE in numeric form as its only two arguments. + // Let's try to parse the command line to extract them... + std::wstring args = commandLine.substr(sep + doubleSpace.length()); + sep = args.find(std::wstring(L" ")); + if (sep == std::string::npos) { + return OVERLAY_HELPER_ERROR_TOO_FEW_ARGUMENTS; + } + if (args.length() <= sep + 1) { + return OVERLAY_HELPER_ERROR_TOO_FEW_ARGUMENTS; + } + std::wstring magicNumberStr = args.substr(0, sep); + std::wstring handleStr = args.substr(sep+1); + 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 passedInHandle = std::stoul(handleStr); + parent = reinterpret_cast<HANDLE>(passedInHandle & 0xFFFFFFFFUL); + } catch(std::exception &) { + return OVERLAY_HELPER_ERROR_EXE_INVALID_HANDLE_ARGUMENT; + } } if (magic != OVERLAY_MAGIC_NUMBER) { @@ -163,7 +183,7 @@ int main(int argc, char **argv) { return OVERLAY_HELPER_ERROR_EXE_LOOKUP_ENTRY_POINT; } - return entryPoint(magic); + return entryPoint(magic, parent); } int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, wchar_t *cmdArg, int cmdShow) { diff --git a/overlay/overlay_exe/overlay_exe.h b/overlay/overlay_exe/overlay_exe.h index bbd9d2e20..e950aeed9 100644 --- a/overlay/overlay_exe/overlay_exe.h +++ b/overlay/overlay_exe/overlay_exe.h @@ -64,14 +64,27 @@ enum OverlayHelperError { /// the 'OverlayHelperProcessMain' entry point in the
/// overlay DLL.
OVERLAY_HELPER_ERROR_EXE_LOOKUP_ENTRY_POINT = -7,
+ /// The overlay helper process was uanble to parse
+ /// the commandline arguments it was passed.
+ /// The helper process exepcts two arguments, and this
+ /// error occurs if it only finds one.
+ OVERLAY_HELPER_ERROR_TOO_FEW_ARGUMENTS = -8,
+ /// The magic number on the command line of the overlay
+ /// helper process could not be converted to a HANDLE.
+ OVERLAY_HELPER_ERROR_EXE_INVALID_HANDLE_ARGUMENT = -9,
- /// The magic number passed to the overlay DLL's
+ /// The magic number passed to the overlay DLL's
/// OverlayHelperProcessMain function did not match
/// the overlay DLL's built-in magic number.
OVERLAY_HELPER_ERROR_DLL_MAGIC_MISMATCH = -1000,
/// The overlay helper process exited due to an error
/// in the Windows message loop.
OVERLAY_HELPER_ERROR_DLL_MESSAGE_LOOP = -1001,
+ /// The parent death thread could not be created.
+ OVERLAY_HELPER_ERROR_DLL_PDEATH_THREAD_ERROR = -1002,
+ /// The helper's WaitForSingleObject call on its parent
+ /// process failed unexpectedly.
+ OVERLAY_HELPER_ERROR_DLL_PDEATH_WAIT_FAIL = -1003,
};
/// OverlayHelperErrorToString converts an OverlayHelperError value
@@ -86,8 +99,12 @@ static inline const char *OverlayHelperErrorToString(OverlayHelperError err) { OHE(OVERLAY_HELPER_ERROR_EXE_GET_DLL_PATH);
OHE(OVERLAY_HELPER_ERROR_EXE_LOAD_DLL);
OHE(OVERLAY_HELPER_ERROR_EXE_LOOKUP_ENTRY_POINT);
+ OHE(OVERLAY_HELPER_ERROR_TOO_FEW_ARGUMENTS);
+ OHE(OVERLAY_HELPER_ERROR_EXE_INVALID_HANDLE_ARGUMENT);
OHE(OVERLAY_HELPER_ERROR_DLL_MAGIC_MISMATCH);
OHE(OVERLAY_HELPER_ERROR_DLL_MESSAGE_LOOP);
+ OHE(OVERLAY_HELPER_ERROR_DLL_PDEATH_THREAD_ERROR);
+ OHE(OVERLAY_HELPER_ERROR_DLL_PDEATH_WAIT_FAIL);
}
return NULL;
}
diff --git a/src/mumble/Overlay_win.cpp b/src/mumble/Overlay_win.cpp index 4b5dda0bd..e401743ae 100644 --- a/src/mumble/Overlay_win.cpp +++ b/src/mumble/Overlay_win.cpp @@ -74,8 +74,33 @@ static bool canRun64BitPrograms() { OverlayPrivateWin::OverlayPrivateWin(QObject *p) : OverlayPrivate(p) { m_active = false; + // Acquire a handle to ourselves and duplicate it. We duplicate it because + // want it to be inheritable by our helper processes, and the handle returned + // by GetCurrentProcess is not inheritable. Duplicating it makes it inheritable. + // This allows our helper processes to access the handle. + // + // The helper processes need a handle to us, their parent, to be able to listen + // detect when our process dies. + // + // The value of the handle is passed as an argument to the helper processes via + // the command line as a number. The HANDLE type in Windows is typedef'd to LPVOID, + // but for handles that are supposed to be shared between processes (like a process + // handle that we are using), only the lower 32-bits of the HANDLE are considered: + // + // "When sharing a handle between 32-bit and 64-bit applications, only the lower + // 32 bits are significant [...]" + // + // from https://msdn.microsoft.com/en-us/library/aa384203.aspx + HANDLE curproc = GetCurrentProcess(); + if (!DuplicateHandle(curproc, curproc, curproc, &m_mumble_handle, + 0, TRUE, DUPLICATE_SAME_ACCESS)) { + qFatal("OverlayPrivateWin: unable to duplicate handle to the Mumble process."); + return; + } + m_helper_exe_path = QString::fromLatin1("%1/mumble_ol.exe").arg(qApp->applicationDirPath()); - m_helper_exe_args = QStringList(QString::number(OVERLAY_MAGIC_NUMBER)); + m_helper_exe_args << QString::number(OVERLAY_MAGIC_NUMBER) + << QString::number(reinterpret_cast<quintptr>(m_mumble_handle)); m_helper_process = new QProcess(this); connect(m_helper_process, SIGNAL(started()), @@ -124,6 +149,11 @@ OverlayPrivateWin::OverlayPrivateWin(QObject *p) : OverlayPrivate(p) { OverlayPrivateWin::~OverlayPrivateWin() { m_active = false; + + if (!CloseHandle(m_mumble_handle)) { + qFatal("OverlayPrivateWin: unable to close Mumble process handle."); + return; + } } void OverlayPrivateWin::startHelper(QProcess *helper) { diff --git a/src/mumble/Overlay_win.h b/src/mumble/Overlay_win.h index 4fb6bd7c0..171fc32a9 100644 --- a/src/mumble/Overlay_win.h +++ b/src/mumble/Overlay_win.h @@ -71,6 +71,7 @@ class OverlayPrivateWin : public OverlayPrivate { QTimer *m_helper64_restart_timer;
bool m_helper64_enabled;
+ HANDLE m_mumble_handle;
bool m_active;
void startHelper(QProcess *helper);
|