Files
msime-windows/src/IPC/Ipc.cpp
T

961 lines
24 KiB
C++

#include "Ipc.h"
#include <debugapi.h>
#include <handleapi.h>
#include <minwindef.h>
#include <utility>
#include <winnt.h>
#include <winuser.h>
#include "Globals.h"
#include "FanyDefines.h"
#include "MetasequoiaIME.h"
#include <fmt/xchar.h>
static HANDLE hMapFile = nullptr;
static void *pBuf;
static FanyImeSharedMemoryData *sharedData;
static bool canUseSharedMemory = false;
static HANDLE hPipe = nullptr;
static HANDLE hFromServerPipe = nullptr;
static FanyImeNamedpipeData namedpipeData = {};
FanyImeNamedpipeDataToTsf namedpipeDataFromServer = {};
/* Data size transfered from Server process */
static const int ServerDtPipeDataSize = 512;
int InitIpc()
{
//
// Shared memory, open here
//
hMapFile = OpenFileMappingW( //
FILE_MAP_ALL_ACCESS, //
FALSE, //
FANY_IME_SHARED_MEMORY //
);
//
// Shared memory is not available, try to use namedpipe
//
InitNamedpipe();
if (!hMapFile)
{
// Error handling
canUseSharedMemory = false;
// TODO: Log error
return 0;
}
bool alreadyExists = (GetLastError() == ERROR_ALREADY_EXISTS);
pBuf = MapViewOfFile( //
hMapFile, //
FILE_MAP_ALL_ACCESS, //
0, //
0, //
BUFFER_SIZE //
); //
if (!pBuf)
{
// TODO: Error handling
}
sharedData = static_cast<FanyImeSharedMemoryData *>(pBuf);
// Only initialize the shared memory when first created
if (!alreadyExists)
{
// Initialize
*sharedData = {};
sharedData->point[0] = 100;
sharedData->point[1] = 100;
}
return 0;
}
int InitNamedpipe()
{
if (hPipe == nullptr)
{
hPipe = CreateFile( //
FANY_IME_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hPipe == INVALID_HANDLE_VALUE)
{
hPipe = nullptr;
}
else
{
// TODO: Log
}
}
if (hFromServerPipe == nullptr)
{
hFromServerPipe = CreateFile( //
FANY_IME_TO_TSF_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hFromServerPipe == INVALID_HANDLE_VALUE)
{
hFromServerPipe = nullptr;
}
else
{
// TODO: Log
}
}
if (Global::hToTsfWorkerThreadPipe == nullptr)
{
Global::hToTsfWorkerThreadPipe = CreateFile( //
FANY_IME_TO_TSF_WORKER_THREAD_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (Global::hToTsfWorkerThreadPipe == INVALID_HANDLE_VALUE)
{
Global::hToTsfWorkerThreadPipe = nullptr;
}
else
{
// TODO: Log
}
}
if (hPipe == nullptr || hPipe == INVALID_HANDLE_VALUE || hFromServerPipe == nullptr ||
hFromServerPipe == INVALID_HANDLE_VALUE || Global::hToTsfWorkerThreadPipe == nullptr ||
Global::hToTsfWorkerThreadPipe == INVALID_HANDLE_VALUE)
{
hPipe = nullptr;
hFromServerPipe = nullptr;
Global::hToTsfWorkerThreadPipe = nullptr;
return 0;
}
return 1;
}
int ConnectToAllNamedpipe()
{
hPipe = CreateFile( //
FANY_IME_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hPipe == INVALID_HANDLE_VALUE)
{
hPipe = nullptr;
}
else
{
// TODO: Log
}
hFromServerPipe = CreateFile( //
FANY_IME_TO_TSF_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hFromServerPipe == INVALID_HANDLE_VALUE)
{
hFromServerPipe = nullptr;
}
else
{
// TODO: Log
}
Global::hToTsfWorkerThreadPipe = CreateFile( //
FANY_IME_TO_TSF_WORKER_THREAD_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (Global::hToTsfWorkerThreadPipe == INVALID_HANDLE_VALUE)
{
Global::hToTsfWorkerThreadPipe = nullptr;
}
else
{
// TODO: Log
}
if (hPipe == nullptr || hPipe == INVALID_HANDLE_VALUE || hFromServerPipe == nullptr ||
hFromServerPipe == INVALID_HANDLE_VALUE || Global::hToTsfWorkerThreadPipe == nullptr ||
Global::hToTsfWorkerThreadPipe == INVALID_HANDLE_VALUE)
{
hPipe = nullptr;
hFromServerPipe = nullptr;
Global::hToTsfWorkerThreadPipe = nullptr;
return 0;
}
return 1;
}
int ConnectToTsfNamedpipe()
{
if (hFromServerPipe == nullptr || hFromServerPipe == INVALID_HANDLE_VALUE)
{
hFromServerPipe = CreateFile( //
FANY_IME_TO_TSF_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hFromServerPipe == INVALID_HANDLE_VALUE)
{
hFromServerPipe = nullptr;
}
else
{
// TODO: Log
}
}
if (hFromServerPipe == nullptr || hFromServerPipe == INVALID_HANDLE_VALUE)
{
return 0;
}
return 1;
}
int CloseIpc()
{
//
// Namedpipe
//
CloseNamedpipe();
if (!canUseSharedMemory)
{
return -1;
}
//
// Shared memory
//
if (pBuf)
{
UnmapViewOfFile(pBuf);
pBuf = nullptr;
}
if (hMapFile)
{
CloseHandle(hMapFile);
hMapFile = nullptr;
}
//
// Events
//
for (const auto &eventName : FANY_IME_EVENT_ARRAY)
{
HANDLE hEvent = OpenEventW( //
EVENT_ALL_ACCESS, //
FALSE, //
eventName.c_str() //
); //
if (hEvent)
{
CloseHandle(hEvent);
}
}
return 0;
}
int CloseNamedpipe()
{
CloseHandle(hPipe);
hPipe = nullptr;
CloseHandle(hFromServerPipe);
hFromServerPipe = nullptr;
CloseHandle(Global::hToTsfWorkerThreadPipe);
Global::hToTsfWorkerThreadPipe = nullptr;
return 0;
}
int WriteDataToSharedMemory( //
UINT keycode, //
WCHAR wch, //
UINT modifiers_down, //
const int point[2], //
int pinyin_length, //
const std::wstring &pinyin_string, //
UINT write_flag //
)
{
if (!canUseSharedMemory)
{
return WriteDataToNamedPipe(keycode, wch, modifiers_down, point, pinyin_length, pinyin_string, write_flag);
}
if (write_flag >> 0 & 1u)
{
sharedData->keycode = keycode;
}
if (write_flag >> 1 & 1u)
{
sharedData->wch = wch;
}
if (write_flag >> 2 & 1u)
{
sharedData->modifiers_down = modifiers_down;
}
if (write_flag >> 3 & 1u)
{
sharedData->point[0] = point[0];
sharedData->point[1] = point[1];
}
if (write_flag >> 4 & 1u)
{
sharedData->pinyin_length = pinyin_length;
}
if (write_flag >> 5 & 1u)
{
wcscpy_s(sharedData->pinyin_string, pinyin_string.c_str());
sharedData->pinyin_string[pinyin_length] = L'\0';
}
return 0;
}
/**
* @brief
*
* @param keycode
* @param modifiers_down
* @param point
* @param pinyin_length
* @param pinyin_string
* @param write_flag
* @return int
*/
int WriteDataToNamedPipe( //
UINT keycode, //
WCHAR wch, //
UINT modifiers_down, //
const int point[2], //
int pinyin_length, //
const std::wstring &pinyin_string, //
UINT write_flag //
)
{
if (write_flag >> 0 & 1u)
{
namedpipeData.keycode = keycode;
}
if (write_flag >> 1 & 1u)
{
namedpipeData.wch = wch;
}
if (write_flag >> 2 & 1u)
{
namedpipeData.modifiers_down = modifiers_down;
}
if (write_flag >> 3 & 1u)
{
namedpipeData.point[0] = point[0];
namedpipeData.point[1] = point[1];
}
if (write_flag >> 4 & 1u)
{
namedpipeData.pinyin_length = pinyin_length;
}
if (write_flag >> 5 & 1u)
{
wcscpy_s(namedpipeData.pinyin_string, pinyin_string.c_str());
namedpipeData.pinyin_string[pinyin_length] = L'\0';
}
return 0;
}
int SendKeyEventToUIProcess()
{
if (!canUseSharedMemory)
{
return SendKeyEventToUIProcessViaNamedPipe();
}
HANDLE hEvent = OpenEventW( //
EVENT_MODIFY_STATE, //
FALSE, //
FANY_IME_EVENT_ARRAY[0].c_str() //
); //
if (!hEvent)
{
// TODO: Error handling
}
if (!SetEvent(hEvent))
{
// TODO: Error handling
}
CloseHandle(hEvent);
return 0;
}
int SendHideCandidateWndEventToUIProcess()
{
if (!canUseSharedMemory)
{
return SendHideCandidateWndEventToUIProcessViaNamedPipe();
}
HANDLE hEvent = OpenEventW( //
EVENT_MODIFY_STATE, //
FALSE, //
FANY_IME_EVENT_ARRAY[1].c_str() // FanyHideCandidateWndEvent
); //
if (!hEvent)
{
// TODO: Error handling
}
if (!SetEvent(hEvent))
{
// TODO: Error handling
}
CloseHandle(hEvent);
return 0;
}
int SendShowCandidateWndEventToUIProcess()
{
if (!canUseSharedMemory)
{
return SendShowCandidateWndEventToUIProcessViaNamedPipe();
}
HANDLE hEvent = OpenEventW( //
EVENT_MODIFY_STATE, //
FALSE, //
FANY_IME_EVENT_ARRAY[2].c_str() // FanyShowCandidateWndEvent
); //
if (!hEvent)
{
// TODO: Error handling
}
if (!SetEvent(hEvent))
{
// TODO: Error handling
}
CloseHandle(hEvent);
return 0;
}
int SendMoveCandidateWndEventToUIProcess()
{
if (!canUseSharedMemory)
{
return SendMoveCandidateWndEventToUIProcessViaNamedPipe();
}
HANDLE hEvent = OpenEventW( //
EVENT_MODIFY_STATE, //
FALSE, //
FANY_IME_EVENT_ARRAY[3].c_str() // FanyMoveCandidateWndEvent
); //
if (!hEvent)
{
// TODO: Error handling
}
if (!SetEvent(hEvent))
{
// TODO: Error handling
}
CloseHandle(hEvent);
return 0;
}
int SendLangbarRightClickEventToUIProcess(const RECT *prcArea)
{
if (!canUseSharedMemory)
{
return SendLangbarRightClickEventToUIProcessViaNamedPipe(prcArea);
}
return 0;
}
//
// Named pipe
//
void SendToNamedpipe()
{
if (!hPipe || hPipe == INVALID_HANDLE_VALUE) // Try to reconnect
{
hPipe = CreateFile( //
FANY_IME_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hPipe == INVALID_HANDLE_VALUE)
{
// TODO: Log
return;
}
else
{
// TODO: Log
}
}
DWORD bytesWritten = 0;
BOOL ret = WriteFile( //
hPipe, //
&namedpipeData, //
sizeof(namedpipeData), //
&bytesWritten, //
NULL //
);
if (!ret || bytesWritten != sizeof(namedpipeData))
{
/* Error handling: 必须将 hPipe 置为无效,否则将留下脏句柄,导致有些情况下无法再次连接 */
DWORD err = GetLastError();
// if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
// {
/* 将两个方向的管道同时置为无效 */
CloseHandle(hPipe);
CloseHandle(hFromServerPipe);
// CloseHandle(Global::hToTsfWorkerThreadPipe);
hPipe = INVALID_HANDLE_VALUE;
hFromServerPipe = INVALID_HANDLE_VALUE;
// Global::hToTsfWorkerThreadPipe = INVALID_HANDLE_VALUE;
/* 向 Server 端发送 kill 同时重置两个管道的 connect */
SendToAuxNamedpipe(L"kill");
OutputDebugString(fmt::format(L"SendToNamedpipe failed eventually01.").c_str());
// }
//
// 等待 Server 准备好,再重连几次
//
/* toTsfNamedPipe 放到消息窗口的线程中去处理 */
PostMessage(Global::msgWndHandle, WM_ConnectToTsfNamedpipe, 0, 0);
for (int i = 0; i < 10; ++i)
{
Sleep(10);
hPipe = CreateFile( //
FANY_IME_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hPipe != INVALID_HANDLE_VALUE)
break;
}
if (hPipe == INVALID_HANDLE_VALUE)
{
// TODO: Log
OutputDebugString(fmt::format(L"SendToNamedpipe failed eventually02.").c_str());
return;
}
else
{
// TODO: Log
}
DWORD bytesWritten = 0;
BOOL ret = WriteFile( //
hPipe, //
&namedpipeData, //
sizeof(namedpipeData), //
&bytesWritten, //
NULL //
);
if (!ret || bytesWritten != sizeof(namedpipeData))
{
// TODO: Error handling
OutputDebugString(fmt::format(L"SendToNamedpipe failed eventually03.").c_str());
}
return;
}
}
/**
* @brief Clear namedpipe data if exists, cause sometimes there may be some useless data sent by last key event from
* server
*
*/
void ClearNamedpipeDataIfExists()
{
bool isSpaceOrNumber = Global::Keycode == VK_SPACE || //
(Global::Keycode > '0' && Global::Keycode < '9') || //
(Global::CommitWithFirstCandPunc.count(Global::wch) > 0);
/* Only clear namedpipe data if keycode is space or number, for better performance */
if (!isSpaceOrNumber)
{
return;
}
if (!hFromServerPipe || hFromServerPipe == INVALID_HANDLE_VALUE) // Try to reconnect
{
hFromServerPipe = CreateFile( //
FANY_IME_TO_TSF_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hFromServerPipe == INVALID_HANDLE_VALUE)
{
// TODO: Log
return;
}
else
{
// TODO: Log
}
}
DWORD bytesAvailable = 0;
DWORD bytesRead = 0;
while (true)
{
BOOL peekOk = PeekNamedPipe( //
hFromServerPipe, //
nullptr, //
0, //
nullptr, //
&bytesAvailable, //
nullptr //
);
if (!peekOk || bytesAvailable == 0)
{
break; // no more data or pipe error
}
BOOL readOk = ReadFile( //
hFromServerPipe, //
&namedpipeDataFromServer, //
sizeof(namedpipeDataFromServer), //
&bytesRead, //
NULL //
);
if (!readOk || bytesRead == 0)
{
break;
}
}
}
/**
* @brief Try to read selected candiate string data from server pipe with timeout
*
* @return struct FanyImeNamedpipeDataToTsf*
*/
struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithTimeout()
{
std::pair<UINT, std::wstring> ret = {0, L""};
int timeoutMs = 10; // Default timeout 10ms
int intervalMs = 1; // Default interval 1ms
if (!hFromServerPipe || hFromServerPipe == INVALID_HANDLE_VALUE) // Try to reconnect
{
hFromServerPipe = CreateFile( //
FANY_IME_TO_TSF_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hFromServerPipe == INVALID_HANDLE_VALUE)
{
// TODO: Log
namedpipeDataFromServer.msg_type = Global::DataFromServerMsgType::Normal;
// wcscpy_s(namedpipeDataFromServer.candidate_string, L"PipeOpenError");
wcscpy_s(namedpipeDataFromServer.candidate_string, L"X");
return &namedpipeDataFromServer;
}
else
{
// TODO: Log
}
}
DWORD bytesAvailable = 0;
int waited = 0;
while (waited < timeoutMs)
{
// TODO: Do not log
#ifdef FANY_DEBUG
OutputDebugString(fmt::format(L"current waited: {}", waited).c_str());
#endif
if (PeekNamedPipe(hFromServerPipe, nullptr, 0, nullptr, &bytesAvailable, nullptr) && bytesAvailable > 0)
{
auto ret = ReadDataFromServerViaNamedPipe();
#ifdef FANY_DEBUG
OutputDebugString(fmt::format(L"PeekNamedPipe: {}", waited).c_str());
#endif
return ret;
}
Sleep(intervalMs); // TODO: Maybe could use less time
waited += intervalMs;
}
/* Error handling: 必须将 hFromServerPipe 置为无效,否则将留下脏句柄,导致有些情况下无法再次连接 */
CloseHandle(hFromServerPipe);
hFromServerPipe = INVALID_HANDLE_VALUE;
namedpipeDataFromServer.msg_type = Global::DataFromServerMsgType::Normal;
// Pipe timeout error
wcscpy_s(namedpipeDataFromServer.candidate_string, L"T");
return &namedpipeDataFromServer;
}
/**
* @brief Read data sent by server
*
* TODO: Cancel when time exceed, we should set a timeout
*
* @return struct FanyImeNamedpipeDataToTsf*
*/
struct FanyImeNamedpipeDataToTsf *ReadDataFromServerViaNamedPipe()
{
if (!hFromServerPipe || hFromServerPipe == INVALID_HANDLE_VALUE) // Try to reconnect
{
hFromServerPipe = CreateFile( //
FANY_IME_TO_TSF_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (hFromServerPipe == INVALID_HANDLE_VALUE)
{
// TODO: Log
namedpipeDataFromServer.msg_type = 0;
// wcscpy_s(namedpipeDataFromServer.candidate_string, L"PipeOpenError");
wcscpy_s(namedpipeDataFromServer.candidate_string, L"X");
return &namedpipeDataFromServer;
}
else
{
// TODO: Log
}
}
DWORD bytesRead = 0;
BOOL readResult = ReadFile( //
hFromServerPipe, //
&namedpipeDataFromServer, //
sizeof(namedpipeDataFromServer), //
&bytesRead, //
NULL //
);
if (!readResult || bytesRead == 0) // Disconnected or error
{
// TODO: Log
}
else
{
#ifdef FANY_DEBUG
OutputDebugString(namedpipeDataFromServer.candidate_string);
#endif
return &namedpipeDataFromServer;
}
namedpipeDataFromServer.msg_type = 0;
wcscpy_s(namedpipeDataFromServer.candidate_string, L"ReadDataError");
return &namedpipeDataFromServer;
}
/**
* @brief 即用即断,不要求一直连接,但是,要让 Server 端的管道一直在 connect 状态阻塞住等待连接
*
* @param pipeData
*/
void SendToAuxNamedpipe(std::wstring pipeData)
{
HANDLE hAuxPipe = CreateFileW( //
FANY_IME_AUX_NAMED_PIPE, //
GENERIC_READ | GENERIC_WRITE, //
0, //
nullptr, //
OPEN_EXISTING, //
0, //
nullptr //
);
if (!hAuxPipe || hAuxPipe == INVALID_HANDLE_VALUE)
{
#ifdef FANY_DEBUG
OutputDebugString(fmt::format(L"SendToAuxNamedpipe: PipeOpenError").c_str());
#endif
return;
}
DWORD bytesWritten = 0;
BOOL ret = WriteFile( //
hAuxPipe, //
pipeData.c_str(), //
pipeData.length() * sizeof(wchar_t), //
&bytesWritten, //
NULL //
);
if (!ret || bytesWritten != pipeData.length() * sizeof(wchar_t))
{
// TODO: Error handling
}
CloseHandle(hAuxPipe);
}
/**
* event_type
* 0: FanyImeKeyEvent
* 1: FanyHideCandidateWndEvent
* 2: FanyShowCandidateWndEvent
* 3: FanyMoveCandidateWndEvent
*/
int SendKeyEventToUIProcessViaNamedPipe()
{
namedpipeData.event_type = 0;
SendToNamedpipe();
return 0;
}
int SendHideCandidateWndEventToUIProcessViaNamedPipe()
{
namedpipeData.event_type = 1;
SendToNamedpipe();
return 0;
}
int SendShowCandidateWndEventToUIProcessViaNamedPipe()
{
namedpipeData.event_type = 2;
SendToNamedpipe();
return 0;
}
int SendMoveCandidateWndEventToUIProcessViaNamedPipe()
{
namedpipeData.event_type = 3;
SendToNamedpipe();
return 0;
}
int SendLangbarRightClickEventToUIProcessViaNamedPipe(const RECT *prcArea)
{
namedpipeData.event_type = 4;
/* 利用其他的字段,把图标的坐标传递过去 */
namedpipeData.point[0] = prcArea->left;
namedpipeData.point[1] = prcArea->top;
namedpipeData.keycode = prcArea->right;
namedpipeData.modifiers_down = prcArea->bottom;
SendToNamedpipe();
return 0;
}
int SendIMEActivationEventToUIProcessViaNamedPipe()
{
namedpipeData.event_type = 5;
SendToNamedpipe();
return 0;
}
int SendIMEDeactivationEventToUIProcessViaNamedPipe()
{
namedpipeData.event_type = 6;
SendToNamedpipe();
return 0;
}
int SendIMESwitchEventToUIProcessViaNamedPipe(UINT uImeStatus)
{
namedpipeData.event_type = 7;
/* 利用其他的字段,把 IME 的中英状态传递过去 */
namedpipeData.keycode = uImeStatus;
SendToNamedpipe();
return 0;
}