summaryrefslogtreecommitdiff
path: root/src/tools/thmviewer/load.cpp
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/tools/thmviewer/load.cpp
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/tools/thmviewer/load.cpp')
-rw-r--r--src/tools/thmviewer/load.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/tools/thmviewer/load.cpp b/src/tools/thmviewer/load.cpp
new file mode 100644
index 00000000..0267402a
--- /dev/null
+++ b/src/tools/thmviewer/load.cpp
@@ -0,0 +1,221 @@
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}