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