feat(master): blocked IME incorrect switching mode
This commit is contained in:
@ -3,14 +3,18 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <psapi.h>
|
#include <psapi.h>
|
||||||
|
#include <imm.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#pragma comment(lib, "psapi.lib")
|
#pragma comment(lib, "psapi.lib")
|
||||||
|
#pragma comment(lib, "imm32.lib")
|
||||||
|
|
||||||
#pragma warning(disable: 4996)
|
#pragma warning(disable: 4996)
|
||||||
|
|
||||||
@ -23,14 +27,142 @@ bool GShouldExit = false;
|
|||||||
|
|
||||||
void PrintLog(const std::string& Text);
|
void PrintLog(const std::string& Text);
|
||||||
|
|
||||||
void CALLBACK FocusEventHookProc(HWINEVENTHOOK, DWORD Event, HWND Window, LONG, LONG, DWORD, DWORD);
|
void Tick()
|
||||||
|
|
||||||
LRESULT CALLBACK MainWindowProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam);
|
|
||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int)
|
|
||||||
{
|
{
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
// ReSharper disable CppInconsistentNaming
|
||||||
|
// ReSharper disable CppClangTidyPerformanceEnumSize
|
||||||
|
|
||||||
|
enum { IMC_GETCONVERSIONMODE = 0x0001 };
|
||||||
|
enum { IMC_SETCONVERSIONMODE = 0x0002 };
|
||||||
|
enum { IMC_GETOPENSTATUS = 0x0005 };
|
||||||
|
|
||||||
|
// ReSharper restore CppInconsistentNaming
|
||||||
|
// ReSharper restore CppClangTidyPerformanceEnumSize
|
||||||
|
|
||||||
|
enum class EIMEConversionMode : std::uint8_t
|
||||||
|
{
|
||||||
|
Default,
|
||||||
|
English,
|
||||||
|
Chinese,
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::map<std::string, EIMEConversionMode> CachedIMEMode;
|
||||||
|
|
||||||
|
static HWND LastWindow = nullptr;
|
||||||
|
|
||||||
|
static std::string CachedProcess;
|
||||||
|
|
||||||
|
if (LastWindow != nullptr)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (CachedIMEMode.contains(CachedProcess) && CachedIMEMode[CachedProcess] == EIMEConversionMode::Default) break;
|
||||||
|
|
||||||
|
HKL KeyboardLayout = GetKeyboardLayout(GetWindowThreadProcessId(LastWindow, nullptr));
|
||||||
|
|
||||||
|
LANGID LanguageID = LOWORD(KeyboardLayout);
|
||||||
|
|
||||||
|
if (PRIMARYLANGID(LanguageID) != LANG_CHINESE) break;
|
||||||
|
|
||||||
|
if (const HWND IMEWindow = ImmGetDefaultIMEWnd(LastWindow))
|
||||||
|
{
|
||||||
|
const LRESULT ConversionMode = SendMessage(IMEWindow, WM_IME_CONTROL, IMC_GETCONVERSIONMODE, 0);
|
||||||
|
const LRESULT OpenStatus = SendMessage(IMEWindow, WM_IME_CONTROL, IMC_GETOPENSTATUS, 0);
|
||||||
|
|
||||||
|
if (!OpenStatus) break;
|
||||||
|
|
||||||
|
const EIMEConversionMode CurrentMode =
|
||||||
|
ConversionMode == 0x0000 ? EIMEConversionMode::English :
|
||||||
|
ConversionMode == 0x0401 ? EIMEConversionMode::Chinese : EIMEConversionMode::Default;
|
||||||
|
|
||||||
|
if (CurrentMode == EIMEConversionMode::Default) break;
|
||||||
|
|
||||||
|
if (!CachedIMEMode.contains(CachedProcess) || CachedIMEMode[CachedProcess] != CurrentMode)
|
||||||
|
{
|
||||||
|
CachedIMEMode[CachedProcess] = CurrentMode;
|
||||||
|
|
||||||
|
PrintLog("Update the conversion mode cached for process '" + CachedProcess + "' to " +
|
||||||
|
(CurrentMode == EIMEConversionMode::English ? "English" :
|
||||||
|
CurrentMode == EIMEConversionMode::Chinese ? "Chinese" : "Default"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const HWND ForegroundWindow = GetForegroundWindow();
|
||||||
|
|
||||||
|
if (ForegroundWindow == LastWindow) return;
|
||||||
|
|
||||||
|
if (ForegroundWindow == nullptr)
|
||||||
|
{
|
||||||
|
LastWindow = nullptr;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
DWORD PID = 0;
|
||||||
|
|
||||||
|
GetWindowThreadProcessId(ForegroundWindow, &PID);
|
||||||
|
|
||||||
|
const HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID);
|
||||||
|
|
||||||
|
if (ProcessHandle == nullptr) break;
|
||||||
|
|
||||||
|
char Buffer[MAX_PATH] = { };
|
||||||
|
|
||||||
|
if (GetModuleBaseName(ProcessHandle, nullptr, Buffer, sizeof(Buffer)) == 0)
|
||||||
|
{
|
||||||
|
CloseHandle(ProcessHandle);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(ProcessHandle);
|
||||||
|
|
||||||
|
LastWindow = ForegroundWindow;
|
||||||
|
|
||||||
|
CachedProcess = Buffer;
|
||||||
|
|
||||||
|
if (!CachedIMEMode.contains(CachedProcess) || CachedIMEMode[CachedProcess] == EIMEConversionMode::Default) break;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(50ms);
|
||||||
|
|
||||||
|
if (const HWND IMEWindow = ImmGetDefaultIMEWnd(LastWindow))
|
||||||
|
{
|
||||||
|
const LRESULT ConversionMode = SendMessage(IMEWindow, WM_IME_CONTROL, IMC_GETCONVERSIONMODE, 0);
|
||||||
|
const LRESULT OpenStatus = SendMessage(IMEWindow, WM_IME_CONTROL, IMC_GETOPENSTATUS, 0);
|
||||||
|
|
||||||
|
if (!OpenStatus) break;
|
||||||
|
|
||||||
|
LPARAM TargetMode = CachedIMEMode[CachedProcess] == EIMEConversionMode::Chinese ? 0x0401 : 0x0000;
|
||||||
|
|
||||||
|
if (ConversionMode != TargetMode)
|
||||||
|
{
|
||||||
|
if (SendMessage(IMEWindow, WM_IME_CONTROL, IMC_SETCONVERSIONMODE, TargetMode) == 0)
|
||||||
|
{
|
||||||
|
PrintLog("Successfully set conversion mode for process '" + CachedProcess + "'");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrintLog("Failed to set conversion mode for process '" + CachedProcess + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK MainWindowProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam);
|
||||||
|
|
||||||
|
void CALLBACK FocusEventHookProc(HWINEVENTHOOK, DWORD, HWND, LONG, LONG, DWORD, DWORD) { Tick(); }
|
||||||
|
|
||||||
|
int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int)
|
||||||
|
{
|
||||||
const HANDLE Mutex = CreateMutex(nullptr, TRUE, "FocusIME_SingleInstance_Mutex");
|
const HANDLE Mutex = CreateMutex(nullptr, TRUE, "FocusIME_SingleInstance_Mutex");
|
||||||
|
|
||||||
if (Mutex == nullptr)
|
if (Mutex == nullptr)
|
||||||
@ -150,7 +282,7 @@ int WINAPI WinMain(HINSTANCE Instance, HINSTANCE, LPSTR, int)
|
|||||||
|
|
||||||
while (!GShouldExit)
|
while (!GShouldExit)
|
||||||
{
|
{
|
||||||
BOOL bResult = GetMessage(&Message, nullptr, 0, 0);
|
const BOOL bResult = GetMessage(&Message, nullptr, 0, 0);
|
||||||
|
|
||||||
if (bResult > 0)
|
if (bResult > 0)
|
||||||
{
|
{
|
||||||
@ -209,9 +341,14 @@ LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPA
|
|||||||
{
|
{
|
||||||
static NOTIFYICONDATA NotifyIconData;
|
static NOTIFYICONDATA NotifyIconData;
|
||||||
|
|
||||||
constexpr UINT TrayIconMessage = WM_USER + 1;
|
// ReSharper disable CppInconsistentNaming
|
||||||
|
// ReSharper disable CppClangTidyPerformanceEnumSize
|
||||||
|
|
||||||
constexpr UINT ExitMessage = 1001;
|
enum { WM_TRAY_ICON = WM_USER + 1 };
|
||||||
|
enum { ID_TRAY_EXIT = 1001 };
|
||||||
|
|
||||||
|
// ReSharper restore CppInconsistentNaming
|
||||||
|
// ReSharper restore CppClangTidyPerformanceEnumSize
|
||||||
|
|
||||||
switch (Message)
|
switch (Message)
|
||||||
{
|
{
|
||||||
@ -223,7 +360,7 @@ LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPA
|
|||||||
NotifyIconData.hWnd = Window;
|
NotifyIconData.hWnd = Window;
|
||||||
NotifyIconData.uID = 1;
|
NotifyIconData.uID = 1;
|
||||||
NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||||
NotifyIconData.uCallbackMessage = TrayIconMessage;
|
NotifyIconData.uCallbackMessage = WM_TRAY_ICON;
|
||||||
|
|
||||||
NotifyIconData.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
|
NotifyIconData.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
|
||||||
|
|
||||||
@ -233,7 +370,7 @@ LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPA
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TrayIconMessage:
|
case WM_TRAY_ICON:
|
||||||
|
|
||||||
switch (LParam)
|
switch (LParam)
|
||||||
{
|
{
|
||||||
@ -246,9 +383,9 @@ LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPA
|
|||||||
|
|
||||||
if (const HMENU Menu = CreatePopupMenu())
|
if (const HMENU Menu = CreatePopupMenu())
|
||||||
{
|
{
|
||||||
InsertMenu(Menu, -1, MF_BYPOSITION | MF_STRING, ExitMessage, "Exit(&X)");
|
InsertMenu(Menu, -1, MF_BYPOSITION | MF_STRING, ID_TRAY_EXIT, "Exit(&X)");
|
||||||
|
|
||||||
SetMenuDefaultItem(Menu, ExitMessage, FALSE);
|
SetMenuDefaultItem(Menu, ID_TRAY_EXIT, FALSE);
|
||||||
|
|
||||||
SetForegroundWindow(Window);
|
SetForegroundWindow(Window);
|
||||||
|
|
||||||
@ -268,7 +405,7 @@ LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPA
|
|||||||
|
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
|
|
||||||
if (LOWORD(WParam) == ExitMessage)
|
if (LOWORD(WParam) == ID_TRAY_EXIT)
|
||||||
{
|
{
|
||||||
PrintLog("Exit command received from tray menu");
|
PrintLog("Exit command received from tray menu");
|
||||||
|
|
||||||
@ -293,59 +430,6 @@ LRESULT CALLBACK MainWindowProc(const HWND Window, const UINT Message, const WPA
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CALLBACK FocusEventHookProc(HWINEVENTHOOK, const DWORD Event, const HWND Window, LONG, LONG, DWORD, DWORD)
|
|
||||||
{
|
|
||||||
if (Event == EVENT_OBJECT_FOCUS && Window != nullptr)
|
|
||||||
{
|
|
||||||
std::string ProcessName;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
DWORD PID = 0;
|
|
||||||
|
|
||||||
GetWindowThreadProcessId(Window, &PID);
|
|
||||||
|
|
||||||
const HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID);
|
|
||||||
|
|
||||||
if (ProcessHandle == nullptr)
|
|
||||||
{
|
|
||||||
ProcessName = "Unknown";
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
char Buffer[MAX_PATH] = { };
|
|
||||||
|
|
||||||
if (GetModuleBaseName(ProcessHandle, nullptr, Buffer, sizeof(Buffer)) == 0)
|
|
||||||
{
|
|
||||||
CloseHandle(ProcessHandle);
|
|
||||||
|
|
||||||
ProcessName = "Unknown";
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(ProcessHandle);
|
|
||||||
|
|
||||||
ProcessName = Buffer;
|
|
||||||
}
|
|
||||||
while (false);
|
|
||||||
|
|
||||||
char Title[1024] = { };
|
|
||||||
|
|
||||||
GetWindowText(Window, Title, sizeof(Title));
|
|
||||||
|
|
||||||
std::string MessageBuffer = "Focus changed to: " + ProcessName;
|
|
||||||
|
|
||||||
if (strlen(Title) > 0)
|
|
||||||
{
|
|
||||||
MessageBuffer += " (" + std::string(Title) + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintLog(MessageBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintLog(const std::string& Text)
|
void PrintLog(const std::string& Text)
|
||||||
{
|
{
|
||||||
const auto Now = std::chrono::system_clock::now();
|
const auto Now = std::chrono::system_clock::now();
|
||||||
|
Reference in New Issue
Block a user