[KBSWITCH]

- Use strsafe functions
- Use Shell Hooks in dll for detecting keyboard layout switching
- Fix bug in loading kbsdll

* Detection switch keyboard layout is now working in Windovs, but does not work in ReactOS (WH_SHELL not work in win32k)

svn path=/trunk/; revision=72143
This commit is contained in:
Dmitry Chapyshev 2016-08-06 20:48:33 +00:00
parent a32f426a76
commit bc10137a10
3 changed files with 97 additions and 106 deletions

View file

@ -7,9 +7,10 @@
#include "../kbswitch.h" #include "../kbswitch.h"
HHOOK hKeyboardHook, hLangHook, hWinHook; HHOOK hWinHook = NULL;
HINSTANCE hInstance; HHOOK hShellHook = NULL;
HWND hKbSwitchWnd; HINSTANCE hInstance = NULL;
HWND hKbSwitchWnd = NULL;
static VOID static VOID
SendMessageToMainWnd(UINT Msg, WPARAM wParam, LPARAM lParam) SendMessageToMainWnd(UINT Msg, WPARAM wParam, LPARAM lParam)
@ -17,40 +18,6 @@ SendMessageToMainWnd(UINT Msg, WPARAM wParam, LPARAM lParam)
PostMessage(hKbSwitchWnd, Msg, wParam, lParam); PostMessage(hKbSwitchWnd, Msg, wParam, lParam);
} }
/* Not used yet */
LRESULT CALLBACK
KeyboardHookProc(int code, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
}
LRESULT CALLBACK
LangHookProc(int code, WPARAM wParam, LPARAM lParam)
{
PMSG msg;
msg = (PMSG) lParam;
switch (msg->message)
{
case WM_INPUTLANGCHANGEREQUEST:
{
SendMessageToMainWnd(WM_LANG_CHANGED, wParam, msg->lParam);
}
break;
case WM_HOTKEY:
{
if (msg->hwnd)
{
SendMessageToMainWnd(WM_LOAD_LAYOUT, (WPARAM)msg->hwnd, msg->lParam);
}
}
break;
}
return CallNextHookEx(hLangHook, code, wParam, lParam);
}
LRESULT CALLBACK LRESULT CALLBACK
WinHookProc(int code, WPARAM wParam, LPARAM lParam) WinHookProc(int code, WPARAM wParam, LPARAM lParam)
{ {
@ -69,18 +36,6 @@ WinHookProc(int code, WPARAM wParam, LPARAM lParam)
} }
} }
break; break;
case HCBT_CREATEWND:
{
RegisterHotKey((HWND)wParam, id, MOD_ALT, VK_F10);
}
break;
case HCBT_DESTROYWND:
{
UnregisterHotKey((HWND)wParam, id);
}
break;
} }
GlobalDeleteAtom(id); GlobalDeleteAtom(id);
@ -88,25 +43,40 @@ WinHookProc(int code, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(hWinHook, code, wParam, lParam); return CallNextHookEx(hWinHook, code, wParam, lParam);
} }
LRESULT CALLBACK
ShellHookProc(int code, WPARAM wParam, LPARAM lParam)
{
switch (code)
{
case HSHELL_LANGUAGE:
{
SendMessageToMainWnd(WM_LANG_CHANGED, wParam, lParam);
}
break;
}
return CallNextHookEx(hShellHook, code, wParam, lParam);
}
BOOL WINAPI BOOL WINAPI
KbSwitchSetHooks(VOID) KbSwitchSetHooks(VOID)
{ {
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc, hInstance, 0);
hLangHook = SetWindowsHookEx(WH_GETMESSAGE, LangHookProc, hInstance, 0);
hWinHook = SetWindowsHookEx(WH_CBT, WinHookProc, hInstance, 0); hWinHook = SetWindowsHookEx(WH_CBT, WinHookProc, hInstance, 0);
hShellHook = SetWindowsHookEx(WH_SHELL, ShellHookProc, hInstance, 0);
if ((hKeyboardHook)&&(hLangHook)&&(hWinHook)) if (!hWinHook || !hShellHook)
return TRUE; {
else
return FALSE; return FALSE;
}
return TRUE;
} }
VOID WINAPI VOID WINAPI
KbSwitchDeleteHooks(VOID) KbSwitchDeleteHooks(VOID)
{ {
if (hKeyboardHook) UnhookWindowsHookEx(hKeyboardHook);
if (hLangHook) UnhookWindowsHookEx(hLangHook);
if (hWinHook) UnhookWindowsHookEx(hWinHook); if (hWinHook) UnhookWindowsHookEx(hWinHook);
if (hShellHook) UnhookWindowsHookEx(hShellHook);
} }
BOOL WINAPI BOOL WINAPI
@ -117,10 +87,15 @@ DllMain(IN HINSTANCE hinstDLL,
switch (dwReason) switch (dwReason)
{ {
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
{
hInstance = hinstDLL; hInstance = hinstDLL;
hKbSwitchWnd = FindWindow(szKbSwitcherName, NULL); hKbSwitchWnd = FindWindow(szKbSwitcherName, NULL);
if (!hKbSwitchWnd) return FALSE; if (!hKbSwitchWnd)
break; {
return FALSE;
}
}
break;
} }
return TRUE; return TRUE;

View file

@ -10,15 +10,15 @@
#define WM_NOTIFYICONMSG (WM_USER + 248) #define WM_NOTIFYICONMSG (WM_USER + 248)
PROC KbSwitchSetHooks = NULL; PKBSWITCHSETHOOKS KbSwitchSetHooks = NULL;
PROC KbSwitchDeleteHooks = NULL; PKBSWITCHDELETEHOOKS KbSwitchDeleteHooks = NULL;
static BOOL static BOOL
GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID); GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID, SIZE_T LCIDLength);
static BOOL static BOOL
GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName); GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName, SIZE_T NameLength);
HINSTANCE hInst; HINSTANCE hInst;
HANDLE hProcessHeap; HANDLE hProcessHeap;
@ -41,9 +41,9 @@ CreateTrayIcon(LPTSTR szLCID)
if (GetLocaleInfo(lId, if (GetLocaleInfo(lId,
LOCALE_SISO639LANGNAME, LOCALE_SISO639LANGNAME,
szBuf, szBuf,
sizeof(szBuf) / sizeof(TCHAR)) == 0) ARRAYSIZE(szBuf)) == 0)
{ {
lstrcpy(szBuf, _T("??")); StringCchCopy(szBuf, ARRAYSIZE(szBuf), _T("??"));
} }
hdcsrc = GetDC(NULL); hdcsrc = GetDC(NULL);
@ -103,9 +103,10 @@ AddTrayIcon(HWND hwnd)
TCHAR szLCID[CCH_LAYOUT_ID + 1]; TCHAR szLCID[CCH_LAYOUT_ID + 1];
TCHAR szName[MAX_PATH]; TCHAR szName[MAX_PATH];
GetLayoutID(_T("1"), szLCID); GetLayoutID(_T("1"), szLCID, ARRAYSIZE(szLCID));
GetLayoutName(_T("1"), szName); GetLayoutName(_T("1"), szName, ARRAYSIZE(szName));
memset(&tnid, 0, sizeof(tnid));
tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.cbSize = sizeof(NOTIFYICONDATA);
tnid.hWnd = hwnd; tnid.hWnd = hwnd;
tnid.uID = 1; tnid.uID = 1;
@ -113,7 +114,7 @@ AddTrayIcon(HWND hwnd)
tnid.uCallbackMessage = WM_NOTIFYICONMSG; tnid.uCallbackMessage = WM_NOTIFYICONMSG;
tnid.hIcon = CreateTrayIcon(szLCID); tnid.hIcon = CreateTrayIcon(szLCID);
lstrcpyn(tnid.szTip, szName, sizeof(tnid.szTip) / sizeof(TCHAR)); StringCchCopy(tnid.szTip, ARRAYSIZE(tnid.szTip), szName);
Shell_NotifyIcon(NIM_ADD, &tnid); Shell_NotifyIcon(NIM_ADD, &tnid);
} }
@ -123,6 +124,7 @@ DelTrayIcon(HWND hwnd)
{ {
NOTIFYICONDATA tnid; NOTIFYICONDATA tnid;
memset(&tnid, 0, sizeof(tnid));
tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.cbSize = sizeof(NOTIFYICONDATA);
tnid.hWnd = hwnd; tnid.hWnd = hwnd;
tnid.uID = 1; tnid.uID = 1;
@ -135,6 +137,7 @@ UpdateTrayIcon(HWND hwnd, LPTSTR szLCID, LPTSTR szName)
{ {
NOTIFYICONDATA tnid; NOTIFYICONDATA tnid;
memset(&tnid, 0, sizeof(tnid));
tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.cbSize = sizeof(NOTIFYICONDATA);
tnid.hWnd = hwnd; tnid.hWnd = hwnd;
tnid.uID = 1; tnid.uID = 1;
@ -142,13 +145,13 @@ UpdateTrayIcon(HWND hwnd, LPTSTR szLCID, LPTSTR szName)
tnid.uCallbackMessage = WM_NOTIFYICONMSG; tnid.uCallbackMessage = WM_NOTIFYICONMSG;
tnid.hIcon = CreateTrayIcon(szLCID); tnid.hIcon = CreateTrayIcon(szLCID);
lstrcpyn(tnid.szTip, szName, sizeof(tnid.szTip) / sizeof(TCHAR)); StringCchCopy(tnid.szTip, ARRAYSIZE(tnid.szTip), szName);
Shell_NotifyIcon(NIM_MODIFY, &tnid); Shell_NotifyIcon(NIM_MODIFY, &tnid);
} }
static BOOL static BOOL
GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID) GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID, SIZE_T LCIDLength)
{ {
DWORD dwBufLen; DWORD dwBufLen;
DWORD dwRes; DWORD dwRes;
@ -178,7 +181,7 @@ GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID)
if (RegQueryValueEx(hKey, szTempLCID, NULL, NULL, (LPBYTE)szLCID, &dwBufLen) != ERROR_SUCCESS) if (RegQueryValueEx(hKey, szTempLCID, NULL, NULL, (LPBYTE)szLCID, &dwBufLen) != ERROR_SUCCESS)
{ {
// No substitute found, then use the old LCID // No substitute found, then use the old LCID
lstrcpy(szLCID, szTempLCID); StringCchCopy(szLCID, LCIDLength, szTempLCID);
} }
RegCloseKey(hKey); RegCloseKey(hKey);
@ -186,24 +189,24 @@ GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID)
else else
{ {
// Substitutes key couldn't be opened, so use the old LCID // Substitutes key couldn't be opened, so use the old LCID
lstrcpy(szLCID, szTempLCID); StringCchCopy(szLCID, LCIDLength, szTempLCID);
} }
return TRUE; return TRUE;
} }
VOID VOID
GetLayoutIDByHkl(HKL hKl, LPTSTR szLayoutID) GetLayoutIDByHkl(HKL hKl, LPTSTR szLayoutID, SIZE_T LayoutIDLength)
{ {
/* /*
FIXME!!! This way of getting layout ID incorrect! FIXME!!! This way of getting layout ID incorrect!
This will not work correctly for 0001040a, 00010410, etc This will not work correctly for 0001040a, 00010410, etc
*/ */
wsprintf(szLayoutID, _T("%08x"), LOWORD(hKl)); StringCchPrintf(szLayoutID, LayoutIDLength, _T("%08x"), LOWORD(hKl));
} }
static BOOL static BOOL
GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName) GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName, SIZE_T NameLength)
{ {
HKEY hKey; HKEY hKey;
DWORD dwBufLen; DWORD dwBufLen;
@ -212,20 +215,22 @@ GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName)
HANDLE hLib; HANDLE hLib;
UINT i, j, k; UINT i, j, k;
if(!GetLayoutID(szLayoutNum, szLCID)) if (!GetLayoutID(szLayoutNum, szLCID, ARRAYSIZE(szLCID)))
return FALSE; return FALSE;
wsprintf(szBuf, _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID); StringCchPrintf(szBuf, ARRAYSIZE(szBuf), _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)szBuf, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)szBuf, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{ {
dwBufLen = sizeof(szBuf); dwBufLen = sizeof(szDispName);
if (RegQueryValueEx(hKey, _T("Layout Display Name"), NULL, NULL, (LPBYTE)szDispName, &dwBufLen) == ERROR_SUCCESS) if (RegQueryValueEx(hKey, _T("Layout Display Name"), NULL, NULL, (LPBYTE)szDispName, &dwBufLen) == ERROR_SUCCESS)
{ {
if (szDispName[0] == '@') if (szDispName[0] == '@')
{ {
for (i = 0; i < _tcslen(szDispName); i++) size_t len = _tcslen(szDispName);
for (i = 0; i < len; i++)
{ {
if ((szDispName[i] == ',') && (szDispName[i + 1] == '-')) if ((szDispName[i] == ',') && (szDispName[i + 1] == '-'))
{ {
@ -239,14 +244,14 @@ GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName)
else szDispName[i] = szDispName[i + 1]; else szDispName[i] = szDispName[i + 1];
} }
if (ExpandEnvironmentStrings(szDispName, szPath, MAX_PATH)) if (ExpandEnvironmentStrings(szDispName, szPath, ARRAYSIZE(szPath)))
{ {
hLib = LoadLibrary(szPath); hLib = LoadLibrary(szPath);
if (hLib) if (hLib)
{ {
if (LoadString(hLib, _ttoi(szIndex), szPath, sizeof(szPath) / sizeof(TCHAR)) != 0) if (LoadString(hLib, _ttoi(szIndex), szPath, ARRAYSIZE(szPath)) != 0)
{ {
_tcscpy(szName, szPath); StringCchCopy(szName, NameLength, szPath);
RegCloseKey(hKey); RegCloseKey(hKey);
FreeLibrary(hLib); FreeLibrary(hLib);
return TRUE; return TRUE;
@ -257,7 +262,7 @@ GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName)
} }
} }
dwBufLen = sizeof(szBuf); dwBufLen = NameLength * sizeof(TCHAR);
if (RegQueryValueEx(hKey, _T("Layout Text"), NULL, NULL, (LPBYTE)szName, &dwBufLen) == ERROR_SUCCESS) if (RegQueryValueEx(hKey, _T("Layout Text"), NULL, NULL, (LPBYTE)szName, &dwBufLen) == ERROR_SUCCESS)
{ {
@ -287,10 +292,10 @@ ActivateLayout(HWND hwnd, ULONG uLayoutNum)
TCHAR szLangName[MAX_PATH]; TCHAR szLangName[MAX_PATH];
_ultot(uLayoutNum, szLayoutNum, 10); _ultot(uLayoutNum, szLayoutNum, 10);
GetLayoutID(szLayoutNum, szLCID); GetLayoutID(szLayoutNum, szLCID, ARRAYSIZE(szLCID));
// Switch to the new keyboard layout // Switch to the new keyboard layout
GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, sizeof(szLangName) / sizeof(TCHAR)); GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, ARRAYSIZE(szLangName));
UpdateTrayIcon(hwnd, szLCID, szLangName); UpdateTrayIcon(hwnd, szLCID, szLangName);
hKl = LoadKeyboardLayout(szLCID, KLF_ACTIVATE); hKl = LoadKeyboardLayout(szLCID, KLF_ACTIVATE);
@ -313,19 +318,19 @@ BuildLeftPopupMenu(VOID)
// Add the keyboard layouts to the popup menu // Add the keyboard layouts to the popup menu
if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{ {
for(dwIndex = 0; ; dwIndex++) for (dwIndex = 0; ; dwIndex++)
{ {
dwSize = sizeof(szLayoutNum); dwSize = sizeof(szLayoutNum);
if(RegEnumValue(hKey, dwIndex, szLayoutNum, &dwSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) if (RegEnumValue(hKey, dwIndex, szLayoutNum, &dwSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
break; break;
if(!GetLayoutName(szLayoutNum, szName)) if (!GetLayoutName(szLayoutNum, szName, ARRAYSIZE(szName)))
break; break;
AppendMenu(hMenu, MF_STRING, _ttoi(szLayoutNum), szName); AppendMenu(hMenu, MF_STRING, _ttoi(szLayoutNum), szName);
} }
(void)CheckMenuItem(hMenu, ulCurrentLayoutNum, MF_CHECKED); CheckMenuItem(hMenu, ulCurrentLayoutNum, MF_CHECKED);
RegCloseKey(hKey); RegCloseKey(hKey);
} }
@ -337,13 +342,18 @@ BOOL
SetHooks(VOID) SetHooks(VOID)
{ {
hDllLib = LoadLibrary(_T("kbsdll.dll")); hDllLib = LoadLibrary(_T("kbsdll.dll"));
if (!hDllLib) return FALSE; if (!hDllLib)
{
KbSwitchSetHooks = (PROC) GetProcAddress(hDllLib, MAKEINTRESOURCEA(1));
KbSwitchDeleteHooks = (PROC) GetProcAddress(hDllLib, MAKEINTRESOURCEA(2));
if ((KbSwitchSetHooks == NULL)||(KbSwitchDeleteHooks == NULL))
return FALSE; return FALSE;
}
KbSwitchSetHooks = (PKBSWITCHSETHOOKS) GetProcAddress(hDllLib, "KbSwitchSetHooks");
KbSwitchDeleteHooks = (PKBSWITCHDELETEHOOKS) GetProcAddress(hDllLib, "KbSwitchDeleteHooks");
if (KbSwitchSetHooks == NULL || KbSwitchDeleteHooks == NULL)
{
return FALSE;
}
return KbSwitchSetHooks(); return KbSwitchSetHooks();
} }
@ -362,21 +372,21 @@ GetNextLayout(VOID)
ULONG Ret = ulCurrentLayoutNum; ULONG Ret = ulCurrentLayoutNum;
_ultot(ulCurrentLayoutNum, szLayoutNum, 10); _ultot(ulCurrentLayoutNum, szLayoutNum, 10);
if (!GetLayoutID(szLayoutNum, szLayoutID)) if (!GetLayoutID(szLayoutNum, szLayoutID, ARRAYSIZE(szLayoutID)))
{ {
return -1; return -1;
} }
_ultot(Ret + 1, szLayoutNum, 10); _ultot(Ret + 1, szLayoutNum, 10);
if (GetLayoutID(szLayoutNum, szLayoutID)) if (GetLayoutID(szLayoutNum, szLayoutID, ARRAYSIZE(szLayoutID)))
{ {
return (Ret + 1); return (Ret + 1);
} }
else else
{ {
_ultot(Ret - 1, szLayoutNum, 10); _ultot(Ret - 1, szLayoutNum, 10);
if (GetLayoutID(szLayoutNum, szLayoutID)) if (GetLayoutID(szLayoutNum, szLayoutID, ARRAYSIZE(szLayoutID)))
return (Ret - 1); return (Ret - 1);
else else
return -1; return -1;
@ -408,8 +418,8 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
case WM_LANG_CHANGED: case WM_LANG_CHANGED:
{ {
GetLayoutIDByHkl((HKL)lParam, szLCID); GetLayoutIDByHkl((HKL)lParam, szLCID, ARRAYSIZE(szLCID));
GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, sizeof(szLangName) / sizeof(TCHAR)); GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, ARRAYSIZE(szLangName));
UpdateTrayIcon(hwnd, szLCID, szLangName); UpdateTrayIcon(hwnd, szLCID, szLangName);
return 0; return 0;
@ -424,8 +434,8 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
case WM_WINDOW_ACTIVATE: case WM_WINDOW_ACTIVATE:
{ {
GetLayoutIDByHkl(GetKeyboardLayout(GetWindowThreadProcessId((HWND)wParam, 0)), szLCID); GetLayoutIDByHkl(GetKeyboardLayout(GetWindowThreadProcessId((HWND)wParam, 0)), szLCID, ARRAYSIZE(szLCID));
GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, sizeof(szLangName) / sizeof(TCHAR)); GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, ARRAYSIZE(szLangName));
UpdateTrayIcon(hwnd, szLCID, szLangName); UpdateTrayIcon(hwnd, szLCID, szLangName);
return 0; return 0;
@ -526,11 +536,11 @@ _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdSh
switch (GetUserDefaultUILanguage()) switch (GetUserDefaultUILanguage())
{ {
case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
SetProcessDefaultLayout(LAYOUT_RTL); SetProcessDefaultLayout(LAYOUT_RTL);
break; break;
default: default:
break; break;
} }
hMutex = CreateMutex(NULL, FALSE, szKbSwitcherName); hMutex = CreateMutex(NULL, FALSE, szKbSwitcherName);

View file

@ -1,3 +1,5 @@
#pragma once
#include <stdarg.h> #include <stdarg.h>
#include <windef.h> #include <windef.h>
#include <winbase.h> #include <winbase.h>
@ -7,6 +9,7 @@
#include <wingdi.h> #include <wingdi.h>
#include <shellapi.h> #include <shellapi.h>
#include <tchar.h> #include <tchar.h>
#include <strsafe.h>
#include "resource.h" #include "resource.h"
@ -21,4 +24,7 @@
#define WM_WINDOW_ACTIVATE (WM_USER + 10300) #define WM_WINDOW_ACTIVATE (WM_USER + 10300)
#define WM_LOAD_LAYOUT (WM_USER + 10400) #define WM_LOAD_LAYOUT (WM_USER + 10400)
typedef BOOL (WINAPI *PKBSWITCHSETHOOKS) (VOID);
typedef VOID (WINAPI *PKBSWITCHDELETEHOOKS) (VOID);
TCHAR szKbSwitcherName[] = _T("kbswitcher"); TCHAR szKbSwitcherName[] = _T("kbswitcher");