aboutsummaryrefslogtreecommitdiff
path: root/src/samples/thmviewer/thmviewer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/samples/thmviewer/thmviewer.cpp')
-rw-r--r--src/samples/thmviewer/thmviewer.cpp564
1 files changed, 0 insertions, 564 deletions
diff --git a/src/samples/thmviewer/thmviewer.cpp b/src/samples/thmviewer/thmviewer.cpp
deleted file mode 100644
index 38f3c4dc..00000000
--- a/src/samples/thmviewer/thmviewer.cpp
+++ /dev/null
@@ -1,564 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static const LPCWSTR THMVWR_WINDOW_CLASS_MAIN = L"ThmViewerMain";
6
7static THEME* vpTheme = NULL;
8static DWORD vdwDisplayThreadId = 0;
9static LPWSTR vsczThemeLoadErrors = NULL;
10
11enum THMVWR_CONTROL
12{
13 // Non-paged controls
14 THMVWR_CONTROL_TREE = THEME_FIRST_ASSIGN_CONTROL_ID,
15};
16
17// Internal functions
18
19static HRESULT ProcessCommandLine(
20 __in_z_opt LPCWSTR wzCommandLine,
21 __out_z LPWSTR* psczThemeFile,
22 __out_z LPWSTR* psczWxlFile
23 );
24static HRESULT CreateTheme(
25 __in HINSTANCE hInstance,
26 __out THEME** ppTheme
27 );
28static HRESULT CreateMainWindowClass(
29 __in HINSTANCE hInstance,
30 __in THEME* pTheme,
31 __out ATOM* pAtom
32 );
33static LRESULT CALLBACK MainWndProc(
34 __in HWND hWnd,
35 __in UINT uMsg,
36 __in WPARAM wParam,
37 __in LPARAM lParam
38 );
39static void OnThemeLoadBegin(
40 __in_z_opt LPWSTR sczThemeLoadErrors
41 );
42static void OnThemeLoadError(
43 __in THEME* pTheme,
44 __in HRESULT hrFailure
45 );
46static void OnNewTheme(
47 __in THEME* pTheme,
48 __in HWND hWnd,
49 __in HANDLE_THEME* pHandle
50 );
51static BOOL OnThemeLoadingControl(
52 __in const THEME_LOADINGCONTROL_ARGS* pArgs,
53 __in THEME_LOADINGCONTROL_RESULTS* pResults
54 );
55static BOOL OnThemeControlWmNotify(
56 __in const THEME_CONTROLWMNOTIFY_ARGS* pArgs,
57 __in THEME_CONTROLWMNOTIFY_RESULTS* pResults
58 );
59static void CALLBACK ThmviewerTraceError(
60 __in_z LPCSTR szFile,
61 __in int iLine,
62 __in REPORT_LEVEL rl,
63 __in UINT source,
64 __in HRESULT hrError,
65 __in_z __format_string LPCSTR szFormat,
66 __in va_list args
67 );
68
69
70int WINAPI wWinMain(
71 __in HINSTANCE hInstance,
72 __in_opt HINSTANCE /* hPrevInstance */,
73 __in_z LPWSTR lpCmdLine,
74 __in int /*nCmdShow*/
75 )
76{
77 ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
78
79 HRESULT hr = S_OK;
80 BOOL fComInitialized = FALSE;
81 LPWSTR sczThemeFile = NULL;
82 LPWSTR sczWxlFile = NULL;
83 ATOM atom = 0;
84 HWND hWnd = NULL;
85
86 HANDLE hDisplayThread = NULL;
87 HANDLE hLoadThread = NULL;
88
89 BOOL fRet = FALSE;
90 MSG msg = { };
91
92 hr = ::CoInitialize(NULL);
93 ExitOnFailure(hr, "Failed to initialize COM.");
94 fComInitialized = TRUE;
95
96 DutilInitialize(&ThmviewerTraceError);
97
98 hr = ProcessCommandLine(lpCmdLine, &sczThemeFile, &sczWxlFile);
99 ExitOnFailure(hr, "Failed to process command line.");
100
101 hr = CreateTheme(hInstance, &vpTheme);
102 ExitOnFailure(hr, "Failed to create theme.");
103
104 hr = CreateMainWindowClass(hInstance, vpTheme, &atom);
105 ExitOnFailure(hr, "Failed to create main window.");
106
107 hr = ThemeCreateParentWindow(vpTheme, 0, reinterpret_cast<LPCWSTR>(atom), vpTheme->sczCaption, vpTheme->dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, hInstance, NULL, THEME_WINDOW_INITIAL_POSITION_DEFAULT, &hWnd);
108 ExitOnFailure(hr, "Failed to create window.");
109
110 if (!sczThemeFile)
111 {
112 // Prompt for a path to the theme file.
113 OPENFILENAMEW ofn = { };
114 WCHAR wzFile[MAX_PATH] = { };
115
116 ofn.lStructSize = sizeof(ofn);
117 ofn.hwndOwner = hWnd;
118 ofn.lpstrFile = wzFile;
119 ofn.nMaxFile = countof(wzFile);
120 ofn.lpstrFilter = L"Theme Files (*.thm)\0*.thm\0XML Files (*.xml)\0*.xml\0All Files (*.*)\0*.*\0";
121 ofn.nFilterIndex = 1;
122 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
123 ofn.lpstrTitle = vpTheme->sczCaption;
124
125 if (::GetOpenFileNameW(&ofn))
126 {
127 hr = StrAllocString(&sczThemeFile, wzFile, 0);
128 ExitOnFailure(hr, "Failed to copy opened file to theme file.");
129 }
130 else
131 {
132 ::MessageBoxW(hWnd, L"Must specify a path to theme file.", vpTheme->sczCaption, MB_OK | MB_ICONERROR);
133 ExitFunction1(hr = E_INVALIDARG);
134 }
135 }
136
137 hr = DisplayStart(hInstance, hWnd, &hDisplayThread, &vdwDisplayThreadId);
138 ExitOnFailure(hr, "Failed to start display.");
139
140 hr = LoadStart(sczThemeFile, sczWxlFile, hWnd, &hLoadThread);
141 ExitOnFailure(hr, "Failed to start load.");
142
143 // message pump
144 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
145 {
146 if (-1 == fRet)
147 {
148 hr = E_UNEXPECTED;
149 ExitOnFailure(hr, "Unexpected return value from message pump.");
150 }
151 else if (!ThemeHandleKeyboardMessage(vpTheme, msg.hwnd, &msg))
152 {
153 ::TranslateMessage(&msg);
154 ::DispatchMessageW(&msg);
155 }
156 }
157
158LExit:
159 if (::IsWindow(hWnd))
160 {
161 ::DestroyWindow(hWnd);
162 }
163
164 if (hDisplayThread)
165 {
166 ::PostThreadMessageW(vdwDisplayThreadId, WM_QUIT, 0, 0);
167 ::WaitForSingleObject(hDisplayThread, 10000);
168 ::CloseHandle(hDisplayThread);
169 }
170
171 // TODO: come up with a good way to kill the load thread, probably need to switch
172 // the ReadDirectoryW() to overlapped mode.
173 ReleaseHandle(hLoadThread);
174
175 if (atom && !::UnregisterClassW(reinterpret_cast<LPCWSTR>(atom), hInstance))
176 {
177 DWORD er = ::GetLastError();
178 er = er;
179 }
180
181 ThemeFree(vpTheme);
182 ThemeUninitialize();
183 DutilUninitialize();
184
185 // uninitialize COM
186 if (fComInitialized)
187 {
188 ::CoUninitialize();
189 }
190
191 ReleaseNullStr(vsczThemeLoadErrors);
192 ReleaseStr(sczThemeFile);
193 ReleaseStr(sczWxlFile);
194 return hr;
195}
196
197static void CALLBACK ThmviewerTraceError(
198 __in_z LPCSTR /*szFile*/,
199 __in int /*iLine*/,
200 __in REPORT_LEVEL /*rl*/,
201 __in UINT source,
202 __in HRESULT hrError,
203 __in_z __format_string LPCSTR szFormat,
204 __in va_list args
205 )
206{
207 HRESULT hr = S_OK;
208 LPSTR sczFormattedAnsi = NULL;
209 LPWSTR sczMessage = NULL;
210
211 if (DUTIL_SOURCE_THMUTIL != source)
212 {
213 ExitFunction();
214 }
215
216 hr = StrAnsiAllocFormattedArgs(&sczFormattedAnsi, szFormat, args);
217 ExitOnFailure(hr, "Failed to format error log string.");
218
219 hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x: %S\r\n", hrError, sczFormattedAnsi);
220 ExitOnFailure(hr, "Failed to prepend error number to error log string.");
221
222 hr = StrAllocConcat(&vsczThemeLoadErrors, sczMessage, 0);
223 ExitOnFailure(hr, "Failed to append theme load error.");
224
225LExit:
226 ReleaseStr(sczFormattedAnsi);
227 ReleaseStr(sczMessage);
228}
229
230
231//
232// ProcessCommandLine - process the provided command line arguments.
233//
234static HRESULT ProcessCommandLine(
235 __in_z_opt LPCWSTR wzCommandLine,
236 __out_z LPWSTR* psczThemeFile,
237 __out_z LPWSTR* psczWxlFile
238 )
239{
240 HRESULT hr = S_OK;
241 int argc = 0;
242 LPWSTR* argv = NULL;
243
244 if (wzCommandLine && *wzCommandLine)
245 {
246 hr = AppParseCommandLine(wzCommandLine, &argc, &argv);
247 ExitOnFailure(hr, "Failed to parse command line.");
248
249 for (int i = 0; i < argc; ++i)
250 {
251 if (argv[i][0] == L'-' || argv[i][0] == L'/')
252 {
253 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1))
254 {
255 if (i + 1 >= argc)
256 {
257 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a language.");
258 }
259
260 ++i;
261 }
262 }
263 else
264 {
265 LPCWSTR wzExtension = PathExtension(argv[i]);
266 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzExtension, -1, L".wxl", -1))
267 {
268 hr = StrAllocString(psczWxlFile, argv[i], 0);
269 }
270 else
271 {
272 hr = StrAllocString(psczThemeFile, argv[i], 0);
273 }
274 ExitOnFailure(hr, "Failed to copy path to file.");
275 }
276 }
277 }
278
279LExit:
280 if (argv)
281 {
282 AppFreeCommandLineArgs(argv);
283 }
284
285 return hr;
286}
287
288static HRESULT CreateTheme(
289 __in HINSTANCE hInstance,
290 __out THEME** ppTheme
291 )
292{
293 HRESULT hr = S_OK;
294
295 hr = ThemeInitialize(hInstance);
296 ExitOnFailure(hr, "Failed to initialize theme manager.");
297
298 hr = ThemeLoadFromResource(hInstance, MAKEINTRESOURCEA(THMVWR_RES_THEME_FILE), ppTheme);
299 ExitOnFailure(hr, "Failed to load theme from thmviewer.thm.");
300
301LExit:
302 return hr;
303}
304
305static HRESULT CreateMainWindowClass(
306 __in HINSTANCE hInstance,
307 __in THEME* pTheme,
308 __out ATOM* pAtom
309 )
310{
311 HRESULT hr = S_OK;
312 ATOM atom = 0;
313 WNDCLASSW wc = { };
314
315 ThemeInitializeWindowClass(pTheme, &wc, MainWndProc, hInstance, THMVWR_WINDOW_CLASS_MAIN);
316
317 atom = ::RegisterClassW(&wc);
318 if (!atom)
319 {
320 ExitWithLastError(hr, "Failed to register main windowclass .");
321 }
322
323 *pAtom = atom;
324
325LExit:
326 return hr;
327}
328
329static LRESULT CALLBACK MainWndProc(
330 __in HWND hWnd,
331 __in UINT uMsg,
332 __in WPARAM wParam,
333 __in LPARAM lParam
334 )
335{
336 HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
337
338 switch (uMsg)
339 {
340 case WM_NCCREATE:
341 {
342 //LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
343 //pBA = reinterpret_cast<CWixStandardBootstrapperApplication*>(lpcs->lpCreateParams);
344 //::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
345 }
346 break;
347
348 case WM_NCDESTROY:
349 DecrementHandleTheme(pHandleTheme);
350 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
351 ::PostQuitMessage(0);
352 break;
353
354 case WM_THMVWR_THEME_LOAD_BEGIN:
355 OnThemeLoadBegin(vsczThemeLoadErrors);
356 return 0;
357
358 case WM_THMVWR_THEME_LOAD_ERROR:
359 OnThemeLoadError(vpTheme, lParam);
360 return 0;
361
362 case WM_THMVWR_NEW_THEME:
363 OnNewTheme(vpTheme, hWnd, reinterpret_cast<HANDLE_THEME*>(lParam));
364 return 0;
365
366 case WM_THMUTIL_LOADING_CONTROL:
367 return OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam));
368
369 case WM_THMUTIL_CONTROL_WM_NOTIFY:
370 return OnThemeControlWmNotify(reinterpret_cast<THEME_CONTROLWMNOTIFY_ARGS*>(wParam), reinterpret_cast<THEME_CONTROLWMNOTIFY_RESULTS*>(lParam));
371 }
372
373 return ThemeDefWindowProc(vpTheme, hWnd, uMsg, wParam, lParam);
374}
375
376static void OnThemeLoadBegin(
377 __in_z_opt LPWSTR sczThemeLoadErrors
378 )
379{
380 ReleaseNullStr(sczThemeLoadErrors);
381}
382
383static void OnThemeLoadError(
384 __in THEME* pTheme,
385 __in HRESULT hrFailure
386 )
387{
388 HRESULT hr = S_OK;
389 LPWSTR sczMessage = NULL;
390 LPWSTR* psczErrors = NULL;
391 UINT cErrors = 0;
392 TVINSERTSTRUCTW tvi = { };
393 const THEME_CONTROL* pTreeControl = NULL;
394
395 if (!ThemeControlExistsById(pTheme, THMVWR_CONTROL_TREE, &pTreeControl))
396 {
397 ExitWithRootFailure(hr, E_INVALIDSTATE, "THMVWR_CONTROL_TREE control doesn't exist.");
398 }
399
400 // Add the application node.
401 tvi.hParent = NULL;
402 tvi.hInsertAfter = TVI_ROOT;
403 tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
404 tvi.item.lParam = 0;
405 tvi.item.pszText = L"Failed to load theme.";
406 tvi.hParent = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
407
408 if (!vsczThemeLoadErrors)
409 {
410 hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x.", hrFailure);
411 ExitOnFailure(hr, "Failed to format error message.");
412
413 tvi.item.pszText = sczMessage;
414 ::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
415
416 hr = StrAllocFromError(&sczMessage, hrFailure, NULL);
417 ExitOnFailure(hr, "Failed to format error message text.");
418
419 tvi.item.pszText = sczMessage;
420 ::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
421 }
422 else
423 {
424 hr = StrSplitAllocArray(&psczErrors, &cErrors, vsczThemeLoadErrors, L"\r\n");
425 ExitOnFailure(hr, "Failed to split theme load errors.");
426
427 for (DWORD i = 0; i < cErrors; ++i)
428 {
429 tvi.item.pszText = psczErrors[i];
430 ::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
431 }
432 }
433
434 ::SendMessage(pTreeControl->hWnd, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
435
436LExit:
437 ReleaseStr(sczMessage);
438 ReleaseMem(psczErrors);
439}
440
441
442static void OnNewTheme(
443 __in THEME* pTheme,
444 __in HWND hWnd,
445 __in HANDLE_THEME* pHandle
446 )
447{
448 const THEME_CONTROL* pTreeControl = NULL;
449 HANDLE_THEME* pOldHandle = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
450 THEME* pNewTheme = pHandle->pTheme;
451
452 WCHAR wzSelectedPage[MAX_PATH] = { };
453 HTREEITEM htiSelected = NULL;
454 TVINSERTSTRUCTW tvi = { };
455 TVITEMW item = { };
456
457 if (pOldHandle)
458 {
459 DecrementHandleTheme(pOldHandle);
460 pOldHandle = NULL;
461 }
462
463 // Pass the new theme handle to the display thread so it can get the display window prepared
464 // to show the new theme.
465 IncrementHandleTheme(pHandle);
466 ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle));
467
468 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pHandle));
469
470 if (!ThemeControlExistsById(pTheme, THMVWR_CONTROL_TREE, &pTreeControl))
471 {
472 TraceError(E_INVALIDSTATE, "Tree control doesn't exist.");
473 return;
474 }
475
476 // Remember the currently selected item by name so we can try to automatically select it later.
477 // Otherwise, the user would see their window destroyed after every save of their theme file and
478 // have to click to get the window back.
479 item.mask = TVIF_TEXT;
480 item.pszText = wzSelectedPage;
481 item.cchTextMax = countof(wzSelectedPage);
482 item.hItem = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_GETNEXTITEM, TVGN_CARET, NULL));
483 ::SendMessage(pTreeControl->hWnd, TVM_GETITEM, 0, reinterpret_cast<LPARAM>(&item));
484
485 // Remove the previous items in the tree.
486 ::SendMessage(pTreeControl->hWnd, TVM_DELETEITEM, 0, reinterpret_cast<LPARAM>(TVI_ROOT));
487
488 // Add the application node.
489 tvi.hParent = NULL;
490 tvi.hInsertAfter = TVI_ROOT;
491 tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
492 tvi.item.lParam = 0;
493 tvi.item.pszText = pHandle && pHandle->pTheme && pHandle->pTheme->sczCaption ? pHandle->pTheme->sczCaption : L"Window";
494
495 // Add the pages.
496 tvi.hParent = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
497 tvi.hInsertAfter = TVI_SORT;
498 for (DWORD i = 0; i < pNewTheme->cPages; ++i)
499 {
500 THEME_PAGE* pPage = pNewTheme->rgPages + i;
501 if (pPage->sczName && *pPage->sczName)
502 {
503 tvi.item.pszText = pPage->sczName;
504 tvi.item.lParam = i + 1; //prgdwPageIds[i]; - TODO: do the right thing here by calling ThemeGetPageIds(), should not assume we know how the page ids will be calculated.
505
506 HTREEITEM hti = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
507 if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pPage->sczName, -1, wzSelectedPage, -1))
508 {
509 htiSelected = hti;
510 }
511 }
512 }
513
514 if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Application", -1, wzSelectedPage, -1))
515 {
516 htiSelected = tvi.hParent;
517 }
518
519 ::SendMessage(pTreeControl->hWnd, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
520 if (htiSelected)
521 {
522 ::SendMessage(pTreeControl->hWnd, TVM_SELECTITEM, TVGN_CARET, reinterpret_cast<LPARAM>(htiSelected));
523 }
524}
525
526static BOOL OnThemeLoadingControl(
527 __in const THEME_LOADINGCONTROL_ARGS* pArgs,
528 __in THEME_LOADINGCONTROL_RESULTS* pResults
529 )
530{
531 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pArgs->pThemeControl->sczName, -1, L"Tree", -1))
532 {
533 pResults->wId = THMVWR_CONTROL_TREE;
534 }
535
536 pResults->hr = S_OK;
537 return TRUE;
538}
539
540static BOOL OnThemeControlWmNotify(
541 __in const THEME_CONTROLWMNOTIFY_ARGS* pArgs,
542 __in THEME_CONTROLWMNOTIFY_RESULTS* /*pResults*/
543 )
544{
545 BOOL fProcessed = FALSE;
546
547 switch (pArgs->lParam->code)
548 {
549 case TVN_SELCHANGEDW:
550 switch (pArgs->pThemeControl->wId)
551 {
552 case THMVWR_CONTROL_TREE:
553 NMTREEVIEWW* ptv = reinterpret_cast<NMTREEVIEWW*>(pArgs->lParam);
554 ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_HIDE, ptv->itemOld.lParam);
555 ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_SHOW, ptv->itemNew.lParam);
556
557 fProcessed = TRUE;
558 break;
559 }
560 break;
561 }
562
563 return fProcessed;
564}