1 /* 2 * PROJECT: Keyboard Layout Switcher 3 * FILE: base/applications/kbswitch/kbswitch.c 4 * PURPOSE: Switching Keyboard Layouts 5 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org) 6 * Colin Finck (mail@colinfinck.de) 7 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 8 */ 9 10 #include "kbswitch.h" 11 12 #define WM_NOTIFYICONMSG (WM_USER + 248) 13 #define CX_ICON 16 14 #define CY_ICON 16 15 16 PKBSWITCHSETHOOKS KbSwitchSetHooks = NULL; 17 PKBSWITCHDELETEHOOKS KbSwitchDeleteHooks = NULL; 18 UINT ShellHookMessage = 0; 19 20 HINSTANCE hInst; 21 HANDLE hProcessHeap; 22 HMODULE g_hHookDLL = NULL; 23 ULONG ulCurrentLayoutNum = 1; 24 HICON g_hTrayIcon = NULL; 25 26 static BOOL 27 GetLayoutID(LPCTSTR szLayoutNum, LPTSTR szLCID, SIZE_T LCIDLength) 28 { 29 DWORD dwBufLen, dwRes; 30 HKEY hKey; 31 TCHAR szTempLCID[CCH_LAYOUT_ID + 1]; 32 33 /* Get the Layout ID */ 34 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE, 35 &hKey) == ERROR_SUCCESS) 36 { 37 dwBufLen = sizeof(szTempLCID); 38 dwRes = RegQueryValueEx(hKey, szLayoutNum, NULL, NULL, (LPBYTE)szTempLCID, &dwBufLen); 39 if (dwRes != ERROR_SUCCESS) 40 { 41 RegCloseKey(hKey); 42 return FALSE; 43 } 44 45 RegCloseKey(hKey); 46 } 47 48 /* Look for a substitute of this layout */ 49 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Substitutes"), 0, 50 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 51 { 52 dwBufLen = sizeof(szTempLCID); 53 if (RegQueryValueEx(hKey, szTempLCID, NULL, NULL, (LPBYTE)szLCID, &dwBufLen) != ERROR_SUCCESS) 54 { 55 /* No substitute found, then use the old LCID */ 56 StringCchCopy(szLCID, LCIDLength, szTempLCID); 57 } 58 59 RegCloseKey(hKey); 60 } 61 else 62 { 63 /* Substitutes key couldn't be opened, so use the old LCID */ 64 StringCchCopy(szLCID, LCIDLength, szTempLCID); 65 } 66 67 return TRUE; 68 } 69 70 static BOOL 71 GetLayoutName(LPCTSTR szLayoutNum, LPTSTR szName, SIZE_T NameLength) 72 { 73 HKEY hKey; 74 DWORD dwBufLen; 75 TCHAR szBuf[MAX_PATH], szDispName[MAX_PATH], szIndex[MAX_PATH], szPath[MAX_PATH]; 76 TCHAR szLCID[CCH_LAYOUT_ID + 1]; 77 HANDLE hLib; 78 UINT i, j, k; 79 80 if (!GetLayoutID(szLayoutNum, szLCID, ARRAYSIZE(szLCID))) 81 return FALSE; 82 83 StringCchPrintf(szBuf, ARRAYSIZE(szBuf), 84 _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID); 85 86 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuf, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) 87 { 88 return FALSE; 89 } 90 91 /* Use "Layout Display Name" value as an entry name if possible */ 92 dwBufLen = sizeof(szDispName); 93 if (RegQueryValueEx(hKey, _T("Layout Display Name"), NULL, NULL, 94 (LPBYTE)szDispName, &dwBufLen) == ERROR_SUCCESS) 95 { 96 /* FIXME: Use shlwapi!SHLoadRegUIStringW instead if it was implemented */ 97 if (szDispName[0] == '@') 98 { 99 size_t len = _tcslen(szDispName); 100 101 for (i = 0; i < len; i++) 102 { 103 if ((szDispName[i] == ',') && (szDispName[i + 1] == '-')) 104 { 105 for (j = i + 2, k = 0; j < _tcslen(szDispName)+1; j++, k++) 106 { 107 szIndex[k] = szDispName[j]; 108 } 109 szDispName[i - 1] = '\0'; 110 break; 111 } 112 else szDispName[i] = szDispName[i + 1]; 113 } 114 115 if (ExpandEnvironmentStrings(szDispName, szPath, ARRAYSIZE(szPath))) 116 { 117 hLib = LoadLibrary(szPath); 118 if (hLib) 119 { 120 if (LoadString(hLib, _ttoi(szIndex), szPath, ARRAYSIZE(szPath))) 121 { 122 StringCchCopy(szName, NameLength, szPath); 123 RegCloseKey(hKey); 124 FreeLibrary(hLib); 125 return TRUE; 126 } 127 FreeLibrary(hLib); 128 } 129 } 130 } 131 } 132 133 /* Otherwise, use "Layout Text" value as an entry name */ 134 dwBufLen = NameLength * sizeof(TCHAR); 135 if (RegQueryValueEx(hKey, _T("Layout Text"), NULL, NULL, 136 (LPBYTE)szName, &dwBufLen) == ERROR_SUCCESS) 137 { 138 RegCloseKey(hKey); 139 return TRUE; 140 } 141 142 RegCloseKey(hKey); 143 return FALSE; 144 } 145 146 static HICON 147 CreateTrayIcon(LPTSTR szLCID) 148 { 149 LANGID LangID; 150 TCHAR szBuf[3]; 151 HDC hdc; 152 HBITMAP hbmColor, hbmMono, hBmpOld; 153 RECT rect; 154 HFONT hFontOld, hFont; 155 ICONINFO IconInfo; 156 HICON hIcon; 157 LOGFONT lf; 158 159 /* Getting "EN", "FR", etc. from English, French, ... */ 160 LangID = (LANGID)_tcstoul(szLCID, NULL, 16); 161 if (!GetLocaleInfo(LangID, LOCALE_SISO639LANGNAME, szBuf, ARRAYSIZE(szBuf))) 162 { 163 StringCchCopy(szBuf, ARRAYSIZE(szBuf), _T("??")); 164 } 165 CharUpper(szBuf); 166 167 /* Create hdc, hbmColor and hbmMono */ 168 hdc = CreateCompatibleDC(NULL); 169 hbmColor = CreateCompatibleBitmap(hdc, CX_ICON, CY_ICON); 170 hbmMono = CreateBitmap(CX_ICON, CY_ICON, 1, 1, NULL); 171 172 /* Create a font */ 173 ZeroMemory(&lf, sizeof(lf)); 174 lf.lfHeight = -11; 175 lf.lfCharSet = ANSI_CHARSET; 176 lf.lfWeight = FW_NORMAL; 177 StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), _T("Tahoma")); 178 hFont = CreateFontIndirect(&lf); 179 180 /* Checking NULL */ 181 if (!hdc || !hbmColor || !hbmMono || !hFont) 182 { 183 if (hdc) 184 DeleteDC(hdc); 185 if (hbmColor) 186 DeleteObject(hbmColor); 187 if (hbmMono) 188 DeleteObject(hbmMono); 189 if (hFont) 190 DeleteObject(hFont); 191 return NULL; 192 } 193 194 SetRect(&rect, 0, 0, CX_ICON, CY_ICON); 195 196 /* Draw hbmColor */ 197 hBmpOld = SelectObject(hdc, hbmColor); 198 SetDCBrushColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); 199 FillRect(hdc, &rect, (HBRUSH)GetStockObject(DC_BRUSH)); 200 hFontOld = SelectObject(hdc, hFont); 201 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 202 SetBkMode(hdc, TRANSPARENT); 203 DrawText(hdc, szBuf, 2, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); 204 SelectObject(hdc, hFontOld); 205 SelectObject(hdc, hBmpOld); 206 207 /* Fill hbmMono by black */ 208 hBmpOld = SelectObject(hdc, hbmMono); 209 PatBlt(hdc, 0, 0, CX_ICON, CY_ICON, BLACKNESS); 210 SelectObject(hdc, hBmpOld); 211 212 /* Create an icon from hbmColor and hbmMono */ 213 IconInfo.hbmColor = hbmColor; 214 IconInfo.hbmMask = hbmMono; 215 IconInfo.fIcon = TRUE; 216 hIcon = CreateIconIndirect(&IconInfo); 217 218 /* Clean up */ 219 DeleteObject(hbmColor); 220 DeleteObject(hbmMono); 221 DeleteObject(hFont); 222 DeleteDC(hdc); 223 224 return hIcon; 225 } 226 227 static VOID 228 AddTrayIcon(HWND hwnd) 229 { 230 NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1, NIF_ICON | NIF_MESSAGE | NIF_TIP }; 231 TCHAR szLCID[CCH_LAYOUT_ID + 1], szName[MAX_PATH]; 232 233 GetLayoutID(_T("1"), szLCID, ARRAYSIZE(szLCID)); 234 GetLayoutName(_T("1"), szName, ARRAYSIZE(szName)); 235 236 tnid.uCallbackMessage = WM_NOTIFYICONMSG; 237 tnid.hIcon = CreateTrayIcon(szLCID); 238 StringCchCopy(tnid.szTip, ARRAYSIZE(tnid.szTip), szName); 239 240 Shell_NotifyIcon(NIM_ADD, &tnid); 241 242 if (g_hTrayIcon) 243 DestroyIcon(g_hTrayIcon); 244 g_hTrayIcon = tnid.hIcon; 245 } 246 247 static VOID 248 DeleteTrayIcon(HWND hwnd) 249 { 250 NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1 }; 251 Shell_NotifyIcon(NIM_DELETE, &tnid); 252 253 if (g_hTrayIcon) 254 { 255 DestroyIcon(g_hTrayIcon); 256 g_hTrayIcon = NULL; 257 } 258 } 259 260 static VOID 261 UpdateTrayIcon(HWND hwnd, LPTSTR szLCID, LPTSTR szName) 262 { 263 NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1, NIF_ICON | NIF_MESSAGE | NIF_TIP }; 264 tnid.uCallbackMessage = WM_NOTIFYICONMSG; 265 tnid.hIcon = CreateTrayIcon(szLCID); 266 StringCchCopy(tnid.szTip, ARRAYSIZE(tnid.szTip), szName); 267 268 Shell_NotifyIcon(NIM_MODIFY, &tnid); 269 270 if (g_hTrayIcon) 271 DestroyIcon(g_hTrayIcon); 272 g_hTrayIcon = tnid.hIcon; 273 } 274 275 static VOID 276 GetLayoutIDByHkl(HKL hKl, LPTSTR szLayoutID, SIZE_T LayoutIDLength) 277 { 278 StringCchPrintf(szLayoutID, LayoutIDLength, _T("%08lx"), (DWORD)(DWORD_PTR)(hKl)); 279 } 280 281 static BOOL CALLBACK 282 EnumWindowsProc(HWND hwnd, LPARAM lParam) 283 { 284 PostMessage(hwnd, WM_INPUTLANGCHANGEREQUEST, 0, lParam); 285 return TRUE; 286 } 287 288 static VOID 289 ActivateLayout(HWND hwnd, ULONG uLayoutNum) 290 { 291 HKL hKl; 292 TCHAR szLayoutNum[CCH_ULONG_DEC + 1], szLCID[CCH_LAYOUT_ID + 1], szLangName[MAX_PATH]; 293 LANGID LangID; 294 295 /* The layout number starts from one. Zero is invalid */ 296 if (uLayoutNum == 0 || uLayoutNum > 0xFF) /* Invalid */ 297 return; 298 299 _ultot(uLayoutNum, szLayoutNum, 10); 300 GetLayoutID(szLayoutNum, szLCID, ARRAYSIZE(szLCID)); 301 LangID = (LANGID)_tcstoul(szLCID, NULL, 16); 302 303 /* Switch to the new keyboard layout */ 304 GetLocaleInfo(LangID, LOCALE_SLANGUAGE, szLangName, ARRAYSIZE(szLangName)); 305 UpdateTrayIcon(hwnd, szLCID, szLangName); 306 hKl = LoadKeyboardLayout(szLCID, KLF_ACTIVATE); 307 308 /* Post WM_INPUTLANGCHANGEREQUEST to every top-level window */ 309 EnumWindows(EnumWindowsProc, (LPARAM) hKl); 310 311 ulCurrentLayoutNum = uLayoutNum; 312 } 313 314 static HMENU 315 BuildLeftPopupMenu(VOID) 316 { 317 HMENU hMenu = CreatePopupMenu(); 318 HKEY hKey; 319 DWORD dwIndex, dwSize; 320 TCHAR szLayoutNum[CCH_ULONG_DEC + 1], szName[MAX_PATH]; 321 322 /* Add the keyboard layouts to the popup menu */ 323 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, 324 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 325 { 326 for (dwIndex = 0; ; dwIndex++) 327 { 328 dwSize = sizeof(szLayoutNum); 329 if (RegEnumValue(hKey, dwIndex, szLayoutNum, &dwSize, NULL, NULL, 330 NULL, NULL) != ERROR_SUCCESS) 331 { 332 break; 333 } 334 335 if (!GetLayoutName(szLayoutNum, szName, ARRAYSIZE(szName))) 336 break; 337 338 AppendMenu(hMenu, MF_STRING, _ttoi(szLayoutNum), szName); 339 } 340 341 CheckMenuItem(hMenu, ulCurrentLayoutNum, MF_CHECKED); 342 343 RegCloseKey(hKey); 344 } 345 346 return hMenu; 347 } 348 349 static ULONG 350 GetMaxLayoutNum(VOID) 351 { 352 HKEY hKey; 353 ULONG dwIndex, dwSize, uLayoutNum, uMaxLayoutNum = 0; 354 TCHAR szLayoutNum[CCH_ULONG_DEC + 1], szLayoutID[CCH_LAYOUT_ID + 1]; 355 356 /* Get the maximum layout number in the Preload key */ 357 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, 358 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 359 { 360 for (dwIndex = 0; ; dwIndex++) 361 { 362 dwSize = sizeof(szLayoutNum); 363 if (RegEnumValue(hKey, dwIndex, szLayoutNum, &dwSize, NULL, NULL, 364 NULL, NULL) != ERROR_SUCCESS) 365 { 366 break; 367 } 368 369 if (GetLayoutID(szLayoutNum, szLayoutID, ARRAYSIZE(szLayoutID))) 370 { 371 uLayoutNum = _ttoi(szLayoutNum); 372 if (uMaxLayoutNum < uLayoutNum) 373 uMaxLayoutNum = uLayoutNum; 374 } 375 } 376 377 RegCloseKey(hKey); 378 } 379 380 return uMaxLayoutNum; 381 } 382 383 BOOL 384 SetHooks(VOID) 385 { 386 g_hHookDLL = LoadLibrary(_T("kbsdll.dll")); 387 if (!g_hHookDLL) 388 { 389 return FALSE; 390 } 391 392 KbSwitchSetHooks = (PKBSWITCHSETHOOKS) GetProcAddress(g_hHookDLL, "KbSwitchSetHooks"); 393 KbSwitchDeleteHooks = (PKBSWITCHDELETEHOOKS) GetProcAddress(g_hHookDLL, "KbSwitchDeleteHooks"); 394 395 if (KbSwitchSetHooks == NULL || KbSwitchDeleteHooks == NULL) 396 { 397 return FALSE; 398 } 399 400 return KbSwitchSetHooks(); 401 } 402 403 VOID 404 DeleteHooks(VOID) 405 { 406 if (KbSwitchDeleteHooks) KbSwitchDeleteHooks(); 407 if (g_hHookDLL) FreeLibrary(g_hHookDLL); 408 } 409 410 ULONG 411 GetNextLayout(VOID) 412 { 413 TCHAR szLayoutNum[3 + 1], szLayoutID[CCH_LAYOUT_ID + 1]; 414 ULONG uLayoutNum, uMaxNum = GetMaxLayoutNum(); 415 416 for (uLayoutNum = ulCurrentLayoutNum + 1; ; ++uLayoutNum) 417 { 418 if (uLayoutNum > uMaxNum) 419 uLayoutNum = 1; 420 if (uLayoutNum == ulCurrentLayoutNum) 421 break; 422 423 _ultot(uLayoutNum, szLayoutNum, 10); 424 if (GetLayoutID(szLayoutNum, szLayoutID, ARRAYSIZE(szLayoutID))) 425 return uLayoutNum; 426 } 427 428 return ulCurrentLayoutNum; 429 } 430 431 LRESULT 432 UpdateLanguageDisplay(HWND hwnd, HKL hKl) 433 { 434 TCHAR szLCID[MAX_PATH], szLangName[MAX_PATH]; 435 LANGID LangID; 436 437 GetLayoutIDByHkl(hKl, szLCID, ARRAYSIZE(szLCID)); 438 LangID = (LANGID)_tcstoul(szLCID, NULL, 16); 439 GetLocaleInfo(LangID, LOCALE_SLANGUAGE, szLangName, ARRAYSIZE(szLangName)); 440 UpdateTrayIcon(hwnd, szLCID, szLangName); 441 442 return 0; 443 } 444 445 LRESULT 446 UpdateLanguageDisplayCurrent(HWND hwnd, WPARAM wParam) 447 { 448 DWORD dwThreadID = GetWindowThreadProcessId((HWND)wParam, 0); 449 HKL hKL = GetKeyboardLayout(dwThreadID); 450 return UpdateLanguageDisplay(hwnd, hKL); 451 } 452 453 LRESULT CALLBACK 454 WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 455 { 456 static HMENU s_hMenu = NULL, s_hRightPopupMenu = NULL; 457 static UINT s_uTaskbarRestart; 458 POINT pt; 459 HMENU hLeftPopupMenu; 460 461 switch (Message) 462 { 463 case WM_CREATE: 464 { 465 if (!SetHooks()) 466 return -1; 467 468 AddTrayIcon(hwnd); 469 470 ActivateLayout(hwnd, ulCurrentLayoutNum); 471 s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); 472 break; 473 } 474 475 case WM_LANG_CHANGED: 476 { 477 return UpdateLanguageDisplay(hwnd, (HKL)lParam); 478 } 479 480 case WM_LOAD_LAYOUT: 481 { 482 ULONG uNextNum = GetNextLayout(); 483 if (ulCurrentLayoutNum != uNextNum) 484 ActivateLayout(hwnd, uNextNum); 485 break; 486 } 487 488 case WM_WINDOW_ACTIVATE: 489 { 490 return UpdateLanguageDisplayCurrent(hwnd, wParam); 491 } 492 493 case WM_NOTIFYICONMSG: 494 { 495 switch (lParam) 496 { 497 case WM_RBUTTONUP: 498 case WM_LBUTTONUP: 499 { 500 GetCursorPos(&pt); 501 SetForegroundWindow(hwnd); 502 503 if (lParam == WM_LBUTTONUP) 504 { 505 /* Rebuild the left popup menu on every click to take care of keyboard layout changes */ 506 hLeftPopupMenu = BuildLeftPopupMenu(); 507 TrackPopupMenu(hLeftPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); 508 DestroyMenu(hLeftPopupMenu); 509 } 510 else 511 { 512 if (!s_hRightPopupMenu) 513 { 514 s_hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_POPUP)); 515 s_hRightPopupMenu = GetSubMenu(s_hMenu, 0); 516 } 517 TrackPopupMenu(s_hRightPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); 518 } 519 520 PostMessage(hwnd, WM_NULL, 0, 0); 521 break; 522 } 523 } 524 break; 525 } 526 527 case WM_COMMAND: 528 switch (LOWORD(wParam)) 529 { 530 case ID_EXIT: 531 { 532 SendMessage(hwnd, WM_CLOSE, 0, 0); 533 break; 534 } 535 536 case ID_PREFERENCES: 537 { 538 SHELLEXECUTEINFO shInputDll = { sizeof(shInputDll) }; 539 shInputDll.hwnd = hwnd; 540 shInputDll.lpVerb = _T("open"); 541 shInputDll.lpFile = _T("rundll32.exe"); 542 shInputDll.lpParameters = _T("shell32.dll,Control_RunDLL input.dll"); 543 if (!ShellExecuteEx(&shInputDll)) 544 MessageBox(hwnd, _T("Can't start input.dll"), NULL, MB_OK | MB_ICONERROR); 545 546 break; 547 } 548 549 default: 550 { 551 ActivateLayout(hwnd, LOWORD(wParam)); 552 break; 553 } 554 } 555 break; 556 557 case WM_SETTINGCHANGE: 558 { 559 if (wParam == SPI_SETDEFAULTINPUTLANG) 560 { 561 //FIXME: Should detect default language changes by CPL applet or by other tools and update UI 562 } 563 if (wParam == SPI_SETNONCLIENTMETRICS) 564 { 565 return UpdateLanguageDisplayCurrent(hwnd, wParam); 566 } 567 } 568 break; 569 570 case WM_DESTROY: 571 { 572 DeleteHooks(); 573 DestroyMenu(s_hMenu); 574 DeleteTrayIcon(hwnd); 575 PostQuitMessage(0); 576 break; 577 } 578 579 default: 580 { 581 if (Message == s_uTaskbarRestart) 582 { 583 AddTrayIcon(hwnd); 584 break; 585 } 586 else if (Message == ShellHookMessage && wParam == HSHELL_LANGUAGE) 587 { 588 PostMessage(hwnd, WM_LANG_CHANGED, wParam, lParam); 589 break; 590 } 591 return DefWindowProc(hwnd, Message, wParam, lParam); 592 } 593 } 594 595 return 0; 596 } 597 598 INT WINAPI 599 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdShow) 600 { 601 WNDCLASS WndClass; 602 MSG msg; 603 HANDLE hMutex; 604 HWND hwnd; 605 606 switch (GetUserDefaultUILanguage()) 607 { 608 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): 609 SetProcessDefaultLayout(LAYOUT_RTL); 610 break; 611 default: 612 break; 613 } 614 615 hMutex = CreateMutex(NULL, FALSE, szKbSwitcherName); 616 if (!hMutex) 617 return 1; 618 619 if (GetLastError() == ERROR_ALREADY_EXISTS) 620 { 621 CloseHandle(hMutex); 622 return 1; 623 } 624 625 hInst = hInstance; 626 hProcessHeap = GetProcessHeap(); 627 628 ZeroMemory(&WndClass, sizeof(WndClass)); 629 WndClass.lpfnWndProc = WndProc; 630 WndClass.hInstance = hInstance; 631 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); 632 WndClass.lpszClassName = szKbSwitcherName; 633 if (!RegisterClass(&WndClass)) 634 { 635 CloseHandle(hMutex); 636 return 1; 637 } 638 639 hwnd = CreateWindow(szKbSwitcherName, NULL, 0, 0, 0, 1, 1, HWND_DESKTOP, NULL, hInstance, NULL); 640 ShellHookMessage = RegisterWindowMessage(L"SHELLHOOK"); 641 RegisterShellHookWindow(hwnd); 642 643 while (GetMessage(&msg, NULL, 0, 0)) 644 { 645 TranslateMessage(&msg); 646 DispatchMessage(&msg); 647 } 648 649 CloseHandle(hMutex); 650 return 0; 651 } 652