xref: /reactos/base/applications/clipbrd/clipbrd.c (revision cc439606)
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