aboutsummaryrefslogtreecommitdiff
path: root/src/samples/thmviewer
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-07-14 15:19:53 -0700
committerRob Mensching <rob@firegiant.com>2022-07-14 16:02:24 -0700
commit229242cf7c328b89b5aa65ed7a04e33c8b93b393 (patch)
treede0a9547e73e46490b0946d6850228d5b30258b8 /src/samples/thmviewer
parentf46ca6a9dce91607ffc9855270dd6998216e1a8b (diff)
downloadwix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.tar.gz
wix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.tar.bz2
wix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.zip
Rename "samples" segment to "tools"
This segment is a bit of a "miscellaneous section" in the WiX repo. As such it has been difficult to name. I originally eschewed the name "tools" because what is in the "wix" segment was once called "tools". However, now that wix.exe is firmly established as the entry point for WiX operations, I've become comfortable with its segment being named "wix". That meant "tools" was again available and "tools" better describes the content of this section.
Diffstat (limited to 'src/samples/thmviewer')
-rw-r--r--src/samples/thmviewer/Resources/LoremIpsum.rtfbin4870 -> 0 bytes
-rw-r--r--src/samples/thmviewer/Resources/thm.xml11
-rw-r--r--src/samples/thmviewer/display.cpp339
-rw-r--r--src/samples/thmviewer/load.cpp221
-rw-r--r--src/samples/thmviewer/precomp.cpp3
-rw-r--r--src/samples/thmviewer/precomp.h72
-rw-r--r--src/samples/thmviewer/resource.h16
-rw-r--r--src/samples/thmviewer/thmviewer.cpp564
-rw-r--r--src/samples/thmviewer/thmviewer.manifest19
-rw-r--r--src/samples/thmviewer/thmviewer.rc12
-rw-r--r--src/samples/thmviewer/thmviewer.v3.ncrunchproject7
-rw-r--r--src/samples/thmviewer/thmviewer.vcxproj69
-rw-r--r--src/samples/thmviewer/thmviewer.vcxproj.filters56
13 files changed, 0 insertions, 1389 deletions
diff --git a/src/samples/thmviewer/Resources/LoremIpsum.rtf b/src/samples/thmviewer/Resources/LoremIpsum.rtf
deleted file mode 100644
index 1ab0e65b..00000000
--- a/src/samples/thmviewer/Resources/LoremIpsum.rtf
+++ /dev/null
Binary files differ
diff --git a/src/samples/thmviewer/Resources/thm.xml b/src/samples/thmviewer/Resources/thm.xml
deleted file mode 100644
index 6394f0f1..00000000
--- a/src/samples/thmviewer/Resources/thm.xml
+++ /dev/null
@@ -1,11 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3
4
5<Theme>
6 <Font Id="0" Height="-48" Weight="700" Foreground="$windowtext">Consolas</Font>
7 <Font Id="1" Height="-12" Weight="700" Foreground="$windowtext" Background="$window">Consolas</Font>
8 <Window Width="220" Height="200" FontId="0" Caption="Theme Viewer">
9 <TreeView Name="Tree" X="0" Y="0" Width="0" Height="0" FontId="1" Visible="yes" FullRowSelect="yes" HasButtons="yes" AlwaysShowSelect="yes" HasLines="yes" LinesAtRoot="yes"/>
10 </Window>
11</Theme>
diff --git a/src/samples/thmviewer/display.cpp b/src/samples/thmviewer/display.cpp
deleted file mode 100644
index 444c6cfb..00000000
--- a/src/samples/thmviewer/display.cpp
+++ /dev/null
@@ -1,339 +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_DISPLAY = L"ThmViewerDisplay";
6
7struct DISPLAY_THREAD_CONTEXT
8{
9 HWND hWnd;
10 HINSTANCE hInstance;
11
12 HANDLE hInit;
13};
14
15static DWORD WINAPI DisplayThreadProc(
16 __in LPVOID pvContext
17 );
18static LRESULT CALLBACK DisplayWndProc(
19 __in HWND hWnd,
20 __in UINT uMsg,
21 __in WPARAM wParam,
22 __in LPARAM lParam
23 );
24static BOOL DisplayOnThmLoadedControl(
25 __in THEME* pTheme,
26 __in const THEME_LOADEDCONTROL_ARGS* args,
27 __in THEME_LOADEDCONTROL_RESULTS* results
28 );
29
30
31extern "C" HRESULT DisplayStart(
32 __in HINSTANCE hInstance,
33 __in HWND hWnd,
34 __out HANDLE *phThread,
35 __out DWORD* pdwThreadId
36 )
37{
38 HRESULT hr = S_OK;
39 HANDLE rgHandles[2] = { };
40 DISPLAY_THREAD_CONTEXT context = { };
41
42 rgHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
43 ExitOnNullWithLastError(rgHandles[0], hr, "Failed to create load init event.");
44
45 context.hWnd = hWnd;
46 context.hInstance = hInstance;
47 context.hInit = rgHandles[0];
48
49 rgHandles[1] = ::CreateThread(NULL, 0, DisplayThreadProc, reinterpret_cast<LPVOID>(&context), 0, pdwThreadId);
50 ExitOnNullWithLastError(rgHandles[1], hr, "Failed to create display thread.");
51
52 ::WaitForMultipleObjects(countof(rgHandles), rgHandles, FALSE, INFINITE);
53
54 *phThread = rgHandles[1];
55 rgHandles[1] = NULL;
56
57LExit:
58 ReleaseHandle(rgHandles[1]);
59 ReleaseHandle(rgHandles[0]);
60 return hr;
61}
62
63static DWORD WINAPI DisplayThreadProc(
64 __in LPVOID pvContext
65 )
66{
67 HRESULT hr = S_OK;
68
69 DISPLAY_THREAD_CONTEXT* pContext = static_cast<DISPLAY_THREAD_CONTEXT*>(pvContext);
70 HINSTANCE hInstance = pContext->hInstance;
71 HWND hwndParent = pContext->hWnd;
72
73 // We can signal the initialization event as soon as we have copied the context
74 // values into local variables.
75 ::SetEvent(pContext->hInit);
76
77 BOOL fComInitialized = FALSE;
78
79 HANDLE_THEME* pCurrentHandle = NULL;
80 ATOM atomWc = 0;
81 WNDCLASSW wc = { };
82 HWND hWnd = NULL;
83 RECT rc = { };
84 int x = CW_USEDEFAULT;
85 int y = CW_USEDEFAULT;
86
87 BOOL fRedoMsg = FALSE;
88 BOOL fRet = FALSE;
89 MSG msg = { };
90
91 BOOL fCreateIfNecessary = FALSE;
92
93 hr = ::CoInitialize(NULL);
94 ExitOnFailure(hr, "Failed to initialize COM on display thread.");
95 fComInitialized = TRUE;
96
97 // As long as the parent window is alive and kicking, keep this thread going (with or without a theme to display ).
98 while (::IsWindow(hwndParent))
99 {
100 if (pCurrentHandle && fCreateIfNecessary)
101 {
102 THEME* pTheme = pCurrentHandle->pTheme;
103
104 if (CW_USEDEFAULT == x && CW_USEDEFAULT == y && ::GetWindowRect(hwndParent, &rc))
105 {
106 x = rc.left;
107 y = rc.bottom + 20;
108 }
109
110 hr = ThemeCreateParentWindow(pTheme, 0, wc.lpszClassName, pTheme->sczCaption, pTheme->dwStyle, x, y, hwndParent, hInstance, pCurrentHandle, THEME_WINDOW_INITIAL_POSITION_DEFAULT, &hWnd);
111 ExitOnFailure(hr, "Failed to create display window.");
112
113 fCreateIfNecessary = FALSE;
114 }
115
116 // message pump
117 while (fRedoMsg || 0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
118 {
119 if (fRedoMsg)
120 {
121 fRedoMsg = FALSE;
122 }
123
124 if (-1 == fRet)
125 {
126 hr = E_UNEXPECTED;
127 ExitOnFailure(hr, "Unexpected return value from display message pump.");
128 }
129 else if (NULL == msg.hwnd) // Thread message.
130 {
131 if (WM_THMVWR_NEW_THEME == msg.message)
132 {
133 // If there is already a handle, release it.
134 if (pCurrentHandle)
135 {
136 DecrementHandleTheme(pCurrentHandle);
137 pCurrentHandle = NULL;
138 }
139
140 // If the window was created, remember its window location before we destroy
141 // it so so we can open the new window in the same place.
142 if (::IsWindow(hWnd))
143 {
144 ::GetWindowRect(hWnd, &rc);
145 x = rc.left;
146 y = rc.top;
147
148 ::DestroyWindow(hWnd);
149 }
150
151 // If the display window class was registered, unregister it so we can
152 // reuse the same window class name for the new theme.
153 if (atomWc)
154 {
155 if (!::UnregisterClassW(reinterpret_cast<LPCWSTR>(atomWc), hInstance))
156 {
157 DWORD er = ::GetLastError();
158 er = er;
159 }
160
161 atomWc = 0;
162 }
163
164 // If we were provided a new theme handle, create a new window class to
165 // support it.
166 pCurrentHandle = reinterpret_cast<HANDLE_THEME*>(msg.lParam);
167 if (pCurrentHandle)
168 {
169 ThemeInitializeWindowClass(pCurrentHandle->pTheme, &wc, DisplayWndProc, hInstance, THMVWR_WINDOW_CLASS_DISPLAY);
170
171 atomWc = ::RegisterClassW(&wc);
172 if (!atomWc)
173 {
174 ExitWithLastError(hr, "Failed to register display window class.");
175 }
176 }
177 }
178 else if (WM_THMVWR_SHOWPAGE == msg.message)
179 {
180 if (pCurrentHandle && ::IsWindow(hWnd) && pCurrentHandle->pTheme->hwndParent == hWnd)
181 {
182 DWORD dwPageId = static_cast<DWORD>(msg.lParam);
183 int nCmdShow = static_cast<int>(msg.wParam);
184
185 // First show/hide the controls not associated with a page.
186 for (DWORD i = 0; i < pCurrentHandle->pTheme->cControls; ++i)
187 {
188 THEME_CONTROL* pControl = pCurrentHandle->pTheme->rgControls + i;
189 if (!pControl->wPageId)
190 {
191 ThemeShowControl(pControl, nCmdShow);
192 }
193 }
194
195 // If a page id was provided also, show/hide those controls
196 if (dwPageId)
197 {
198 // Ignore error since we aren't using variables and it can only fail when using variables.
199 ThemeShowPage(pCurrentHandle->pTheme, dwPageId, nCmdShow);
200 }
201 }
202 else // display window isn't visible or it doesn't match the current handle.
203 {
204 // Keep the current message around to try again after we break out of this loop
205 // and create the window.
206 fRedoMsg = TRUE;
207 fCreateIfNecessary = TRUE;
208 break;
209 }
210 }
211 }
212 else if (!ThemeHandleKeyboardMessage(pCurrentHandle->pTheme, hwndParent, &msg)) // Window message.
213 {
214 ::TranslateMessage(&msg);
215 ::DispatchMessageW(&msg);
216 }
217 }
218 }
219
220LExit:
221 if (::IsWindow(hWnd))
222 {
223 ::DestroyWindow(hWnd);
224 }
225
226 if (atomWc)
227 {
228 if (!::UnregisterClassW(THMVWR_WINDOW_CLASS_DISPLAY, hInstance))
229 {
230 DWORD er = ::GetLastError();
231 er = er;
232 }
233 }
234
235 DecrementHandleTheme(pCurrentHandle);
236
237 if (fComInitialized)
238 {
239 ::CoUninitialize();
240 }
241
242 return hr;
243}
244
245static LRESULT CALLBACK DisplayWndProc(
246 __in HWND hWnd,
247 __in UINT uMsg,
248 __in WPARAM wParam,
249 __in LPARAM lParam
250 )
251{
252 static DWORD dwProgress = 0;
253 HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
254
255 switch (uMsg)
256 {
257 case WM_NCCREATE:
258 {
259 LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
260 pHandleTheme = reinterpret_cast<HANDLE_THEME*>(lpcs->lpCreateParams);
261 IncrementHandleTheme(pHandleTheme);
262 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pHandleTheme));
263 }
264 break;
265
266 case WM_TIMER:
267 if (!lParam && SUCCEEDED(ThemeSetProgressControl(reinterpret_cast<THEME_CONTROL*>(wParam), dwProgress)))
268 {
269 dwProgress += rand() % 10 + 1;
270 if (dwProgress > 100)
271 {
272 dwProgress = 0;
273 }
274
275 return 0;
276 }
277 break;
278
279 case WM_COMMAND:
280 {
281 WCHAR wzText[1024];
282 ::StringCchPrintfW(wzText, countof(wzText), L"Command %u\r\n", LOWORD(wParam));
283 OutputDebugStringW(wzText);
284 //::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
285 }
286 break;
287
288 case WM_SYSCOMMAND:
289 {
290 WCHAR wzText[1024];
291 ::StringCchPrintfW(wzText, countof(wzText), L"SysCommand %u\r\n", LOWORD(wParam));
292 OutputDebugStringW(wzText);
293 //::MessageBoxW(hWnd, wzText, L"Command fired", MB_OK);
294 }
295 break;
296
297 case WM_NCDESTROY:
298 DecrementHandleTheme(pHandleTheme);
299 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
300 ::PostQuitMessage(0);
301 break;
302
303 case WM_THMUTIL_LOADED_CONTROL:
304 if (pHandleTheme)
305 {
306 return DisplayOnThmLoadedControl(pHandleTheme->pTheme, reinterpret_cast<THEME_LOADEDCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADEDCONTROL_RESULTS*>(lParam));
307 }
308 }
309
310 return ThemeDefWindowProc(pHandleTheme ? pHandleTheme->pTheme : NULL, hWnd, uMsg, wParam, lParam);
311}
312
313static BOOL DisplayOnThmLoadedControl(
314 __in THEME* pTheme,
315 __in const THEME_LOADEDCONTROL_ARGS* args,
316 __in THEME_LOADEDCONTROL_RESULTS* results
317 )
318{
319 HRESULT hr = S_OK;
320 const THEME_CONTROL* pControl = args->pThemeControl;
321
322 // Pre-populate some control types with data.
323 if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type)
324 {
325 hr = WnduLoadRichEditFromResource(pControl->hWnd, MAKEINTRESOURCEA(THMVWR_RES_RICHEDIT_FILE), ::GetModuleHandleW(NULL));
326 ExitOnFailure(hr, "Failed to load richedit text.");
327 }
328 else if (THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type)
329 {
330 UINT_PTR timerId = reinterpret_cast<UINT_PTR>(pControl);
331 UINT_PTR id = ::SetTimer(pTheme->hwndParent, timerId, 500, NULL);
332 id = id; // prevents warning in "ship" build.
333 Assert(id == timerId);
334 }
335
336LExit:
337 results->hr = hr;
338 return TRUE;
339}
diff --git a/src/samples/thmviewer/load.cpp b/src/samples/thmviewer/load.cpp
deleted file mode 100644
index 0267402a..00000000
--- a/src/samples/thmviewer/load.cpp
+++ /dev/null
@@ -1,221 +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
5struct LOAD_THREAD_CONTEXT
6{
7 HWND hWnd;
8 LPCWSTR wzThemePath;
9 LPCWSTR wzWxlPath;
10
11 HANDLE hInit;
12};
13
14static DWORD WINAPI LoadThreadProc(
15 __in LPVOID pvContext
16 );
17
18
19extern "C" HRESULT LoadStart(
20 __in_z LPCWSTR wzThemePath,
21 __in_z_opt LPCWSTR wzWxlPath,
22 __in HWND hWnd,
23 __out HANDLE* phThread
24 )
25{
26 HRESULT hr = S_OK;
27 HANDLE rgHandles[2] = { };
28 LOAD_THREAD_CONTEXT context = { };
29
30 rgHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
31 ExitOnNullWithLastError(rgHandles[0], hr, "Failed to create load init event.");
32
33 context.hWnd = hWnd;
34 context.wzThemePath = wzThemePath;
35 context.wzWxlPath = wzWxlPath;
36 context.hInit = rgHandles[0];
37
38 rgHandles[1] = ::CreateThread(NULL, 0, LoadThreadProc, &context, 0, NULL);
39 ExitOnNullWithLastError(rgHandles[1], hr, "Failed to create load thread.");
40
41 ::WaitForMultipleObjects(countof(rgHandles), rgHandles, FALSE, INFINITE);
42
43 *phThread = rgHandles[1];
44 rgHandles[1] = NULL;
45
46LExit:
47 ReleaseHandle(rgHandles[1]);
48 ReleaseHandle(rgHandles[0]);
49 return hr;
50}
51
52
53static DWORD WINAPI LoadThreadProc(
54 __in LPVOID pvContext
55 )
56{
57 HRESULT hr = S_OK;
58 WIX_LOCALIZATION* pWixLoc = NULL;
59 LPWSTR sczThemePath = NULL;
60 LPWSTR sczWxlPath = NULL;
61 BOOL fComInitialized = FALSE;
62 HANDLE hDirectory = INVALID_HANDLE_VALUE;
63 LPWSTR sczDirectory = NULL;
64 LPWSTR wzFileName = NULL;
65
66 THEME* pTheme = NULL;
67 HANDLE_THEME* pHandle = NULL;
68
69 LOAD_THREAD_CONTEXT* pContext = static_cast<LOAD_THREAD_CONTEXT*>(pvContext);
70 HWND hWnd = pContext->hWnd;
71
72 hr = StrAllocString(&sczThemePath, pContext->wzThemePath, 0);
73 ExitOnFailure(hr, "Failed to copy path to initial theme file.");
74
75 if (pContext->wzWxlPath)
76 {
77 hr = StrAllocString(&sczWxlPath, pContext->wzWxlPath, 0);
78 ExitOnFailure(hr, "Failed to copy .wxl path to initial file.");
79 }
80
81 // We can signal the initialization event as soon as we have copied the context
82 // values into local variables.
83 ::SetEvent(pContext->hInit);
84
85 hr = ::CoInitialize(NULL);
86 ExitOnFailure(hr, "Failed to initialize COM on load thread.");
87 fComInitialized = TRUE;
88
89 // Open a handle to the directory so we can put a notification on it.
90 hr = PathGetDirectory(sczThemePath, &sczDirectory);
91 ExitOnFailure(hr, "Failed to get path directory.");
92
93 wzFileName = PathFile(sczThemePath);
94
95 hDirectory = ::CreateFileW(sczDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
96 if (INVALID_HANDLE_VALUE == hDirectory)
97 {
98 ExitWithLastError(hr, "Failed to open directory: %ls", sczDirectory);
99 }
100
101 BOOL fUpdated = FALSE;
102 do
103 {
104 // Get the last modified time on the file we're loading for verification that the
105 // file actually gets changed down below.
106 FILETIME ftModified = { };
107 FileGetTime(sczThemePath, NULL, NULL, &ftModified);
108
109 ::SendMessageW(hWnd, WM_THMVWR_THEME_LOAD_BEGIN, 0, 0);
110
111 // Try to load the theme file.
112 hr = ThemeLoadFromFile(sczThemePath, &pTheme);
113 if (FAILED(hr))
114 {
115 ::SendMessageW(hWnd, WM_THMVWR_THEME_LOAD_ERROR, 0, hr);
116 }
117 else
118 {
119 if (sczWxlPath)
120 {
121 hr = LocLoadFromFile(sczWxlPath, &pWixLoc);
122 ExitOnFailure(hr, "Failed to load loc file from path: %ls", sczWxlPath);
123
124 hr = ThemeLocalize(pTheme, pWixLoc);
125 ExitOnFailure(hr, "Failed to localize theme: %ls", sczWxlPath);
126 }
127
128 hr = AllocHandleTheme(pTheme, &pHandle);
129 ExitOnFailure(hr, "Failed to allocate handle to theme");
130
131 ::SendMessageW(hWnd, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle));
132 pHandle = NULL;
133 }
134
135 fUpdated = FALSE;
136 do
137 {
138 DWORD rgbNotifications[1024];
139 DWORD cbRead = 0;
140 if (!::ReadDirectoryChangesW(hDirectory, rgbNotifications, sizeof(rgbNotifications), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &cbRead, NULL, NULL))
141 {
142 ExitWithLastError(hr, "Failed while watching directory: %ls", sczDirectory);
143 }
144
145 // Wait for half a second to let all the file handles get closed to minimize access
146 // denied errors.
147 ::Sleep(500);
148
149 FILE_NOTIFY_INFORMATION* pNotification = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(rgbNotifications);
150 while (pNotification)
151 {
152 // If our file was updated, check to see if the modified time really changed. The notifications
153 // are often trigger happy thinking the file changed two or three times in a row. Maybe it's AV
154 // software creating the problems but actually checking the modified date works well.
155 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pNotification->FileName, pNotification->FileNameLength / sizeof(WCHAR), wzFileName, -1))
156 {
157 FILETIME ft = { };
158 FileGetTime(sczThemePath, NULL, NULL, &ft);
159
160 fUpdated = (ftModified.dwHighDateTime < ft.dwHighDateTime) || (ftModified.dwHighDateTime == ft.dwHighDateTime && ftModified.dwLowDateTime < ft.dwLowDateTime);
161 break;
162 }
163
164 pNotification = pNotification->NextEntryOffset ? reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(pNotification) + pNotification->NextEntryOffset) : NULL;
165 }
166 } while (!fUpdated);
167 } while(fUpdated);
168
169LExit:
170 if (fComInitialized)
171 {
172 ::CoUninitialize();
173 }
174
175 LocFree(pWixLoc);
176 ReleaseFileHandle(hDirectory);
177 ReleaseStr(sczDirectory);
178 ReleaseStr(sczThemePath);
179 ReleaseStr(sczWxlPath);
180 return hr;
181}
182
183extern "C" HRESULT AllocHandleTheme(
184 __in THEME* pTheme,
185 __out HANDLE_THEME** ppHandle
186 )
187{
188 HRESULT hr = S_OK;
189 HANDLE_THEME* pHandle = NULL;
190
191 pHandle = static_cast<HANDLE_THEME*>(MemAlloc(sizeof(HANDLE_THEME), TRUE));
192 ExitOnNull(pHandle, hr, E_OUTOFMEMORY, "Failed to allocate theme handle.");
193
194 pHandle->cReferences = 1;
195 pHandle->pTheme = pTheme;
196
197 *ppHandle = pHandle;
198 pHandle = NULL;
199
200LExit:
201 ReleaseMem(pHandle);
202 return hr;
203}
204
205extern "C" void IncrementHandleTheme(
206 __in HANDLE_THEME* pHandle
207 )
208{
209 ::InterlockedIncrement(reinterpret_cast<LONG*>(&pHandle->cReferences));
210}
211
212extern "C" void DecrementHandleTheme(
213 __in HANDLE_THEME* pHandle
214 )
215{
216 if (pHandle && 0 == ::InterlockedDecrement(reinterpret_cast<LONG*>(&pHandle->cReferences)))
217 {
218 ThemeFree(pHandle->pTheme);
219 MemFree(pHandle);
220 }
221}
diff --git a/src/samples/thmviewer/precomp.cpp b/src/samples/thmviewer/precomp.cpp
deleted file mode 100644
index 37664a1c..00000000
--- a/src/samples/thmviewer/precomp.cpp
+++ /dev/null
@@ -1,3 +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"
diff --git a/src/samples/thmviewer/precomp.h b/src/samples/thmviewer/precomp.h
deleted file mode 100644
index 762a0623..00000000
--- a/src/samples/thmviewer/precomp.h
+++ /dev/null
@@ -1,72 +0,0 @@
1#pragma once
2// 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.
3
4
5#include <windows.h>
6#include <msiquery.h>
7#include <objbase.h>
8#include <shlobj.h>
9#include <shlwapi.h>
10#include <stdlib.h>
11#include <strsafe.h>
12
13#pragma warning(push)
14#pragma warning(disable:4458)
15#include <gdiplus.h>
16#pragma warning(pop)
17
18#include "dutil.h"
19#include "apputil.h"
20#include "memutil.h"
21#include "dictutil.h"
22#include "dirutil.h"
23#include "fileutil.h"
24#include "locutil.h"
25#include "logutil.h"
26#include "pathutil.h"
27#include "resrutil.h"
28#include "shelutil.h"
29#include "strutil.h"
30#include "thmutil.h"
31#include "wndutil.h"
32
33#include "resource.h"
34
35struct HANDLE_THEME
36{
37 DWORD cReferences;
38 THEME* pTheme;
39};
40
41enum WM_THMVWR
42{
43 WM_THMVWR_SHOWPAGE = WM_APP,
44 WM_THMVWR_PARSE_FILE,
45 WM_THMVWR_NEW_THEME,
46 WM_THMVWR_THEME_LOAD_ERROR,
47 WM_THMVWR_THEME_LOAD_BEGIN,
48};
49
50extern "C" HRESULT DisplayStart(
51 __in HINSTANCE hInstance,
52 __in HWND hWnd,
53 __out HANDLE *phThread,
54 __out DWORD* pdwThreadId
55 );
56extern "C" HRESULT LoadStart(
57 __in_z LPCWSTR wzThemePath,
58 __in_z LPCWSTR wzWxlPath,
59 __in HWND hWnd,
60 __out HANDLE* phThread
61 );
62
63extern "C" HRESULT AllocHandleTheme(
64 __in THEME* pTheme,
65 __out HANDLE_THEME** ppHandle
66 );
67extern "C" void IncrementHandleTheme(
68 __in HANDLE_THEME* pHandle
69 );
70extern "C" void DecrementHandleTheme(
71 __in HANDLE_THEME* pHandle
72 );
diff --git a/src/samples/thmviewer/resource.h b/src/samples/thmviewer/resource.h
deleted file mode 100644
index 4acc32cc..00000000
--- a/src/samples/thmviewer/resource.h
+++ /dev/null
@@ -1,16 +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#define IDC_STATIC -1
4#define THMVWR_RES_THEME_FILE 1
5#define THMVWR_RES_RICHEDIT_FILE 2
6
7// Next default values for new objects
8//
9#ifdef APSTUDIO_INVOKED
10#ifndef APSTUDIO_READONLY_SYMBOLS
11#define _APS_NEXT_RESOURCE_VALUE 102
12#define _APS_NEXT_COMMAND_VALUE 40001
13#define _APS_NEXT_CONTROL_VALUE 1003
14#define _APS_NEXT_SYMED_VALUE 101
15#endif
16#endif
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}
diff --git a/src/samples/thmviewer/thmviewer.manifest b/src/samples/thmviewer/thmviewer.manifest
deleted file mode 100644
index 4663b61c..00000000
--- a/src/samples/thmviewer/thmviewer.manifest
+++ /dev/null
@@ -1,19 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3
4
5<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
6 <assemblyIdentity name="thmviewer.exe" version="1.0.0.0" processorArchitecture="x86" type="win32"/>
7 <description>WiX Toolset Theme Viewer</description>
8 <dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /></dependentAssembly></dependency>
9 <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"><application><supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/><supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/></application></compatibility>
10 <application xmlns="urn:schemas-microsoft-com:asm.v3">
11 <windowsSettings xmlns="urn:schemas-microsoft-com:asm.v3">
12 <!-- pre-Win10 1607 -->
13 <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
14 <!-- Win10 picks the first one it recognizes -->
15 <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor, System</dpiAwareness>
16 </windowsSettings>
17 </application>
18 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security></trustInfo>
19</assembly>
diff --git a/src/samples/thmviewer/thmviewer.rc b/src/samples/thmviewer/thmviewer.rc
deleted file mode 100644
index dc6d7242..00000000
--- a/src/samples/thmviewer/thmviewer.rc
+++ /dev/null
@@ -1,12 +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 <winver.h>
4#include <windows.h>
5#include "resource.h"
6
7//#define MANIFEST_RESOURCE_ID 1
8//MANIFEST_RESOURCE_ID RT_MANIFEST "thmviewer.manifest"
9
10LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
11THMVWR_RES_THEME_FILE RCDATA "Resources\\thm.xml"
12THMVWR_RES_RICHEDIT_FILE RCDATA "Resources\\LoremIpsum.rtf"
diff --git a/src/samples/thmviewer/thmviewer.v3.ncrunchproject b/src/samples/thmviewer/thmviewer.v3.ncrunchproject
deleted file mode 100644
index 3cffd6ce..00000000
--- a/src/samples/thmviewer/thmviewer.v3.ncrunchproject
+++ /dev/null
@@ -1,7 +0,0 @@
1<ProjectConfiguration>
2 <Settings>
3 <AdditionalFilesToIncludeForProject>
4 <Value>thmviewer.manifest</Value>
5 </AdditionalFilesToIncludeForProject>
6 </Settings>
7</ProjectConfiguration> \ No newline at end of file
diff --git a/src/samples/thmviewer/thmviewer.vcxproj b/src/samples/thmviewer/thmviewer.vcxproj
deleted file mode 100644
index 3fd87878..00000000
--- a/src/samples/thmviewer/thmviewer.vcxproj
+++ /dev/null
@@ -1,69 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|Win32">
7 <Configuration>Debug</Configuration>
8 <Platform>Win32</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Release|Win32">
11 <Configuration>Release</Configuration>
12 <Platform>Win32</Platform>
13 </ProjectConfiguration>
14 </ItemGroup>
15
16 <PropertyGroup Label="Globals">
17 <ProjectGuid>{95228C13-97F5-484A-B4A2-ECF4618B0881}</ProjectGuid>
18 <Keyword>Win32Proj</Keyword>
19 <ConfigurationType>Application</ConfigurationType>
20 <CharacterSet>Unicode</CharacterSet>
21 <Description>WiX Toolset Theme Viewer</Description>
22 </PropertyGroup>
23
24 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
25 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
26
27 <ImportGroup Label="ExtensionSettings">
28 </ImportGroup>
29
30 <ImportGroup Label="Shared">
31 </ImportGroup>
32
33 <PropertyGroup>
34 <ProjectAdditionalLinkLibraries>comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
35 </PropertyGroup>
36
37 <ItemGroup>
38 <ClCompile Include="display.cpp" />
39 <ClCompile Include="load.cpp" />
40 <ClCompile Include="precomp.cpp">
41 <PrecompiledHeader>Create</PrecompiledHeader>
42 </ClCompile>
43 <ClCompile Include="thmviewer.cpp" />
44 </ItemGroup>
45 <ItemGroup>
46 <ClInclude Include="precomp.h" />
47 <ClInclude Include="resource.h" />
48 </ItemGroup>
49 <ItemGroup>
50 <None Include="packages.config" />
51 <None Include="Resources\LoremIpsum.rtf" />
52 <None Include="Resources\thm.xml" />
53 </ItemGroup>
54 <ItemGroup>
55 <ResourceCompile Include="thmviewer.rc" />
56 </ItemGroup>
57 <ItemGroup>
58 <Manifest Include="thmviewer.manifest" />
59 </ItemGroup>
60
61 <ItemGroup>
62 <PackageReference Include="WixToolset.DUtil" />
63
64 <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
65 <PackageReference Include="GitInfo" PrivateAssets="All" />
66 </ItemGroup>
67
68 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
69</Project>
diff --git a/src/samples/thmviewer/thmviewer.vcxproj.filters b/src/samples/thmviewer/thmviewer.vcxproj.filters
deleted file mode 100644
index 488d5510..00000000
--- a/src/samples/thmviewer/thmviewer.vcxproj.filters
+++ /dev/null
@@ -1,56 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup>
4 <Filter Include="Source Files">
5 <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6 <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7 </Filter>
8 <Filter Include="Header Files">
9 <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10 <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11 </Filter>
12 <Filter Include="Resource Files">
13 <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14 <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 <ClCompile Include="thmviewer.cpp">
19 <Filter>Source Files</Filter>
20 </ClCompile>
21 <ClCompile Include="display.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
24 <ClCompile Include="load.cpp">
25 <Filter>Source Files</Filter>
26 </ClCompile>
27 <ClCompile Include="precomp.cpp">
28 <Filter>Source Files</Filter>
29 </ClCompile>
30 </ItemGroup>
31 <ItemGroup>
32 <None Include="Resources\thm.xml">
33 <Filter>Resource Files</Filter>
34 </None>
35 <None Include="Resources\LoremIpsum.rtf">
36 <Filter>Resource Files</Filter>
37 </None>
38 <None Include="packages.config" />
39 </ItemGroup>
40 <ItemGroup>
41 <ClInclude Include="precomp.h">
42 <Filter>Header Files</Filter>
43 </ClInclude>
44 <ClInclude Include="resource.h">
45 <Filter>Header Files</Filter>
46 </ClInclude>
47 </ItemGroup>
48 <ItemGroup>
49 <ResourceCompile Include="thmviewer.rc">
50 <Filter>Resource Files</Filter>
51 </ResourceCompile>
52 </ItemGroup>
53 <ItemGroup>
54 <Manifest Include="thmviewer.manifest" />
55 </ItemGroup>
56</Project> \ No newline at end of file