1 /* 2 * PROJECT: ReactOS Clipboard Viewer 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Provides a view of the contents of the ReactOS clipboard. 5 * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke 6 * Copyright 2015-2018 Hermes Belusca-Maito 7 */ 8 9 #include "precomp.h" 10 11 static const WCHAR szClassName[] = L"ClipBookWClass"; 12 13 CLIPBOARD_GLOBALS Globals; 14 SCROLLSTATE Scrollstate; 15 16 static void SaveClipboardToFile(void) 17 { 18 OPENFILENAMEW sfn; 19 LPWSTR c; 20 WCHAR szFileName[MAX_PATH]; 21 WCHAR szFilterMask[MAX_STRING_LEN + 10]; 22 23 ZeroMemory(&szFilterMask, sizeof(szFilterMask)); 24 c = szFilterMask + LoadStringW(Globals.hInstance, STRING_FORMAT_NT, szFilterMask, MAX_STRING_LEN) + 1; 25 wcscpy(c, L"*.clp"); 26 27 ZeroMemory(&szFileName, sizeof(szFileName)); 28 ZeroMemory(&sfn, sizeof(sfn)); 29 sfn.lStructSize = sizeof(sfn); 30 sfn.hwndOwner = Globals.hMainWnd; 31 sfn.hInstance = Globals.hInstance; 32 sfn.lpstrFilter = szFilterMask; 33 sfn.lpstrFile = szFileName; 34 sfn.nMaxFile = ARRAYSIZE(szFileName); 35 sfn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; 36 sfn.lpstrDefExt = L"clp"; 37 38 if (!GetSaveFileNameW(&sfn)) 39 return; 40 41 if (!OpenClipboard(Globals.hMainWnd)) 42 { 43 ShowLastWin32Error(Globals.hMainWnd); 44 return; 45 } 46 47 WriteClipboardFile(szFileName, CLIP_FMT_NT /* CLIP_FMT_31 */); 48 49 CloseClipboard(); 50 } 51 52 static void LoadClipboardDataFromFile(LPWSTR lpszFileName) 53 { 54 if (MessageBoxRes(Globals.hMainWnd, Globals.hInstance, 55 STRING_DELETE_MSG, STRING_DELETE_TITLE, 56 MB_ICONWARNING | MB_YESNO) != IDYES) 57 { 58 return; 59 } 60 61 if (!OpenClipboard(Globals.hMainWnd)) 62 { 63 ShowLastWin32Error(Globals.hMainWnd); 64 return; 65 } 66 67 EmptyClipboard(); 68 ReadClipboardFile(lpszFileName); 69 70 CloseClipboard(); 71 } 72 73 static void LoadClipboardFromFile(void) 74 { 75 OPENFILENAMEW ofn; 76 LPWSTR c; 77 WCHAR szFileName[MAX_PATH]; 78 WCHAR szFilterMask[MAX_STRING_LEN + 10]; 79 80 ZeroMemory(&szFilterMask, sizeof(szFilterMask)); 81 c = szFilterMask + LoadStringW(Globals.hInstance, STRING_FORMAT_GEN, szFilterMask, MAX_STRING_LEN) + 1; 82 wcscpy(c, L"*.clp"); 83 84 ZeroMemory(&szFileName, sizeof(szFileName)); 85 ZeroMemory(&ofn, sizeof(ofn)); 86 ofn.lStructSize = sizeof(ofn); 87 ofn.hwndOwner = Globals.hMainWnd; 88 ofn.hInstance = Globals.hInstance; 89 ofn.lpstrFilter = szFilterMask; 90 ofn.lpstrFile = szFileName; 91 ofn.nMaxFile = ARRAYSIZE(szFileName); 92 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST; 93 94 if (!GetOpenFileNameW(&ofn)) 95 return; 96 97 LoadClipboardDataFromFile(szFileName); 98 } 99 100 static void LoadClipboardFromDrop(HDROP hDrop) 101 { 102 WCHAR szFileName[MAX_PATH]; 103 104 DragQueryFileW(hDrop, 0, szFileName, ARRAYSIZE(szFileName)); 105 DragFinish(hDrop); 106 107 LoadClipboardDataFromFile(szFileName); 108 } 109 110 static void SetDisplayFormat(UINT uFormat) 111 { 112 RECT rc; 113 114 CheckMenuItem(Globals.hMenu, Globals.uCheckedItem, MF_BYCOMMAND | MF_UNCHECKED); 115 Globals.uCheckedItem = uFormat + CMD_AUTOMATIC; 116 CheckMenuItem(Globals.hMenu, Globals.uCheckedItem, MF_BYCOMMAND | MF_CHECKED); 117 118 if (uFormat == 0) 119 { 120 Globals.uDisplayFormat = GetAutomaticClipboardFormat(); 121 } 122 else 123 { 124 Globals.uDisplayFormat = uFormat; 125 } 126 127 GetClipboardDataDimensions(Globals.uDisplayFormat, &rc); 128 Scrollstate.CurrentX = Scrollstate.CurrentY = 0; 129 Scrollstate.iWheelCarryoverX = Scrollstate.iWheelCarryoverY = 0; 130 UpdateWindowScrollState(Globals.hMainWnd, rc.right, rc.bottom, &Scrollstate); 131 132 InvalidateRect(Globals.hMainWnd, NULL, TRUE); 133 } 134 135 static void InitMenuPopup(HMENU hMenu, LPARAM index) 136 { 137 if ((GetMenuItemID(hMenu, 0) == CMD_DELETE) || (GetMenuItemID(hMenu, 1) == CMD_SAVE_AS)) 138 { 139 if (CountClipboardFormats() == 0) 140 { 141 EnableMenuItem(hMenu, CMD_DELETE, MF_GRAYED); 142 EnableMenuItem(hMenu, CMD_SAVE_AS, MF_GRAYED); 143 } 144 else 145 { 146 EnableMenuItem(hMenu, CMD_DELETE, MF_ENABLED); 147 EnableMenuItem(hMenu, CMD_SAVE_AS, MF_ENABLED); 148 } 149 } 150 151 DrawMenuBar(Globals.hMainWnd); 152 } 153 154 static void UpdateDisplayMenu(void) 155 { 156 UINT uFormat; 157 HMENU hMenu; 158 WCHAR szFormatName[MAX_FMT_NAME_LEN + 1]; 159 160 hMenu = GetSubMenu(Globals.hMenu, DISPLAY_MENU_POS); 161 162 while (GetMenuItemCount(hMenu) > 1) 163 { 164 DeleteMenu(hMenu, 1, MF_BYPOSITION); 165 } 166 167 if (CountClipboardFormats() == 0) 168 return; 169 170 if (!OpenClipboard(Globals.hMainWnd)) 171 return; 172 173 AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL); 174 175 /* Display the supported clipboard formats first */ 176 for (uFormat = EnumClipboardFormats(0); uFormat; 177 uFormat = EnumClipboardFormats(uFormat)) 178 { 179 if (IsClipboardFormatSupported(uFormat)) 180 { 181 RetrieveClipboardFormatName(Globals.hInstance, uFormat, TRUE, 182 szFormatName, ARRAYSIZE(szFormatName)); 183 AppendMenuW(hMenu, MF_STRING, CMD_AUTOMATIC + uFormat, szFormatName); 184 } 185 } 186 187 /* Now display the unsupported clipboard formats */ 188 for (uFormat = EnumClipboardFormats(0); uFormat; 189 uFormat = EnumClipboardFormats(uFormat)) 190 { 191 if (!IsClipboardFormatSupported(uFormat)) 192 { 193 RetrieveClipboardFormatName(Globals.hInstance, uFormat, TRUE, 194 szFormatName, ARRAYSIZE(szFormatName)); 195 AppendMenuW(hMenu, MF_STRING | MF_GRAYED, 0, szFormatName); 196 } 197 } 198 199 CloseClipboard(); 200 } 201 202 static int OnCommand(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 203 { 204 switch (LOWORD(wParam)) 205 { 206 case CMD_OPEN: 207 { 208 LoadClipboardFromFile(); 209 break; 210 } 211 212 case CMD_SAVE_AS: 213 { 214 SaveClipboardToFile(); 215 break; 216 } 217 218 case CMD_EXIT: 219 { 220 PostMessageW(Globals.hMainWnd, WM_CLOSE, 0, 0); 221 break; 222 } 223 224 case CMD_DELETE: 225 { 226 if (MessageBoxRes(Globals.hMainWnd, Globals.hInstance, 227 STRING_DELETE_MSG, STRING_DELETE_TITLE, 228 MB_ICONWARNING | MB_YESNO) != IDYES) 229 { 230 break; 231 } 232 233 DeleteClipboardContent(); 234 break; 235 } 236 237 case CMD_AUTOMATIC: 238 { 239 SetDisplayFormat(0); 240 break; 241 } 242 243 case CMD_HELP: 244 { 245 HtmlHelpW(Globals.hMainWnd, L"clipbrd.chm", 0, 0); 246 break; 247 } 248 249 case CMD_ABOUT: 250 { 251 HICON hIcon; 252 WCHAR szTitle[MAX_STRING_LEN]; 253 254 hIcon = LoadIconW(Globals.hInstance, MAKEINTRESOURCE(CLIPBRD_ICON)); 255 LoadStringW(Globals.hInstance, STRING_CLIPBOARD, szTitle, ARRAYSIZE(szTitle)); 256 ShellAboutW(Globals.hMainWnd, szTitle, NULL, hIcon); 257 DeleteObject(hIcon); 258 break; 259 } 260 261 default: 262 { 263 break; 264 } 265 } 266 return 0; 267 } 268 269 static void OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) 270 { 271 HDC hdc; 272 PAINTSTRUCT ps; 273 COLORREF crOldBkColor, crOldTextColor; 274 RECT rc; 275 276 if (!OpenClipboard(Globals.hMainWnd)) 277 return; 278 279 hdc = BeginPaint(hWnd, &ps); 280 281 /* Erase the background if needed */ 282 if (ps.fErase) 283 FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1)); 284 285 /* Set the correct background and text colors */ 286 crOldBkColor = SetBkColor(ps.hdc, GetSysColor(COLOR_WINDOW)); 287 crOldTextColor = SetTextColor(ps.hdc, GetSysColor(COLOR_WINDOWTEXT)); 288 289 /* Realize the clipboard palette if there is one */ 290 RealizeClipboardPalette(ps.hdc); 291 292 switch (Globals.uDisplayFormat) 293 { 294 case CF_NONE: 295 { 296 /* The clipboard is empty */ 297 break; 298 } 299 300 case CF_DSPTEXT: 301 case CF_TEXT: 302 case CF_OEMTEXT: 303 case CF_UNICODETEXT: 304 { 305 DrawTextFromClipboard(Globals.uDisplayFormat, ps, Scrollstate); 306 break; 307 } 308 309 case CF_DSPBITMAP: 310 case CF_BITMAP: 311 { 312 BitBltFromClipboard(ps, Scrollstate, SRCCOPY); 313 break; 314 } 315 316 case CF_DIB: 317 case CF_DIBV5: 318 { 319 SetDIBitsToDeviceFromClipboard(Globals.uDisplayFormat, ps, Scrollstate, DIB_RGB_COLORS); 320 break; 321 } 322 323 case CF_DSPMETAFILEPICT: 324 case CF_METAFILEPICT: 325 { 326 GetClientRect(hWnd, &rc); 327 PlayMetaFileFromClipboard(hdc, &rc); 328 break; 329 } 330 331 case CF_DSPENHMETAFILE: 332 case CF_ENHMETAFILE: 333 { 334 GetClientRect(hWnd, &rc); 335 PlayEnhMetaFileFromClipboard(hdc, &rc); 336 break; 337 } 338 339 // case CF_PALETTE: 340 // TODO: Draw a palette with squares filled with colors. 341 // break; 342 343 case CF_OWNERDISPLAY: 344 { 345 HGLOBAL hglb; 346 PPAINTSTRUCT pps; 347 348 hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(ps)); 349 if (hglb) 350 { 351 pps = GlobalLock(hglb); 352 CopyMemory(pps, &ps, sizeof(ps)); 353 GlobalUnlock(hglb); 354 355 SendClipboardOwnerMessage(TRUE, WM_PAINTCLIPBOARD, 356 (WPARAM)hWnd, (LPARAM)hglb); 357 358 GlobalFree(hglb); 359 } 360 break; 361 } 362 363 default: 364 { 365 GetClientRect(hWnd, &rc); 366 DrawTextFromResource(Globals.hInstance, ERROR_UNSUPPORTED_FORMAT, 367 hdc, &rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX); 368 break; 369 } 370 } 371 372 /* Restore the original colors */ 373 SetTextColor(ps.hdc, crOldTextColor); 374 SetBkColor(ps.hdc, crOldBkColor); 375 376 EndPaint(hWnd, &ps); 377 378 CloseClipboard(); 379 } 380 381 static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 382 { 383 switch(uMsg) 384 { 385 case WM_CREATE: 386 { 387 TEXTMETRICW tm; 388 HDC hDC = GetDC(hWnd); 389 390 /* 391 * Note that the method with GetObjectW just returns 392 * the original parameters with which the font was created. 393 */ 394 if (GetTextMetricsW(hDC, &tm)) 395 { 396 Globals.CharWidth = tm.tmMaxCharWidth; // tm.tmAveCharWidth; 397 Globals.CharHeight = tm.tmHeight + tm.tmExternalLeading; 398 } 399 ReleaseDC(hWnd, hDC); 400 401 402 Globals.hMenu = GetMenu(hWnd); 403 Globals.hWndNext = SetClipboardViewer(hWnd); 404 405 // For now, the Help dialog item is disabled because of lacking of HTML support 406 EnableMenuItem(Globals.hMenu, CMD_HELP, MF_BYCOMMAND | MF_GRAYED); 407 408 UpdateLinesToScroll(&Scrollstate); 409 410 UpdateDisplayMenu(); 411 SetDisplayFormat(0); 412 413 DragAcceptFiles(hWnd, TRUE); 414 break; 415 } 416 417 case WM_CLOSE: 418 { 419 DestroyWindow(hWnd); 420 break; 421 } 422 423 case WM_DESTROY: 424 { 425 ChangeClipboardChain(hWnd, Globals.hWndNext); 426 427 if (Globals.uDisplayFormat == CF_OWNERDISPLAY) 428 { 429 HGLOBAL hglb; 430 PRECT prc; 431 432 hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(*prc)); 433 if (hglb) 434 { 435 prc = GlobalLock(hglb); 436 SetRectEmpty(prc); 437 GlobalUnlock(hglb); 438 439 SendClipboardOwnerMessage(TRUE, WM_SIZECLIPBOARD, 440 (WPARAM)hWnd, (LPARAM)hglb); 441 442 GlobalFree(hglb); 443 } 444 } 445 446 PostQuitMessage(0); 447 break; 448 } 449 450 case WM_PAINT: 451 { 452 OnPaint(hWnd, wParam, lParam); 453 break; 454 } 455 456 case WM_KEYDOWN: 457 { 458 OnKeyScroll(hWnd, wParam, lParam, &Scrollstate); 459 break; 460 } 461 462 case WM_MOUSEWHEEL: 463 case WM_MOUSEHWHEEL: 464 { 465 OnMouseScroll(hWnd, uMsg, wParam, lParam, &Scrollstate); 466 break; 467 } 468 469 case WM_HSCROLL: 470 { 471 // NOTE: Windows uses an offset of 16 pixels 472 OnScroll(hWnd, SB_HORZ, wParam, 5, &Scrollstate); 473 break; 474 } 475 476 case WM_VSCROLL: 477 { 478 // NOTE: Windows uses an offset of 16 pixels 479 OnScroll(hWnd, SB_VERT, wParam, 5, &Scrollstate); 480 break; 481 } 482 483 case WM_SIZE: 484 { 485 RECT rc; 486 487 if (Globals.uDisplayFormat == CF_OWNERDISPLAY) 488 { 489 HGLOBAL hglb; 490 PRECT prc; 491 492 hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(*prc)); 493 if (hglb) 494 { 495 prc = GlobalLock(hglb); 496 if (wParam == SIZE_MINIMIZED) 497 SetRectEmpty(prc); 498 else 499 GetClientRect(hWnd, prc); 500 GlobalUnlock(hglb); 501 502 SendClipboardOwnerMessage(TRUE, WM_SIZECLIPBOARD, 503 (WPARAM)hWnd, (LPARAM)hglb); 504 505 GlobalFree(hglb); 506 } 507 break; 508 } 509 510 GetClipboardDataDimensions(Globals.uDisplayFormat, &rc); 511 UpdateWindowScrollState(hWnd, rc.right, rc.bottom, &Scrollstate); 512 513 // NOTE: There still are little problems drawing 514 // the background when displaying clipboard text. 515 if (!IsClipboardFormatSupported(Globals.uDisplayFormat) || 516 Globals.uDisplayFormat == CF_DSPTEXT || 517 Globals.uDisplayFormat == CF_TEXT || 518 Globals.uDisplayFormat == CF_OEMTEXT || 519 Globals.uDisplayFormat == CF_UNICODETEXT) 520 { 521 InvalidateRect(Globals.hMainWnd, NULL, TRUE); 522 } 523 else 524 { 525 InvalidateRect(Globals.hMainWnd, NULL, FALSE); 526 } 527 528 break; 529 } 530 531 case WM_CHANGECBCHAIN: 532 { 533 /* Transmit through the clipboard viewer chain */ 534 if ((HWND)wParam == Globals.hWndNext) 535 { 536 Globals.hWndNext = (HWND)lParam; 537 } 538 else if (Globals.hWndNext != NULL) 539 { 540 SendMessageW(Globals.hWndNext, uMsg, wParam, lParam); 541 } 542 543 break; 544 } 545 546 case WM_DESTROYCLIPBOARD: 547 break; 548 549 case WM_RENDERALLFORMATS: 550 { 551 /* 552 * When the user has cleared the clipboard via the DELETE command, 553 * we (clipboard viewer) become the clipboard owner. When we are 554 * subsequently closed, this message is then sent to us so that 555 * we get a chance to render everything we can. Since we don't have 556 * anything to render, just empty the clipboard. 557 */ 558 DeleteClipboardContent(); 559 break; 560 } 561 562 case WM_RENDERFORMAT: 563 // TODO! 564 break; 565 566 case WM_DRAWCLIPBOARD: 567 { 568 UpdateDisplayMenu(); 569 SetDisplayFormat(0); 570 571 /* Pass the message to the next window in clipboard viewer chain */ 572 SendMessageW(Globals.hWndNext, uMsg, wParam, lParam); 573 break; 574 } 575 576 case WM_COMMAND: 577 { 578 if ((LOWORD(wParam) > CMD_AUTOMATIC)) 579 { 580 SetDisplayFormat(LOWORD(wParam) - CMD_AUTOMATIC); 581 } 582 else 583 { 584 OnCommand(hWnd, uMsg, wParam, lParam); 585 } 586 break; 587 } 588 589 case WM_INITMENUPOPUP: 590 { 591 InitMenuPopup((HMENU)wParam, lParam); 592 break; 593 } 594 595 case WM_DROPFILES: 596 { 597 LoadClipboardFromDrop((HDROP)wParam); 598 break; 599 } 600 601 case WM_PALETTECHANGED: 602 { 603 /* Ignore if this comes from ourselves */ 604 if ((HWND)wParam == hWnd) 605 break; 606 607 /* Fall back to WM_QUERYNEWPALETTE */ 608 } 609 610 case WM_QUERYNEWPALETTE: 611 { 612 BOOL Success; 613 HDC hDC; 614 615 if (!OpenClipboard(Globals.hMainWnd)) 616 return FALSE; 617 618 hDC = GetDC(hWnd); 619 if (!hDC) 620 { 621 CloseClipboard(); 622 return FALSE; 623 } 624 625 Success = RealizeClipboardPalette(hDC); 626 627 ReleaseDC(hWnd, hDC); 628 CloseClipboard(); 629 630 if (Success) 631 { 632 InvalidateRect(hWnd, NULL, TRUE); 633 UpdateWindow(hWnd); 634 return TRUE; 635 } 636 return FALSE; 637 } 638 639 case WM_SYSCOLORCHANGE: 640 { 641 SetDisplayFormat(Globals.uDisplayFormat); 642 break; 643 } 644 645 case WM_SETTINGCHANGE: 646 { 647 if (wParam == SPI_SETWHEELSCROLLLINES) 648 { 649 UpdateLinesToScroll(&Scrollstate); 650 } 651 break; 652 } 653 654 default: 655 { 656 return DefWindowProc(hWnd, uMsg, wParam, lParam); 657 } 658 } 659 660 return 0; 661 } 662 663 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) 664 { 665 MSG msg; 666 HACCEL hAccel; 667 HWND hPrevWindow; 668 WNDCLASSEXW wndclass; 669 WCHAR szBuffer[MAX_STRING_LEN]; 670 671 hPrevWindow = FindWindowW(szClassName, NULL); 672 if (hPrevWindow) 673 { 674 BringWindowToFront(hPrevWindow); 675 return 0; 676 } 677 678 switch (GetUserDefaultUILanguage()) 679 { 680 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): 681 SetProcessDefaultLayout(LAYOUT_RTL); 682 break; 683 684 default: 685 break; 686 } 687 688 ZeroMemory(&Globals, sizeof(Globals)); 689 Globals.hInstance = hInstance; 690 691 ZeroMemory(&wndclass, sizeof(wndclass)); 692 wndclass.cbSize = sizeof(wndclass); 693 wndclass.lpfnWndProc = MainWndProc; 694 wndclass.hInstance = hInstance; 695 wndclass.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(CLIPBRD_ICON)); 696 wndclass.hCursor = LoadCursorW(0, IDC_ARROW); 697 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 698 wndclass.lpszMenuName = MAKEINTRESOURCEW(MAIN_MENU); 699 wndclass.lpszClassName = szClassName; 700 701 if (!RegisterClassExW(&wndclass)) 702 { 703 ShowLastWin32Error(NULL); 704 return 0; 705 } 706 707 ZeroMemory(&Scrollstate, sizeof(Scrollstate)); 708 709 LoadStringW(hInstance, STRING_CLIPBOARD, szBuffer, ARRAYSIZE(szBuffer)); 710 Globals.hMainWnd = CreateWindowExW(WS_EX_CLIENTEDGE | WS_EX_ACCEPTFILES, 711 szClassName, 712 szBuffer, 713 WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, 714 CW_USEDEFAULT, 715 CW_USEDEFAULT, 716 CW_USEDEFAULT, 717 CW_USEDEFAULT, 718 NULL, 719 NULL, 720 Globals.hInstance, 721 NULL); 722 if (!Globals.hMainWnd) 723 { 724 ShowLastWin32Error(NULL); 725 return 0; 726 } 727 728 ShowWindow(Globals.hMainWnd, nCmdShow); 729 UpdateWindow(Globals.hMainWnd); 730 731 hAccel = LoadAcceleratorsW(Globals.hInstance, MAKEINTRESOURCEW(ID_ACCEL)); 732 if (!hAccel) 733 { 734 ShowLastWin32Error(Globals.hMainWnd); 735 } 736 737 /* If the user provided a path to a clipboard data file, try to open it */ 738 if (__argc >= 2) 739 LoadClipboardDataFromFile(__wargv[1]); 740 741 while (GetMessageW(&msg, 0, 0, 0)) 742 { 743 if (!TranslateAcceleratorW(Globals.hMainWnd, hAccel, &msg)) 744 { 745 TranslateMessage(&msg); 746 DispatchMessageW(&msg); 747 } 748 } 749 750 return (int)msg.wParam; 751 } 752