xref: /reactos/base/applications/clipbrd/clipbrd.c (revision f90a1956)
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 
SaveClipboardToFile(void)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_EXPLORER | 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 
LoadClipboardDataFromFile(LPWSTR lpszFileName)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 
LoadClipboardFromFile(void)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 
LoadClipboardFromDrop(HDROP hDrop)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 
SetDisplayFormat(UINT uFormat)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 
InitMenuPopup(HMENU hMenu,LPARAM index)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 
UpdateDisplayMenu(void)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 
OnCommand(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)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             WCHAR szTitle[MAX_STRING_LEN];
252 
253             LoadStringW(Globals.hInstance, STRING_CLIPBOARD, szTitle, ARRAYSIZE(szTitle));
254             ShellAboutW(Globals.hMainWnd, szTitle, NULL,
255                         LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(CLIPBRD_ICON)));
256             break;
257         }
258 
259         default:
260         {
261             break;
262         }
263     }
264     return 0;
265 }
266 
OnPaint(HWND hWnd,WPARAM wParam,LPARAM lParam)267 static void OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
268 {
269     HDC hdc;
270     PAINTSTRUCT ps;
271     COLORREF crOldBkColor, crOldTextColor;
272     RECT rc;
273 
274     if (!OpenClipboard(Globals.hMainWnd))
275         return;
276 
277     hdc = BeginPaint(hWnd, &ps);
278 
279     /* Erase the background if needed */
280     if (ps.fErase)
281         FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
282 
283     /* Set the correct background and text colors */
284     crOldBkColor   = SetBkColor(ps.hdc, GetSysColor(COLOR_WINDOW));
285     crOldTextColor = SetTextColor(ps.hdc, GetSysColor(COLOR_WINDOWTEXT));
286 
287     /* Realize the clipboard palette if there is one */
288     RealizeClipboardPalette(ps.hdc);
289 
290     switch (Globals.uDisplayFormat)
291     {
292         case CF_NONE:
293         {
294             /* The clipboard is empty */
295             break;
296         }
297 
298         case CF_DSPTEXT:
299         case CF_TEXT:
300         case CF_OEMTEXT:
301         case CF_UNICODETEXT:
302         {
303             DrawTextFromClipboard(Globals.uDisplayFormat, ps, Scrollstate);
304             break;
305         }
306 
307         case CF_DSPBITMAP:
308         case CF_BITMAP:
309         {
310             BitBltFromClipboard(ps, Scrollstate, SRCCOPY);
311             break;
312         }
313 
314         case CF_DIB:
315         case CF_DIBV5:
316         {
317             SetDIBitsToDeviceFromClipboard(Globals.uDisplayFormat, ps, Scrollstate, DIB_RGB_COLORS);
318             break;
319         }
320 
321         case CF_DSPMETAFILEPICT:
322         case CF_METAFILEPICT:
323         {
324             GetClientRect(hWnd, &rc);
325             PlayMetaFileFromClipboard(hdc, &rc);
326             break;
327         }
328 
329         case CF_DSPENHMETAFILE:
330         case CF_ENHMETAFILE:
331         {
332             GetClientRect(hWnd, &rc);
333             PlayEnhMetaFileFromClipboard(hdc, &rc);
334             break;
335         }
336 
337         // case CF_PALETTE:
338             // TODO: Draw a palette with squares filled with colors.
339             // break;
340 
341         case CF_OWNERDISPLAY:
342         {
343             HGLOBAL hglb;
344             PPAINTSTRUCT pps;
345 
346             hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(ps));
347             if (hglb)
348             {
349                 pps = GlobalLock(hglb);
350                 CopyMemory(pps, &ps, sizeof(ps));
351                 GlobalUnlock(hglb);
352 
353                 SendClipboardOwnerMessage(TRUE, WM_PAINTCLIPBOARD,
354                                           (WPARAM)hWnd, (LPARAM)hglb);
355 
356                 GlobalFree(hglb);
357             }
358             break;
359         }
360 
361         case CF_HDROP:
362         {
363             GetClientRect(hWnd, &rc);
364             HDropFromClipboard(hdc, &rc);
365             break;
366         }
367 
368         default:
369         {
370             GetClientRect(hWnd, &rc);
371             DrawTextFromResource(Globals.hInstance, ERROR_UNSUPPORTED_FORMAT,
372                                  hdc, &rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX);
373             break;
374         }
375     }
376 
377     /* Restore the original colors */
378     SetTextColor(ps.hdc, crOldTextColor);
379     SetBkColor(ps.hdc, crOldBkColor);
380 
381     EndPaint(hWnd, &ps);
382 
383     CloseClipboard();
384 }
385 
MainWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)386 static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
387 {
388     switch(uMsg)
389     {
390         case WM_CREATE:
391         {
392             TEXTMETRICW tm;
393             HDC hDC = GetDC(hWnd);
394 
395             /*
396              * Note that the method with GetObjectW just returns
397              * the original parameters with which the font was created.
398              */
399             if (GetTextMetricsW(hDC, &tm))
400             {
401                 Globals.CharWidth  = tm.tmMaxCharWidth; // tm.tmAveCharWidth;
402                 Globals.CharHeight = tm.tmHeight + tm.tmExternalLeading;
403             }
404             ReleaseDC(hWnd, hDC);
405 
406 
407             Globals.hMenu = GetMenu(hWnd);
408             Globals.hWndNext = SetClipboardViewer(hWnd);
409 
410             // For now, the Help dialog item is disabled because of lacking of HTML support
411             EnableMenuItem(Globals.hMenu, CMD_HELP, MF_BYCOMMAND | MF_GRAYED);
412 
413             UpdateLinesToScroll(&Scrollstate);
414 
415             UpdateDisplayMenu();
416             SetDisplayFormat(0);
417 
418             DragAcceptFiles(hWnd, TRUE);
419             break;
420         }
421 
422         case WM_CLOSE:
423         {
424             DestroyWindow(hWnd);
425             break;
426         }
427 
428         case WM_DESTROY:
429         {
430             ChangeClipboardChain(hWnd, Globals.hWndNext);
431 
432             if (Globals.uDisplayFormat == CF_OWNERDISPLAY)
433             {
434                 HGLOBAL hglb;
435                 PRECT prc;
436 
437                 hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(*prc));
438                 if (hglb)
439                 {
440                     prc = GlobalLock(hglb);
441                     SetRectEmpty(prc);
442                     GlobalUnlock(hglb);
443 
444                     SendClipboardOwnerMessage(TRUE, WM_SIZECLIPBOARD,
445                                               (WPARAM)hWnd, (LPARAM)hglb);
446 
447                     GlobalFree(hglb);
448                 }
449             }
450 
451             PostQuitMessage(0);
452             break;
453         }
454 
455         case WM_PAINT:
456         {
457             OnPaint(hWnd, wParam, lParam);
458             break;
459         }
460 
461         case WM_KEYDOWN:
462         {
463             OnKeyScroll(hWnd, wParam, lParam, &Scrollstate);
464             break;
465         }
466 
467         case WM_MOUSEWHEEL:
468         case WM_MOUSEHWHEEL:
469         {
470             OnMouseScroll(hWnd, uMsg, wParam, lParam, &Scrollstate);
471             break;
472         }
473 
474         case WM_HSCROLL:
475         {
476             // NOTE: Windows uses an offset of 16 pixels
477             OnScroll(hWnd, SB_HORZ, wParam, 5, &Scrollstate);
478             break;
479         }
480 
481         case WM_VSCROLL:
482         {
483             // NOTE: Windows uses an offset of 16 pixels
484             OnScroll(hWnd, SB_VERT, wParam, 5, &Scrollstate);
485             break;
486         }
487 
488         case WM_SIZE:
489         {
490             RECT rc;
491 
492             if (Globals.uDisplayFormat == CF_OWNERDISPLAY)
493             {
494                 HGLOBAL hglb;
495                 PRECT prc;
496 
497                 hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(*prc));
498                 if (hglb)
499                 {
500                     prc = GlobalLock(hglb);
501                     if (wParam == SIZE_MINIMIZED)
502                         SetRectEmpty(prc);
503                     else
504                         GetClientRect(hWnd, prc);
505                     GlobalUnlock(hglb);
506 
507                     SendClipboardOwnerMessage(TRUE, WM_SIZECLIPBOARD,
508                                               (WPARAM)hWnd, (LPARAM)hglb);
509 
510                     GlobalFree(hglb);
511                 }
512                 break;
513             }
514 
515             GetClipboardDataDimensions(Globals.uDisplayFormat, &rc);
516             UpdateWindowScrollState(hWnd, rc.right, rc.bottom, &Scrollstate);
517 
518             // NOTE: There still are little problems drawing
519             // the background when displaying clipboard text.
520             if (!IsClipboardFormatSupported(Globals.uDisplayFormat) ||
521                 Globals.uDisplayFormat == CF_DSPTEXT ||
522                 Globals.uDisplayFormat == CF_TEXT    ||
523                 Globals.uDisplayFormat == CF_OEMTEXT ||
524                 Globals.uDisplayFormat == CF_UNICODETEXT)
525             {
526                 InvalidateRect(Globals.hMainWnd, NULL, TRUE);
527             }
528             else
529             {
530                 InvalidateRect(Globals.hMainWnd, NULL, FALSE);
531             }
532 
533             break;
534         }
535 
536         case WM_CHANGECBCHAIN:
537         {
538             /* Transmit through the clipboard viewer chain */
539             if ((HWND)wParam == Globals.hWndNext)
540             {
541                 Globals.hWndNext = (HWND)lParam;
542             }
543             else if (Globals.hWndNext != NULL)
544             {
545                 SendMessageW(Globals.hWndNext, uMsg, wParam, lParam);
546             }
547 
548             break;
549         }
550 
551         case WM_DESTROYCLIPBOARD:
552             break;
553 
554         case WM_RENDERALLFORMATS:
555         {
556             /*
557              * When the user has cleared the clipboard via the DELETE command,
558              * we (clipboard viewer) become the clipboard owner. When we are
559              * subsequently closed, this message is then sent to us so that
560              * we get a chance to render everything we can. Since we don't have
561              * anything to render, just empty the clipboard.
562              */
563             DeleteClipboardContent();
564             break;
565         }
566 
567         case WM_RENDERFORMAT:
568             // TODO!
569             break;
570 
571         case WM_DRAWCLIPBOARD:
572         {
573             UpdateDisplayMenu();
574             SetDisplayFormat(0);
575 
576             /* Pass the message to the next window in clipboard viewer chain */
577             SendMessageW(Globals.hWndNext, uMsg, wParam, lParam);
578             break;
579         }
580 
581         case WM_COMMAND:
582         {
583             if ((LOWORD(wParam) > CMD_AUTOMATIC))
584             {
585                 SetDisplayFormat(LOWORD(wParam) - CMD_AUTOMATIC);
586             }
587             else
588             {
589                 OnCommand(hWnd, uMsg, wParam, lParam);
590             }
591             break;
592         }
593 
594         case WM_INITMENUPOPUP:
595         {
596             InitMenuPopup((HMENU)wParam, lParam);
597             break;
598         }
599 
600         case WM_DROPFILES:
601         {
602             LoadClipboardFromDrop((HDROP)wParam);
603             break;
604         }
605 
606         case WM_PALETTECHANGED:
607         {
608             /* Ignore if this comes from ourselves */
609             if ((HWND)wParam == hWnd)
610                 break;
611 
612             /* Fall back to WM_QUERYNEWPALETTE */
613         }
614 
615         case WM_QUERYNEWPALETTE:
616         {
617             BOOL Success;
618             HDC hDC;
619 
620             if (!OpenClipboard(Globals.hMainWnd))
621                 return FALSE;
622 
623             hDC = GetDC(hWnd);
624             if (!hDC)
625             {
626                 CloseClipboard();
627                 return FALSE;
628             }
629 
630             Success = RealizeClipboardPalette(hDC);
631 
632             ReleaseDC(hWnd, hDC);
633             CloseClipboard();
634 
635             if (Success)
636             {
637                 InvalidateRect(hWnd, NULL, TRUE);
638                 UpdateWindow(hWnd);
639                 return TRUE;
640             }
641             return FALSE;
642         }
643 
644         case WM_SYSCOLORCHANGE:
645         {
646             SetDisplayFormat(Globals.uDisplayFormat);
647             break;
648         }
649 
650         case WM_SETTINGCHANGE:
651         {
652             if (wParam == SPI_SETWHEELSCROLLLINES)
653             {
654                 UpdateLinesToScroll(&Scrollstate);
655             }
656             break;
657         }
658 
659         default:
660         {
661             return DefWindowProc(hWnd, uMsg, wParam, lParam);
662         }
663     }
664 
665     return 0;
666 }
667 
wWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPWSTR lpCmdLine,int nCmdShow)668 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
669 {
670     MSG msg;
671     HACCEL hAccel;
672     HWND hPrevWindow;
673     WNDCLASSEXW wndclass;
674     WCHAR szBuffer[MAX_STRING_LEN];
675 
676     hPrevWindow = FindWindowW(szClassName, NULL);
677     if (hPrevWindow)
678     {
679         BringWindowToFront(hPrevWindow);
680         return 0;
681     }
682 
683     switch (GetUserDefaultUILanguage())
684     {
685         case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
686             SetProcessDefaultLayout(LAYOUT_RTL);
687             break;
688 
689         default:
690             break;
691     }
692 
693     ZeroMemory(&Globals, sizeof(Globals));
694     Globals.hInstance = hInstance;
695 
696     ZeroMemory(&wndclass, sizeof(wndclass));
697     wndclass.cbSize = sizeof(wndclass);
698     wndclass.lpfnWndProc = MainWndProc;
699     wndclass.hInstance = hInstance;
700     wndclass.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(CLIPBRD_ICON));
701     wndclass.hCursor = LoadCursorW(0, IDC_ARROW);
702     wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
703     wndclass.lpszMenuName = MAKEINTRESOURCEW(MAIN_MENU);
704     wndclass.lpszClassName = szClassName;
705 
706     if (!RegisterClassExW(&wndclass))
707     {
708         ShowLastWin32Error(NULL);
709         return 0;
710     }
711 
712     ZeroMemory(&Scrollstate, sizeof(Scrollstate));
713 
714     LoadStringW(hInstance, STRING_CLIPBOARD, szBuffer, ARRAYSIZE(szBuffer));
715     Globals.hMainWnd = CreateWindowExW(WS_EX_CLIENTEDGE | WS_EX_ACCEPTFILES,
716                                        szClassName,
717                                        szBuffer,
718                                        WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
719                                        CW_USEDEFAULT,
720                                        CW_USEDEFAULT,
721                                        CW_USEDEFAULT,
722                                        CW_USEDEFAULT,
723                                        NULL,
724                                        NULL,
725                                        Globals.hInstance,
726                                        NULL);
727     if (!Globals.hMainWnd)
728     {
729         ShowLastWin32Error(NULL);
730         return 0;
731     }
732 
733     ShowWindow(Globals.hMainWnd, nCmdShow);
734     UpdateWindow(Globals.hMainWnd);
735 
736     hAccel = LoadAcceleratorsW(Globals.hInstance, MAKEINTRESOURCEW(ID_ACCEL));
737     if (!hAccel)
738     {
739         ShowLastWin32Error(Globals.hMainWnd);
740     }
741 
742     /* If the user provided a path to a clipboard data file, try to open it */
743     if (__argc >= 2)
744         LoadClipboardDataFromFile(__wargv[1]);
745 
746     while (GetMessageW(&msg, 0, 0, 0))
747     {
748         if (!TranslateAcceleratorW(Globals.hMainWnd, hAccel, &msg))
749         {
750             TranslateMessage(&msg);
751             DispatchMessageW(&msg);
752         }
753     }
754 
755     return (int)msg.wParam;
756 }
757