spiegel-keyman/windows/src/engine/keyman32/serialkeyeventclient.cpp
Marc Durdin ccc6498739 fix(windows): Keyman was crashing sporadically
Fixes #2147.

This potentially addresses the issues reported in #2147 where Keyman
would crash due to a race condition as a critical section was not
deleted before attempting to recreate it.

Some logging has been added for debugging these issues.

Also fixes memory leaks and another race on startup relating to loading
the taskbar icon.

There are still some known memory leaks because DLL_THREAD_DETACH is
not called for all threads before keyman32.dll is unloaded, so those
threads don't get the opportunity to clean themsselves up. However, the
key danger with the csGlobals critical section has been addressed.
2019-11-06 17:44:32 +11:00

166 lines
4.4 KiB
C++

#include "pch.h"
#include "serialkeyeventclient.h"
#include "security.h"
//
// Client application functionality
//
class SerialKeyEventClient : public ISerialKeyEventClient {
private:
HANDLE m_hKeyEvent = 0;
HANDLE m_hKeyMutex = 0;
HANDLE m_hMMF = 0;
SerialKeyEventSharedData *m_pSharedData = NULL;
public:
SerialKeyEventClient() {
//
// Initialisation
//
m_pSharedData = NULL;
m_hKeyEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, GLOBAL_KEY_EVENT_NAME);
if (m_hKeyEvent == 0) {
DebugLastError("OpenEvent");
return;
}
m_hKeyMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, GLOBAL_KEY_MUTEX_NAME);
if (m_hKeyMutex == 0) {
DebugLastError("OpenMutex");
return;
}
m_hMMF = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, GLOBAL_FILE_MAPPING_NAME);
if (m_hMMF == 0) {
DebugLastError("OpenFileMapping");
return;
}
m_pSharedData = (SerialKeyEventSharedData *)MapViewOfFile(m_hMMF, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SerialKeyEventSharedData));
if (!m_pSharedData) {
DebugLastError("MapViewOfFile");
return;
}
}
SerialKeyEventClient::~SerialKeyEventClient() {
if (m_pSharedData != NULL) {
if (!UnmapViewOfFile(m_pSharedData)) {
DebugLastError("UnmapViewOfFile");
}
}
if (m_hMMF != NULL) {
if (!CloseHandle(m_hMMF)) {
DebugLastError("CloseHandle(m_hMMF)");
}
}
if (m_hKeyMutex != NULL) {
if (!CloseHandle(m_hKeyMutex)) {
DebugLastError("CloseHandle(m_hKeyMutex)");
}
}
if (m_hKeyEvent != NULL) {
if (!CloseHandle(m_hKeyEvent)) {
DebugLastError("CloseHandle(m_hKeyEvent)");
}
}
}
/**
Provide a copy of the input data to the Key Event Sender thread and signal it
to send the input back to the client application.
*/
BOOL SerialKeyEventClient::SignalServer(PINPUT pInputs, DWORD nInputs) {
if (m_pSharedData == NULL) {
// Initialisation failed, don't even try
return FALSE;
}
//
// Check inputs
//
if (nInputs > MAX_KEYEVENT_INPUTS) {
SendDebugMessageFormat(0, sdmGlobal, 0, "Too many INPUT events for queue (%d)", nInputs);
nInputs = MAX_KEYEVENT_INPUTS;
}
if (nInputs == 0) {
// Don't need to signal sender, nothing to send
return TRUE;
}
//
// Capture the mutex and copy input buffer into global shared buffer. We have a fairly
// short buffer here to avoid stalling the active application for too long if for some
// reason Keyman's g_SerialKeyEventServer thread is not responding.
//
switch (WaitForSingleObject(m_hKeyMutex, 500)) {
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
SendDebugMessage(0, sdmGlobal, 0, "Timed out waiting to send input to host app");
return FALSE;
case WAIT_ABANDONED_0:
SendDebugMessage(0, sdmGlobal, 0, "Host app closed mutex");
return FALSE;
case WAIT_FAILED:
default:
DebugLastError("WaitForSingleObject");
return FALSE;
}
//
// Copy the INPUT objects into our cross-platform-safe structure
//
m_pSharedData->nInputs = nInputs;
for (DWORD i = 0; i < nInputs; i++) {
m_pSharedData->inputs[i].wVk = pInputs[i].ki.wVk;
m_pSharedData->inputs[i].wScan = pInputs[i].ki.wScan;
m_pSharedData->inputs[i].dwFlags = pInputs[i].ki.dwFlags;
m_pSharedData->inputs[i].time = pInputs[i].ki.time;
m_pSharedData->inputs[i].extraInfo = pInputs[i].ki.dwExtraInfo;
}
//
// Force CPU to complete all memory operations before we signal the host
//
MemoryBarrier();
//
// Release the mutex and signal the host application to process the input
//
if (!ReleaseMutex(m_hKeyMutex)) {
DebugLastError("ReleaseMutex");
return FALSE;
}
if (!SetEvent(m_hKeyEvent)) {
DebugLastError("SetEvent");
return FALSE;
}
return TRUE;
}
};
void ISerialKeyEventClient::Startup() {
OutputThreadDebugString("ISerialKeyEventClient::Startup");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (_td) {
_td->pSerialKeyEventClient = new SerialKeyEventClient();
}
}
void ISerialKeyEventClient::Shutdown() {
OutputThreadDebugString("ISerialKeyEventClient::Shutdown");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (_td && _td->pSerialKeyEventClient) {
delete _td->pSerialKeyEventClient;
_td->pSerialKeyEventClient = NULL;
}
}