aboutsummaryrefslogtreecommitdiff
path: root/src/tools/thmviewer/load.cpp
diff options
context:
space:
mode:
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}