@joebigelow / wix-1 / commits / 5d837500

Initial commit

Rob Mensching committed Sep 3, 2017 at 11:22 UTC 5d8375007754101ff2889d0e79486c8f9b7cf5ab
120 files changed +49351
dutil.sln new
+31
@@ -0,0 +1,31 @@
1 +
2 +Microsoft Visual Studio Solution File, Format Version 12.00
3 +# Visual Studio 15
4 +VisualStudioVersion = 15.0.26730.12
5 +MinimumVisualStudioVersion = 15.0.26124.0
6 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}"
7 +EndProject
8 +Global
9 + GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 + Debug|x64 = Debug|x64
11 + Debug|x86 = Debug|x86
12 + Release|x64 = Release|x64
13 + Release|x86 = Release|x86
14 + EndGlobalSection
15 + GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64
17 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64
18 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32
19 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32
20 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64
21 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64
22 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32
23 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32
24 + EndGlobalSection
25 + GlobalSection(SolutionProperties) = preSolution
26 + HideSolutionNode = FALSE
27 + EndGlobalSection
28 + GlobalSection(ExtensibilityGlobals) = postSolution
29 + SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133}
30 + EndGlobalSection
31 +EndGlobal
src/dutil/acl2util.cpp new
+121
@@ -0,0 +1,121 @@
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 +
5 +/********************************************************************
6 +AclCalculateServiceSidString - gets the SID string for the given service name
7 +
8 +NOTE: psczSid should be freed with StrFree()
9 +********************************************************************/
10 +extern "C" HRESULT DAPI AclCalculateServiceSidString(
11 + __in LPCWSTR wzServiceName,
12 + __in int cchServiceName,
13 + __deref_out_z LPWSTR* psczSid
14 + )
15 +{
16 + // TODO: use undocumented RtlCreateServiceSid function?
17 + // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx
18 + // Assume little endian.
19 + HRESULT hr = S_OK;
20 + LPWSTR sczUpperServiceName = NULL;
21 + DWORD cbHash = SHA1_HASH_LEN;
22 + BYTE* pbHash = NULL;
23 +
24 + Assert(psczSid);
25 +
26 + if (0 == cchServiceName)
27 + {
28 + hr = ::StringCchLengthW(wzServiceName, INT_MAX, reinterpret_cast<size_t*>(&cchServiceName));
29 + ExitOnFailure(hr, "Failed to get the length of the service name.");
30 + }
31 +
32 + hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName);
33 + ExitOnFailure(hr, "Failed to upper case the service name.");
34 +
35 + pbHash = reinterpret_cast<BYTE*>(MemAlloc(cbHash, TRUE));
36 + ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array.");
37 +
38 + hr = CrypHashBuffer(reinterpret_cast<BYTE*>(sczUpperServiceName), cchServiceName * 2, PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash);
39 + ExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name.");
40 +
41 + hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u",
42 + MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])),
43 + MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])),
44 + MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])),
45 + MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])),
46 + MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19])));
47 +
48 +LExit:
49 + ReleaseMem(pbHash);
50 + ReleaseStr(sczUpperServiceName);
51 +
52 + return hr;
53 +}
54 +
55 +
56 +/********************************************************************
57 +AclGetAccountSidStringEx - gets a string version of the account's SID
58 + calculates a service's SID if lookup fails
59 +
60 +NOTE: psczSid should be freed with StrFree()
61 +********************************************************************/
62 +extern "C" HRESULT DAPI AclGetAccountSidStringEx(
63 + __in_z LPCWSTR wzSystem,
64 + __in_z LPCWSTR wzAccount,
65 + __deref_out_z LPWSTR* psczSid
66 + )
67 +{
68 + HRESULT hr = S_OK;
69 + int cchAccount = 0;
70 + PSID psid = NULL;
71 + LPWSTR pwz = NULL;
72 + LPWSTR sczSid = NULL;
73 +
74 + Assert(psczSid);
75 +
76 + hr = AclGetAccountSid(wzSystem, wzAccount, &psid);
77 + if (SUCCEEDED(hr))
78 + {
79 + Assert(::IsValidSid(psid));
80 +
81 + if (!::ConvertSidToStringSidW(psid, &pwz))
82 + {
83 + ExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount);
84 + }
85 +
86 + hr = StrAllocString(psczSid, pwz, 0);
87 + }
88 + else
89 + {
90 + if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr)
91 + {
92 + HRESULT hrLength = ::StringCchLengthW(wzAccount, INT_MAX, reinterpret_cast<size_t*>(&cchAccount));
93 + ExitOnFailure(hrLength, "Failed to get the length of the account name.");
94 +
95 + if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11))
96 + {
97 + // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it.
98 + LPCWSTR wzServiceName = &wzAccount[11];
99 + hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid);
100 + ExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName);
101 +
102 + *psczSid = sczSid;
103 + sczSid = NULL;
104 + }
105 + }
106 + ExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount);
107 + }
108 +
109 +LExit:
110 + ReleaseStr(sczSid);
111 + if (pwz)
112 + {
113 + ::LocalFree(pwz);
114 + }
115 + if (psid)
116 + {
117 + AclFreeSid(psid);
118 + }
119 +
120 + return hr;
121 +}
src/dutil/aclutil.cpp new
+1030
@@ -0,0 +1,1030 @@
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 +
5 +/********************************************************************
6 +AclCheckAccess - determines if token has appropriate privileges
7 +
8 +NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero
9 +if hToken is NULL, the thread will be checked
10 +if hToken is not NULL the token must be an impersonation token
11 +********************************************************************/
12 +extern "C" HRESULT DAPI AclCheckAccess(
13 + __in HANDLE hToken,
14 + __in ACL_ACCESS* paa
15 + )
16 +{
17 + HRESULT hr = S_OK;
18 + PSID psid = NULL;
19 + BOOL fIsMember = FALSE;
20 +
21 + ExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check");
22 + Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask);
23 +
24 + if (paa->pwzAccountName)
25 + {
26 + hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid);
27 + ExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName);
28 + }
29 + else
30 + {
31 + if (!::AllocateAndInitializeSid(&paa->sia, paa->nSubAuthorityCount, paa->nSubAuthority[0], paa->nSubAuthority[1], paa->nSubAuthority[2], paa->nSubAuthority[3], paa->nSubAuthority[4], paa->nSubAuthority[5], paa->nSubAuthority[6], paa->nSubAuthority[7], &psid))
32 + {
33 + ExitWithLastError(hr, "failed to initialize SID");
34 + }
35 + }
36 +
37 + if (!::CheckTokenMembership(hToken, psid, &fIsMember))
38 + {
39 + ExitWithLastError(hr, "failed to check membership");
40 + }
41 +
42 + fIsMember ? hr = S_OK : hr = S_FALSE;
43 +
44 +LExit:
45 + if (psid)
46 + {
47 + ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid?
48 + }
49 +
50 + return hr;
51 +}
52 +
53 +
54 +/********************************************************************
55 +AclCheckAdministratorAccess - determines if token has Administrator privileges
56 +
57 +NOTE: if hToken is NULL, the thread will be checked
58 +if hToken is not NULL the token must be an impersonation token
59 +********************************************************************/
60 +extern "C" HRESULT DAPI AclCheckAdministratorAccess(
61 + __in HANDLE hToken
62 + )
63 +{
64 + ACL_ACCESS aa;
65 + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
66 +
67 + memset(&aa, 0, sizeof(aa));
68 + aa.sia = siaNt;
69 + aa.nSubAuthorityCount = 2;
70 + aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
71 + aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
72 +
73 + return AclCheckAccess(hToken, &aa);
74 +}
75 +
76 +
77 +/********************************************************************
78 +AclCheckLocalSystemAccess - determines if token has LocalSystem privileges
79 +
80 +NOTE: if hToken is NULL, the thread will be checked
81 +if hToken is not NULL the token must be an impersonation token
82 +********************************************************************/
83 +extern "C" HRESULT DAPI AclCheckLocalSystemAccess(
84 + __in HANDLE hToken
85 + )
86 +{
87 + ACL_ACCESS aa;
88 + SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
89 +
90 + memset(&aa, 0, sizeof(aa));
91 + aa.sia = siaNt;
92 + aa.nSubAuthorityCount = 1;
93 + aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID;
94 +
95 + return AclCheckAccess(hToken, &aa);
96 +}
97 +
98 +
99 +/********************************************************************
100 +AclGetWellKnownSid - returns a SID for the specified account
101 +
102 +********************************************************************/
103 +extern "C" HRESULT DAPI AclGetWellKnownSid(
104 + __in WELL_KNOWN_SID_TYPE wkst,
105 + __deref_out PSID* ppsid
106 + )
107 +{
108 + Assert(ppsid);
109 +
110 + HRESULT hr = S_OK;;
111 + PSID psid = NULL;
112 + DWORD cbSid = SECURITY_MAX_SID_SIZE;
113 +
114 + PSID psidTemp = NULL;
115 +#if(_WIN32_WINNT < 0x0501)
116 + SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
117 + SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
118 + SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
119 + BOOL fSuccess = FALSE;
120 +#endif
121 +
122 + //
123 + // allocate memory for the SID and get it
124 + //
125 + psid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
126 + ExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID");
127 +
128 +#if(_WIN32_WINNT < 0x0501)
129 + switch (wkst)
130 + {
131 + case WinWorldSid: // Everyone
132 + fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
133 + break;
134 + case WinAuthenticatedUserSid: // Authenticated Users
135 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
136 + break;
137 + case WinLocalSystemSid: // LocalSystem
138 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
139 + break;
140 + case WinLocalServiceSid: // LocalService
141 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
142 + break;
143 + case WinNetworkServiceSid: // NetworkService
144 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
145 + break;
146 + case WinBuiltinGuestsSid: // Guests
147 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp);
148 + break;
149 + case WinBuiltinAdministratorsSid: // Administrators
150 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp);
151 + break;
152 + case WinBuiltinUsersSid: // Users
153 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp);
154 + break;
155 + case WinCreatorOwnerSid: //CREATOR OWNER
156 + fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
157 + break;
158 + case WinInteractiveSid: // INTERACTIVE
159 + fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
160 + break;
161 + default:
162 + hr = E_INVALIDARG;
163 + ExitOnFailure(hr, "unknown well known SID: %d", wkst);
164 + }
165 +
166 + if (!fSuccess)
167 + ExitOnLastError(hr, "failed to allocate well known SID: %d", wkst);
168 +
169 + if (!::CopySid(cbSid, psid, psidTemp))
170 + ExitOnLastError(hr, "failed to create well known SID: %d", wkst);
171 +#else
172 + Assert(NULL == psidTemp);
173 + if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid))
174 + {
175 + ExitWithLastError(hr, "failed to create well known SID: %d", wkst);
176 + }
177 +#endif
178 +
179 + *ppsid = psid;
180 + psid = NULL; // null it here so it won't be released below
181 +
182 + Assert(S_OK == hr && ::IsValidSid(*ppsid));
183 +LExit:
184 + if (psidTemp)
185 + {
186 + ::FreeSid(psidTemp);
187 + }
188 +
189 + ReleaseMem(psid);
190 +
191 + return hr;
192 +}
193 +
194 +
195 +/********************************************************************
196 +AclGetAccountSid - returns a SID for the specified account
197 +
198 +********************************************************************/
199 +extern "C" HRESULT DAPI AclGetAccountSid(
200 + __in_opt LPCWSTR wzSystem,
201 + __in_z LPCWSTR wzAccount,
202 + __deref_out PSID* ppsid
203 + )
204 +{
205 + Assert(wzAccount && *wzAccount && ppsid);
206 +
207 + HRESULT hr = S_OK;
208 + UINT er = ERROR_SUCCESS;
209 + PSID psid = NULL;
210 + DWORD cbSid = SECURITY_MAX_SID_SIZE;
211 + LPWSTR pwzDomainName = NULL;
212 + DWORD cbDomainName = 255;
213 + SID_NAME_USE peUse;
214 +
215 + //
216 + // allocate memory for the SID and domain name
217 + //
218 + psid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
219 + ExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID");
220 + hr = StrAlloc(&pwzDomainName, cbDomainName);
221 + ExitOnFailure(hr, "failed to allocate string for domain name");
222 +
223 + //
224 + // try to lookup the account now
225 + //
226 + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse))
227 + {
228 + // if one of the buffers wasn't large enough
229 + er = ::GetLastError();
230 + if (ERROR_INSUFFICIENT_BUFFER == er)
231 + {
232 + if (SECURITY_MAX_SID_SIZE < cbSid)
233 + {
234 + PSID psidNew = static_cast<PSID>(MemReAlloc(psid, cbSid, TRUE));
235 + ExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount);
236 +
237 + psid = psidNew;
238 + }
239 + if (255 < cbDomainName)
240 + {
241 + hr = StrAlloc(&pwzDomainName, cbDomainName);
242 + ExitOnFailure(hr, "failed to allocate string for domain name");
243 + }
244 +
245 + if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse))
246 + {
247 + ExitWithLastError(hr, "failed to lookup account: %ls", wzAccount);
248 + }
249 + }
250 + else
251 + {
252 + ExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount);
253 + }
254 + }
255 +
256 + *ppsid = psid;
257 + psid = NULL;
258 +
259 + hr = S_OK;
260 +LExit:
261 + ReleaseStr(pwzDomainName);
262 + ReleaseMem(psid);
263 +
264 + return hr;
265 +}
266 +
267 +
268 +/********************************************************************
269 +AclGetAccountSidString - gets a string version of the user's SID
270 +
271 +NOTE: ppwzSid should be freed with StrFree()
272 +********************************************************************/
273 +extern "C" HRESULT DAPI AclGetAccountSidString(
274 + __in_z LPCWSTR wzSystem,
275 + __in_z LPCWSTR wzAccount,
276 + __deref_out_z LPWSTR* ppwzSid
277 + )
278 +{
279 + Assert(ppwzSid);
280 + HRESULT hr = S_OK;
281 + PSID psid = NULL;
282 + LPWSTR pwz = NULL;
283 +
284 + *ppwzSid = NULL;
285 +
286 + hr = AclGetAccountSid(wzSystem, wzAccount, &psid);
287 + ExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount);
288 + Assert(::IsValidSid(psid));
289 +
290 + if (!::ConvertSidToStringSidW(psid, &pwz))
291 + {
292 + ExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount);
293 + }
294 +
295 + hr = StrAllocString(ppwzSid, pwz, 0);
296 +
297 +LExit:
298 + if (FAILED(hr))
299 + {
300 + ReleaseNullStr(*ppwzSid);
301 + }
302 +
303 + if (pwz)
304 + {
305 + ::LocalFree(pwz);
306 + }
307 +
308 + if (psid)
309 + {
310 + AclFreeSid(psid);
311 + }
312 +
313 + return hr;
314 +}
315 +
316 +
317 +/********************************************************************
318 +AclCreateDacl - creates a DACL from ACL_ACE structures
319 +
320 +********************************************************************/
321 +extern "C" HRESULT DAPI AclCreateDacl(
322 + __in_ecount(cDeny) ACL_ACE rgaaDeny[],
323 + __in DWORD cDeny,
324 + __in_ecount(cAllow) ACL_ACE rgaaAllow[],
325 + __in DWORD cAllow,
326 + __deref_out ACL** ppAcl
327 + )
328 +{
329 + Assert(ppAcl);
330 + HRESULT hr = S_OK;
331 + ACL* pAcl = NULL;
332 + DWORD cbAcl = 0;
333 + DWORD i;
334 +
335 + *ppAcl = NULL;
336 +
337 + // initialize the ACL
338 + cbAcl = sizeof(ACL);
339 + for (i = 0; i < cDeny; ++i)
340 + {
341 + cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD);
342 + }
343 +
344 + for (i = 0; i < cAllow; ++i)
345 + {
346 + cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD);
347 + }
348 +
349 + pAcl = static_cast<ACL*>(MemAlloc(cbAcl, TRUE));
350 + ExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL");
351 +
352 +#pragma prefast(push)
353 +#pragma prefast(disable:25029)
354 + if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION))
355 +#pragma prefast(pop)
356 + {
357 + ExitWithLastError(hr, "failed to initialize ACL");
358 + }
359 +
360 + // add in the ACEs (denied first)
361 + for (i = 0; i < cDeny; ++i)
362 + {
363 +#pragma prefast(push)
364 +#pragma prefast(disable:25029)
365 + if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid))
366 +#pragma prefast(pop)
367 + {
368 + ExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i);
369 + }
370 + }
371 + for (i = 0; i < cAllow; ++i)
372 + {
373 +#pragma prefast(push)
374 +#pragma prefast(disable:25029)
375 + if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid))
376 +#pragma prefast(pop)
377 + {
378 + ExitWithLastError(hr, "failed to add access allowed ACE #$d to ACL", i);
379 + }
380 + }
381 +
382 + *ppAcl = pAcl;
383 + pAcl = NULL;
384 + AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL");
385 + Assert(S_OK == hr);
386 +LExit:
387 + if (pAcl)
388 + {
389 + AclFreeDacl(pAcl);
390 + }
391 +
392 + return hr;
393 +}
394 +
395 +
396 +/********************************************************************
397 +AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure
398 +
399 +********************************************************************/
400 +extern "C" HRESULT DAPI AclAddToDacl(
401 + __in ACL* pAcl,
402 + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[],
403 + __in DWORD cDeny,
404 + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[],
405 + __in DWORD cAllow,
406 + __deref_out ACL** ppAclNew
407 + )
408 +{
409 + Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew);
410 + HRESULT hr = S_OK;
411 +
412 + ACL_SIZE_INFORMATION asi;
413 + ACL_ACE* paaNewDeny = NULL;
414 + DWORD cNewDeny = 0;
415 + ACL_ACE* paaNewAllow = NULL;
416 + DWORD cNewAllow = 0;
417 +
418 + ACCESS_ALLOWED_ACE* paaa;
419 + ACCESS_DENIED_ACE* pada;
420 + DWORD i;
421 +
422 + // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay)
423 + if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation))
424 + {
425 + ExitWithLastError(hr, "failed to get information about original ACL");
426 + }
427 +
428 + if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow
429 + (asi.AceCount + cDeny) < cDeny || // check for overflow
430 + (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE))
431 + {
432 + hr = E_OUTOFMEMORY;
433 + ExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny));
434 + }
435 +
436 + paaNewDeny = static_cast<ACL_ACE*>(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE));
437 + ExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs");
438 +
439 + if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow
440 + (asi.AceCount + cAllow) < cAllow || // check for overflow
441 + (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE))
442 + {
443 + hr = E_OUTOFMEMORY;
444 + ExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow));
445 + }
446 +
447 + paaNewAllow = static_cast<ACL_ACE*>(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE));
448 + ExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs");
449 +
450 + // fill in the new structures with old data then new data (denied first)
451 + for (i = 0; i < asi.AceCount; ++i)
452 + {
453 + if (!::GetAce(pAcl, i, reinterpret_cast<LPVOID*>(&pada)))
454 + {
455 + ExitWithLastError(hr, "failed to get ACE #%d from ACL", i);
456 + }
457 +
458 + if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType)
459 + {
460 + continue; // skip non-denied aces
461 + }
462 +
463 + paaNewDeny[i].dwFlags = pada->Header.AceFlags;
464 + paaNewDeny[i].dwMask = pada->Mask;
465 + paaNewDeny[i].psid = reinterpret_cast<PSID>(&(pada->SidStart));
466 + ++cNewDeny;
467 + }
468 +
469 + memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny);
470 + cNewDeny += cDeny;
471 +
472 +
473 + for (i = 0; i < asi.AceCount; ++i)
474 + {
475 + if (!::GetAce(pAcl, i, reinterpret_cast<LPVOID*>(&paaa)))
476 + {
477 + ExitWithLastError(hr, "failed to get ACE #%d from ACL", i);
478 + }
479 +
480 + if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType)
481 + {
482 + continue; // skip non-allowed aces
483 + }
484 +
485 + paaNewAllow[i].dwFlags = paaa->Header.AceFlags;
486 + paaNewAllow[i].dwMask = paaa->Mask;
487 + paaNewAllow[i].psid = reinterpret_cast<PSID>(&(paaa->SidStart));
488 + ++cNewAllow;
489 + }
490 +
491 + memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow);
492 + cNewAllow += cAllow;
493 +
494 + // create the dacl with the new
495 + hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew);
496 + ExitOnFailure(hr, "failed to create new ACL from existing ACL");
497 +
498 + AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL");
499 + Assert(S_OK == hr);
500 +LExit:
501 + ReleaseMem(paaNewAllow);
502 + ReleaseMem(paaNewDeny);
503 +
504 + return hr;
505 +}
506 +
507 +
508 +/********************************************************************
509 +AclMergeDacls - creates a new DACL from two existing ACLs
510 +
511 +********************************************************************/
512 +extern "C" HRESULT DAPI AclMergeDacls(
513 + __in const ACL* pAcl1,
514 + __in const ACL* pAcl2,
515 + __deref_out ACL** ppAclNew
516 + )
517 +{
518 + HRESULT hr = E_NOTIMPL;
519 +
520 + Assert(pAcl1 && pAcl2 && ppAclNew);
521 + UNREFERENCED_PARAMETER(pAcl1);
522 + UNREFERENCED_PARAMETER(pAcl2);
523 + UNREFERENCED_PARAMETER(ppAclNew);
524 +
525 +//LExit:
526 + return hr;
527 +}
528 +
529 +
530 +/********************************************************************
531 +AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure
532 +
533 +********************************************************************/
534 +extern "C" HRESULT DAPI AclCreateDaclOld(
535 + __in_ecount(cAclAccesses) ACL_ACCESS* paa,
536 + __in DWORD cAclAccesses,
537 + __deref_out ACL** ppACL
538 + )
539 +{
540 + Assert(ppACL);
541 + HRESULT hr = S_OK;
542 + DWORD* pdwAccessMask = NULL;
543 + PSID* ppsid = NULL;
544 +
545 + DWORD i;
546 + int cbAcl;
547 +
548 + *ppACL = NULL;
549 +
550 + //
551 + // create the SIDs and calculate the space for the ACL
552 + //
553 + pdwAccessMask = static_cast<DWORD*>(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE));
554 + ExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask");
555 + ppsid = static_cast<PSID*>(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE));
556 + ExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid");
557 +
558 + cbAcl = sizeof (ACL); // start with the size of the header
559 + for (i = 0; i < cAclAccesses; ++i)
560 + {
561 + if (paa[i].pwzAccountName)
562 + {
563 + hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i);
564 + ExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName);
565 + }
566 + else
567 + {
568 + if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount,
569 + paa[i].nSubAuthority[0], paa[i].nSubAuthority[1],
570 + paa[i].nSubAuthority[2], paa[i].nSubAuthority[3],
571 + paa[i].nSubAuthority[4], paa[i].nSubAuthority[5],
572 + paa[i].nSubAuthority[6], paa[i].nSubAuthority[7],
573 + (void**)(ppsid + i))))
574 + {
575 + ExitWithLastError(hr, "failed to initialize SIDs #%u", i);
576 + }
577 + }
578 +
579 + // add the newly allocated SID size to the count of bytes for this ACL
580 + cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD);
581 + if (paa[i].fDenyAccess)
582 + {
583 + cbAcl += sizeof(ACCESS_DENIED_ACE);
584 + }
585 + else
586 + {
587 + cbAcl += sizeof(ACCESS_ALLOWED_ACE);
588 + }
589 +
590 + pdwAccessMask[i] = paa[i].dwAccessMask;
591 + }
592 +
593 + //
594 + // allocate the ACL and set the appropriate ACEs
595 + //
596 + *ppACL = static_cast<ACL*>(MemAlloc(cbAcl, FALSE));
597 + ExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL");
598 +
599 +#pragma prefast(push)
600 +#pragma prefast(disable:25029)
601 + if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION))
602 +#pragma prefast(pop)
603 + {
604 + ExitWithLastError(hr, "failed to initialize ACLs");
605 + }
606 +
607 + // add an access-allowed ACE for each of the SIDs
608 + for (i = 0; i < cAclAccesses; ++i)
609 + {
610 + if (paa[i].fDenyAccess)
611 + {
612 +#pragma prefast(push)
613 +#pragma prefast(disable:25029)
614 + if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i)))
615 +#pragma prefast(pop)
616 + {
617 + ExitWithLastError(hr, "failed to add access denied for ACE");
618 + }
619 + }
620 + else
621 + {
622 +#pragma prefast(push)
623 +#pragma prefast(disable:25029)
624 + if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i)))
625 +#pragma prefast(pop)
626 + {
627 + ExitWithLastError(hr, "failed to add access allowed for ACE");
628 + }
629 + }
630 + }
631 +
632 +LExit:
633 + if (FAILED(hr))
634 + {
635 + ReleaseNullMem(*ppACL);
636 + }
637 +
638 + if (ppsid)
639 + {
640 + for (i = 0; i < cAclAccesses; ++i)
641 + {
642 + if (ppsid[i])
643 + {
644 + ::FreeSid(ppsid[i]);
645 + }
646 + }
647 +
648 + MemFree(ppsid);
649 + }
650 +
651 + ReleaseMem(pdwAccessMask);
652 +
653 + return hr;
654 +}
655 +
656 +
657 +/********************************************************************
658 +AclCreateSecurityDescriptorFromDacl - creates a self-relative security
659 +descriptor from an existing DACL
660 +
661 +********************************************************************/
662 +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl(
663 + __in ACL* pACL,
664 + __deref_out SECURITY_DESCRIPTOR** ppsd
665 + )
666 +{
667 + HRESULT hr = S_OK;
668 +
669 + SECURITY_DESCRIPTOR sd;
670 + DWORD cbSD;
671 +
672 + ExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided");
673 + ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided");
674 +
675 + *ppsd = NULL;
676 +
677 + //
678 + // create the absolute security descriptor
679 + //
680 +
681 + // initialize our security descriptor, throw the ACL into it, and set the owner
682 +#pragma prefast(push)
683 +#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs
684 +#pragma prefast(disable:25029)
685 + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) ||
686 + (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) ||
687 + (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE)))
688 +#pragma prefast(pop)
689 + {
690 + ExitWithLastError(hr, "failed to initialize security descriptor");
691 + }
692 +
693 + //
694 + // create the self-relative security descriptor
695 + //
696 + cbSD = ::GetSecurityDescriptorLength(&sd);
697 + *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, FALSE));
698 + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
699 +
700 + ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD);
701 + Assert(::IsValidSecurityDescriptor(*ppsd));
702 +
703 +LExit:
704 + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
705 + {
706 + MemFree(*ppsd);
707 + *ppsd = NULL;
708 + }
709 +
710 + return hr;
711 +}
712 +
713 +
714 +/********************************************************************
715 +AclCreateSecurityDescriptor - creates a self-relative security descriptor from an
716 +ACL_ACCESS structure
717 +
718 +NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
719 +********************************************************************/
720 +extern "C" HRESULT DAPI AclCreateSecurityDescriptor(
721 + __in_ecount(cAclAccesses) ACL_ACCESS* paa,
722 + __in DWORD cAclAccesses,
723 + __deref_out SECURITY_DESCRIPTOR** ppsd
724 + )
725 +{
726 + Assert(ppsd);
727 + HRESULT hr = S_OK;
728 +
729 + ACL* pACL;
730 +
731 + *ppsd = NULL;
732 +
733 + //
734 + // create the DACL
735 + //
736 + hr = AclCreateDaclOld(paa, cAclAccesses, &pACL);
737 + ExitOnFailure(hr, "failed to create DACL for security descriptor");
738 +
739 + //
740 + // create self-relative security descriptor
741 + //
742 + hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd);
743 +
744 +LExit:
745 + return hr;
746 +}
747 +
748 +
749 +/********************************************************************
750 +AclCreateSecurityDescriptorFromString - creates a self-relative security
751 +descriptor from an SDDL string
752 +
753 +NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
754 +********************************************************************/
755 +extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString(
756 + __deref_out SECURITY_DESCRIPTOR** ppsd,
757 + __in_z __format_string LPCWSTR wzSddlFormat,
758 + ...
759 + )
760 +{
761 + Assert(ppsd);
762 + HRESULT hr = S_OK;
763 + LPWSTR pwzSddl = NULL;
764 + va_list args;
765 + PSECURITY_DESCRIPTOR psd = NULL;
766 + DWORD cbSD = 0;
767 +
768 + *ppsd = NULL;
769 +
770 + va_start(args, wzSddlFormat);
771 + hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args);
772 + va_end(args);
773 + ExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat);
774 +
775 + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD))
776 + {
777 + ExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl);
778 + }
779 +
780 + *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, FALSE));
781 + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor");
782 +
783 + memcpy(*ppsd, psd, cbSD);
784 + Assert(::IsValidSecurityDescriptor(*ppsd));
785 +
786 + Assert(S_OK == hr);
787 +
788 +LExit:
789 + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
790 + {
791 + MemFree(*ppsd);
792 + *ppsd = NULL;
793 + }
794 +
795 + if (psd)
796 + {
797 + ::LocalFree(psd);
798 + }
799 +
800 + ReleaseStr(pwzSddl);
801 + return hr;
802 +}
803 +
804 +
805 +/********************************************************************
806 +AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor
807 +
808 +NOTE: passed in security descriptor must be in self-relative format
809 +********************************************************************/
810 +extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor(
811 + __in SECURITY_DESCRIPTOR* psd,
812 + __deref_out SECURITY_DESCRIPTOR** ppsd
813 + )
814 +{
815 + HRESULT hr = S_OK;
816 + DWORD cbSD;
817 +
818 + ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided");
819 + *ppsd = NULL;
820 +
821 + //
822 + // create the self-relative security descriptor
823 + //
824 + cbSD = ::GetSecurityDescriptorLength(psd);
825 + *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, 0));
826 + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
827 +
828 + memcpy(*ppsd, psd, cbSD);
829 + Assert(::IsValidSecurityDescriptor(*ppsd));
830 +
831 +LExit:
832 + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
833 + {
834 + MemFree(*ppsd);
835 + *ppsd = NULL;
836 + }
837 +
838 + return hr;
839 +}
840 +
841 +
842 +/********************************************************************
843 +AclGetSecurityDescriptor - returns self-relative security descriptor for named object
844 +
845 +NOTE: free ppsd with AclFreeSecurityDescriptor()
846 +********************************************************************/
847 +extern "C" HRESULT DAPI AclGetSecurityDescriptor(
848 + __in_z LPCWSTR wzObject,
849 + __in SE_OBJECT_TYPE sot,
850 + __in SECURITY_INFORMATION securityInformation,
851 + __deref_out SECURITY_DESCRIPTOR** ppsd
852 + )
853 +{
854 + HRESULT hr = S_OK;
855 + DWORD er;
856 + PSECURITY_DESCRIPTOR psd = NULL;
857 + DWORD cbSD;
858 +
859 + ExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided");
860 + *ppsd = NULL;
861 +
862 + // get the security descriptor for the object
863 + er = ::GetNamedSecurityInfoW(const_cast<LPWSTR>(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd);
864 + ExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject);
865 + Assert(::IsValidSecurityDescriptor(psd));
866 +
867 + // copy the self-relative security descriptor
868 + cbSD = ::GetSecurityDescriptorLength(psd);
869 + *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, 0));
870 + ExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
871 +
872 + memcpy(*ppsd, psd, cbSD);
873 + Assert(::IsValidSecurityDescriptor(*ppsd));
874 +
875 +LExit:
876 + if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
877 + {
878 + MemFree(*ppsd);
879 + *ppsd = NULL;
880 + }
881 +
882 + if (psd)
883 + {
884 + ::LocalFree(psd);
885 + }
886 +
887 + return hr;
888 +}
889 +
890 +
891 +extern "C" HRESULT DAPI AclSetSecurityWithRetry(
892 + __in_z LPCWSTR wzObject,
893 + __in SE_OBJECT_TYPE sot,
894 + __in SECURITY_INFORMATION securityInformation,
895 + __in_opt PSID psidOwner,
896 + __in_opt PSID psidGroup,
897 + __in_opt PACL pDacl,
898 + __in_opt PACL pSacl,
899 + __in DWORD cRetry,
900 + __in DWORD dwWaitMilliseconds
901 + )
902 +{
903 + HRESULT hr = S_OK;
904 + LPWSTR sczObject = NULL;
905 + DWORD i = 0;
906 +
907 + hr = StrAllocString(&sczObject, wzObject, 0);
908 + ExitOnFailure(hr, "Failed to copy object to secure.");
909 +
910 + hr = E_FAIL;
911 + for (i = 0; FAILED(hr) && i <= cRetry; ++i)
912 + {
913 + if (0 < i)
914 + {
915 + ::Sleep(dwWaitMilliseconds);
916 + }
917 +
918 + DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl);
919 + hr = HRESULT_FROM_WIN32(er);
920 + }
921 + ExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i);
922 +
923 +LExit:
924 + ReleaseStr(sczObject);
925 +
926 + return hr;
927 +}
928 +
929 +
930 +/********************************************************************
931 +AclFreeSid - frees a SID created by any Acl* functions
932 +
933 +********************************************************************/
934 +extern "C" HRESULT DAPI AclFreeSid(
935 + __in PSID psid
936 + )
937 +{
938 + Assert(psid && ::IsValidSid(psid));
939 + HRESULT hr = S_OK;
940 +
941 + hr = MemFree(psid);
942 +
943 + return hr;
944 +}
945 +
946 +
947 +/********************************************************************
948 +AclFreeDacl - frees a DACL created by any Acl* functions
949 +
950 +********************************************************************/
951 +extern "C" HRESULT DAPI AclFreeDacl(
952 + __in ACL* pACL
953 + )
954 +{
955 + Assert(pACL);
956 + HRESULT hr = S_OK;
957 +
958 + hr = MemFree(pACL);
959 +
960 + return hr;
961 +}
962 +
963 +
964 +/********************************************************************
965 +AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions
966 +
967 +********************************************************************/
968 +extern "C" HRESULT DAPI AclFreeSecurityDescriptor(
969 + __in SECURITY_DESCRIPTOR* psd
970 + )
971 +{
972 + Assert(psd && ::IsValidSecurityDescriptor(psd));
973 + HRESULT hr = S_OK;
974 +
975 + hr = MemFree(psd);
976 +
977 + return hr;
978 +}
979 +
980 +
981 +/********************************************************************
982 +AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor
983 +
984 +********************************************************************/
985 +extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor(
986 + __in SECURITY_DESCRIPTOR* pSecurity,
987 + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew
988 + )
989 +{
990 + HRESULT hr = S_OK;
991 + PACL pAcl = NULL;
992 + PACL pAclNew = NULL;
993 + BOOL fValid, fDaclDefaulted;
994 + ACL_ACE ace[1];
995 + SECURITY_DESCRIPTOR* pSecurityNew;
996 +
997 + if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid)
998 + {
999 + ExitOnLastError(hr, "Failed to get acl from security descriptor");
1000 + }
1001 +
1002 + hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid);
1003 + ExitOnFailure(hr, "failed to get sid for Administrators group");
1004 +
1005 + ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE;
1006 + ace[0].dwMask = GENERIC_ALL;
1007 +
1008 + hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew);
1009 + ExitOnFailure(hr, "failed to add Administrators ACE to ACL");
1010 +
1011 + hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew);
1012 + ExitOnLastError(hr, "Failed to create new security descriptor");
1013 +
1014 + // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it.
1015 + pAclNew = NULL;
1016 +
1017 + *ppSecurityNew = pSecurityNew;
1018 +
1019 +LExit:
1020 + if (pAclNew)
1021 + {
1022 + AclFreeDacl(pAclNew);
1023 + }
1024 + if (ace[0].psid)
1025 + {
1026 + AclFreeSid(ace[0].psid);
1027 + }
1028 +
1029 + return hr;
1030 +}
src/dutil/apputil.cpp new
+109
@@ -0,0 +1,109 @@
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 +
5 +const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800;
6 +typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD);
7 +typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR);
8 +
9 +extern "C" void DAPI AppFreeCommandLineArgs(
10 + __in LPWSTR* argv
11 + )
12 +{
13 + // The "ignored" hack in AppParseCommandLine requires an adjustment.
14 + LPWSTR* argvOriginal = argv - 1;
15 + ::LocalFree(argvOriginal);
16 +}
17 +
18 +/********************************************************************
19 +AppInitialize - initializes the standard safety precautions for an
20 + installation application.
21 +
22 +********************************************************************/
23 +extern "C" void DAPI AppInitialize(
24 + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[],
25 + __in DWORD cSafelyLoadSystemDlls
26 + )
27 +{
28 + HRESULT hr = S_OK;
29 + HMODULE hIgnored = NULL;
30 + BOOL fSetDefaultDllDirectories = FALSE;
31 +
32 + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
33 +
34 + // Best effort call to initialize default DLL directories to system only.
35 + HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32");
36 + LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories");
37 + if (pfnSetDefaultDllDirectories)
38 + {
39 + if (pfnSetDefaultDllDirectories(PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32))
40 + {
41 + fSetDefaultDllDirectories = TRUE;
42 + }
43 + else
44 + {
45 + hr = HRESULT_FROM_WIN32(::GetLastError());
46 + TraceError(hr, "Failed to call SetDefaultDllDirectories.");
47 + }
48 + }
49 +
50 + // Only need to safely load if the default DLL directories was not
51 + // able to be set.
52 + if (!fSetDefaultDllDirectories)
53 + {
54 + // Remove current working directory from search order.
55 + LPFN_SETDLLDIRECTORYW pfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW");
56 + if (!pfnSetDllDirectory || !pfnSetDllDirectory(L""))
57 + {
58 + hr = HRESULT_FROM_WIN32(::GetLastError());
59 + TraceError(hr, "Failed to call SetDllDirectory.");
60 + }
61 +
62 + for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i)
63 + {
64 + hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored);
65 + if (FAILED(hr))
66 + {
67 + TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]);
68 + }
69 + }
70 + }
71 +}
72 +
73 +extern "C" void DAPI AppInitializeUnsafe()
74 +{
75 + ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
76 +}
77 +
78 +extern "C" DAPI_(HRESULT) AppParseCommandLine(
79 + __in LPCWSTR wzCommandLine,
80 + __in int* pArgc,
81 + __in LPWSTR** pArgv
82 + )
83 +{
84 + HRESULT hr = S_OK;
85 + LPWSTR sczCommandLine = NULL;
86 + LPWSTR* argv = NULL;
87 + int argc = 0;
88 +
89 + // CommandLineToArgvW tries to treat the first argument as the path to the process,
90 + // which fails pretty miserably if your first argument is something like
91 + // FOO="C:\Program Files\My Company". So give it something harmless to play with.
92 + hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0);
93 + ExitOnFailure(hr, "Failed to initialize command line.");
94 +
95 + hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0);
96 + ExitOnFailure(hr, "Failed to copy command line.");
97 +
98 + argv = ::CommandLineToArgvW(sczCommandLine, &argc);
99 + ExitOnNullWithLastError(argv, hr, "Failed to parse command line.");
100 +
101 + // Skip "ignored" argument/hack.
102 + *pArgv = argv + 1;
103 + *pArgc = argc - 1;
104 +
105 +LExit:
106 + ReleaseStr(sczCommandLine);
107 +
108 + return hr;
109 +}
src/dutil/apuputil.cpp new
+658
@@ -0,0 +1,658 @@
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 +
5 +#define SHA256_DIGEST_LEN 32
6 +
7 +// prototypes
8 +static HRESULT ProcessEntry(
9 + __in ATOM_ENTRY* pAtomEntry,
10 + __in LPCWSTR wzDefaultAppId,
11 + __out APPLICATION_UPDATE_ENTRY* pApupEntry
12 + );
13 +static HRESULT ParseEnclosure(
14 + __in ATOM_LINK* pLink,
15 + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
16 + );
17 +static __callback int __cdecl CompareEntries(
18 + void* pvContext,
19 + const void* pvLeft,
20 + const void* pvRight
21 + );
22 +static HRESULT FilterEntries(
23 + __in APPLICATION_UPDATE_ENTRY* rgEntries,
24 + __in DWORD cEntries,
25 + __in DWORD64 dw64CurrentVersion,
26 + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries,
27 + __inout DWORD* pcFilteredEntries
28 + );
29 +static HRESULT CopyEntry(
30 + __in const APPLICATION_UPDATE_ENTRY* pSrc,
31 + __in APPLICATION_UPDATE_ENTRY* pDest
32 + );
33 +static HRESULT CopyEnclosure(
34 + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc,
35 + __in APPLICATION_UPDATE_ENCLOSURE* pDest
36 + );
37 +static void FreeEntry(
38 + __in APPLICATION_UPDATE_ENTRY* pApupEntry
39 + );
40 +static void FreeEnclosure(
41 + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
42 + );
43 +
44 +
45 +//
46 +// ApupCalculateChainFromAtom - returns the chain of application updates found in an ATOM feed.
47 +//
48 +extern "C" HRESULT DAPI ApupAllocChainFromAtom(
49 + __in ATOM_FEED* pFeed,
50 + __out APPLICATION_UPDATE_CHAIN** ppChain
51 + )
52 +{
53 + HRESULT hr = S_OK;
54 + APPLICATION_UPDATE_CHAIN* pChain = NULL;
55 +
56 + pChain = static_cast<APPLICATION_UPDATE_CHAIN*>(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE));
57 +
58 + // First search the ATOM feed's custom elements to try and find the default application identity.
59 + for (ATOM_UNKNOWN_ELEMENT* pElement = pFeed->pUnknownElements; pElement; pElement = pElement->pNext)
60 + {
61 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1))
62 + {
63 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1))
64 + {
65 + hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0);
66 + ExitOnFailure(hr, "Failed to allocate default application id.");
67 +
68 + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
69 + {
70 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1))
71 + {
72 + hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0);
73 + ExitOnFailure(hr, "Failed to allocate default application type.");
74 + }
75 + }
76 + }
77 + }
78 + }
79 +
80 + // Assume there will be as many application updates entries as their are feed entries.
81 + if (pFeed->cEntries)
82 + {
83 + pChain->rgEntries = static_cast<APPLICATION_UPDATE_ENTRY*>(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE));
84 + ExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries.");
85 +
86 + // Process each entry, building up the chain.
87 + for (DWORD i = 0; i < pFeed->cEntries; ++i)
88 + {
89 + hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries);
90 + ExitOnFailure(hr, "Failed to process ATOM entry.");
91 +
92 + if (S_FALSE != hr)
93 + {
94 + ++pChain->cEntries;
95 + }
96 + }
97 +
98 + // Sort the chain by descending version and ascending total size.
99 + qsort_s(pChain->rgEntries, pChain->cEntries, sizeof(APPLICATION_UPDATE_ENTRY), CompareEntries, NULL);
100 + }
101 +
102 + // Trim the unused entries from the end, if any of the entries failed to parse or validate
103 + if (pChain->cEntries != pFeed->cEntries)
104 + {
105 + if (pChain->cEntries > 0)
106 + {
107 + pChain->rgEntries = static_cast<APPLICATION_UPDATE_ENTRY*>(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE));
108 + ExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries.");
109 + }
110 + else
111 + {
112 + ReleaseNullMem(pChain->rgEntries);
113 + }
114 + }
115 +
116 + *ppChain = pChain;
117 + pChain = NULL;
118 +
119 +LExit:
120 + ReleaseApupChain(pChain);
121 +
122 + return hr;
123 +}
124 +
125 +
126 +//
127 +// ApupFilterChain - remove the unneeded update elements from the chain.
128 +//
129 +HRESULT DAPI ApupFilterChain(
130 + __in APPLICATION_UPDATE_CHAIN* pChain,
131 + __in DWORD64 dw64Version,
132 + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain
133 + )
134 +{
135 + HRESULT hr = S_OK;
136 + APPLICATION_UPDATE_CHAIN* pNewChain = NULL;
137 + APPLICATION_UPDATE_ENTRY* prgEntries = NULL;
138 + DWORD cEntries = NULL;
139 +
140 + pNewChain = static_cast<APPLICATION_UPDATE_CHAIN*>(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE));
141 + ExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain.");
142 +
143 + hr = FilterEntries(pChain->rgEntries, pChain->cEntries, dw64Version, &prgEntries, &cEntries);
144 + ExitOnFailure(hr, "Failed to filter entries by version.");
145 +
146 + if (pChain->wzDefaultApplicationId)
147 + {
148 + hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0);
149 + ExitOnFailure(hr, "Failed to copy default application id.");
150 + }
151 +
152 + if (pChain->wzDefaultApplicationType)
153 + {
154 + hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0);
155 + ExitOnFailure(hr, "Failed to copy default application type.");
156 + }
157 +
158 + pNewChain->rgEntries = prgEntries;
159 + pNewChain->cEntries = cEntries;
160 +
161 + *ppFilteredChain = pNewChain;
162 + pNewChain = NULL;
163 +
164 +LExit:
165 + ReleaseApupChain(pNewChain);
166 + return hr;
167 +}
168 +
169 +
170 +//
171 +// ApupFreeChain - frees a previously allocated application update chain.
172 +//
173 +extern "C" void DAPI ApupFreeChain(
174 + __in APPLICATION_UPDATE_CHAIN* pChain
175 + )
176 +{
177 + if (pChain)
178 + {
179 + for (DWORD i = 0; i < pChain->cEntries; ++i)
180 + {
181 + FreeEntry(pChain->rgEntries + i);
182 + }
183 +
184 + ReleaseMem(pChain->rgEntries);
185 + ReleaseStr(pChain->wzDefaultApplicationType);
186 + ReleaseStr(pChain->wzDefaultApplicationId);
187 + ReleaseMem(pChain);
188 + }
189 +}
190 +
191 +
192 +static HRESULT ProcessEntry(
193 + __in ATOM_ENTRY* pAtomEntry,
194 + __in LPCWSTR wzDefaultAppId,
195 + __out APPLICATION_UPDATE_ENTRY* pApupEntry
196 + )
197 +{
198 + HRESULT hr = S_OK;
199 + BOOL fVersionFound = FALSE;
200 +
201 + // First search the ATOM entry's custom elements to try and find the application update information.
202 + for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext)
203 + {
204 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1))
205 + {
206 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1))
207 + {
208 + hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0);
209 + ExitOnFailure(hr, "Failed to allocate application identity.");
210 +
211 + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
212 + {
213 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1))
214 + {
215 + hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0);
216 + ExitOnFailure(hr, "Failed to allocate application type.");
217 + }
218 + }
219 + }
220 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1))
221 + {
222 + hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0);
223 + ExitOnFailure(hr, "Failed to allocate upgrade id.");
224 +
225 + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
226 + {
227 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1))
228 + {
229 + DWORD dwMajor = 0;
230 + DWORD dwMinor = 0;
231 +
232 + hr = FileVersionFromString(pAttribute->wzValue, &dwMajor, &dwMinor);
233 + ExitOnFailure(hr, "Failed to parse version string from ATOM entry.");
234 +
235 + pApupEntry->dw64UpgradeVersion = static_cast<DWORD64>(dwMajor) << 32 | dwMinor;
236 + }
237 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1))
238 + {
239 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzValue, -1, L"true", -1))
240 + {
241 + pApupEntry->fUpgradeExclusive = TRUE;
242 + }
243 + }
244 + }
245 + }
246 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1))
247 + {
248 + DWORD dwMajor = 0;
249 + DWORD dwMinor = 0;
250 +
251 + hr = FileVersionFromString(pElement->wzValue, &dwMajor, &dwMinor);
252 + ExitOnFailure(hr, "Failed to parse version string from ATOM entry.");
253 +
254 + pApupEntry->dw64Version = static_cast<DWORD64>(dwMajor) << 32 | dwMinor;
255 + fVersionFound = TRUE;
256 + }
257 + }
258 + }
259 +
260 + // If there is no application identity or no version, skip the whole thing.
261 + if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !fVersionFound)
262 + {
263 + ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version.
264 + }
265 +
266 + if (pApupEntry->dw64UpgradeVersion >= pApupEntry->dw64Version)
267 + {
268 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
269 + ExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version.");
270 + }
271 +
272 + if (pAtomEntry->wzTitle)
273 + {
274 + hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0);
275 + ExitOnFailure(hr, "Failed to allocate application title.");
276 + }
277 +
278 + if (pAtomEntry->wzSummary)
279 + {
280 + hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0);
281 + ExitOnFailure(hr, "Failed to allocate application summary.");
282 + }
283 +
284 + if (pAtomEntry->pContent)
285 + {
286 + if (pAtomEntry->pContent->wzType)
287 + {
288 + hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0);
289 + ExitOnFailure(hr, "Failed to allocate content type.");
290 + }
291 +
292 + if (pAtomEntry->pContent->wzValue)
293 + {
294 + hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0);
295 + ExitOnFailure(hr, "Failed to allocate content.");
296 + }
297 + }
298 + // Now process the enclosures. Assume every link in the ATOM entry is an enclosure.
299 + pApupEntry->rgEnclosures = static_cast<APPLICATION_UPDATE_ENCLOSURE*>(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE));
300 + ExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry.");
301 +
302 + for (DWORD i = 0; i < pAtomEntry->cLinks; ++i)
303 + {
304 + ATOM_LINK* pLink = pAtomEntry->rgLinks + i;
305 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1))
306 + {
307 + hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures);
308 + ExitOnFailure(hr, "Failed to parse enclosure.");
309 +
310 + pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures
311 +
312 + ++pApupEntry->cEnclosures;
313 + }
314 + }
315 +
316 +LExit:
317 + if (S_OK != hr) // if anything went wrong, free the entry.
318 + {
319 + FreeEntry(pApupEntry);
320 + memset(pApupEntry, 0, sizeof(APPLICATION_UPDATE_ENTRY));
321 + }
322 +
323 + return hr;
324 +}
325 +
326 +
327 +static HRESULT ParseEnclosure(
328 + __in ATOM_LINK* pLink,
329 + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
330 + )
331 +{
332 + HRESULT hr = S_OK;
333 +
334 + // First search the ATOM link's custom elements to try and find the application update enclosure information.
335 + for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext)
336 + {
337 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1))
338 + {
339 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1))
340 + {
341 + // Find the digest[@algorithm='sha256'] which is required. Everything else is ignored.
342 + for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
343 + {
344 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1))
345 + {
346 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1))
347 + {
348 + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5;
349 + }
350 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1))
351 + {
352 + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1;
353 + }
354 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1))
355 + {
356 + pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256;
357 + }
358 + break;
359 + }
360 + }
361 +
362 + if (APUP_HASH_ALGORITHM_SHA256 == pEnclosure->digestAlgorithm)
363 + {
364 + if (64 != lstrlenW(pElement->wzValue))
365 + {
366 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
367 + ExitOnRootFailure(hr, "Invalid digest length for SHA256 algorithm.");
368 + }
369 +
370 + pEnclosure->cbDigest = sizeof(BYTE) * SHA256_DIGEST_LEN;
371 + pEnclosure->rgbDigest = static_cast<BYTE*>(MemAlloc(pEnclosure->cbDigest, TRUE));
372 + ExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest.");
373 +
374 + hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest);
375 + ExitOnFailure(hr, "Failed to decode digest value.");
376 + }
377 + else
378 + {
379 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
380 + ExitOnRootFailure(hr, "Unknown algorithm type for digest.");
381 + }
382 +
383 + break;
384 + }
385 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1))
386 + {
387 + hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0);
388 + ExitOnFailure(hr, "Failed to copy local name.");
389 + }
390 + }
391 + }
392 +
393 + pEnclosure->dw64Size = pLink->dw64Length;
394 +
395 + hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0);
396 + ExitOnFailure(hr, "Failed to allocate enclosure URL.");
397 +
398 + pEnclosure->fInstaller = FALSE;
399 + pEnclosure->wzLocalName = NULL;
400 +
401 +LExit:
402 + return hr;
403 +}
404 +
405 +
406 +static __callback int __cdecl CompareEntries(
407 + void* pvContext,
408 + const void* pvLeft,
409 + const void* pvRight
410 + )
411 +{
412 + UNREFERENCED_PARAMETER(pvContext);
413 +
414 + int ret = 0;
415 + const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast<const APPLICATION_UPDATE_ENTRY*>(pvLeft);
416 + const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast<const APPLICATION_UPDATE_ENTRY*>(pvRight);
417 +
418 + if (pEntryLeft->dw64Version == pEntryRight->dw64Version)
419 + {
420 + if (pEntryLeft->dw64UpgradeVersion == pEntryRight->dw64UpgradeVersion)
421 + {
422 + ret = (pEntryRight->dw64TotalSize < pEntryLeft->dw64TotalSize) ? -1 : 1;
423 + }
424 + else
425 + {
426 + ret = (pEntryLeft->dw64UpgradeVersion > pEntryRight->dw64UpgradeVersion) ? -1 : 1;
427 + }
428 + }
429 + else
430 + {
431 + ret = (pEntryLeft->dw64Version > pEntryRight->dw64Version) ? -1 : 1;
432 + }
433 +
434 + return ret;
435 +}
436 +
437 +
438 +static HRESULT FilterEntries(
439 + __in APPLICATION_UPDATE_ENTRY* rgEntries,
440 + __in DWORD cEntries,
441 + __in DWORD64 dw64CurrentVersion,
442 + __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries,
443 + __inout DWORD* pcFilteredEntries
444 + )
445 +{
446 + HRESULT hr = S_OK;
447 + size_t cbAllocSize = 0;
448 + const APPLICATION_UPDATE_ENTRY* pRequired = NULL;;
449 + LPVOID pv = NULL;
450 +
451 + if (cEntries)
452 + {
453 + for (DWORD i = 0; i < cEntries; ++i)
454 + {
455 + const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i;
456 + if (((pEntry->fUpgradeExclusive && dw64CurrentVersion > pEntry->dw64UpgradeVersion) || (!pEntry->fUpgradeExclusive && dw64CurrentVersion >= pEntry->dw64UpgradeVersion)) &&
457 + dw64CurrentVersion < pEntry->dw64Version)
458 + {
459 + pRequired = pEntry;
460 + break;
461 + }
462 + }
463 +
464 + if (pRequired)
465 + {
466 + DWORD cNewFilteredEntries = *pcFilteredEntries + 1;
467 +
468 + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize);
469 + ExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries);
470 +
471 + if (*prgFilteredEntries)
472 + {
473 + pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE);
474 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries.");
475 + }
476 + else
477 + {
478 + pv = MemAlloc(cbAllocSize, TRUE);
479 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries.");
480 + }
481 +
482 + *pcFilteredEntries = cNewFilteredEntries;
483 + *prgFilteredEntries = static_cast<APPLICATION_UPDATE_ENTRY*>(pv);
484 + pv = NULL;
485 +
486 + hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1);
487 + ExitOnFailure(hr, "Failed to deep copy entry.");
488 +
489 + if (pRequired->dw64Version < rgEntries[0].dw64Version)
490 + {
491 + FilterEntries(rgEntries, cEntries, pRequired->dw64Version, prgFilteredEntries, pcFilteredEntries);
492 + }
493 + }
494 + }
495 +
496 +LExit:
497 + ReleaseMem(pv);
498 + return hr;
499 +}
500 +
501 +
502 +static HRESULT CopyEntry(
503 + __in const APPLICATION_UPDATE_ENTRY* pSrc,
504 + __in APPLICATION_UPDATE_ENTRY* pDest
505 + )
506 +{
507 + HRESULT hr = S_OK;
508 + size_t cbAllocSize = 0;
509 +
510 + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENTRY));
511 +
512 + if (pSrc->wzApplicationId)
513 + {
514 + hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0);
515 + ExitOnFailure(hr, "Failed to copy application id.");
516 + }
517 +
518 + if (pSrc->wzApplicationType)
519 + {
520 + hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0);
521 + ExitOnFailure(hr, "Failed to copy application type.");
522 + }
523 +
524 + if (pSrc->wzUpgradeId)
525 + {
526 + hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0);
527 + ExitOnFailure(hr, "Failed to copy upgrade id.");
528 + }
529 +
530 + if (pSrc->wzTitle)
531 + {
532 + hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0);
533 + ExitOnFailure(hr, "Failed to copy title.");
534 + }
535 +
536 + if (pSrc->wzSummary)
537 + {
538 + hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0);
539 + ExitOnFailure(hr, "Failed to copy summary.");
540 + }
541 +
542 + if (pSrc->wzContentType)
543 + {
544 + hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0);
545 + ExitOnFailure(hr, "Failed to copy content type.");
546 + }
547 +
548 + if (pSrc->wzContent)
549 + {
550 + hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0);
551 + ExitOnFailure(hr, "Failed to copy content.");
552 + }
553 +
554 + pDest->dw64TotalSize = pSrc->dw64TotalSize;
555 + pDest->dw64UpgradeVersion = pSrc->dw64UpgradeVersion;
556 + pDest->dw64Version = pSrc->dw64Version;
557 + pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive;
558 +
559 + hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize);
560 + ExitOnRootFailure(hr, "Overflow while calculating memory allocation size");
561 +
562 + pDest->rgEnclosures = static_cast<APPLICATION_UPDATE_ENCLOSURE*>(MemAlloc(cbAllocSize, TRUE));
563 + ExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures.");
564 +
565 + pDest->cEnclosures = pSrc->cEnclosures;
566 +
567 + for (DWORD i = 0; i < pDest->cEnclosures; ++i)
568 + {
569 + hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i);
570 + ExitOnFailure(hr, "Failed to copy enclosure.");
571 + }
572 +
573 +LExit:
574 + if (FAILED(hr))
575 + {
576 + FreeEntry(pDest);
577 + }
578 +
579 + return hr;
580 +}
581 +
582 +
583 +static HRESULT CopyEnclosure(
584 + __in const APPLICATION_UPDATE_ENCLOSURE* pSrc,
585 + __in APPLICATION_UPDATE_ENCLOSURE* pDest
586 + )
587 +{
588 + HRESULT hr = S_OK;
589 +
590 + memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENCLOSURE));
591 +
592 + if (pSrc->wzUrl)
593 + {
594 + hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0);
595 + ExitOnFailure(hr, "Failed copy url.");
596 + }
597 +
598 + if (pSrc->wzLocalName)
599 + {
600 + hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0);
601 + ExitOnFailure(hr, "Failed copy url.");
602 + }
603 +
604 + pDest->rgbDigest = static_cast<BYTE*>(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE));
605 + ExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest.");
606 +
607 + pDest->cbDigest = pSrc->cbDigest;
608 +
609 + memcpy_s(pDest->rgbDigest, sizeof(BYTE) * pDest->cbDigest, pSrc->rgbDigest, sizeof(BYTE) * pSrc->cbDigest);
610 +
611 + pDest->digestAlgorithm = pSrc->digestAlgorithm;
612 +
613 + pDest->dw64Size = pSrc->dw64Size;
614 + pDest->fInstaller = pSrc->fInstaller;
615 +
616 +LExit:
617 + if (FAILED(hr))
618 + {
619 + FreeEnclosure(pDest);
620 + }
621 +
622 + return hr;
623 +}
624 +
625 +
626 +static void FreeEntry(
627 + __in APPLICATION_UPDATE_ENTRY* pEntry
628 + )
629 +{
630 + if (pEntry)
631 + {
632 + for (DWORD i = 0; i < pEntry->cEnclosures; ++i)
633 + {
634 + FreeEnclosure(pEntry->rgEnclosures + i);
635 + }
636 +
637 + ReleaseStr(pEntry->wzUpgradeId);
638 + ReleaseStr(pEntry->wzApplicationType);
639 + ReleaseStr(pEntry->wzApplicationId);
640 + ReleaseStr(pEntry->wzTitle);
641 + ReleaseStr(pEntry->wzSummary);
642 + ReleaseStr(pEntry->wzContentType);
643 + ReleaseStr(pEntry->wzContent);
644 + }
645 +}
646 +
647 +
648 +static void FreeEnclosure(
649 + __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
650 + )
651 +{
652 + if (pEnclosure)
653 + {
654 + ReleaseMem(pEnclosure->rgbDigest);
655 + ReleaseStr(pEnclosure->wzLocalName);
656 + ReleaseStr(pEnclosure->wzUrl);
657 + }
658 +}
src/dutil/atomutil.cpp new
+1284
@@ -0,0 +1,1284 @@
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 +
5 +
6 +static HRESULT ParseAtomDocument(
7 + __in IXMLDOMDocument *pixd,
8 + __out ATOM_FEED **ppFeed
9 + );
10 +static HRESULT ParseAtomFeed(
11 + __in IXMLDOMNode *pixnFeed,
12 + __out ATOM_FEED **ppFeed
13 + );
14 +static HRESULT ParseAtomAuthor(
15 + __in IXMLDOMNode* pixnAuthor,
16 + __in ATOM_AUTHOR* pAuthor
17 + );
18 +static HRESULT ParseAtomCategory(
19 + __in IXMLDOMNode* pixnCategory,
20 + __in ATOM_CATEGORY* pCategory
21 + );
22 +static HRESULT ParseAtomEntry(
23 + __in IXMLDOMNode* pixnEntry,
24 + __in ATOM_ENTRY* pEntry
25 + );
26 +static HRESULT ParseAtomLink(
27 + __in IXMLDOMNode* pixnLink,
28 + __in ATOM_LINK* pLink
29 + );
30 +static HRESULT ParseAtomUnknownElement(
31 + __in IXMLDOMNode *pNode,
32 + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement
33 + );
34 +static HRESULT ParseAtomUnknownAttribute(
35 + __in IXMLDOMNode *pNode,
36 + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
37 + );
38 +static HRESULT AssignDateTime(
39 + __in FILETIME* pft,
40 + __in IXMLDOMNode* pNode
41 + );
42 +static HRESULT AssignString(
43 + __out_z LPWSTR* pwzValue,
44 + __in IXMLDOMNode* pNode
45 + );
46 +static void FreeAtomAuthor(
47 + __in_opt ATOM_AUTHOR* pAuthor
48 + );
49 +static void FreeAtomContent(
50 + __in_opt ATOM_CONTENT* pContent
51 + );
52 +static void FreeAtomCategory(
53 + __in_opt ATOM_CATEGORY* pCategory
54 + );
55 +static void FreeAtomEntry(
56 + __in_opt ATOM_ENTRY* pEntry
57 + );
58 +static void FreeAtomLink(
59 + __in_opt ATOM_LINK* pLink
60 + );
61 +static void FreeAtomUnknownElementList(
62 + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement
63 + );
64 +static void FreeAtomUnknownAttributeList(
65 + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute
66 + );
67 +
68 +template<class T> static HRESULT AllocateAtomType(
69 + __in IXMLDOMNode* pixnParent,
70 + __in LPCWSTR wzT,
71 + __out T** pprgT,
72 + __out DWORD* pcT
73 + );
74 +
75 +
76 +/********************************************************************
77 + AtomInitialize - Initialize ATOM utilities.
78 +
79 +*********************************************************************/
80 +extern "C" HRESULT DAPI AtomInitialize()
81 +{
82 + return XmlInitialize();
83 +}
84 +
85 +
86 +/********************************************************************
87 + AtomUninitialize - Uninitialize ATOM utilities.
88 +
89 +*********************************************************************/
90 +extern "C" void DAPI AtomUninitialize()
91 +{
92 + XmlUninitialize();
93 +}
94 +
95 +
96 +/********************************************************************
97 + AtomParseFromString - parses out an ATOM feed from a string.
98 +
99 +*********************************************************************/
100 +extern "C" HRESULT DAPI AtomParseFromString(
101 + __in LPCWSTR wzAtomString,
102 + __out ATOM_FEED **ppFeed
103 + )
104 +{
105 + Assert(wzAtomString);
106 + Assert(ppFeed);
107 +
108 + HRESULT hr = S_OK;
109 + ATOM_FEED *pNewFeed = NULL;
110 + IXMLDOMDocument *pixdAtom = NULL;
111 +
112 + hr = XmlLoadDocument(wzAtomString, &pixdAtom);
113 + ExitOnFailure(hr, "Failed to load ATOM string as XML document.");
114 +
115 + hr = ParseAtomDocument(pixdAtom, &pNewFeed);
116 + ExitOnFailure(hr, "Failed to parse ATOM document.");
117 +
118 + *ppFeed = pNewFeed;
119 + pNewFeed = NULL;
120 +
121 +LExit:
122 + ReleaseAtomFeed(pNewFeed);
123 + ReleaseObject(pixdAtom);
124 +
125 + return hr;
126 +}
127 +
128 +
129 +/********************************************************************
130 + AtomParseFromFile - parses out an ATOM feed from a file path.
131 +
132 +*********************************************************************/
133 +extern "C" HRESULT DAPI AtomParseFromFile(
134 + __in LPCWSTR wzAtomFile,
135 + __out ATOM_FEED **ppFeed
136 + )
137 +{
138 + Assert(wzAtomFile);
139 + Assert(ppFeed);
140 +
141 + HRESULT hr = S_OK;
142 + ATOM_FEED *pNewFeed = NULL;
143 + IXMLDOMDocument *pixdAtom = NULL;
144 +
145 + hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom);
146 + ExitOnFailure(hr, "Failed to load ATOM string as XML document.");
147 +
148 + hr = ParseAtomDocument(pixdAtom, &pNewFeed);
149 + ExitOnFailure(hr, "Failed to parse ATOM document.");
150 +
151 + *ppFeed = pNewFeed;
152 + pNewFeed = NULL;
153 +
154 +LExit:
155 + ReleaseAtomFeed(pNewFeed);
156 + ReleaseObject(pixdAtom);
157 +
158 + return hr;
159 +}
160 +
161 +
162 +/********************************************************************
163 + AtomParseFromDocument - parses out an ATOM feed from an XML document.
164 +
165 +*********************************************************************/
166 +extern "C" HRESULT DAPI AtomParseFromDocument(
167 + __in IXMLDOMDocument* pixdDocument,
168 + __out ATOM_FEED **ppFeed
169 + )
170 +{
171 + Assert(pixdDocument);
172 + Assert(ppFeed);
173 +
174 + HRESULT hr = S_OK;
175 + ATOM_FEED *pNewFeed = NULL;
176 +
177 + hr = ParseAtomDocument(pixdDocument, &pNewFeed);
178 + ExitOnFailure(hr, "Failed to parse ATOM document.");
179 +
180 + *ppFeed = pNewFeed;
181 + pNewFeed = NULL;
182 +
183 +LExit:
184 + ReleaseAtomFeed(pNewFeed);
185 +
186 + return hr;
187 +}
188 +
189 +
190 +/********************************************************************
191 + AtomFreeFeed - parses out an ATOM feed from a string.
192 +
193 +*********************************************************************/
194 +extern "C" void DAPI AtomFreeFeed(
195 + __in_xcount(pFeed->cItems) ATOM_FEED *pFeed
196 + )
197 +{
198 + if (pFeed)
199 + {
200 + FreeAtomUnknownElementList(pFeed->pUnknownElements);
201 + ReleaseObject(pFeed->pixn);
202 +
203 + for (DWORD i = 0; i < pFeed->cLinks; ++i)
204 + {
205 + FreeAtomLink(pFeed->rgLinks + i);
206 + }
207 + ReleaseMem(pFeed->rgLinks);
208 +
209 + for (DWORD i = 0; i < pFeed->cEntries; ++i)
210 + {
211 + FreeAtomEntry(pFeed->rgEntries + i);
212 + }
213 + ReleaseMem(pFeed->rgEntries);
214 +
215 + for (DWORD i = 0; i < pFeed->cCategories; ++i)
216 + {
217 + FreeAtomCategory(pFeed->rgCategories + i);
218 + }
219 + ReleaseMem(pFeed->rgCategories);
220 +
221 + for (DWORD i = 0; i < pFeed->cAuthors; ++i)
222 + {
223 + FreeAtomAuthor(pFeed->rgAuthors + i);
224 + }
225 + ReleaseMem(pFeed->rgAuthors);
226 +
227 + ReleaseStr(pFeed->wzGenerator);
228 + ReleaseStr(pFeed->wzIcon);
229 + ReleaseStr(pFeed->wzId);
230 + ReleaseStr(pFeed->wzLogo);
231 + ReleaseStr(pFeed->wzSubtitle);
232 + ReleaseStr(pFeed->wzTitle);
233 +
234 + MemFree(pFeed);
235 + }
236 +}
237 +
238 +
239 +/********************************************************************
240 + ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document.
241 +
242 +*********************************************************************/
243 +static HRESULT ParseAtomDocument(
244 + __in IXMLDOMDocument *pixd,
245 + __out ATOM_FEED **ppFeed
246 + )
247 +{
248 + Assert(pixd);
249 + Assert(ppFeed);
250 +
251 + HRESULT hr = S_OK;
252 + IXMLDOMElement *pFeedElement = NULL;
253 +
254 + ATOM_FEED *pNewFeed = NULL;
255 +
256 + //
257 + // Get the document element and start processing feeds.
258 + //
259 + hr = pixd->get_documentElement(&pFeedElement);
260 + ExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument");
261 +
262 + hr = ParseAtomFeed(pFeedElement, &pNewFeed);
263 + ExitOnFailure(hr, "Failed to parse ATOM feed.");
264 +
265 + if (S_FALSE == hr)
266 + {
267 + hr = S_OK;
268 + }
269 +
270 + *ppFeed = pNewFeed;
271 + pNewFeed = NULL;
272 +
273 +LExit:
274 + ReleaseObject(pFeedElement);
275 +
276 + ReleaseAtomFeed(pNewFeed);
277 +
278 + return hr;
279 +}
280 +
281 +
282 +/********************************************************************
283 + ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element.
284 +
285 +*********************************************************************/
286 +static HRESULT ParseAtomFeed(
287 + __in IXMLDOMNode *pixnFeed,
288 + __out ATOM_FEED **ppFeed
289 + )
290 +{
291 + Assert(pixnFeed);
292 + Assert(ppFeed);
293 +
294 + HRESULT hr = S_OK;
295 + IXMLDOMNodeList *pNodeList = NULL;
296 +
297 + ATOM_FEED *pNewFeed = NULL;
298 + DWORD cAuthors = 0;
299 + DWORD cCategories = 0;
300 + DWORD cEntries = 0;
301 + DWORD cLinks = 0;
302 +
303 + IXMLDOMNode *pNode = NULL;
304 + BSTR bstrNodeName = NULL;
305 +
306 + // First, allocate the new feed and all the possible sub elements.
307 + pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE);
308 + ExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure.");
309 +
310 + pNewFeed->pixn = pixnFeed;
311 + pNewFeed->pixn->AddRef();
312 +
313 + hr = AllocateAtomType<ATOM_AUTHOR>(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors);
314 + ExitOnFailure(hr, "Failed to allocate ATOM feed authors.");
315 +
316 + hr = AllocateAtomType<ATOM_CATEGORY>(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories);
317 + ExitOnFailure(hr, "Failed to allocate ATOM feed categories.");
318 +
319 + hr = AllocateAtomType<ATOM_ENTRY>(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries);
320 + ExitOnFailure(hr, "Failed to allocate ATOM feed entries.");
321 +
322 + hr = AllocateAtomType<ATOM_LINK>(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks);
323 + ExitOnFailure(hr, "Failed to allocate ATOM feed links.");
324 +
325 + // Second, process the elements under a feed.
326 + hr = pixnFeed->get_childNodes(&pNodeList);
327 + ExitOnFailure(hr, "Failed to get child nodes of ATOM feed element.");
328 +
329 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
330 + {
331 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1))
332 + {
333 + hr = AssignString(&pNewFeed->wzGenerator, pNode);
334 + ExitOnFailure(hr, "Failed to allocate ATOM feed generator.");
335 + }
336 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1))
337 + {
338 + hr = AssignString(&pNewFeed->wzIcon, pNode);
339 + ExitOnFailure(hr, "Failed to allocate ATOM feed icon.");
340 + }
341 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1))
342 + {
343 + hr = AssignString(&pNewFeed->wzId, pNode);
344 + ExitOnFailure(hr, "Failed to allocate ATOM feed id.");
345 + }
346 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1))
347 + {
348 + hr = AssignString(&pNewFeed->wzLogo, pNode);
349 + ExitOnFailure(hr, "Failed to allocate ATOM feed logo.");
350 + }
351 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1))
352 + {
353 + hr = AssignString(&pNewFeed->wzSubtitle, pNode);
354 + ExitOnFailure(hr, "Failed to allocate ATOM feed subtitle.");
355 + }
356 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1))
357 + {
358 + hr = AssignString(&pNewFeed->wzTitle, pNode);
359 + ExitOnFailure(hr, "Failed to allocate ATOM feed title.");
360 + }
361 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1))
362 + {
363 + hr = AssignDateTime(&pNewFeed->ftUpdated, pNode);
364 + ExitOnFailure(hr, "Failed to allocate ATOM feed updated.");
365 + }
366 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1))
367 + {
368 + hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]);
369 + ExitOnFailure(hr, "Failed to parse ATOM author.");
370 +
371 + ++cAuthors;
372 + }
373 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1))
374 + {
375 + hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]);
376 + ExitOnFailure(hr, "Failed to parse ATOM category.");
377 +
378 + ++cCategories;
379 + }
380 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1))
381 + {
382 + hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]);
383 + ExitOnFailure(hr, "Failed to parse ATOM entry.");
384 +
385 + ++cEntries;
386 + }
387 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1))
388 + {
389 + hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]);
390 + ExitOnFailure(hr, "Failed to parse ATOM link.");
391 +
392 + ++cLinks;
393 + }
394 + else
395 + {
396 + hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements);
397 + ExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName);
398 + }
399 +
400 + ReleaseNullBSTR(bstrNodeName);
401 + ReleaseNullObject(pNode);
402 + }
403 +
404 + if (!pNewFeed->wzId || !*pNewFeed->wzId)
405 + {
406 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
407 + ExitOnRootFailure(hr, "Failed to find required feed/id element.");
408 + }
409 + else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle)
410 + {
411 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
412 + ExitOnRootFailure(hr, "Failed to find required feed/title element.");
413 + }
414 + else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime)
415 + {
416 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
417 + ExitOnRootFailure(hr, "Failed to find required feed/updated element.");
418 + }
419 +
420 + *ppFeed = pNewFeed;
421 + pNewFeed = NULL;
422 +
423 +LExit:
424 + ReleaseBSTR(bstrNodeName);
425 + ReleaseObject(pNode);
426 + ReleaseObject(pNodeList);
427 +
428 + ReleaseAtomFeed(pNewFeed);
429 +
430 + return hr;
431 +}
432 +
433 +
434 +/********************************************************************
435 + AllocateAtomType - allocates enough space for all of the ATOM elements
436 + of a particular type under a particular node.
437 +
438 +*********************************************************************/
439 +template<class T> static HRESULT AllocateAtomType(
440 + __in IXMLDOMNode* pixnParent,
441 + __in LPCWSTR wzT,
442 + __out T** pprgT,
443 + __out DWORD* pcT
444 + )
445 +{
446 + HRESULT hr = S_OK;
447 + IXMLDOMNodeList *pNodeList = NULL;
448 +
449 + long cT = 0;
450 + T* prgT = NULL;
451 +
452 + hr = XmlSelectNodes(pixnParent, wzT, &pNodeList);
453 + ExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT);
454 +
455 + if (S_OK == hr)
456 + {
457 + hr = pNodeList->get_length(&cT);
458 + ExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT);
459 +
460 + if (cT == 0)
461 + {
462 + ExitFunction();
463 + }
464 +
465 + prgT = static_cast<T*>(MemAlloc(sizeof(T) * cT, TRUE));
466 + ExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM.");
467 +
468 + *pcT = cT;
469 + *pprgT = prgT;
470 + prgT = NULL;
471 + }
472 + else
473 + {
474 + *pprgT = NULL;
475 + *pcT = 0;
476 + }
477 +
478 +LExit:
479 + ReleaseMem(prgT);
480 + ReleaseObject(pNodeList);
481 +
482 + return hr;
483 +}
484 +
485 +
486 +/********************************************************************
487 + ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node.
488 +
489 +*********************************************************************/
490 +static HRESULT ParseAtomAuthor(
491 + __in IXMLDOMNode* pixnAuthor,
492 + __in ATOM_AUTHOR* pAuthor
493 + )
494 +{
495 + HRESULT hr = S_OK;
496 +
497 + IXMLDOMNodeList *pNodeList = NULL;
498 + IXMLDOMNode *pNode = NULL;
499 + BSTR bstrNodeName = NULL;
500 +
501 + hr = pixnAuthor->get_childNodes(&pNodeList);
502 + ExitOnFailure(hr, "Failed to get child nodes of ATOM author element.");
503 +
504 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
505 + {
506 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1))
507 + {
508 + hr = AssignString(&pAuthor->wzName, pNode);
509 + ExitOnFailure(hr, "Failed to allocate ATOM author name.");
510 + }
511 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1))
512 + {
513 + hr = AssignString(&pAuthor->wzEmail, pNode);
514 + ExitOnFailure(hr, "Failed to allocate ATOM author email.");
515 + }
516 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1))
517 + {
518 + hr = AssignString(&pAuthor->wzUrl, pNode);
519 + ExitOnFailure(hr, "Failed to allocate ATOM author uri.");
520 + }
521 +
522 + ReleaseNullBSTR(bstrNodeName);
523 + ReleaseNullObject(pNode);
524 + }
525 + ExitOnFailure(hr, "Failed to process all ATOM author elements.");
526 +
527 + hr = S_OK;
528 +
529 +LExit:
530 + ReleaseBSTR(bstrNodeName);
531 + ReleaseObject(pNode);
532 + ReleaseObject(pNodeList);
533 +
534 + return hr;
535 +}
536 +
537 +
538 +/********************************************************************
539 + ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node.
540 +
541 +*********************************************************************/
542 +static HRESULT ParseAtomCategory(
543 + __in IXMLDOMNode* pixnCategory,
544 + __in ATOM_CATEGORY* pCategory
545 + )
546 +{
547 + HRESULT hr = S_OK;
548 +
549 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
550 + IXMLDOMNodeList *pNodeList = NULL;
551 + IXMLDOMNode *pNode = NULL;
552 + BSTR bstrNodeName = NULL;
553 +
554 + // Process attributes first.
555 + hr = pixnCategory->get_attributes(&pixnnmAttributes);
556 + ExitOnFailure(hr, "Failed get attributes on ATOM unknown element.");
557 +
558 + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName)))
559 + {
560 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1))
561 + {
562 + hr = AssignString(&pCategory->wzLabel, pNode);
563 + ExitOnFailure(hr, "Failed to allocate ATOM category label.");
564 + }
565 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1))
566 + {
567 + hr = AssignString(&pCategory->wzScheme, pNode);
568 + ExitOnFailure(hr, "Failed to allocate ATOM category scheme.");
569 + }
570 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1))
571 + {
572 + hr = AssignString(&pCategory->wzTerm, pNode);
573 + ExitOnFailure(hr, "Failed to allocate ATOM category term.");
574 + }
575 +
576 + ReleaseNullBSTR(bstrNodeName);
577 + ReleaseNullObject(pNode);
578 + }
579 + ExitOnFailure(hr, "Failed to process all ATOM category attributes.");
580 +
581 + // Process elements second.
582 + hr = pixnCategory->get_childNodes(&pNodeList);
583 + ExitOnFailure(hr, "Failed to get child nodes of ATOM category element.");
584 +
585 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
586 + {
587 + hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements);
588 + ExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName);
589 +
590 + ReleaseNullBSTR(bstrNodeName);
591 + ReleaseNullObject(pNode);
592 + }
593 + ExitOnFailure(hr, "Failed to process all ATOM category elements.");
594 +
595 + hr = S_OK;
596 +
597 +LExit:
598 + ReleaseBSTR(bstrNodeName);
599 + ReleaseObject(pNode);
600 + ReleaseObject(pNodeList);
601 + ReleaseObject(pixnnmAttributes);
602 +
603 + return hr;
604 +}
605 +
606 +
607 +/********************************************************************
608 + ParseAtomContent - parses out an ATOM content from a loaded XML DOM node.
609 +
610 +*********************************************************************/
611 +static HRESULT ParseAtomContent(
612 + __in IXMLDOMNode* pixnContent,
613 + __in ATOM_CONTENT* pContent
614 + )
615 +{
616 + HRESULT hr = S_OK;
617 +
618 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
619 + IXMLDOMNodeList *pNodeList = NULL;
620 + IXMLDOMNode *pNode = NULL;
621 + BSTR bstrNodeName = NULL;
622 +
623 + // Process attributes first.
624 + hr = pixnContent->get_attributes(&pixnnmAttributes);
625 + ExitOnFailure(hr, "Failed get attributes on ATOM unknown element.");
626 +
627 + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName)))
628 + {
629 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1))
630 + {
631 + hr = AssignString(&pContent->wzType, pNode);
632 + ExitOnFailure(hr, "Failed to allocate ATOM content type.");
633 + }
634 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1))
635 + {
636 + hr = AssignString(&pContent->wzUrl, pNode);
637 + ExitOnFailure(hr, "Failed to allocate ATOM content scheme.");
638 + }
639 +
640 + ReleaseNullBSTR(bstrNodeName);
641 + ReleaseNullObject(pNode);
642 + }
643 + ExitOnFailure(hr, "Failed to process all ATOM content attributes.");
644 +
645 + // Process elements second.
646 + hr = pixnContent->get_childNodes(&pNodeList);
647 + ExitOnFailure(hr, "Failed to get child nodes of ATOM content element.");
648 +
649 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
650 + {
651 + hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements);
652 + ExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName);
653 +
654 + ReleaseNullBSTR(bstrNodeName);
655 + ReleaseNullObject(pNode);
656 + }
657 + ExitOnFailure(hr, "Failed to process all ATOM content elements.");
658 +
659 + hr = AssignString(&pContent->wzValue, pixnContent);
660 + ExitOnFailure(hr, "Failed to allocate ATOM content value.");
661 +
662 +LExit:
663 + ReleaseBSTR(bstrNodeName);
664 + ReleaseObject(pNode);
665 + ReleaseObject(pNodeList);
666 + ReleaseObject(pixnnmAttributes);
667 +
668 + return hr;
669 +}
670 +
671 +
672 +/********************************************************************
673 + ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node.
674 +
675 +*********************************************************************/
676 +static HRESULT ParseAtomEntry(
677 + __in IXMLDOMNode* pixnEntry,
678 + __in ATOM_ENTRY* pEntry
679 + )
680 +{
681 + HRESULT hr = S_OK;
682 +
683 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
684 + IXMLDOMNodeList *pNodeList = NULL;
685 + IXMLDOMNode *pNode = NULL;
686 + BSTR bstrNodeName = NULL;
687 +
688 + DWORD cAuthors = 0;
689 + DWORD cCategories = 0;
690 + DWORD cLinks = 0;
691 +
692 + pEntry->pixn = pixnEntry;
693 + pEntry->pixn->AddRef();
694 +
695 + // First, allocate all the possible sub elements.
696 + hr = AllocateAtomType<ATOM_AUTHOR>(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors);
697 + ExitOnFailure(hr, "Failed to allocate ATOM entry authors.");
698 +
699 + hr = AllocateAtomType<ATOM_CATEGORY>(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories);
700 + ExitOnFailure(hr, "Failed to allocate ATOM entry categories.");
701 +
702 + hr = AllocateAtomType<ATOM_LINK>(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks);
703 + ExitOnFailure(hr, "Failed to allocate ATOM entry links.");
704 +
705 + // Second, process elements.
706 + hr = pixnEntry->get_childNodes(&pNodeList);
707 + ExitOnFailure(hr, "Failed to get child nodes of ATOM entry element.");
708 +
709 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
710 + {
711 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1))
712 + {
713 + hr = AssignString(&pEntry->wzId, pNode);
714 + ExitOnFailure(hr, "Failed to allocate ATOM entry id.");
715 + }
716 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1))
717 + {
718 + hr = AssignString(&pEntry->wzSummary, pNode);
719 + ExitOnFailure(hr, "Failed to allocate ATOM entry summary.");
720 + }
721 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1))
722 + {
723 + hr = AssignString(&pEntry->wzTitle, pNode);
724 + ExitOnFailure(hr, "Failed to allocate ATOM entry title.");
725 + }
726 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1))
727 + {
728 + hr = AssignDateTime(&pEntry->ftPublished, pNode);
729 + ExitOnFailure(hr, "Failed to allocate ATOM entry published.");
730 + }
731 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1))
732 + {
733 + hr = AssignDateTime(&pEntry->ftUpdated, pNode);
734 + ExitOnFailure(hr, "Failed to allocate ATOM entry updated.");
735 + }
736 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1))
737 + {
738 + hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]);
739 + ExitOnFailure(hr, "Failed to parse ATOM entry author.");
740 +
741 + ++cAuthors;
742 + }
743 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1))
744 + {
745 + hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]);
746 + ExitOnFailure(hr, "Failed to parse ATOM entry category.");
747 +
748 + ++cCategories;
749 + }
750 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1))
751 + {
752 + if (NULL != pEntry->pContent)
753 + {
754 + hr = E_UNEXPECTED;
755 + ExitOnFailure(hr, "Cannot have two content elements in ATOM entry.");
756 + }
757 +
758 + pEntry->pContent = static_cast<ATOM_CONTENT*>(MemAlloc(sizeof(ATOM_CONTENT), TRUE));
759 + ExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content.");
760 +
761 + hr = ParseAtomContent(pNode, pEntry->pContent);
762 + ExitOnFailure(hr, "Failed to parse ATOM entry content.");
763 + }
764 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1))
765 + {
766 + hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]);
767 + ExitOnFailure(hr, "Failed to parse ATOM entry link.");
768 +
769 + ++cLinks;
770 + }
771 + else
772 + {
773 + hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements);
774 + ExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName);
775 + }
776 +
777 + ReleaseNullBSTR(bstrNodeName);
778 + ReleaseNullObject(pNode);
779 + }
780 + ExitOnFailure(hr, "Failed to process all ATOM entry elements.");
781 +
782 + if (!pEntry->wzId || !*pEntry->wzId)
783 + {
784 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
785 + ExitOnRootFailure(hr, "Failed to find required feed/entry/id element.");
786 + }
787 + else if (!pEntry->wzTitle || !*pEntry->wzTitle)
788 + {
789 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
790 + ExitOnRootFailure(hr, "Failed to find required feed/entry/title element.");
791 + }
792 + else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime)
793 + {
794 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
795 + ExitOnRootFailure(hr, "Failed to find required feed/entry/updated element.");
796 + }
797 +
798 + hr = S_OK;
799 +
800 +LExit:
801 + ReleaseBSTR(bstrNodeName);
802 + ReleaseObject(pNode);
803 + ReleaseObject(pNodeList);
804 + ReleaseObject(pixnnmAttributes);
805 +
806 + return hr;
807 +}
808 +
809 +
810 +/********************************************************************
811 + ParseAtomLink - parses out an ATOM link from a loaded XML DOM node.
812 +
813 +*********************************************************************/
814 +static HRESULT ParseAtomLink(
815 + __in IXMLDOMNode* pixnLink,
816 + __in ATOM_LINK* pLink
817 + )
818 +{
819 + HRESULT hr = S_OK;
820 +
821 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
822 + IXMLDOMNodeList *pNodeList = NULL;
823 + IXMLDOMNode *pNode = NULL;
824 + BSTR bstrNodeName = NULL;
825 +
826 + // Process attributes first.
827 + hr = pixnLink->get_attributes(&pixnnmAttributes);
828 + ExitOnFailure(hr, "Failed get attributes for ATOM link.");
829 +
830 + while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName)))
831 + {
832 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1))
833 + {
834 + hr = AssignString(&pLink->wzRel, pNode);
835 + ExitOnFailure(hr, "Failed to allocate ATOM link rel.");
836 + }
837 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1))
838 + {
839 + hr = AssignString(&pLink->wzUrl, pNode);
840 + ExitOnFailure(hr, "Failed to allocate ATOM link href.");
841 + }
842 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1))
843 + {
844 + hr = XmlGetAttributeLargeNumber(pixnLink, bstrNodeName, &pLink->dw64Length);
845 + if (E_INVALIDARG == hr)
846 + {
847 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
848 + }
849 + ExitOnFailure(hr, "Failed to parse ATOM link length.");
850 + }
851 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1))
852 + {
853 + hr = AssignString(&pLink->wzTitle, pNode);
854 + ExitOnFailure(hr, "Failed to allocate ATOM link title.");
855 + }
856 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1))
857 + {
858 + hr = AssignString(&pLink->wzType, pNode);
859 + ExitOnFailure(hr, "Failed to allocate ATOM link type.");
860 + }
861 + else
862 + {
863 + hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes);
864 + ExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName);
865 + }
866 +
867 + ReleaseNullBSTR(bstrNodeName);
868 + ReleaseNullObject(pNode);
869 + }
870 + ExitOnFailure(hr, "Failed to process all ATOM link attributes.");
871 +
872 + // Process elements second.
873 + hr = pixnLink->get_childNodes(&pNodeList);
874 + ExitOnFailure(hr, "Failed to get child nodes of ATOM link element.");
875 +
876 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
877 + {
878 + hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements);
879 + ExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName);
880 +
881 + ReleaseNullBSTR(bstrNodeName);
882 + ReleaseNullObject(pNode);
883 + }
884 + ExitOnFailure(hr, "Failed to process all ATOM link elements.");
885 +
886 + hr = AssignString(&pLink->wzValue, pixnLink);
887 + ExitOnFailure(hr, "Failed to allocate ATOM link value.");
888 +
889 +LExit:
890 + ReleaseBSTR(bstrNodeName);
891 + ReleaseObject(pNode);
892 + ReleaseObject(pNodeList);
893 + ReleaseObject(pixnnmAttributes);
894 +
895 + return hr;
896 +}
897 +
898 +
899 +/********************************************************************
900 + ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node.
901 +
902 +*********************************************************************/
903 +static HRESULT ParseAtomUnknownElement(
904 + __in IXMLDOMNode *pNode,
905 + __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement
906 + )
907 +{
908 + Assert(ppUnknownElement);
909 +
910 + HRESULT hr = S_OK;
911 + BSTR bstrNodeNamespace = NULL;
912 + BSTR bstrNodeName = NULL;
913 + BSTR bstrNodeValue = NULL;
914 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
915 + IXMLDOMNode* pixnAttribute = NULL;
916 + ATOM_UNKNOWN_ELEMENT* pNewUnknownElement;
917 +
918 + pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE);
919 + ExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element.");
920 +
921 + hr = pNode->get_namespaceURI(&bstrNodeNamespace);
922 + if (S_OK == hr)
923 + {
924 + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0);
925 + ExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace.");
926 + }
927 + else if (S_FALSE == hr)
928 + {
929 + hr = S_OK;
930 + }
931 + ExitOnFailure(hr, "Failed to get unknown element namespace.");
932 +
933 + hr = pNode->get_baseName(&bstrNodeName);
934 + ExitOnFailure(hr, "Failed to get unknown element name.");
935 +
936 + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0);
937 + ExitOnFailure(hr, "Failed to allocate ATOM unknown element name.");
938 +
939 + hr = XmlGetText(pNode, &bstrNodeValue);
940 + ExitOnFailure(hr, "Failed to get unknown element value.");
941 +
942 + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0);
943 + ExitOnFailure(hr, "Failed to allocate ATOM unknown element value.");
944 +
945 + hr = pNode->get_attributes(&pixnnmAttributes);
946 + ExitOnFailure(hr, "Failed get attributes on ATOM unknown element.");
947 +
948 + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute)))
949 + {
950 + hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes);
951 + ExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element.");
952 +
953 + ReleaseNullObject(pixnAttribute);
954 + }
955 +
956 + if (S_FALSE == hr)
957 + {
958 + hr = S_OK;
959 + }
960 + ExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element.");
961 +
962 + ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement;
963 + while (*ppTail)
964 + {
965 + ppTail = &(*ppTail)->pNext;
966 + }
967 +
968 + *ppTail = pNewUnknownElement;
969 + pNewUnknownElement = NULL;
970 +
971 +LExit:
972 + FreeAtomUnknownElementList(pNewUnknownElement);
973 +
974 + ReleaseBSTR(bstrNodeNamespace);
975 + ReleaseBSTR(bstrNodeName);
976 + ReleaseBSTR(bstrNodeValue);
977 + ReleaseObject(pixnnmAttributes);
978 + ReleaseObject(pixnAttribute);
979 +
980 + return hr;
981 +}
982 +
983 +
984 +/********************************************************************
985 + ParseAtomUnknownAttribute - parses out attribute from an unknown element
986 +
987 +*********************************************************************/
988 +static HRESULT ParseAtomUnknownAttribute(
989 + __in IXMLDOMNode *pNode,
990 + __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
991 + )
992 +{
993 + Assert(ppUnknownAttribute);
994 +
995 + HRESULT hr = S_OK;
996 + BSTR bstrNodeNamespace = NULL;
997 + BSTR bstrNodeName = NULL;
998 + BSTR bstrNodeValue = NULL;
999 + ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute;
1000 +
1001 + pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE);
1002 + ExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute.");
1003 +
1004 + hr = pNode->get_namespaceURI(&bstrNodeNamespace);
1005 + if (S_OK == hr)
1006 + {
1007 + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0);
1008 + ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace.");
1009 + }
1010 + else if (S_FALSE == hr)
1011 + {
1012 + hr = S_OK;
1013 + }
1014 + ExitOnFailure(hr, "Failed to get unknown attribute namespace.");
1015 +
1016 + hr = pNode->get_baseName(&bstrNodeName);
1017 + ExitOnFailure(hr, "Failed to get unknown attribute name.");
1018 +
1019 + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0);
1020 + ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name.");
1021 +
1022 + hr = XmlGetText(pNode, &bstrNodeValue);
1023 + ExitOnFailure(hr, "Failed to get unknown attribute value.");
1024 +
1025 + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0);
1026 + ExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value.");
1027 +
1028 + ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute;
1029 + while (*ppTail)
1030 + {
1031 + ppTail = &(*ppTail)->pNext;
1032 + }
1033 +
1034 + *ppTail = pNewUnknownAttribute;
1035 + pNewUnknownAttribute = NULL;
1036 +
1037 +LExit:
1038 + FreeAtomUnknownAttributeList(pNewUnknownAttribute);
1039 +
1040 + ReleaseBSTR(bstrNodeNamespace);
1041 + ReleaseBSTR(bstrNodeName);
1042 + ReleaseBSTR(bstrNodeValue);
1043 +
1044 + return hr;
1045 +}
1046 +
1047 +
1048 +/********************************************************************
1049 + AssignDateTime - assigns the value of a node to a FILETIME struct.
1050 +
1051 +*********************************************************************/
1052 +static HRESULT AssignDateTime(
1053 + __in FILETIME* pft,
1054 + __in IXMLDOMNode* pNode
1055 + )
1056 +{
1057 + HRESULT hr = S_OK;
1058 + BSTR bstrValue = NULL;
1059 +
1060 + if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime)
1061 + {
1062 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1063 + ExitOnRootFailure(hr, "Already process this datetime value.");
1064 + }
1065 +
1066 + hr = XmlGetText(pNode, &bstrValue);
1067 + ExitOnFailure(hr, "Failed to get value.");
1068 +
1069 + if (S_OK == hr)
1070 + {
1071 + hr = TimeFromString3339(bstrValue, pft);
1072 + ExitOnFailure(hr, "Failed to convert value to time.");
1073 + }
1074 + else
1075 + {
1076 + ZeroMemory(pft, sizeof(FILETIME));
1077 + hr = S_OK;
1078 + }
1079 +
1080 +LExit:
1081 + ReleaseBSTR(bstrValue);
1082 +
1083 + return hr;
1084 +}
1085 +
1086 +
1087 +/********************************************************************
1088 + AssignString - assigns the value of a node to a dynamic string.
1089 +
1090 +*********************************************************************/
1091 +static HRESULT AssignString(
1092 + __out_z LPWSTR* pwzValue,
1093 + __in IXMLDOMNode* pNode
1094 + )
1095 +{
1096 + HRESULT hr = S_OK;
1097 + BSTR bstrValue = NULL;
1098 +
1099 + if (pwzValue && *pwzValue)
1100 + {
1101 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1102 + ExitOnRootFailure(hr, "Already processed this value.");
1103 + }
1104 +
1105 + hr = XmlGetText(pNode, &bstrValue);
1106 + ExitOnFailure(hr, "Failed to get value.");
1107 +
1108 + if (S_OK == hr)
1109 + {
1110 + hr = StrAllocString(pwzValue, bstrValue, 0);
1111 + ExitOnFailure(hr, "Failed to allocate value.");
1112 + }
1113 + else
1114 + {
1115 + ReleaseNullStr(pwzValue);
1116 + hr = S_OK;
1117 + }
1118 +
1119 +LExit:
1120 + ReleaseBSTR(bstrValue);
1121 +
1122 + return hr;
1123 +}
1124 +
1125 +
1126 +/********************************************************************
1127 + FreeAtomAuthor - releases all of the memory used by an ATOM author.
1128 +
1129 +*********************************************************************/
1130 +static void FreeAtomAuthor(
1131 + __in_opt ATOM_AUTHOR* pAuthor
1132 + )
1133 +{
1134 + if (pAuthor)
1135 + {
1136 + ReleaseStr(pAuthor->wzUrl);
1137 + ReleaseStr(pAuthor->wzEmail);
1138 + ReleaseStr(pAuthor->wzName);
1139 + }
1140 +}
1141 +
1142 +
1143 +/********************************************************************
1144 + FreeAtomCategory - releases all of the memory used by an ATOM category.
1145 +
1146 +*********************************************************************/
1147 +static void FreeAtomCategory(
1148 + __in_opt ATOM_CATEGORY* pCategory
1149 + )
1150 +{
1151 + if (pCategory)
1152 + {
1153 + FreeAtomUnknownElementList(pCategory->pUnknownElements);
1154 +
1155 + ReleaseStr(pCategory->wzTerm);
1156 + ReleaseStr(pCategory->wzScheme);
1157 + ReleaseStr(pCategory->wzLabel);
1158 + }
1159 +}
1160 +
1161 +
1162 +/********************************************************************
1163 + FreeAtomContent - releases all of the memory used by an ATOM content.
1164 +
1165 +*********************************************************************/
1166 +static void FreeAtomContent(
1167 + __in_opt ATOM_CONTENT* pContent
1168 + )
1169 +{
1170 + if (pContent)
1171 + {
1172 + FreeAtomUnknownElementList(pContent->pUnknownElements);
1173 +
1174 + ReleaseStr(pContent->wzValue);
1175 + ReleaseStr(pContent->wzUrl);
1176 + ReleaseStr(pContent->wzType);
1177 + }
1178 +}
1179 +
1180 +
1181 +/********************************************************************
1182 + FreeAtomEntry - releases all of the memory used by an ATOM entry.
1183 +
1184 +*********************************************************************/
1185 +static void FreeAtomEntry(
1186 + __in_opt ATOM_ENTRY* pEntry
1187 + )
1188 +{
1189 + if (pEntry)
1190 + {
1191 + FreeAtomUnknownElementList(pEntry->pUnknownElements);
1192 + ReleaseObject(pEntry->pixn);
1193 +
1194 + for (DWORD i = 0; i < pEntry->cLinks; ++i)
1195 + {
1196 + FreeAtomLink(pEntry->rgLinks + i);
1197 + }
1198 + ReleaseMem(pEntry->rgLinks);
1199 +
1200 + for (DWORD i = 0; i < pEntry->cCategories; ++i)
1201 + {
1202 + FreeAtomCategory(pEntry->rgCategories + i);
1203 + }
1204 + ReleaseMem(pEntry->rgCategories);
1205 +
1206 + for (DWORD i = 0; i < pEntry->cAuthors; ++i)
1207 + {
1208 + FreeAtomAuthor(pEntry->rgAuthors + i);
1209 + }
1210 + ReleaseMem(pEntry->rgAuthors);
1211 +
1212 + FreeAtomContent(pEntry->pContent);
1213 + ReleaseMem(pEntry->pContent);
1214 +
1215 + ReleaseStr(pEntry->wzTitle);
1216 + ReleaseStr(pEntry->wzSummary);
1217 + ReleaseStr(pEntry->wzId);
1218 + }
1219 +}
1220 +
1221 +
1222 +/********************************************************************
1223 + FreeAtomLink - releases all of the memory used by an ATOM link.
1224 +
1225 +*********************************************************************/
1226 +static void FreeAtomLink(
1227 + __in_opt ATOM_LINK* pLink
1228 + )
1229 +{
1230 + if (pLink)
1231 + {
1232 + FreeAtomUnknownElementList(pLink->pUnknownElements);
1233 + FreeAtomUnknownAttributeList(pLink->pUnknownAttributes);
1234 +
1235 + ReleaseStr(pLink->wzValue);
1236 + ReleaseStr(pLink->wzUrl);
1237 + ReleaseStr(pLink->wzType);
1238 + ReleaseStr(pLink->wzTitle);
1239 + ReleaseStr(pLink->wzRel);
1240 + }
1241 +}
1242 +
1243 +
1244 +/********************************************************************
1245 + FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements
1246 +
1247 +*********************************************************************/
1248 +static void FreeAtomUnknownElementList(
1249 + __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement
1250 + )
1251 +{
1252 + while (pUnknownElement)
1253 + {
1254 + ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement;
1255 + pUnknownElement = pUnknownElement->pNext;
1256 +
1257 + FreeAtomUnknownAttributeList(pFree->pAttributes);
1258 + ReleaseStr(pFree->wzNamespace);
1259 + ReleaseStr(pFree->wzElement);
1260 + ReleaseStr(pFree->wzValue);
1261 + MemFree(pFree);
1262 + }
1263 +}
1264 +
1265 +
1266 +/********************************************************************
1267 + FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes
1268 +
1269 +*********************************************************************/
1270 +static void FreeAtomUnknownAttributeList(
1271 + __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute
1272 + )
1273 +{
1274 + while (pUnknownAttribute)
1275 + {
1276 + ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute;
1277 + pUnknownAttribute = pUnknownAttribute->pNext;
1278 +
1279 + ReleaseStr(pFree->wzNamespace);
1280 + ReleaseStr(pFree->wzAttribute);
1281 + ReleaseStr(pFree->wzValue);
1282 + MemFree(pFree);
1283 + }
1284 +}
src/dutil/buffutil.cpp new
+419
@@ -0,0 +1,419 @@
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 +
5 +
6 +// constants
7 +
8 +#define BUFFER_INCREMENT 128
9 +
10 +
11 +// helper function declarations
12 +
13 +static HRESULT EnsureBufferSize(
14 + __deref_out_bcount(cbSize) BYTE** ppbBuffer,
15 + __in SIZE_T cbSize
16 + );
17 +
18 +
19 +// functions
20 +
21 +extern "C" HRESULT BuffReadNumber(
22 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
23 + __in SIZE_T cbBuffer,
24 + __inout SIZE_T* piBuffer,
25 + __out DWORD* pdw
26 + )
27 +{
28 + Assert(pbBuffer);
29 + Assert(piBuffer);
30 + Assert(pdw);
31 +
32 + HRESULT hr = S_OK;
33 + SIZE_T cbAvailable = 0;
34 +
35 + // get availiable data size
36 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
37 + ExitOnRootFailure(hr, "Failed to calculate available data size.");
38 +
39 + // verify buffer size
40 + if (sizeof(DWORD) > cbAvailable)
41 + {
42 + hr = E_INVALIDARG;
43 + ExitOnRootFailure(hr, "Buffer too small.");
44 + }
45 +
46 + *pdw = *(const DWORD*)(pbBuffer + *piBuffer);
47 + *piBuffer += sizeof(DWORD);
48 +
49 +LExit:
50 + return hr;
51 +}
52 +
53 +extern "C" HRESULT BuffReadNumber64(
54 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
55 + __in SIZE_T cbBuffer,
56 + __inout SIZE_T* piBuffer,
57 + __out DWORD64* pdw64
58 + )
59 +{
60 + Assert(pbBuffer);
61 + Assert(piBuffer);
62 + Assert(pdw64);
63 +
64 + HRESULT hr = S_OK;
65 + SIZE_T cbAvailable = 0;
66 +
67 + // get availiable data size
68 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
69 + ExitOnRootFailure(hr, "Failed to calculate available data size.");
70 +
71 + // verify buffer size
72 + if (sizeof(DWORD64) > cbAvailable)
73 + {
74 + hr = E_INVALIDARG;
75 + ExitOnRootFailure(hr, "Buffer too small.");
76 + }
77 +
78 + *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer);
79 + *piBuffer += sizeof(DWORD64);
80 +
81 +LExit:
82 + return hr;
83 +}
84 +
85 +extern "C" HRESULT BuffReadString(
86 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
87 + __in SIZE_T cbBuffer,
88 + __inout SIZE_T* piBuffer,
89 + __deref_out_z LPWSTR* pscz
90 + )
91 +{
92 + Assert(pbBuffer);
93 + Assert(piBuffer);
94 + Assert(pscz);
95 +
96 + HRESULT hr = S_OK;
97 + DWORD cch = 0;
98 + DWORD cb = 0;
99 + SIZE_T cbAvailable = 0;
100 +
101 + // get availiable data size
102 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
103 + ExitOnRootFailure(hr, "Failed to calculate available data size for character count.");
104 +
105 + // verify buffer size
106 + if (sizeof(DWORD) > cbAvailable)
107 + {
108 + hr = E_INVALIDARG;
109 + ExitOnRootFailure(hr, "Buffer too small.");
110 + }
111 +
112 + // read character count
113 + cch = *(const DWORD*)(pbBuffer + *piBuffer);
114 +
115 + hr = ::DWordMult(cch, static_cast<DWORD>(sizeof(WCHAR)), &cb);
116 + ExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size");
117 +
118 + hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer);
119 + ExitOnRootFailure(hr, "Overflow while adding to calculate buffer size");
120 +
121 + // get availiable data size
122 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
123 + ExitOnRootFailure(hr, "Failed to calculate available data size for character buffer.");
124 +
125 + // verify buffer size
126 + if (cb > cbAvailable)
127 + {
128 + hr = E_INVALIDARG;
129 + ExitOnRootFailure(hr, "Buffer too small to hold character data.");
130 + }
131 +
132 + // copy character data
133 + hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch);
134 + ExitOnFailure(hr, "Failed to copy character data.");
135 +
136 + *piBuffer += cb;
137 +
138 +LExit:
139 + return hr;
140 +}
141 +
142 +extern "C" HRESULT BuffReadStringAnsi(
143 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
144 + __in SIZE_T cbBuffer,
145 + __inout SIZE_T* piBuffer,
146 + __deref_out_z LPSTR* pscz
147 + )
148 +{
149 + Assert(pbBuffer);
150 + Assert(piBuffer);
151 + Assert(pscz);
152 +
153 + HRESULT hr = S_OK;
154 + DWORD cch = 0;
155 + DWORD cb = 0;
156 + SIZE_T cbAvailable = 0;
157 +
158 + // get availiable data size
159 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
160 + ExitOnRootFailure(hr, "Failed to calculate available data size for character count.");
161 +
162 + // verify buffer size
163 + if (sizeof(DWORD) > cbAvailable)
164 + {
165 + hr = E_INVALIDARG;
166 + ExitOnRootFailure(hr, "Buffer too small.");
167 + }
168 +
169 + // read character count
170 + cch = *(const DWORD*)(pbBuffer + *piBuffer);
171 +
172 + hr = ::DWordMult(cch, static_cast<DWORD>(sizeof(CHAR)), &cb);
173 + ExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size");
174 +
175 + hr = ::SIZETAdd(*piBuffer, sizeof(DWORD), piBuffer);
176 + ExitOnRootFailure(hr, "Overflow while adding to calculate buffer size");
177 +
178 + // get availiable data size
179 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
180 + ExitOnRootFailure(hr, "Failed to calculate available data size for character buffer.");
181 +
182 + // verify buffer size
183 + if (cb > cbAvailable)
184 + {
185 + hr = E_INVALIDARG;
186 + ExitOnRootFailure(hr, "Buffer too small to hold character count.");
187 + }
188 +
189 + // copy character data
190 + hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch);
191 + ExitOnFailure(hr, "Failed to copy character data.");
192 +
193 + *piBuffer += cb;
194 +
195 +LExit:
196 + return hr;
197 +}
198 +
199 +extern "C" HRESULT BuffReadStream(
200 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
201 + __in SIZE_T cbBuffer,
202 + __inout SIZE_T* piBuffer,
203 + __deref_out_bcount(*pcbStream) BYTE** ppbStream,
204 + __out SIZE_T* pcbStream
205 + )
206 +{
207 + Assert(pbBuffer);
208 + Assert(piBuffer);
209 + Assert(ppbStream);
210 + Assert(pcbStream);
211 +
212 + HRESULT hr = S_OK;
213 + DWORD64 cb = 0;
214 + SIZE_T cbAvailable = 0;
215 +
216 + // get availiable data size
217 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
218 + ExitOnRootFailure(hr, "Failed to calculate available data size for stream size.");
219 +
220 + // verify buffer size
221 + if (sizeof(DWORD64) > cbAvailable)
222 + {
223 + hr = E_INVALIDARG;
224 + ExitOnRootFailure(hr, "Buffer too small.");
225 + }
226 +
227 + // read stream size
228 + cb = *(const DWORD64*)(pbBuffer + *piBuffer);
229 + *piBuffer += sizeof(DWORD64);
230 +
231 + // get availiable data size
232 + hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
233 + ExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer.");
234 +
235 + // verify buffer size
236 + if (cb > cbAvailable)
237 + {
238 + hr = E_INVALIDARG;
239 + ExitOnRootFailure(hr, "Buffer too small to hold byte count.");
240 + }
241 +
242 + // allocate buffer
243 + *ppbStream = (BYTE*)MemAlloc((SIZE_T)cb, TRUE);
244 + ExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream.");
245 +
246 + // read stream data
247 + memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, (SIZE_T)cb);
248 + *piBuffer += (SIZE_T)cb;
249 +
250 + // return stream size
251 + *pcbStream = (SIZE_T)cb;
252 +
253 +LExit:
254 + return hr;
255 +}
256 +
257 +extern "C" HRESULT BuffWriteNumber(
258 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
259 + __inout SIZE_T* piBuffer,
260 + __in DWORD dw
261 + )
262 +{
263 + Assert(ppbBuffer);
264 + Assert(piBuffer);
265 +
266 + HRESULT hr = S_OK;
267 +
268 + // make sure we have a buffer with sufficient space
269 + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD));
270 + ExitOnFailure(hr, "Failed to ensure buffer size.");
271 +
272 + // copy data to buffer
273 + *(DWORD*)(*ppbBuffer + *piBuffer) = dw;
274 + *piBuffer += sizeof(DWORD);
275 +
276 +LExit:
277 + return hr;
278 +}
279 +
280 +extern "C" HRESULT BuffWriteNumber64(
281 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
282 + __inout SIZE_T* piBuffer,
283 + __in DWORD64 dw64
284 + )
285 +{
286 + Assert(ppbBuffer);
287 + Assert(piBuffer);
288 +
289 + HRESULT hr = S_OK;
290 +
291 + // make sure we have a buffer with sufficient space
292 + hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64));
293 + ExitOnFailure(hr, "Failed to ensure buffer size.");
294 +
295 + // copy data to buffer
296 + *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64;
297 + *piBuffer += sizeof(DWORD64);
298 +
299 +LExit:
300 + return hr;
301 +}
302 +
303 +extern "C" HRESULT BuffWriteString(
304 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
305 + __inout SIZE_T* piBuffer,
306 + __in_z_opt LPCWSTR scz
307 + )
308 +{
309 + Assert(ppbBuffer);
310 + Assert(piBuffer);
311 +
312 + HRESULT hr = S_OK;
313 + DWORD cch = (DWORD)lstrlenW(scz);
314 + SIZE_T cb = cch * sizeof(WCHAR);
315 +
316 + // make sure we have a buffer with sufficient space
317 + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb));
318 + ExitOnFailure(hr, "Failed to ensure buffer size.");
319 +
320 + // copy character count to buffer
321 + *(DWORD*)(*ppbBuffer + *piBuffer) = cch;
322 + *piBuffer += sizeof(DWORD);
323 +
324 + // copy data to buffer
325 + memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb);
326 + *piBuffer += cb;
327 +
328 +LExit:
329 + return hr;
330 +}
331 +
332 +extern "C" HRESULT BuffWriteStringAnsi(
333 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
334 + __inout SIZE_T* piBuffer,
335 + __in_z_opt LPCSTR scz
336 + )
337 +{
338 + Assert(ppbBuffer);
339 + Assert(piBuffer);
340 +
341 + HRESULT hr = S_OK;
342 + DWORD cch = (DWORD)lstrlenA(scz);
343 + SIZE_T cb = cch * sizeof(CHAR);
344 +
345 + // make sure we have a buffer with sufficient space
346 + hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(DWORD) + cb));
347 + ExitOnFailure(hr, "Failed to ensure buffer size.");
348 +
349 + // copy character count to buffer
350 + *(DWORD*)(*ppbBuffer + *piBuffer) = cch;
351 + *piBuffer += sizeof(DWORD);
352 +
353 + // copy data to buffer
354 + memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb);
355 + *piBuffer += cb;
356 +
357 +LExit:
358 + return hr;
359 +}
360 +
361 +extern "C" HRESULT BuffWriteStream(
362 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
363 + __inout SIZE_T* piBuffer,
364 + __in_bcount(cbStream) const BYTE* pbStream,
365 + __in SIZE_T cbStream
366 + )
367 +{
368 + Assert(ppbBuffer);
369 + Assert(piBuffer);
370 + Assert(pbStream);
371 +
372 + HRESULT hr = S_OK;
373 + DWORD64 cb = cbStream;
374 +
375 + // make sure we have a buffer with sufficient space
376 + hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(DWORD64));
377 + ExitOnFailure(hr, "Failed to ensure buffer size.");
378 +
379 + // copy byte count to buffer
380 + *(DWORD64*)(*ppbBuffer + *piBuffer) = cb;
381 + *piBuffer += sizeof(DWORD64);
382 +
383 + // copy data to buffer
384 + memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream);
385 + *piBuffer += cbStream;
386 +
387 +LExit:
388 + return hr;
389 +}
390 +
391 +
392 +// helper functions
393 +
394 +static HRESULT EnsureBufferSize(
395 + __deref_out_bcount(cbSize) BYTE** ppbBuffer,
396 + __in SIZE_T cbSize
397 + )
398 +{
399 + HRESULT hr = S_OK;
400 + SIZE_T cbTarget = ((cbSize / BUFFER_INCREMENT) + 1) * BUFFER_INCREMENT;
401 +
402 + if (*ppbBuffer)
403 + {
404 + if (MemSize(*ppbBuffer) < cbTarget)
405 + {
406 + LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE);
407 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer.");
408 + *ppbBuffer = (BYTE*)pv;
409 + }
410 + }
411 + else
412 + {
413 + *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE);
414 + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer.");
415 + }
416 +
417 +LExit:
418 + return hr;
419 +}
src/dutil/butil.cpp new
+271
@@ -0,0 +1,271 @@
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 +#include "butil.h"
5 +
6 +// constants
7 +// From engine/registration.h
8 +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
9 +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
10 +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
11 +
12 +// Forward declarations.
13 +static HRESULT OpenBundleKey(
14 + __in LPCWSTR wzBundleId,
15 + __in BUNDLE_INSTALL_CONTEXT context,
16 + __inout HKEY *key);
17 +
18 +/********************************************************************
19 +BundleGetBundleInfo - Queries the bundle installation metadata for a given property
20 +
21 +RETURNS:
22 + E_INVALIDARG
23 + An invalid parameter was passed to the function.
24 + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT)
25 + The bundle is not installed
26 + HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY)
27 + The property is unrecognized
28 + HRESULT_FROM_WIN32(ERROR_MORE_DATA)
29 + A buffer is too small to hold the requested data.
30 + E_NOTIMPL:
31 + Tried to read a bundle attribute for a type which has not been implemented
32 +
33 + All other returns are unexpected returns from other dutil methods.
34 +********************************************************************/
35 +extern "C" HRESULT DAPI BundleGetBundleInfo(
36 + __in LPCWSTR wzBundleId,
37 + __in LPCWSTR wzAttribute,
38 + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf,
39 + __inout_opt LPDWORD pcchValueBuf
40 + )
41 +{
42 + Assert(wzBundleId && wzAttribute);
43 +
44 + HRESULT hr = S_OK;
45 + BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE;
46 + LPWSTR sczValue = NULL;
47 + HKEY hkBundle = NULL;
48 + DWORD cchSource = 0;
49 + DWORD dwType = 0;
50 + DWORD dwValue = 0;
51 +
52 + if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute)
53 + {
54 + ExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
55 + }
56 +
57 + if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) &&
58 + FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle)))
59 + {
60 + ExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path.");
61 + }
62 +
63 + // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY
64 + hr = RegGetType(hkBundle, wzAttribute, &dwType);
65 + ExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property.");
66 +
67 + switch (dwType)
68 + {
69 + case REG_SZ:
70 + hr = RegReadString(hkBundle, wzAttribute, &sczValue);
71 + ExitOnFailure(hr, "Failed to read string property.");
72 + break;
73 + case REG_DWORD:
74 + hr = RegReadNumber(hkBundle, wzAttribute, &dwValue);
75 + ExitOnFailure(hr, "Failed to read dword property.");
76 +
77 + hr = StrAllocFormatted(&sczValue, L"%d", dwValue);
78 + ExitOnFailure(hr, "Failed to format dword property as string.");
79 + break;
80 + default:
81 + ExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType);
82 +
83 + }
84 +
85 + hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
86 + ExitOnFailure(hr, "Failed to calculate length of string");
87 +
88 + if (lpValueBuf)
89 + {
90 + // cchSource is the length of the string not including the terminating null character
91 + if (*pcchValueBuf <= cchSource)
92 + {
93 + *pcchValueBuf = ++cchSource;
94 + ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data.");
95 + }
96 +
97 + hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
98 + ExitOnFailure(hr, "Failed to copy the property value to the output buffer.");
99 +
100 + *pcchValueBuf = cchSource++;
101 + }
102 +
103 +LExit:
104 + ReleaseRegKey(hkBundle);
105 + ReleaseStr(sczValue);
106 +
107 + return hr;
108 +}
109 +
110 +/********************************************************************
111 +BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code
112 +
113 +NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long.
114 + The first 38 characters are for the GUID, and the last character is for the terminating null character.
115 +RETURNS:
116 + E_INVALIDARG
117 + An invalid parameter was passed to the function.
118 +
119 + All other returns are unexpected returns from other dutil methods.
120 +********************************************************************/
121 +HRESULT DAPI BundleEnumRelatedBundle(
122 + __in LPCWSTR wzUpgradeCode,
123 + __in BUNDLE_INSTALL_CONTEXT context,
124 + __inout PDWORD pdwStartIndex,
125 + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf
126 + )
127 +{
128 + HRESULT hr = S_OK;
129 + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
130 + HKEY hkUninstall = NULL;
131 + HKEY hkBundle = NULL;
132 + LPWSTR sczUninstallSubKey = NULL;
133 + DWORD cchUninstallSubKey = 0;
134 + LPWSTR sczUninstallSubKeyPath = NULL;
135 + LPWSTR sczValue = NULL;
136 + DWORD dwType = 0;
137 +
138 + LPWSTR* rgsczBundleUpgradeCodes = NULL;
139 + DWORD cBundleUpgradeCodes = 0;
140 + BOOL fUpgradeCodeFound = FALSE;
141 +
142 + if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex)
143 + {
144 + ExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
145 + }
146 +
147 + hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall);
148 + ExitOnFailure(hr, "Failed to open bundle uninstall key path.");
149 +
150 + for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++)
151 + {
152 + hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey);
153 + ExitOnFailure(hr, "Failed to enumerate bundle uninstall key path.");
154 +
155 + hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey);
156 + ExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
157 +
158 + hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle);
159 + ExitOnFailure(hr, "Failed to open uninstall key path.");
160 +
161 + // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ
162 + hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType);
163 + if (FAILED(hr))
164 + {
165 + ReleaseRegKey(hkBundle);
166 + ReleaseNullStr(sczUninstallSubKey);
167 + ReleaseNullStr(sczUninstallSubKeyPath);
168 + // Not a bundle
169 + continue;
170 + }
171 +
172 + switch (dwType)
173 + {
174 + case REG_SZ:
175 + hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue);
176 + ExitOnFailure(hr, "Failed to read BundleUpgradeCode string property.");
177 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1))
178 + {
179 + *pdwStartIndex = dwIndex;
180 + fUpgradeCodeFound = TRUE;
181 + break;
182 + }
183 +
184 + ReleaseNullStr(sczValue);
185 +
186 + break;
187 + case REG_MULTI_SZ:
188 + hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes);
189 + ExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property.");
190 +
191 + for (DWORD i = 0; i < cBundleUpgradeCodes; i++)
192 + {
193 + LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i];
194 + if (wzBundleUpgradeCode && *wzBundleUpgradeCode)
195 + {
196 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1))
197 + {
198 + *pdwStartIndex = dwIndex;
199 + fUpgradeCodeFound = TRUE;
200 + break;
201 + }
202 + }
203 + }
204 + ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
205 +
206 + break;
207 +
208 + default:
209 + ExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType);
210 +
211 + }
212 +
213 + if (fUpgradeCodeFound)
214 + {
215 + if (lpBundleIdBuf)
216 + {
217 + hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchUninstallSubKey));
218 + ExitOnFailure(hr, "Failed to calculate length of string");
219 +
220 + hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
221 + ExitOnFailure(hr, "Failed to copy the property value to the output buffer.");
222 + }
223 +
224 + break;
225 + }
226 +
227 + // Cleanup before next iteration
228 + ReleaseRegKey(hkBundle);
229 + ReleaseNullStr(sczUninstallSubKey);
230 + ReleaseNullStr(sczUninstallSubKeyPath);
231 + }
232 +
233 +LExit:
234 + ReleaseStr(sczValue);
235 + ReleaseStr(sczUninstallSubKey);
236 + ReleaseStr(sczUninstallSubKeyPath);
237 + ReleaseRegKey(hkBundle);
238 + ReleaseRegKey(hkUninstall);
239 + ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
240 +
241 + return hr;
242 +}
243 +
244 +/********************************************************************
245 +OpenBundleKey - Opens the bundle uninstallation key for a given bundle
246 +
247 +NOTE: caller is responsible for closing key
248 +********************************************************************/
249 +HRESULT OpenBundleKey(
250 + __in LPCWSTR wzBundleId,
251 + __in BUNDLE_INSTALL_CONTEXT context,
252 + __inout HKEY *key)
253 +{
254 + Assert(key && wzBundleId);
255 + AssertSz(NULL == *key, "*key should be null");
256 +
257 + HRESULT hr = S_OK;
258 + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
259 + LPWSTR sczKeypath = NULL;
260 +
261 + hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId);
262 + ExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
263 +
264 + hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key);
265 + ExitOnFailure(hr, "Failed to open bundle uninstall key path.");
266 +
267 +LExit:
268 + ReleaseStr(sczKeypath);
269 +
270 + return hr;
271 +}
src/dutil/cabcutil.cpp new
+1532
@@ -0,0 +1,1532 @@
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 +
5 +static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?';
6 +static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024;
7 +
8 +// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder()
9 +// too often (because of duplicates too close together) we theoretically ruin our compression ratio -
10 +// left at zero to maximize install-time performance, because even a small minimum threshhold seems to
11 +// have a high install-time performance cost for little or no size benefit. The value is left here for
12 +// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB.
13 +static const DWORD MINFLUSHTHRESHHOLD = 0;
14 +
15 +// structs
16 +struct MS_CABINET_HEADER
17 +{
18 + DWORD sig;
19 + DWORD csumHeader;
20 + DWORD cbCabinet;
21 + DWORD csumFolders;
22 + DWORD coffFiles;
23 + DWORD csumFiles;
24 + WORD version;
25 + WORD cFolders;
26 + WORD cFiles;
27 + WORD flags;
28 + WORD setID;
29 + WORD iCabinet;
30 +};
31 +
32 +
33 +struct MS_CABINET_ITEM
34 +{
35 + DWORD cbFile;
36 + DWORD uoffFolderStart;
37 + WORD iFolder;
38 + WORD date;
39 + WORD time;
40 + WORD attribs;
41 +};
42 +
43 +struct CABC_INTERNAL_ADDFILEINFO
44 +{
45 + LPCWSTR wzSourcePath;
46 + LPCWSTR wzEmptyPath;
47 +};
48 +
49 +struct CABC_DUPLICATEFILE
50 +{
51 + DWORD dwFileArrayIndex;
52 + DWORD dwDuplicateCabFileIndex;
53 + LPWSTR pwzSourcePath;
54 + LPWSTR pwzToken;
55 +};
56 +
57 +
58 +struct CABC_FILE
59 +{
60 + DWORD dwCabFileIndex;
61 + LPWSTR pwzSourcePath;
62 + LPWSTR pwzToken;
63 + PMSIFILEHASHINFO pmfHash;
64 + LONGLONG llFileSize;
65 + BOOL fHasDuplicates;
66 +};
67 +
68 +
69 +struct CABC_DATA
70 +{
71 + LONGLONG llBytesSinceLastFlush;
72 + LONGLONG llFlushThreshhold;
73 +
74 + STRINGDICT_HANDLE shDictHandle;
75 +
76 + WCHAR wzCabinetPath[MAX_PATH];
77 + WCHAR wzEmptyFile[MAX_PATH];
78 + HANDLE hEmptyFile;
79 + DWORD dwLastFileIndex;
80 +
81 + DWORD cFilePaths;
82 + DWORD cMaxFilePaths;
83 + CABC_FILE *prgFiles;
84 +
85 + DWORD cDuplicates;
86 + DWORD cMaxDuplicates;
87 + CABC_DUPLICATEFILE *prgDuplicates;
88 +
89 + HRESULT hrLastError;
90 + BOOL fGoodCab;
91 +
92 + HFCI hfci;
93 + ERF erf;
94 + CCAB ccab;
95 + TCOMP tc;
96 +
97 + // Below Field are used for Cabinet Splitting
98 + BOOL fCabinetSplittingEnabled;
99 + FileSplitCabNamesCallback fileSplitCabNamesCallback;
100 + WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting
101 +};
102 +
103 +const int CABC_HANDLE_BYTES = sizeof(CABC_DATA);
104 +
105 +//
106 +// prototypes
107 +//
108 +static void FreeCabCData(
109 + __in CABC_DATA* pcd
110 + );
111 +static HRESULT CheckForDuplicateFile(
112 + __in CABC_DATA *pcd,
113 + __out CABC_FILE **ppcf,
114 + __in LPCWSTR wzFileName,
115 + __in PMSIFILEHASHINFO *ppmfHash,
116 + __in LONGLONG llFileSize
117 + );
118 +static HRESULT AddDuplicateFile(
119 + __in CABC_DATA *pcd,
120 + __in DWORD dwFileArrayIndex,
121 + __in_z LPCWSTR wzSourcePath,
122 + __in_opt LPCWSTR wzToken,
123 + __in DWORD dwDuplicateCabFileIndex
124 + );
125 +static HRESULT AddNonDuplicateFile(
126 + __in CABC_DATA *pcd,
127 + __in LPCWSTR wzFile,
128 + __in_opt LPCWSTR wzToken,
129 + __in_opt const MSIFILEHASHINFO* pmfHash,
130 + __in LONGLONG llFileSize,
131 + __in DWORD dwCabFileIndex
132 + );
133 +static HRESULT UpdateDuplicateFiles(
134 + __in const CABC_DATA *pcd
135 + );
136 +static HRESULT DuplicateFile(
137 + __in MS_CABINET_HEADER *pHeader,
138 + __in const CABC_DATA *pcd,
139 + __in const CABC_DUPLICATEFILE *pDuplicate
140 + );
141 +static HRESULT UtcFileTimeToLocalDosDateTime(
142 + __in const FILETIME* pFileTime,
143 + __out USHORT* pDate,
144 + __out USHORT* pTime
145 + );
146 +
147 +static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __out_bcount(CABC_HANDLE_BYTES) void *pv);
148 +static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb);
149 +static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv);
150 +static __callback INT_PTR DIAMONDAPI CabCOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
151 +static __callback UINT FAR DIAMONDAPI CabCRead(__in INT_PTR hf, __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
152 +static __callback UINT FAR DIAMONDAPI CabCWrite(__in INT_PTR hf, __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
153 +static __callback long FAR DIAMONDAPI CabCSeek(__in INT_PTR hf, __in long dist, __in int seektype, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
154 +static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
155 +static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
156 +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __out_bcount(CABC_HANDLE_BYTES) void *pv);
157 +__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv);
158 +static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(__in_z PSTR pszName, __out USHORT *pdate, __out USHORT *ptime, __out USHORT *pattribs, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
159 +static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __out_bcount(CABC_HANDLE_BYTES) void *pv);
160 +
161 +
162 +/********************************************************************
163 +CabcBegin - begins creating a cabinet
164 +
165 +NOTE: phContext must be the same handle used in AddFile and Finish.
166 + wzCabDir can be L"", but not NULL.
167 + dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case.
168 +
169 +********************************************************************/
170 +extern "C" HRESULT DAPI CabCBegin(
171 + __in_z LPCWSTR wzCab,
172 + __in_z LPCWSTR wzCabDir,
173 + __in DWORD dwMaxFiles,
174 + __in DWORD dwMaxSize,
175 + __in DWORD dwMaxThresh,
176 + __in COMPRESSION_TYPE ct,
177 + __out HANDLE *phContext
178 + )
179 +{
180 + Assert(wzCab && *wzCab && phContext);
181 +
182 + HRESULT hr = S_OK;
183 + CABC_DATA *pcd = NULL;
184 + WCHAR wzTempPath[MAX_PATH] = { };
185 +
186 + C_ASSERT(sizeof(MSIFILEHASHINFO) == 20);
187 +
188 + WCHAR wzPathBuffer [MAX_PATH] = L"";
189 + size_t cchPathBuffer;
190 + if (wzCabDir)
191 + {
192 + hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer);
193 + ExitOnFailure(hr, "Failed to get length of cab directory");
194 +
195 + // Need room to terminate with L'\\' and L'\0'
196 + if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer)
197 + {
198 + hr = E_INVALIDARG;
199 + ExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer);
200 + }
201 +
202 + hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir);
203 + ExitOnFailure(hr, "Failed to copy cab directory to buffer");
204 +
205 + if (L'\\' != wzPathBuffer[cchPathBuffer - 1])
206 + {
207 + hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\");
208 + ExitOnFailure(hr, "Failed to cat \\ to end of buffer");
209 + ++cchPathBuffer;
210 + }
211 + }
212 +
213 + pcd = static_cast<CABC_DATA*>(MemAlloc(sizeof(CABC_DATA), TRUE));
214 + ExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure");
215 +
216 + pcd->hrLastError = S_OK;
217 + pcd->fGoodCab = TRUE;
218 + pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD;
219 +
220 + pcd->hEmptyFile = INVALID_HANDLE_VALUE;
221 +
222 + pcd->fileSplitCabNamesCallback = NULL;
223 +
224 + if (NULL == dwMaxSize)
225 + {
226 + pcd->ccab.cb = CAB_MAX_SIZE;
227 + pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired
228 + }
229 + else
230 + {
231 + pcd->ccab.cb = dwMaxSize * 1024 * 1024;
232 + pcd->fCabinetSplittingEnabled = TRUE;
233 + }
234 +
235 + if (0 == dwMaxThresh)
236 + {
237 + // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work.
238 + pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16;
239 + }
240 + else
241 + {
242 + pcd->ccab.cbFolderThresh = dwMaxThresh;
243 + }
244 +
245 + // Translate the compression type
246 + if (COMPRESSION_TYPE_NONE == ct)
247 + {
248 + pcd->tc = tcompTYPE_NONE;
249 + }
250 + else if (COMPRESSION_TYPE_LOW == ct)
251 + {
252 + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO;
253 + }
254 + else if (COMPRESSION_TYPE_MEDIUM == ct)
255 + {
256 + pcd->tc = TCOMPfromLZXWindow(18);
257 + }
258 + else if (COMPRESSION_TYPE_HIGH == ct)
259 + {
260 + pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI;
261 + }
262 + else if (COMPRESSION_TYPE_MSZIP == ct)
263 + {
264 + pcd->tc = tcompTYPE_MSZIP;
265 + }
266 + else
267 + {
268 + hr = E_INVALIDARG;
269 + ExitOnFailure(hr, "Invalid compression type specified.");
270 + }
271 +
272 + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL))
273 + {
274 + ExitWithLastError(hr, "failed to convert cab name to multi-byte");
275 + }
276 +
277 + if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL))
278 + {
279 + ExitWithLastError(hr, "failed to convert cab dir to multi-byte");
280 + }
281 +
282 + // Remember the path to the cabinet.
283 + hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer);
284 + ExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer);
285 +
286 + hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab);
287 + ExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab);
288 +
289 + // Get the empty file to use as the blank marker for duplicates.
290 + if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
291 + {
292 + ExitWithLastError(hr, "Failed to get temp path.");
293 + }
294 +
295 + if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile))
296 + {
297 + ExitWithLastError(hr, "Failed to create a temp file name.");
298 + }
299 +
300 + // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us)
301 + // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst
302 + // case is we'll leave a zero byte file behind in the temp folder.
303 + pcd->hEmptyFile = ::CreateFileW(pcd->wzEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
304 +
305 + hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast<void **>(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_NONE);
306 + ExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files");
307 +
308 + // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files
309 + if (1 > dwMaxFiles)
310 + {
311 + dwMaxFiles = 1;
312 + }
313 +
314 + pcd->cMaxFilePaths = dwMaxFiles;
315 + size_t cbFileAllocSize = 0;
316 +
317 + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize));
318 + ExitOnFailure(hr, "Maximum allocation exceeded on initialization.");
319 +
320 + pcd->prgFiles = static_cast<CABC_FILE*>(MemAlloc(cbFileAllocSize, TRUE));
321 + ExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files.");
322 +
323 + // Tell cabinet API about our configuration.
324 + pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd);
325 + if (NULL == pcd->hfci || pcd->erf.fError)
326 + {
327 + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
328 + if (FAILED(pcd->hrLastError))
329 + {
330 + hr = pcd->hrLastError;
331 + }
332 + else
333 + {
334 + ExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
335 + }
336 +
337 + pcd->fGoodCab = FALSE;
338 +
339 + ExitOnFailure(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS?
340 + }
341 +
342 + *phContext = pcd;
343 +
344 +LExit:
345 + if (FAILED(hr) && pcd && pcd->hfci)
346 + {
347 + ::FCIDestroy(pcd->hfci);
348 + }
349 +
350 + return hr;
351 +}
352 +
353 +
354 +/********************************************************************
355 +CabCNextCab - This will be useful when creating multiple cabs.
356 +Haven't needed it yet.
357 +********************************************************************/
358 +extern "C" HRESULT DAPI CabCNextCab(
359 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
360 + )
361 +{
362 + UNREFERENCED_PARAMETER(hContext);
363 + // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls
364 + return E_NOTIMPL;
365 +}
366 +
367 +
368 +/********************************************************************
369 +CabcAddFile - adds a file to a cabinet
370 +
371 +NOTE: hContext must be the same used in Begin and Finish
372 +if wzToken is null, the file's original name is used within the cab
373 +********************************************************************/
374 +extern "C" HRESULT DAPI CabCAddFile(
375 + __in_z LPCWSTR wzFile,
376 + __in_z_opt LPCWSTR wzToken,
377 + __in_opt PMSIFILEHASHINFO pmfHash,
378 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
379 + )
380 +{
381 + Assert(wzFile && *wzFile && hContext);
382 +
383 + HRESULT hr = S_OK;
384 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(hContext);
385 + CABC_FILE *pcfDuplicate = NULL;
386 + LPWSTR sczUpperCaseFile = NULL;
387 + LONGLONG llFileSize = 0;
388 + PMSIFILEHASHINFO pmfLocalHash = pmfHash;
389 +
390 + hr = StrAllocString(&sczUpperCaseFile, wzFile, 0);
391 + ExitOnFailure(hr, "Failed to allocate new string for file %ls", wzFile);
392 +
393 + // Modifies the string in-place
394 + StrStringToUpper(sczUpperCaseFile);
395 +
396 + // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired
397 + // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled
398 + if(!pcd->fCabinetSplittingEnabled)
399 + {
400 + // Store file size, primarily used to determine which files to hash for duplicates
401 + hr = FileSize(wzFile, &llFileSize);
402 + ExitOnFailure(hr, "Failed to check size of file %ls", wzFile);
403 +
404 + hr = CheckForDuplicateFile(pcd, &pcfDuplicate, sczUpperCaseFile, &pmfLocalHash, llFileSize);
405 + ExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile);
406 + }
407 +
408 + if (pcfDuplicate) // This will be null for smart cabbing case
409 + {
410 + DWORD index;
411 + hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index);
412 + ExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath);
413 +
414 + hr = AddDuplicateFile(pcd, index, sczUpperCaseFile, wzToken, pcd->dwLastFileIndex);
415 + ExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath);
416 + }
417 + else
418 + {
419 + hr = AddNonDuplicateFile(pcd, sczUpperCaseFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex);
420 + ExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile);
421 + }
422 +
423 + ++pcd->dwLastFileIndex;
424 +
425 +LExit:
426 + ReleaseStr(sczUpperCaseFile);
427 +
428 + // If we allocated a hash struct ourselves, free it
429 + if (pmfHash != pmfLocalHash)
430 + {
431 + ReleaseMem(pmfLocalHash);
432 + }
433 +
434 + return hr;
435 +}
436 +
437 +
438 +/********************************************************************
439 +CabcFinish - finishes making a cabinet
440 +
441 +NOTE: hContext must be the same used in Begin and AddFile
442 +*********************************************************************/
443 +extern "C" HRESULT DAPI CabCFinish(
444 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext,
445 + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback
446 + )
447 +{
448 + Assert(hContext);
449 +
450 + HRESULT hr = S_OK;
451 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(hContext);
452 + CABC_INTERNAL_ADDFILEINFO fileInfo = { };
453 + DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex
454 + DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array
455 + DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array
456 + LPSTR pszFileToken = NULL;
457 + LONGLONG llFileSize = 0;
458 +
459 + pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback;
460 +
461 + // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile()
462 + // doing so at appropriate times results in install-time performance benefits in the case of duplicate files.
463 + // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump
464 + // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder
465 + // (this is not the same as a regular folder, and is a concept unique to CABs).
466 +
467 + // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files
468 + // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that
469 + // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB
470 + // to close the current folder, and create a new folder for the next file to be added.
471 +
472 + // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence.
473 + // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to")
474 + // And we call FciFlushFolder() after adding the duplicate versions of files (as this will be jumped back "to" to get back in the regular sequence)
475 + BOOL fFlushBefore = FALSE;
476 + BOOL fFlushAfter = FALSE;
477 +
478 + ReleaseDict(pcd->shDictHandle);
479 +
480 + // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added
481 + for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex)
482 + {
483 + if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file
484 + {
485 + // Just a normal, non-duplicated file. We'll add it to the list for later checking of
486 + // duplicates.
487 + fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath;
488 + fileInfo.wzEmptyPath = NULL;
489 +
490 + // Use the provided token, otherwise default to the source file name.
491 + if (pcd->prgFiles[dwArrayFileIndex].pwzToken)
492 + {
493 + LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken;
494 + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
495 + ExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp);
496 + }
497 + else
498 + {
499 + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath);
500 + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
501 + ExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp);
502 + }
503 +
504 + if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates)
505 + {
506 + fFlushBefore = TRUE;
507 + }
508 +
509 + llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize;
510 +
511 + ++dwArrayFileIndex; // Increment into the non-duplicate array
512 + }
513 + else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file
514 + {
515 + // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space
516 + // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero
517 + // byte files to point at their duplicated file index.
518 + //
519 + // Notice that duplicate files are not added to the list of file paths because all duplicate
520 + // files point at the same path (the empty file) so there is no point in tracking them with
521 + // their path.
522 + fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath;
523 + fileInfo.wzEmptyPath = pcd->wzEmptyFile;
524 +
525 + // Use the provided token, otherwise default to the source file name.
526 + if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken)
527 + {
528 + LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken;
529 + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
530 + ExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp);
531 + }
532 + else
533 + {
534 + LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath);
535 + hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
536 + ExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp);
537 + }
538 +
539 + // Flush afterward only if this isn't a duplicate of the previous file, and at least one non-duplicate file remains to be added to the cab
540 + if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) &&
541 + !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) &&
542 + dwArrayFileIndex < pcd->cFilePaths)
543 + {
544 + fFlushAfter = TRUE;
545 + }
546 +
547 + // We're just adding a 0-byte file, so set it appropriately
548 + llFileSize = 0;
549 +
550 + ++dwDupeArrayFileIndex; // Increment into the duplicate array
551 + }
552 + else // If it's neither duplicate nor non-duplicate, throw an error
553 + {
554 + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT);
555 + ExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered");
556 + }
557 +
558 + if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold)
559 + {
560 + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus))
561 + {
562 + ExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
563 + }
564 + pcd->llBytesSinceLastFlush = 0;
565 + }
566 +
567 + pcd->llBytesSinceLastFlush += llFileSize;
568 +
569 + // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct
570 + // through the pointer to an ANSI string. This is neccessary so we can smuggle through the
571 + // path to the empty file (should this be a duplicate file).
572 +#pragma prefast(push)
573 +#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here
574 + if (!::FCIAddFile(pcd->hfci, reinterpret_cast<LPSTR>(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc))
575 +#pragma prefast(pop)
576 + {
577 + pcd->fGoodCab = FALSE;
578 +
579 + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
580 + if (FAILED(pcd->hrLastError))
581 + {
582 + hr = pcd->hrLastError;
583 + }
584 + else
585 + {
586 + ExitWithLastError(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath);
587 + }
588 +
589 + ExitOnFailure(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS?
590 + }
591 +
592 + // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet
593 + // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead
594 + if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError))
595 + {
596 + hr = pcd->hrLastError;
597 + ExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet.");
598 + }
599 +
600 + if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold)
601 + {
602 + if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus))
603 + {
604 + ExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
605 + }
606 + pcd->llBytesSinceLastFlush = 0;
607 + }
608 +
609 + fFlushAfter = FALSE;
610 + fFlushBefore = FALSE;
611 + }
612 +
613 + if (!pcd->fGoodCab)
614 + {
615 + // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
616 + if (FAILED(pcd->hrLastError))
617 + {
618 + hr = pcd->hrLastError;
619 + }
620 + else
621 + {
622 + ExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %s", pcd->erf.erfOper, pcd->erf.erfType);
623 + }
624 +
625 + ExitOnFailure(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %s", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS?
626 + }
627 +
628 + // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs)
629 + if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus))
630 + {
631 + // If we have a last error, use that, otherwise return the useless error
632 + hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL;
633 + ExitOnFailure(hr, "failed to flush FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS?
634 + }
635 +
636 + if (pcd->fGoodCab && pcd->cDuplicates)
637 + {
638 + hr = UpdateDuplicateFiles(pcd);
639 + ExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath);
640 + }
641 +
642 +LExit:
643 + ::FCIDestroy(pcd->hfci);
644 + FreeCabCData(pcd);
645 + ReleaseNullStr(pszFileToken);
646 +
647 + return hr;
648 +}
649 +
650 +
651 +/********************************************************************
652 +CabCCancel - cancels making a cabinet
653 +
654 +NOTE: hContext must be the same used in Begin and AddFile
655 +*********************************************************************/
656 +extern "C" void DAPI CabCCancel(
657 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
658 + )
659 +{
660 + Assert(hContext);
661 +
662 + CABC_DATA* pcd = reinterpret_cast<CABC_DATA*>(hContext);
663 + ::FCIDestroy(pcd->hfci);
664 + FreeCabCData(pcd);
665 +}
666 +
667 +
668 +//
669 +// private
670 +//
671 +
672 +static void FreeCabCData(
673 + __in CABC_DATA* pcd
674 + )
675 +{
676 + if (pcd)
677 + {
678 + ReleaseFileHandle(pcd->hEmptyFile);
679 +
680 + for (DWORD i = 0; i < pcd->cFilePaths; ++i)
681 + {
682 + ReleaseStr(pcd->prgFiles[i].pwzSourcePath);
683 + ReleaseMem(pcd->prgFiles[i].pmfHash);
684 + }
685 + ReleaseMem(pcd->prgFiles);
686 + ReleaseMem(pcd->prgDuplicates);
687 +
688 + ReleaseMem(pcd);
689 + }
690 +}
691 +
692 +/********************************************************************
693 + SmartCab functions
694 +
695 +********************************************************************/
696 +
697 +static HRESULT CheckForDuplicateFile(
698 + __in CABC_DATA *pcd,
699 + __out CABC_FILE **ppcf,
700 + __in LPCWSTR wzFileName,
701 + __in PMSIFILEHASHINFO *ppmfHash,
702 + __in LONGLONG llFileSize
703 + )
704 +{
705 + DWORD i;
706 + HRESULT hr = S_OK;
707 + UINT er = ERROR_SUCCESS;
708 +
709 + ExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file");
710 + ExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file");
711 +
712 + *ppcf = NULL; // By default, we'll set our output to NULL
713 +
714 + hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast<void **>(ppcf));
715 + // If we found it in the hash of previously added source paths, return our match immediately
716 + if (SUCCEEDED(hr))
717 + {
718 + ExitFunction1(hr = S_OK);
719 + }
720 + else if (E_NOTFOUND == hr)
721 + {
722 + hr = S_OK;
723 + }
724 + ExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files");
725 +
726 + for (i = 0; i < pcd->cFilePaths; ++i)
727 + {
728 + // If two files have the same size, use hashing to check if they're a match
729 + if (llFileSize == pcd->prgFiles[i].llFileSize)
730 + {
731 + // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it
732 + if (pcd->prgFiles[i].pmfHash == NULL)
733 + {
734 + pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
735 + ExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash");
736 +
737 + pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
738 + er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash);
739 + ExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath);
740 + }
741 +
742 + // If our own file hasn't yet been hashed, hash it
743 + if (NULL == *ppmfHash)
744 + {
745 + *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
746 + ExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash");
747 +
748 + (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
749 + er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash);
750 + ExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath);
751 + }
752 +
753 + // If the two file hashes are both of the expected size, and they match, we've got a match, so return it!
754 + if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize &&
755 + sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize &&
756 + pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] &&
757 + pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] &&
758 + pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] &&
759 + pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3])
760 + {
761 + *ppcf = pcd->prgFiles + i;
762 + ExitFunction1(hr = S_OK);
763 + }
764 + }
765 + }
766 +
767 +LExit:
768 +
769 + return hr;
770 +}
771 +
772 +
773 +static HRESULT AddDuplicateFile(
774 + __in CABC_DATA *pcd,
775 + __in DWORD dwFileArrayIndex,
776 + __in_z LPCWSTR wzSourcePath,
777 + __in_opt LPCWSTR wzToken,
778 + __in DWORD dwDuplicateCabFileIndex
779 + )
780 +{
781 + HRESULT hr = S_OK;
782 + LPVOID pv = NULL;
783 +
784 + // Ensure there is enough memory to store this duplicate file index.
785 + if (pcd->cDuplicates == pcd->cMaxDuplicates)
786 + {
787 + pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?)
788 + size_t cbDuplicates = 0;
789 +
790 + hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates);
791 + ExitOnFailure(hr, "Maximum allocation exceeded.");
792 +
793 + if (pcd->cDuplicates)
794 + {
795 + pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE);
796 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file.");
797 + }
798 + else
799 + {
800 + pv = MemAlloc(cbDuplicates, FALSE);
801 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file.");
802 + }
803 +
804 + ZeroMemory(reinterpret_cast<BYTE*>(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE));
805 +
806 + pcd->prgDuplicates = static_cast<CABC_DUPLICATEFILE*>(pv);
807 + pv = NULL;
808 + }
809 +
810 + // Store the duplicate file index.
811 + pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex;
812 + pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex;
813 + pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates
814 +
815 + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0);
816 + ExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath);
817 +
818 + if (wzToken && *wzToken)
819 + {
820 + hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0);
821 + ExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken);
822 + }
823 +
824 + ++pcd->cDuplicates;
825 +
826 +LExit:
827 + ReleaseMem(pv);
828 + return hr;
829 +}
830 +
831 +
832 +static HRESULT AddNonDuplicateFile(
833 + __in CABC_DATA *pcd,
834 + __in LPCWSTR wzFile,
835 + __in_opt LPCWSTR wzToken,
836 + __in_opt const MSIFILEHASHINFO* pmfHash,
837 + __in LONGLONG llFileSize,
838 + __in DWORD dwCabFileIndex
839 + )
840 +{
841 + HRESULT hr = S_OK;
842 + LPVOID pv = NULL;
843 +
844 + // Ensure there is enough memory to store this file index.
845 + if (pcd->cFilePaths == pcd->cMaxFilePaths)
846 + {
847 + pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?)
848 + size_t cbFilePaths = 0;
849 +
850 + hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths);
851 + ExitOnFailure(hr, "Maximum allocation exceeded.");
852 +
853 + pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE);
854 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file.");
855 +
856 + ZeroMemory(reinterpret_cast<BYTE*>(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE));
857 +
858 + pcd->prgFiles = static_cast<CABC_FILE*>(pv);
859 + pv = NULL;
860 + }
861 +
862 + // Store the file index information.
863 + // TODO: add this to a sorted list so we can do a binary search later.
864 + CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths;
865 + pcf->dwCabFileIndex = dwCabFileIndex;
866 + pcf->llFileSize = llFileSize;
867 +
868 + if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize)
869 + {
870 + pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
871 + ExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash");
872 +
873 + pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
874 + pcf->pmfHash->dwData[0] = pmfHash->dwData[0];
875 + pcf->pmfHash->dwData[1] = pmfHash->dwData[1];
876 + pcf->pmfHash->dwData[2] = pmfHash->dwData[2];
877 + pcf->pmfHash->dwData[3] = pmfHash->dwData[3];
878 + }
879 +
880 + hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0);
881 + ExitOnFailure(hr, "Failed to copy file path: %ls", wzFile);
882 +
883 + if (wzToken && *wzToken)
884 + {
885 + hr = StrAllocString(&pcf->pwzToken, wzToken, 0);
886 + ExitOnFailure(hr, "Failed to copy file token: %ls", wzToken);
887 + }
888 +
889 + ++pcd->cFilePaths;
890 +
891 + hr = DictAddValue(pcd->shDictHandle, pcf);
892 + ExitOnFailure(hr, "Failed to add file to dictionary of added files");
893 +
894 +LExit:
895 + ReleaseMem(pv);
896 + return hr;
897 +}
898 +
899 +
900 +static HRESULT UpdateDuplicateFiles(
901 + __in const CABC_DATA *pcd
902 + )
903 +{
904 + HRESULT hr = S_OK;
905 + DWORD cbCabinet = 0;
906 + LARGE_INTEGER liCabinetSize = { };
907 + HANDLE hCabinet = INVALID_HANDLE_VALUE;
908 + HANDLE hCabinetMapping = NULL;
909 + LPVOID pv = NULL;
910 + MS_CABINET_HEADER *pCabinetHeader = NULL;
911 +
912 + hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
913 + if (INVALID_HANDLE_VALUE == hCabinet)
914 + {
915 + ExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath);
916 + }
917 +
918 + // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as
919 + // the upper bound for the memory map.
920 + if (!::GetFileSizeEx(hCabinet, &liCabinetSize))
921 + {
922 + ExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath);
923 + }
924 +
925 + if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE)
926 + {
927 + cbCabinet = liCabinetSize.LowPart;
928 + }
929 + else
930 + {
931 + cbCabinet = MAX_CABINET_HEADER_SIZE;
932 + }
933 +
934 + // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE
935 + hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL);
936 + if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping)
937 + {
938 + ExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath);
939 + }
940 +
941 + pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0);
942 + ExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath);
943 +
944 + pCabinetHeader = static_cast<MS_CABINET_HEADER*>(pv);
945 +
946 + for (DWORD i = 0; i < pcd->cDuplicates; ++i)
947 + {
948 + const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i;
949 +
950 + hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile);
951 + ExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex);
952 + }
953 +
954 +LExit:
955 + if (pv)
956 + {
957 + ::UnmapViewOfFile(pv);
958 + }
959 + if (hCabinetMapping)
960 + {
961 + ::CloseHandle(hCabinetMapping);
962 + }
963 + ReleaseFileHandle(hCabinet);
964 +
965 + return hr;
966 +}
967 +
968 +
969 +static HRESULT DuplicateFile(
970 + __in MS_CABINET_HEADER *pHeader,
971 + __in const CABC_DATA *pcd,
972 + __in const CABC_DUPLICATEFILE *pDuplicate
973 + )
974 +{
975 + HRESULT hr = S_OK;
976 + BYTE *pbHeader = reinterpret_cast<BYTE*>(pHeader);
977 + BYTE* pbItem = pbHeader + pHeader->coffFiles;
978 + const MS_CABINET_ITEM *pOriginalItem = NULL;
979 + MS_CABINET_ITEM *pDuplicateItem = NULL;
980 +
981 + if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex ||
982 + pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex ||
983 + pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex)
984 + {
985 + hr = E_UNEXPECTED;
986 + ExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex);
987 + }
988 +
989 + // Step through each cabinet items until we get to the original
990 + // file's index. Notice that the name of the cabinet item is
991 + // appended to the end of the MS_CABINET_INFO, that's why we can't
992 + // index straight to the data we want.
993 + for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i)
994 + {
995 + LPCSTR szItemName = reinterpret_cast<LPCSTR>(pbItem + sizeof(MS_CABINET_ITEM));
996 + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1;
997 + }
998 +
999 + pOriginalItem = reinterpret_cast<const MS_CABINET_ITEM*>(pbItem);
1000 +
1001 + // Now pick up where we left off after the original file's index
1002 + // was found and loop until we find the duplicate file's index.
1003 + for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i)
1004 + {
1005 + LPCSTR szItemName = reinterpret_cast<LPCSTR>(pbItem + sizeof(MS_CABINET_ITEM));
1006 + pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1;
1007 + }
1008 +
1009 + pDuplicateItem = reinterpret_cast<MS_CABINET_ITEM*>(pbItem);
1010 +
1011 + if (0 != pDuplicateItem->cbFile)
1012 + {
1013 + hr = E_UNEXPECTED;
1014 + ExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile);
1015 + }
1016 +
1017 + pDuplicateItem->cbFile = pOriginalItem->cbFile;
1018 + pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart;
1019 + pDuplicateItem->iFolder = pOriginalItem->iFolder;
1020 + // Note: we do *not* duplicate the date/time and attributes metadata from
1021 + // the original item to the duplicate. The following lines are commented
1022 + // so people are not tempted to put them back.
1023 + //pDuplicateItem->date = pOriginalItem->date;
1024 + //pDuplicateItem->time = pOriginalItem->time;
1025 + //pDuplicateItem->attribs = pOriginalItem->attribs;
1026 +
1027 +LExit:
1028 + return hr;
1029 +}
1030 +
1031 +
1032 +static HRESULT UtcFileTimeToLocalDosDateTime(
1033 + __in const FILETIME* pFileTime,
1034 + __out USHORT* pDate,
1035 + __out USHORT* pTime
1036 + )
1037 +{
1038 + HRESULT hr = S_OK;
1039 + FILETIME ftLocal = { };
1040 +
1041 + if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal))
1042 + {
1043 + ExitWithLastError(hr, "Filed to convert file time to local file time.");
1044 + }
1045 +
1046 + if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime))
1047 + {
1048 + ExitWithLastError(hr, "Filed to convert file time to DOS date time.");
1049 + }
1050 +
1051 +LExit:
1052 + return hr;
1053 +}
1054 +
1055 +
1056 +/********************************************************************
1057 + FCI callback functions
1058 +
1059 +*********************************************************************/
1060 +static __callback int DIAMONDAPI CabCFilePlaced(
1061 + __in PCCAB pccab,
1062 + __in_z PSTR szFile,
1063 + __in long cbFile,
1064 + __in BOOL fContinuation,
1065 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1066 + )
1067 +{
1068 + UNREFERENCED_PARAMETER(pccab);
1069 + UNREFERENCED_PARAMETER(szFile);
1070 + UNREFERENCED_PARAMETER(cbFile);
1071 + UNREFERENCED_PARAMETER(fContinuation);
1072 + UNREFERENCED_PARAMETER(pv);
1073 + return 0;
1074 +}
1075 +
1076 +
1077 +static __callback void * DIAMONDAPI CabCAlloc(
1078 + __in ULONG cb
1079 + )
1080 +{
1081 + return MemAlloc(cb, FALSE);
1082 +}
1083 +
1084 +
1085 +static __callback void DIAMONDAPI CabCFree(
1086 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1087 + )
1088 +{
1089 + MemFree(pv);
1090 +}
1091 +
1092 +static __callback INT_PTR DIAMONDAPI CabCOpen(
1093 + __in_z PSTR pszFile,
1094 + __in int oflag,
1095 + __in int pmode,
1096 + __out int *err,
1097 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1098 + )
1099 +{
1100 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1101 + HRESULT hr = S_OK;
1102 + INT_PTR pFile = -1;
1103 + DWORD dwAccess = 0;
1104 + DWORD dwDisposition = 0;
1105 + DWORD dwAttributes = 0;
1106 +
1107 + //
1108 + // Translate flags for CreateFile
1109 + //
1110 + if (oflag & _O_CREAT)
1111 + {
1112 + if (pmode == _S_IREAD)
1113 + dwAccess |= GENERIC_READ;
1114 + else if (pmode == _S_IWRITE)
1115 + dwAccess |= GENERIC_WRITE;
1116 + else if (pmode == (_S_IWRITE | _S_IREAD))
1117 + dwAccess |= GENERIC_READ | GENERIC_WRITE;
1118 +
1119 + if (oflag & _O_SHORT_LIVED)
1120 + dwDisposition = FILE_ATTRIBUTE_TEMPORARY;
1121 + else if (oflag & _O_TEMPORARY)
1122 + dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
1123 + else if (oflag & _O_EXCL)
1124 + dwDisposition = CREATE_NEW;
1125 + }
1126 + if (oflag & _O_TRUNC)
1127 + dwDisposition = CREATE_ALWAYS;
1128 +
1129 + if (!dwAccess)
1130 + dwAccess = GENERIC_READ;
1131 + if (!dwDisposition)
1132 + dwDisposition = OPEN_EXISTING;
1133 + if (!dwAttributes)
1134 + dwAttributes = FILE_ATTRIBUTE_NORMAL;
1135 +
1136 + // Check to see if we were passed the magic character that says 'Unicode string follows'.
1137 + if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile)
1138 + {
1139 + pFile = reinterpret_cast<INT_PTR>(::CreateFileW(reinterpret_cast<LPCWSTR>(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL));
1140 + }
1141 + else
1142 + {
1143 +#pragma prefast(push)
1144 +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1145 + pFile = reinterpret_cast<INT_PTR>(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL));
1146 +#pragma prefast(pop)
1147 + }
1148 +
1149 + if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile))
1150 + {
1151 + ExitOnLastError(hr, "failed to open file: %s", pszFile);
1152 + }
1153 +
1154 +LExit:
1155 + if (FAILED(hr))
1156 + pcd->hrLastError = *err = hr;
1157 +
1158 + return FAILED(hr) ? -1 : pFile;
1159 +}
1160 +
1161 +
1162 +static __callback UINT FAR DIAMONDAPI CabCRead(
1163 + __in INT_PTR hf,
1164 + __out_bcount(cb) void FAR *memory,
1165 + __in UINT cb,
1166 + __out int *err,
1167 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1168 + )
1169 +{
1170 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1171 + HRESULT hr = S_OK;
1172 + DWORD cbRead = 0;
1173 +
1174 + ExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided");
1175 + if (!::ReadFile(reinterpret_cast<HANDLE>(hf), memory, cb, &cbRead, NULL))
1176 + {
1177 + *err = ::GetLastError();
1178 + ExitOnLastError(hr, "failed to read during cabinet extraction");
1179 + }
1180 +
1181 +LExit:
1182 + if (FAILED(hr))
1183 + {
1184 + pcd->hrLastError = *err = hr;
1185 + }
1186 +
1187 + return FAILED(hr) ? -1 : cbRead;
1188 +}
1189 +
1190 +
1191 +static __callback UINT FAR DIAMONDAPI CabCWrite(
1192 + __in INT_PTR hf,
1193 + __in_bcount(cb) void FAR *memory,
1194 + __in UINT cb,
1195 + __out int *err,
1196 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1197 + )
1198 +{
1199 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1200 + HRESULT hr = S_OK;
1201 + DWORD cbWrite = 0;
1202 +
1203 + ExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided");
1204 + if (!::WriteFile(reinterpret_cast<HANDLE>(hf), memory, cb, &cbWrite, NULL))
1205 + {
1206 + *err = ::GetLastError();
1207 + ExitOnLastError(hr, "failed to write during cabinet extraction");
1208 + }
1209 +
1210 +LExit:
1211 + if (FAILED(hr))
1212 + pcd->hrLastError = *err = hr;
1213 +
1214 + return FAILED(hr) ? -1 : cbWrite;
1215 +}
1216 +
1217 +
1218 +static __callback long FAR DIAMONDAPI CabCSeek(
1219 + __in INT_PTR hf,
1220 + __in long dist,
1221 + __in int seektype,
1222 + __out int *err,
1223 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1224 + )
1225 +{
1226 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1227 + HRESULT hr = S_OK;
1228 + DWORD dwMoveMethod;
1229 + LONG lMove = 0;
1230 +
1231 + switch (seektype)
1232 + {
1233 + case 0: // SEEK_SET
1234 + dwMoveMethod = FILE_BEGIN;
1235 + break;
1236 + case 1: /// SEEK_CUR
1237 + dwMoveMethod = FILE_CURRENT;
1238 + break;
1239 + case 2: // SEEK_END
1240 + dwMoveMethod = FILE_END;
1241 + break;
1242 + default :
1243 + dwMoveMethod = 0;
1244 + hr = E_UNEXPECTED;
1245 + ExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype);
1246 + }
1247 +
1248 + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
1249 + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
1250 + // Todo: update these comments for FCI (are they accurate for FCI as well?)
1251 + lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod);
1252 + if (DWORD_MAX == lMove)
1253 + {
1254 + *err = ::GetLastError();
1255 + ExitOnLastError(hr, "failed to move file pointer %d bytes", dist);
1256 + }
1257 +
1258 +LExit:
1259 + if (FAILED(hr))
1260 + {
1261 + pcd->hrLastError = *err = hr;
1262 + }
1263 +
1264 + return FAILED(hr) ? -1 : lMove;
1265 +}
1266 +
1267 +
1268 +static __callback int FAR DIAMONDAPI CabCClose(
1269 + __in INT_PTR hf,
1270 + __out int *err,
1271 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1272 + )
1273 +{
1274 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1275 + HRESULT hr = S_OK;
1276 +
1277 + if (!::CloseHandle(reinterpret_cast<HANDLE>(hf)))
1278 + {
1279 + *err = ::GetLastError();
1280 + ExitOnLastError(hr, "failed to close file during cabinet extraction");
1281 + }
1282 +
1283 +LExit:
1284 + if (FAILED(hr))
1285 + {
1286 + pcd->hrLastError = *err = hr;
1287 + }
1288 +
1289 + return FAILED(hr) ? -1 : 0;
1290 +}
1291 +
1292 +static __callback int DIAMONDAPI CabCDelete(
1293 + __in_z PSTR szFile,
1294 + __out int *err,
1295 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1296 + )
1297 +{
1298 + UNREFERENCED_PARAMETER(err);
1299 + UNREFERENCED_PARAMETER(pv);
1300 +
1301 +#pragma prefast(push)
1302 +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1303 + ::DeleteFileA(szFile);
1304 +#pragma prefast(pop)
1305 +
1306 + return 0;
1307 +}
1308 +
1309 +
1310 +__success(return != FALSE)
1311 +static __callback BOOL DIAMONDAPI CabCGetTempFile(
1312 + __out_bcount_z(cbFile) char *szFile,
1313 + __in int cbFile,
1314 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1315 + )
1316 +{
1317 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1318 + static volatile DWORD dwIndex = 0;
1319 +
1320 + HRESULT hr = S_OK;
1321 + char szTempPath[MAX_PATH] = { };
1322 + DWORD cchTempPath = MAX_PATH;
1323 + DWORD dwProcessId = ::GetCurrentProcessId();
1324 + HANDLE hTempFile = INVALID_HANDLE_VALUE;
1325 +
1326 + if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath))
1327 + {
1328 + ExitWithLastError(hr, "Failed to get temp path during cabinet creation.");
1329 + }
1330 +
1331 + for (DWORD i = 0; i < DWORD_MAX; ++i)
1332 + {
1333 + LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(&dwIndex));
1334 +
1335 + hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId);
1336 + ExitOnFailure(hr, "failed to format log file path.");
1337 +
1338 + hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
1339 + if (INVALID_HANDLE_VALUE != hTempFile)
1340 + {
1341 + // we found one that doesn't exist
1342 + hr = S_OK;
1343 + break;
1344 + }
1345 + else
1346 + {
1347 + hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one.
1348 + }
1349 + }
1350 + ExitOnFailure(hr, "failed to find temporary file.");
1351 +
1352 +LExit:
1353 + ReleaseFileHandle(hTempFile);
1354 +
1355 + if (FAILED(hr))
1356 + {
1357 + pcd->hrLastError = hr;
1358 + }
1359 +
1360 + return FAILED(hr)? FALSE : TRUE;
1361 +}
1362 +
1363 +
1364 +__success(return != FALSE)
1365 +static __callback BOOL DIAMONDAPI CabCGetNextCabinet(
1366 + __in PCCAB pccab,
1367 + __in ULONG ul,
1368 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1369 + )
1370 +{
1371 + UNREFERENCED_PARAMETER(ul);
1372 +
1373 + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........
1374 + CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1375 + HRESULT hr = S_OK;
1376 + LPWSTR pwzFileToken = NULL;
1377 + WCHAR wzNewCabName[MAX_PATH] = L"";
1378 +
1379 + if (pccab->iCab == 1)
1380 + {
1381 + pcd->wzFirstCabinetName[0] = '\0';
1382 + LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath);
1383 + size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName));
1384 + if (len > 4)
1385 + {
1386 + len -= 4; // remove Extention ".cab" of 8.3 Format
1387 + }
1388 + hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len);
1389 + ExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name");
1390 + }
1391 +
1392 + const int nAlphabets = 26; // Number of Alphabets from a to z
1393 + if (pccab->iCab <= nAlphabets)
1394 + {
1395 + // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........
1396 + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab));
1397 + ExitOnFailure(hr, "Failed to create next Cabinet File Name");
1398 + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab));
1399 + ExitOnFailure(hr, "Failed to create next Cabinet File Name");
1400 + }
1401 + else if (pccab->iCab <= nAlphabets*nAlphabets)
1402 + {
1403 + // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ......
1404 + int char2 = (pccab->iCab) % nAlphabets;
1405 + int char1 = (pccab->iCab - char2)/nAlphabets;
1406 + if (char2 == 0)
1407 + {
1408 + // e.g. when iCab = 52, we want az
1409 + char2 = nAlphabets; // Second char must be 'z' in this case
1410 + char1--; // First Char must be decremented by 1
1411 + }
1412 + hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2));
1413 + ExitOnFailure(hr, "Failed to create next Cabinet File Name");
1414 + hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2));
1415 + ExitOnFailure(hr, "Failed to create next Cabinet File Name");
1416 + }
1417 + else
1418 + {
1419 + hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index.
1420 + ExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name");
1421 + }
1422 +
1423 + // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method
1424 + if(pcd->fileSplitCabNamesCallback != 0)
1425 + {
1426 + // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split
1427 + // This code will need updation if we need to send all file tokens for the splitting Cabinets
1428 + if (pcd->prgFiles[0].pwzToken)
1429 + {
1430 + pwzFileToken = pcd->prgFiles[0].pwzToken;
1431 + }
1432 + else
1433 + {
1434 + LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath;
1435 + pwzFileToken = FileFromPath(wzSourcePath);
1436 + }
1437 +
1438 + // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table
1439 + pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken);
1440 + }
1441 +
1442 +LExit:
1443 + if (FAILED(hr))
1444 + {
1445 + // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!!
1446 + // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting
1447 + pcd->hrLastError = hr;
1448 + return FALSE;
1449 + }
1450 + else
1451 + {
1452 + return TRUE;
1453 + }
1454 +}
1455 +
1456 +
1457 +static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(
1458 + __in_z PSTR pszName,
1459 + __out USHORT *pdate,
1460 + __out USHORT *ptime,
1461 + __out USHORT *pattribs,
1462 + __out int *err,
1463 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1464 + )
1465 +{
1466 + HRESULT hr = S_OK;
1467 + CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast<CABC_INTERNAL_ADDFILEINFO*>(pszName);
1468 + LPCWSTR wzFile = NULL;
1469 + DWORD cbFile = 0;
1470 + LPSTR pszFilePlusMagic = NULL;
1471 + DWORD cbFilePlusMagic = 0;
1472 + WIN32_FILE_ATTRIBUTE_DATA fad = { };
1473 + INT_PTR iResult = -1;
1474 +
1475 + // If there is an empty file provided, use that as the source path to cab (since we
1476 + // must be dealing with a duplicate file). Otherwise, use the source path you'd expect.
1477 + wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath;
1478 + cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR);
1479 +
1480 + // Convert the source file path into an Ansi string that our APIs will recognize as
1481 + // a Unicode string (due to the magic character).
1482 + cbFilePlusMagic = cbFile + 1; // add one for the magic.
1483 + pszFilePlusMagic = reinterpret_cast<LPSTR>(MemAlloc(cbFilePlusMagic, TRUE));
1484 +
1485 + *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER;
1486 + memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile);
1487 +
1488 + if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad))
1489 + {
1490 + ExitWithLastError(hr, "Failed to get file attributes on '%s'.", pFileInfo->wzSourcePath);
1491 + }
1492 +
1493 + // Set the attributes but only allow the few attributes that CAB supports.
1494 + *pattribs = static_cast<USHORT>(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
1495 +
1496 + hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime);
1497 + if (FAILED(hr))
1498 + {
1499 + // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were
1500 + // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't
1501 + // found. This would create further problems if the file was written to the CAB without this value. Windows
1502 + // Installer would then fail to extract the file.
1503 + hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime);
1504 + ExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName);
1505 + }
1506 +
1507 + iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv);
1508 +
1509 +LExit:
1510 + ReleaseMem(pszFilePlusMagic);
1511 + if (FAILED(hr))
1512 + {
1513 + *err = (int)hr;
1514 + }
1515 +
1516 + return FAILED(hr) ? -1 : iResult;
1517 +}
1518 +
1519 +
1520 +static __callback long DIAMONDAPI CabCStatus(
1521 + __in UINT ui,
1522 + __in ULONG cb1,
1523 + __in ULONG cb2,
1524 + __out_bcount(CABC_HANDLE_BYTES) void *pv
1525 + )
1526 +{
1527 + UNREFERENCED_PARAMETER(ui);
1528 + UNREFERENCED_PARAMETER(cb1);
1529 + UNREFERENCED_PARAMETER(cb2);
1530 + UNREFERENCED_PARAMETER(pv);
1531 + return 0;
1532 +}
src/dutil/cabutil.cpp new
+567
@@ -0,0 +1,567 @@
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 +
5 +// external prototypes
6 +typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*);
7 +typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF);
8 +typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO);
9 +typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *);
10 +
11 +
12 +//
13 +// static globals
14 +//
15 +static HMODULE vhCabinetDll = NULL;
16 +
17 +static HFDI vhfdi = NULL;
18 +static PFNFDICREATE vpfnFDICreate = NULL;
19 +static PFNFDICOPY vpfnFDICopy = NULL;
20 +static PFNFDIISCABINET vpfnFDIIsCabinet = NULL;
21 +static PFNFDIDESTROY vpfnFDIDestroy = NULL;
22 +static ERF verf;
23 +
24 +static DWORD64 vdw64EmbeddedOffset = 0;
25 +
26 +//
27 +// structs
28 +//
29 +struct CAB_CALLBACK_STRUCT
30 +{
31 + BOOL fStopExtracting; // flag set when no more files are needed
32 + LPCWSTR pwzExtract; // file to extract ("*" means extract all)
33 + LPCWSTR pwzExtractDir; // directory to extract files to
34 +
35 + // possible user data
36 + CAB_CALLBACK_PROGRESS pfnProgress;
37 + LPVOID pvContext;
38 +};
39 +
40 +//
41 +// prototypes
42 +//
43 +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize);
44 +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData);
45 +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode);
46 +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb);
47 +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb);
48 +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf);
49 +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype);
50 +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify);
51 +static HRESULT DAPI CabOperation(__in LPCWSTR wzCabinet, __in LPCWSTR wzExtractFile, __in_opt LPCWSTR wzExtractDir, __in_opt CAB_CALLBACK_PROGRESS pfnProgress, __in_opt LPVOID pvContext, __in_opt STDCALL_PFNFDINOTIFY pfnNotify, __in DWORD64 dw64EmbeddedOffset);
52 +
53 +static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL;
54 +
55 +
56 +inline HRESULT LoadCabinetDll()
57 +{
58 + HRESULT hr = S_OK;
59 + if (!vhCabinetDll)
60 + {
61 + hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll);
62 + ExitOnFailure(hr, "failed to load cabinet.dll");
63 +
64 + // retrieve all address functions
65 + vpfnFDICreate = reinterpret_cast<PFNFDICREATE>(::GetProcAddress(vhCabinetDll, "FDICreate"));
66 + ExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL");
67 + vpfnFDICopy = reinterpret_cast<PFNFDICOPY>(::GetProcAddress(vhCabinetDll, "FDICopy"));
68 + ExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL");
69 + vpfnFDIIsCabinet = reinterpret_cast<PFNFDIISCABINET>(::GetProcAddress(vhCabinetDll, "FDIIsCabinet"));
70 + ExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL");
71 + vpfnFDIDestroy = reinterpret_cast<PFNFDIDESTROY>(::GetProcAddress(vhCabinetDll, "FDIDestroy"));
72 + ExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL");
73 +
74 + vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf);
75 + ExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll");
76 + }
77 +
78 +LExit:
79 + if (FAILED(hr) && vhCabinetDll)
80 + {
81 + ::FreeLibrary(vhCabinetDll);
82 + vhCabinetDll = NULL;
83 + }
84 +
85 + return hr;
86 +}
87 +
88 +
89 +/********************************************************************
90 + CabInitialize - initializes internal static variables
91 +
92 +********************************************************************/
93 +extern "C" HRESULT DAPI CabInitialize(
94 + __in BOOL fDelayLoad
95 + )
96 +{
97 + HRESULT hr = S_OK;
98 +
99 + if (!fDelayLoad)
100 + {
101 + hr = LoadCabinetDll();
102 + ExitOnFailure(hr, "failed to load CABINET.DLL");
103 + }
104 +
105 +LExit:
106 + return hr;
107 +}
108 +
109 +
110 +/********************************************************************
111 + CabUninitialize - initializes internal static variables
112 +
113 +********************************************************************/
114 +extern "C" void DAPI CabUninitialize(
115 + )
116 +{
117 + if (vhfdi)
118 + {
119 + if (vpfnFDIDestroy)
120 + {
121 + vpfnFDIDestroy(vhfdi);
122 + }
123 + vhfdi = NULL;
124 + }
125 +
126 + vpfnFDICreate = NULL;
127 + vpfnFDICopy =NULL;
128 + vpfnFDIIsCabinet = NULL;
129 + vpfnFDIDestroy = NULL;
130 +
131 + if (vhCabinetDll)
132 + {
133 + ::FreeLibrary(vhCabinetDll);
134 + vhCabinetDll = NULL;
135 + }
136 +}
137 +
138 +/********************************************************************
139 + CabEnumerate - list files inside cabinet
140 +
141 + NOTE: wzCabinet must be full path to cabinet file
142 + pfnNotify is callback function to get notified for each file
143 + in the cabinet
144 +********************************************************************/
145 +extern "C" HRESULT DAPI CabEnumerate(
146 + __in LPCWSTR wzCabinet,
147 + __in LPCWSTR wzEnumerateFile,
148 + __in STDCALL_PFNFDINOTIFY pfnNotify,
149 + __in DWORD64 dw64EmbeddedOffset
150 + )
151 +{
152 + return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset);
153 +}
154 +
155 +/********************************************************************
156 + CabExtract - extracts one or all files from a cabinet
157 +
158 + NOTE: wzCabinet must be full path to cabinet file
159 + wzExtractFile can be a single file id or "*" to extract all files
160 + wzExttractDir must be normalized (end in a "\")
161 + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa
162 +********************************************************************/
163 +extern "C" HRESULT DAPI CabExtract(
164 + __in LPCWSTR wzCabinet,
165 + __in LPCWSTR wzExtractFile,
166 + __in LPCWSTR wzExtractDir,
167 + __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
168 + __in_opt LPVOID pvContext,
169 + __in DWORD64 dw64EmbeddedOffset
170 + )
171 +{
172 + return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset);
173 +}
174 +
175 +//
176 +// private
177 +//
178 +/********************************************************************
179 + FDINotify -- wrapper that converts call convention from __cdecl to __stdcall.
180 +
181 + NOTE: Since netfx 1.1 supports only function pointers (delegates)
182 + with __stdcall calling convention and cabinet api uses
183 + __cdecl calling convention, we need this wrapper function.
184 + netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate.
185 + TODO: remove this when upgrading to netfx 2.0.
186 +********************************************************************/
187 +static __callback INT_PTR DIAMONDAPI FDINotify(
188 + __in FDINOTIFICATIONTYPE iNotification,
189 + __inout FDINOTIFICATION *pFDINotify
190 + )
191 +{
192 + if (NULL != v_pfnNetFx11Notify)
193 + {
194 + return v_pfnNetFx11Notify(iNotification, pFDINotify);
195 + }
196 + else
197 + {
198 + return (INT_PTR)0;
199 + }
200 +}
201 +
202 +
203 +/********************************************************************
204 + CabOperation - helper function that enumerates or extracts files
205 + from cabinet
206 +
207 + NOTE: wzCabinet must be full path to cabinet file
208 + wzExtractFile can be a single file id or "*" to extract all files
209 + wzExttractDir must be normalized (end in a "\")
210 + if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa
211 + pfnNotify is callback function to get notified for each file
212 + in the cabinet. If it's NULL, files will be extracted.
213 +********************************************************************/
214 +static HRESULT DAPI CabOperation(
215 + __in LPCWSTR wzCabinet,
216 + __in LPCWSTR wzExtractFile,
217 + __in_opt LPCWSTR wzExtractDir,
218 + __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
219 + __in_opt LPVOID pvContext,
220 + __in_opt STDCALL_PFNFDINOTIFY pfnNotify,
221 + __in DWORD64 dw64EmbeddedOffset
222 + )
223 +{
224 + HRESULT hr = S_OK;
225 + BOOL fResult;
226 +
227 + LPWSTR sczCabinet = NULL;
228 + LPWSTR pwz = NULL;
229 + CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings
230 + CHAR szCabFile[MAX_PATH * 4];
231 +
232 + CAB_CALLBACK_STRUCT ccs;
233 + PFNFDINOTIFY pfnFdiNotify;
234 +
235 + //
236 + // ensure the cabinet.dll is loaded
237 + //
238 + if (!vhfdi)
239 + {
240 + hr = LoadCabinetDll();
241 + ExitOnFailure(hr, "failed to load CABINET.DLL");
242 + }
243 +
244 + hr = StrAllocString(&sczCabinet, wzCabinet, 0);
245 + ExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet);
246 +
247 + //
248 + // split the cabinet full path into directory and filename and convert to multi-byte (ick!)
249 + //
250 + pwz = FileFromPath(sczCabinet);
251 + ExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet);
252 +
253 + if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL))
254 + {
255 + ExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz);
256 + }
257 +
258 + *pwz = '\0';
259 +
260 + // If a full path was not provided, use the relative current directory.
261 + if (wzCabinet == pwz)
262 + {
263 + hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\");
264 + ExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory.");
265 + }
266 + else
267 + {
268 + if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL))
269 + {
270 + ExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
271 + }
272 + }
273 +
274 + //
275 + // iterate through files in cabinet extracting them to the callback function
276 + //
277 + ccs.fStopExtracting = FALSE;
278 + ccs.pwzExtract = wzExtractFile;
279 + ccs.pwzExtractDir = wzExtractDir;
280 + ccs.pfnProgress = pfnProgress;
281 + ccs.pvContext = pvContext;
282 +
283 + vdw64EmbeddedOffset = dw64EmbeddedOffset;
284 +
285 + // if pfnNotify is given, use it, otherwise use default callback
286 + if (NULL == pfnNotify)
287 + {
288 + pfnFdiNotify = CabExtractCallback;
289 + }
290 + else
291 + {
292 + v_pfnNetFx11Notify = pfnNotify;
293 + pfnFdiNotify = FDINotify;
294 + }
295 + fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast<void*>(&ccs));
296 + if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
297 + {
298 + ExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet);
299 + }
300 +
301 +LExit:
302 + ReleaseStr(sczCabinet);
303 + v_pfnNetFx11Notify = NULL;
304 +
305 + return hr;
306 +}
307 +
308 +/****************************************************************************
309 + default extract routines
310 +
311 +****************************************************************************/
312 +static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize)
313 +{
314 + return MemAlloc(dwSize, FALSE);
315 +}
316 +
317 +
318 +static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData)
319 +{
320 + MemFree(pvData);
321 +}
322 +
323 +
324 +static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode)
325 +{
326 + HRESULT hr = S_OK;
327 + INT_PTR pFile = -1;
328 + LPWSTR sczCabFile = NULL;
329 +
330 + // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail
331 + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE)))
332 + {
333 + hr = E_OUTOFMEMORY;
334 + ExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported");
335 + }
336 +
337 + hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8);
338 + ExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string");
339 +
340 + pFile = reinterpret_cast<INT_PTR>(::CreateFileW(sczCabFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
341 + if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile))
342 + {
343 + ExitWithLastError(hr, "failed to open file: %ls", sczCabFile);
344 + }
345 +
346 + if (vdw64EmbeddedOffset)
347 + {
348 + hr = CabExtractSeek(pFile, 0, 0);
349 + ExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset);
350 + }
351 +
352 +LExit:
353 + ReleaseStr(sczCabFile);
354 +
355 + return FAILED(hr) ? -1 : pFile;
356 +}
357 +
358 +
359 +static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb)
360 +{
361 + HRESULT hr = S_OK;
362 + DWORD cbRead = 0;
363 +
364 + ExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read");
365 + if (!::ReadFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbRead, NULL))
366 + {
367 + ExitWithLastError(hr, "failed to read during cabinet extraction");
368 + }
369 +
370 +LExit:
371 + return FAILED(hr) ? -1 : cbRead;
372 +}
373 +
374 +
375 +static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb)
376 +{
377 + HRESULT hr = S_OK;
378 + DWORD cbWrite = 0;
379 +
380 + ExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write");
381 + if (!::WriteFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbWrite, NULL))
382 + {
383 + ExitWithLastError(hr, "failed to write during cabinet extraction");
384 + }
385 +
386 +LExit:
387 + return FAILED(hr) ? -1 : cbWrite;
388 +}
389 +
390 +
391 +static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype)
392 +{
393 + HRESULT hr = S_OK;
394 + DWORD dwMoveMethod;
395 + LONG lMove = 0;
396 +
397 + switch (seektype)
398 + {
399 + case 0: // SEEK_SET
400 + dwMoveMethod = FILE_BEGIN;
401 + dist += static_cast<long>(vdw64EmbeddedOffset);
402 + break;
403 + case 1: /// SEEK_CUR
404 + dwMoveMethod = FILE_CURRENT;
405 + break;
406 + case 2: // SEEK_END
407 + dwMoveMethod = FILE_END;
408 + break;
409 + default :
410 + dwMoveMethod = 0;
411 + hr = E_UNEXPECTED;
412 + ExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype);
413 + }
414 +
415 + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
416 + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
417 + lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod);
418 + if (0xFFFFFFFF == lMove)
419 + {
420 + ExitWithLastError(hr, "failed to move file pointer %d bytes", dist);
421 + }
422 +
423 +LExit:
424 + return FAILED(hr) ? -1 : lMove - static_cast<long>(vdw64EmbeddedOffset);
425 +}
426 +
427 +
428 +static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf)
429 +{
430 + HRESULT hr = S_OK;
431 +
432 + if (!::CloseHandle(reinterpret_cast<HANDLE>(hf)))
433 + {
434 + ExitWithLastError(hr, "failed to close file during cabinet extraction");
435 + }
436 +
437 +LExit:
438 + return FAILED(hr) ? -1 : 0;
439 +}
440 +
441 +
442 +static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify)
443 +{
444 + Assert(pFDINotify->pv);
445 +
446 + HRESULT hr = S_OK;
447 + INT_PTR ipResult = 0; // result to return on success
448 +
449 + CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv);
450 + LPCSTR sz;
451 + WCHAR wz[MAX_PATH];
452 + FILETIME ft;
453 +
454 + switch (iNotification)
455 + {
456 + case fdintCOPY_FILE: // begin extracting a resource from cabinet
457 + ExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert");
458 + ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
459 +
460 + if (pccs->fStopExtracting)
461 + {
462 + ExitFunction1(hr = S_FALSE); // no more extracting
463 + }
464 +
465 + // convert params to useful variables
466 + sz = static_cast<LPCSTR>(pFDINotify->psz1);
467 + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
468 + {
469 + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
470 + }
471 +
472 + if (pccs->pfnProgress)
473 + {
474 + hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext);
475 + if (S_OK != hr)
476 + {
477 + ExitFunction();
478 + }
479 + }
480 +
481 + if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz))
482 + {
483 + // get the created date for the resource in the cabinet
484 + FILETIME ftLocal;
485 + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
486 + {
487 + ExitWithLastError(hr, "failed to get time for resource: %ls", wz);
488 + }
489 + ::LocalFileTimeToFileTime(&ftLocal, &ft);
490 +
491 +
492 + WCHAR wzPath[MAX_PATH];
493 + hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir);
494 + ExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz);
495 + hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
496 + ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
497 +
498 + ipResult = reinterpret_cast<INT_PTR>(::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
499 + if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(ipResult))
500 + {
501 + ExitWithLastError(hr, "failed to create file: %s", wzPath);
502 + }
503 +
504 + ::SetFileTime(reinterpret_cast<HANDLE>(ipResult), &ft, &ft, &ft); // try to set the file time (who cares if it fails)
505 +
506 + if (::SetFilePointer(reinterpret_cast<HANDLE>(ipResult), pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails)
507 + {
508 + if (::SetEndOfFile(reinterpret_cast<HANDLE>(ipResult)))
509 + {
510 + ::SetFilePointer(reinterpret_cast<HANDLE>(ipResult), 0, NULL, FILE_BEGIN); // reset the file pointer
511 + }
512 + }
513 + }
514 + else // resource wasn't requested, skip it
515 + {
516 + hr = S_OK;
517 + ipResult = 0;
518 + }
519 +
520 + break;
521 + case fdintCLOSE_FILE_INFO: // resource extraction complete
522 + Assert(pFDINotify->hf && pFDINotify->psz1);
523 + ExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
524 +
525 + // convert params to useful variables
526 + sz = static_cast<LPCSTR>(pFDINotify->psz1);
527 + ExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided");
528 +
529 + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
530 + {
531 + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
532 + }
533 +
534 + if (NULL != pFDINotify->hf) // just close the file
535 + {
536 + ::CloseHandle(reinterpret_cast<HANDLE>(pFDINotify->hf));
537 + }
538 +
539 + if (pccs->pfnProgress)
540 + {
541 + hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext);
542 + }
543 +
544 + if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going
545 + {
546 + ipResult = TRUE;
547 + }
548 + else // something went wrong or we only needed to extract one file
549 + {
550 + hr = S_OK;
551 + ipResult = FALSE;
552 + pccs->fStopExtracting = TRUE;
553 + }
554 +
555 + break;
556 + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through
557 + case fdintNEXT_CABINET: __fallthrough;
558 + case fdintENUMERATE: __fallthrough;
559 + case fdintCABINET_INFO:
560 + break;
561 + default:
562 + AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
563 + };
564 +
565 +LExit:
566 + return (S_OK == hr) ? ipResult : -1;
567 +}
src/dutil/certutil.cpp new
+327
@@ -0,0 +1,327 @@
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 +
5 +/********************************************************************
6 +CertReadProperty - reads a property from the certificate.
7 +
8 +NOTE: call MemFree() on the returned pvValue.
9 +********************************************************************/
10 +extern "C" HRESULT DAPI CertReadProperty(
11 + __in PCCERT_CONTEXT pCertContext,
12 + __in DWORD dwProperty,
13 + __deref_out_bound LPVOID* ppvValue,
14 + __out_opt DWORD* pcbValue
15 + )
16 +{
17 + HRESULT hr = S_OK;
18 + LPVOID pv = NULL;
19 + DWORD cb = 0;
20 +
21 + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb))
22 + {
23 + ExitWithLastError(hr, "Failed to get size of certificate property.");
24 + }
25 +
26 + pv = MemAlloc(cb, TRUE);
27 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property.");
28 +
29 + if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb))
30 + {
31 + ExitWithLastError(hr, "Failed to get certificate property.");
32 + }
33 +
34 + *ppvValue = pv;
35 + pv = NULL;
36 +
37 + if (pcbValue)
38 + {
39 + *pcbValue = cb;
40 + }
41 +
42 +LExit:
43 + ReleaseMem(pv);
44 + return hr;
45 +}
46 +
47 +
48 +extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp(
49 + __in CMSG_SIGNER_INFO* pSignerInfo,
50 + __out FILETIME* pftSigningTimestamp
51 + )
52 +{
53 + HRESULT hr = S_OK;
54 + CRYPT_INTEGER_BLOB* pBlob = NULL;
55 + PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
56 + DWORD cbSigningTimestamp = sizeof(FILETIME);
57 +
58 + // Find the countersigner blob. The countersigner in Authenticode contains the time
59 + // that signing took place. It's a "countersigner" because the signing time was sent
60 + // off to the certificate authority in the sky to return the verified time signed.
61 + for (DWORD i = 0; i < pSignerInfo->UnauthAttrs.cAttr; ++i)
62 + {
63 + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_counterSign, -1, pSignerInfo->UnauthAttrs.rgAttr[i].pszObjId, -1))
64 + {
65 + pBlob = pSignerInfo->UnauthAttrs.rgAttr[i].rgValue;
66 + break;
67 + }
68 + }
69 +
70 + if (!pBlob)
71 + {
72 + hr = TRUST_E_FAIL;
73 + ExitOnFailure(hr, "Failed to find countersigner in signer information.");
74 + }
75 +
76 + hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast<LPVOID*>(&pCounterSignerInfo), NULL);
77 + ExitOnFailure(hr, "Failed to decode countersigner information.");
78 +
79 + pBlob = NULL; // reset the blob before searching for the signing time.
80 +
81 + // Find the signing time blob in the countersigner.
82 + for (DWORD i = 0; i < pCounterSignerInfo->AuthAttrs.cAttr; ++i)
83 + {
84 + if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_signingTime, -1, pCounterSignerInfo->AuthAttrs.rgAttr[i].pszObjId, -1))
85 + {
86 + pBlob = pCounterSignerInfo->AuthAttrs.rgAttr[i].rgValue;
87 + break;
88 + }
89 + }
90 +
91 + if (!pBlob)
92 + {
93 + hr = TRUST_E_FAIL;
94 + ExitOnFailure(hr, "Failed to find signing time in countersigner information.");
95 + }
96 +
97 + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp))
98 + {
99 + ExitWithLastError(hr, "Failed to decode countersigner signing timestamp.");
100 + }
101 +
102 +LExit:
103 + ReleaseMem(pCounterSignerInfo);
104 +
105 + return hr;
106 +}
107 +
108 +
109 +extern "C" HRESULT DAPI GetCryptProvFromCert(
110 + __in_opt HWND hwnd,
111 + __in PCCERT_CONTEXT pCert,
112 + __out HCRYPTPROV *phCryptProv,
113 + __out DWORD *pdwKeySpec,
114 + __in BOOL *pfDidCryptAcquire,
115 + __deref_opt_out LPWSTR *ppwszTmpContainer,
116 + __deref_opt_out LPWSTR *ppwszProviderName,
117 + __out DWORD *pdwProviderType
118 + )
119 +{
120 + HRESULT hr = S_OK;
121 + HMODULE hMsSign32 = NULL;
122 +
123 + typedef BOOL (WINAPI *GETCRYPTPROVFROMCERTPTR)(HWND, PCCERT_CONTEXT, HCRYPTPROV*, DWORD*,BOOL*,LPWSTR*,LPWSTR*,DWORD*);
124 + GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL;
125 +
126 + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32);
127 + ExitOnFailure(hr, "Failed to get handle to MsSign32.dll");
128 +
129 + pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert");
130 + ExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll");
131 +
132 + if (!pGetCryptProvFromCert(hwnd,
133 + pCert,
134 + phCryptProv,
135 + pdwKeySpec,
136 + pfDidCryptAcquire,
137 + ppwszTmpContainer,
138 + ppwszProviderName,
139 + pdwProviderType))
140 + {
141 + ExitWithLastError(hr, "Failed to get CSP from cert.");
142 + }
143 +LExit:
144 + return hr;
145 +}
146 +
147 +extern "C" HRESULT DAPI FreeCryptProvFromCert(
148 + __in BOOL fAcquired,
149 + __in HCRYPTPROV hProv,
150 + __in_opt LPWSTR pwszCapiProvider,
151 + __in DWORD dwProviderType,
152 + __in_opt LPWSTR pwszTmpContainer
153 + )
154 +{
155 + HRESULT hr = S_OK;
156 + HMODULE hMsSign32 = NULL;
157 +
158 + typedef void (WINAPI *FREECRYPTPROVFROMCERT)(BOOL, HCRYPTPROV, LPWSTR, DWORD, LPWSTR);
159 + FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL;
160 +
161 + hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32);
162 + ExitOnFailure(hr, "Failed to get handle to MsSign32.dll");
163 +
164 + pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert");
165 + ExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll");
166 +
167 + pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer);
168 +LExit:
169 + return hr;
170 +}
171 +
172 +extern "C" HRESULT DAPI GetProvSecurityDesc(
173 + __in HCRYPTPROV hProv,
174 + __deref_out SECURITY_DESCRIPTOR** ppSecurity)
175 +{
176 + HRESULT hr = S_OK;
177 + ULONG ulSize = 0;
178 + SECURITY_DESCRIPTOR* pSecurity = NULL;
179 +
180 + // Get the size of the security descriptor.
181 + if (!::CryptGetProvParam(
182 + hProv,
183 + PP_KEYSET_SEC_DESCR,
184 + NULL,
185 + &ulSize,
186 + DACL_SECURITY_INFORMATION))
187 + {
188 + ExitWithLastError(hr, "Error getting security descriptor size for CSP.");
189 + }
190 +
191 + // Allocate the memory for the security descriptor.
192 + pSecurity = static_cast<SECURITY_DESCRIPTOR *>(MemAlloc(ulSize, TRUE));
193 + ExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL");
194 +
195 + // Get the security descriptor.
196 + if (!::CryptGetProvParam(
197 + hProv,
198 + PP_KEYSET_SEC_DESCR,
199 + (BYTE*)pSecurity,
200 + &ulSize,
201 + DACL_SECURITY_INFORMATION))
202 + {
203 + MemFree(pSecurity);
204 + ExitWithLastError(hr, "Error getting security descriptor for CSP.");
205 + }
206 + *ppSecurity = pSecurity;
207 +
208 +LExit:
209 + return hr;
210 +}
211 +
212 +
213 +extern "C" HRESULT DAPI SetProvSecurityDesc(
214 + __in HCRYPTPROV hProv,
215 + __in SECURITY_DESCRIPTOR* pSecurity)
216 +{
217 + HRESULT hr = S_OK;
218 +
219 + // Set the new security descriptor.
220 + if (!::CryptSetProvParam(
221 + hProv,
222 + PP_KEYSET_SEC_DESCR,
223 + (BYTE*)pSecurity,
224 + DACL_SECURITY_INFORMATION))
225 + {
226 + ExitWithLastError(hr, "Error setting security descriptor for CSP.");
227 + }
228 +LExit:
229 + return hr;
230 +}
231 +
232 +extern "C" BOOL DAPI CertHasPrivateKey(
233 + __in PCCERT_CONTEXT pCertContext,
234 + __out_opt DWORD* pdwKeySpec)
235 +{
236 + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey = NULL;
237 + DWORD dwKeySpec = 0;
238 + // set CRYPT_ACQUIRE_CACHE_FLAG so that we don't have to release the private key handle
239 + BOOL fResult = ::CryptAcquireCertificatePrivateKey(
240 + pCertContext,
241 + CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG,
242 + 0, //pvReserved
243 + &hPrivateKey,
244 + &dwKeySpec,
245 + NULL
246 + );
247 + if (pdwKeySpec)
248 + {
249 + *pdwKeySpec = dwKeySpec;
250 + }
251 + return fResult;
252 +}
253 +
254 +
255 +extern "C" HRESULT DAPI CertInstallSingleCertificate(
256 + __in HCERTSTORE hStore,
257 + __in PCCERT_CONTEXT pCertContext,
258 + __in LPCWSTR wzName
259 + )
260 +{
261 + HRESULT hr = S_OK;
262 + CERT_BLOB blob = { };
263 +
264 + DWORD dwKeySpec = 0;
265 +
266 + HCRYPTPROV hCsp = NULL;
267 + LPWSTR pwszTmpContainer = NULL;
268 + LPWSTR pwszProviderName = NULL;
269 + DWORD dwProviderType = 0;
270 + BOOL fAcquired = TRUE;
271 +
272 + SECURITY_DESCRIPTOR* pSecurity = NULL;
273 + SECURITY_DESCRIPTOR* pSecurityNew = NULL;
274 +
275 + // Update the friendly name of the certificate to be configured.
276 + blob.pbData = (BYTE*)wzName;
277 + blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null
278 +
279 + if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob))
280 + {
281 + ExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName);
282 + }
283 +
284 + if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL))
285 + {
286 + ExitWithLastError(hr, "Failed to add certificate to the store.");
287 + }
288 +
289 + // if the certificate has a private key, grant Administrators access
290 + if (CertHasPrivateKey(pCertContext, &dwKeySpec))
291 + {
292 + if (AT_KEYEXCHANGE == dwKeySpec || AT_SIGNATURE == dwKeySpec)
293 + {
294 + // We added a CSP key
295 + hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType);
296 + ExitOnFailure(hr, "Failed to get handle to CSP");
297 +
298 + hr = GetProvSecurityDesc(hCsp, &pSecurity);
299 + ExitOnFailure(hr, "Failed to get security descriptor of CSP");
300 +
301 + hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew);
302 + ExitOnFailure(hr, "Failed to create new security descriptor");
303 +
304 + hr = SetProvSecurityDesc(hCsp, pSecurityNew);
305 + ExitOnFailure(hr, "Failed to set Admin ACL on CSP");
306 + }
307 +
308 + if (CERT_NCRYPT_KEY_SPEC == dwKeySpec)
309 + {
310 + // We added a CNG key
311 + // TODO change ACL on CNG key
312 + }
313 + }
314 +LExit:
315 + if (hCsp)
316 + {
317 + FreeCryptProvFromCert(fAcquired, hCsp, NULL, dwProviderType, NULL);
318 + }
319 +
320 + ReleaseMem(pSecurity);
321 +
322 + if (pSecurityNew)
323 + {
324 + AclFreeSecurityDescriptor(pSecurityNew);
325 + }
326 + return hr;
327 +}
src/dutil/condutil.cpp new
+20
@@ -0,0 +1,20 @@
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 +
5 +// function definitions
6 +
7 +/********************************************************************
8 +CondEvaluate - evaluates the condition using the given variables.
9 +********************************************************************/
10 +extern "C" HRESULT DAPI CondEvaluate(
11 + __in VARIABLES_HANDLE pVariables,
12 + __in_z LPCWSTR wzCondition,
13 + __out BOOL* pf
14 + )
15 +{
16 + UNREFERENCED_PARAMETER(pVariables);
17 + UNREFERENCED_PARAMETER(wzCondition);
18 + UNREFERENCED_PARAMETER(pf);
19 + return E_NOTIMPL;
20 +}
src/dutil/conutil.cpp new
+656
@@ -0,0 +1,656 @@
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 +
5 +
6 +static HANDLE vhStdIn = INVALID_HANDLE_VALUE;
7 +static HANDLE vhStdOut = INVALID_HANDLE_VALUE;
8 +static BOOL vfConsoleIn = FALSE;
9 +static BOOL vfConsoleOut = FALSE;
10 +static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo;
11 +
12 +
13 +extern "C" HRESULT DAPI ConsoleInitialize()
14 +{
15 + Assert(INVALID_HANDLE_VALUE == vhStdOut);
16 + HRESULT hr = S_OK;
17 + UINT er;
18 +
19 + vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
20 + if (INVALID_HANDLE_VALUE == vhStdIn)
21 + {
22 + ExitOnLastError(hr, "failed to open stdin");
23 + }
24 +
25 + vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
26 + if (INVALID_HANDLE_VALUE == vhStdOut)
27 + {
28 + ExitOnLastError(hr, "failed to open stdout");
29 + }
30 +
31 + // check if we have a std in on the console
32 + if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo))
33 + {
34 + vfConsoleIn = TRUE;
35 + }
36 + else
37 + {
38 + er = ::GetLastError();
39 + if (ERROR_INVALID_HANDLE == er)
40 + {
41 + vfConsoleIn= FALSE;
42 + hr = S_OK;
43 + }
44 + else
45 + {
46 + ExitOnWin32Error(er, hr, "failed to get input console screen buffer info");
47 + }
48 + }
49 +
50 + if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo))
51 + {
52 + vfConsoleOut = TRUE;
53 + }
54 + else // no console
55 + {
56 + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo));
57 + er = ::GetLastError();
58 + if (ERROR_INVALID_HANDLE == er)
59 + {
60 + vfConsoleOut = FALSE;
61 + hr = S_OK;
62 + }
63 + else
64 + {
65 + ExitOnWin32Error(er, hr, "failed to get output console screen buffer info");
66 + }
67 + }
68 +
69 +LExit:
70 + if (FAILED(hr))
71 + {
72 + if (INVALID_HANDLE_VALUE != vhStdOut)
73 + {
74 + ::CloseHandle(vhStdOut);
75 + }
76 +
77 + if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn)
78 + {
79 + ::CloseHandle(vhStdIn);
80 + }
81 +
82 + vhStdOut = INVALID_HANDLE_VALUE;
83 + vhStdIn = INVALID_HANDLE_VALUE;
84 + }
85 +
86 + return hr;
87 +}
88 +
89 +
90 +extern "C" void DAPI ConsoleUninitialize()
91 +{
92 + memset(&vcsbiInfo, 0, sizeof(vcsbiInfo));
93 +
94 + if (INVALID_HANDLE_VALUE != vhStdOut)
95 + {
96 + ::CloseHandle(vhStdOut);
97 + }
98 +
99 + if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn)
100 + {
101 + ::CloseHandle(vhStdIn);
102 + }
103 +
104 + vhStdOut = INVALID_HANDLE_VALUE;
105 + vhStdIn = INVALID_HANDLE_VALUE;
106 +}
107 +
108 +
109 +extern "C" void DAPI ConsoleGreen()
110 +{
111 + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
112 + if (vfConsoleOut)
113 + {
114 + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
115 + }
116 +}
117 +
118 +
119 +extern "C" void DAPI ConsoleRed()
120 +{
121 + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
122 + if (vfConsoleOut)
123 + {
124 + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
125 + }
126 +}
127 +
128 +
129 +extern "C" void DAPI ConsoleYellow()
130 +{
131 + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
132 + if (vfConsoleOut)
133 + {
134 + ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
135 + }
136 +}
137 +
138 +
139 +extern "C" void DAPI ConsoleNormal()
140 +{
141 + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
142 + if (vfConsoleOut)
143 + {
144 + ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes);
145 + }
146 +}
147 +
148 +
149 +/********************************************************************
150 + ConsoleWrite - full color printfA without libc
151 +
152 + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
153 + assumes already in normal color and resets the screen to normal color
154 +********************************************************************/
155 +extern "C" HRESULT DAPI ConsoleWrite(
156 + CONSOLE_COLOR cc,
157 + __in_z __format_string LPCSTR szFormat,
158 + ...
159 + )
160 +{
161 + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
162 + HRESULT hr = S_OK;
163 + LPSTR pszOutput = NULL;
164 + DWORD cchOutput = 0;
165 + DWORD cbWrote = 0;
166 + DWORD cbTotal = 0;
167 +
168 + // set the color
169 + switch (cc)
170 + {
171 + case CONSOLE_COLOR_NORMAL: break; // do nothing
172 + case CONSOLE_COLOR_RED: ConsoleRed(); break;
173 + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
174 + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
175 + }
176 +
177 + va_list args;
178 + va_start(args, szFormat);
179 + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args);
180 + va_end(args);
181 + ExitOnFailure(hr, "failed to format message: \"%s\"", szFormat);
182 +
183 + cchOutput = lstrlenA(pszOutput);
184 + while (cbTotal < (sizeof(*pszOutput) * cchOutput))
185 + {
186 + if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
187 + {
188 + ExitOnLastError(hr, "failed to write output to console: %s", pszOutput);
189 + }
190 +
191 + cbTotal += cbWrote;
192 + }
193 +
194 + // reset the color to normal
195 + if (CONSOLE_COLOR_NORMAL != cc)
196 + {
197 + ConsoleNormal();
198 + }
199 +
200 +LExit:
201 + ReleaseStr(pszOutput);
202 + return hr;
203 +}
204 +
205 +
206 +/********************************************************************
207 + ConsoleWriteLine - full color printfA plus newline without libc
208 +
209 + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
210 + assumes already in normal color and resets the screen to normal color
211 +********************************************************************/
212 +extern "C" HRESULT DAPI ConsoleWriteLine(
213 + CONSOLE_COLOR cc,
214 + __in_z __format_string LPCSTR szFormat,
215 + ...
216 + )
217 +{
218 + AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
219 + HRESULT hr = S_OK;
220 + LPSTR pszOutput = NULL;
221 + DWORD cchOutput = 0;
222 + DWORD cbWrote = 0;
223 + DWORD cbTotal = 0;
224 + LPCSTR szNewLine = "\r\n";
225 +
226 + // set the color
227 + switch (cc)
228 + {
229 + case CONSOLE_COLOR_NORMAL: break; // do nothing
230 + case CONSOLE_COLOR_RED: ConsoleRed(); break;
231 + case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
232 + case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
233 + }
234 +
235 + va_list args;
236 + va_start(args, szFormat);
237 + hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args);
238 + va_end(args);
239 + ExitOnFailure(hr, "failed to format message: \"%s\"", szFormat);
240 +
241 + //
242 + // write the string
243 + //
244 + cchOutput = lstrlenA(pszOutput);
245 + while (cbTotal < (sizeof(*pszOutput) * cchOutput))
246 + {
247 + if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
248 + ExitOnLastError(hr, "failed to write output to console: %s", pszOutput);
249 +
250 + cbTotal += cbWrote;
251 + }
252 +
253 + //
254 + // write the newline
255 + //
256 + if (!::WriteFile(vhStdOut, reinterpret_cast<const BYTE*>(szNewLine), 2, &cbWrote, NULL))
257 + {
258 + ExitOnLastError(hr, "failed to write newline to console");
259 + }
260 +
261 + // reset the color to normal
262 + if (CONSOLE_COLOR_NORMAL != cc)
263 + {
264 + ConsoleNormal();
265 + }
266 +
267 +LExit:
268 + ReleaseStr(pszOutput);
269 + return hr;
270 +}
271 +
272 +
273 +/********************************************************************
274 + ConsoleWriteError - display an error to the screen
275 +
276 + NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d")
277 +********************************************************************/
278 +HRESULT ConsoleWriteError(
279 + HRESULT hrError,
280 + CONSOLE_COLOR cc,
281 + __in_z __format_string LPCSTR szFormat,
282 + ...
283 + )
284 +{
285 + HRESULT hr = S_OK;
286 + LPSTR pszMessage = NULL;
287 +
288 + va_list args;
289 + va_start(args, szFormat);
290 + hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args);
291 + va_end(args);
292 + ExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat);
293 +
294 + if (FAILED(hrError))
295 + {
296 + hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage);
297 + }
298 + else
299 + {
300 + hr = ConsoleWriteLine(cc, "Error: %s", pszMessage);
301 + }
302 +
303 +LExit:
304 + ReleaseStr(pszMessage);
305 + return hr;
306 +}
307 +
308 +
309 +/********************************************************************
310 + ConsoleReadW - get console input without libc
311 +
312 + NOTE: only supports reading ANSI characters
313 +********************************************************************/
314 +extern "C" HRESULT DAPI ConsoleReadW(
315 + __deref_out_z LPWSTR* ppwzBuffer
316 + )
317 +{
318 + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
319 + Assert(ppwzBuffer);
320 +
321 + HRESULT hr = S_OK;
322 + LPSTR psz = NULL;
323 + DWORD cch = 0;
324 + DWORD cchRead = 0;
325 + DWORD cchTotalRead = 0;
326 +
327 + cch = 64;
328 + hr = StrAnsiAlloc(&psz, cch);
329 + ExitOnFailure(hr, "failed to allocate memory to read from console");
330 +
331 + // loop until we read the \r\n from the console
332 + for (;;)
333 + {
334 + // read one character at a time, since that seems to be the only way to make this work
335 + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
336 + ExitOnLastError(hr, "failed to read string from console");
337 +
338 + cchTotalRead += cchRead;
339 + if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1])
340 + {
341 + psz[cchTotalRead - 2] = '\0'; // chop off the \r\n
342 + break;
343 + }
344 + else if (0 == cchRead) // nothing more was read
345 + {
346 + psz[cchTotalRead] = '\0'; // null termintate and bail
347 + break;
348 + }
349 +
350 + if (cchTotalRead == cch)
351 + {
352 + cch *= 2; // double everytime we run out of space
353 + hr = StrAnsiAlloc(&psz, cch);
354 + ExitOnFailure(hr, "failed to allocate memory to read from console");
355 + }
356 + }
357 +
358 + hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP);
359 +
360 +LExit:
361 + ReleaseStr(psz);
362 + return hr;
363 +}
364 +
365 +
366 +/********************************************************************
367 + ConsoleReadNonBlockingW - Read from the console without blocking
368 + Won't work for redirected files (exe < txtfile), but will work for stdin redirected to
369 + an anonymous or named pipe
370 +
371 + if (fReadLine), stop reading immediately when \r\n is found
372 +*********************************************************************/
373 +extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
374 + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
375 + __out DWORD* pcchSize,
376 + BOOL fReadLine
377 + )
378 +{
379 + Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize);
380 + HRESULT hr = S_OK;
381 +
382 + LPSTR psz = NULL;
383 +
384 + ExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided");
385 +
386 + DWORD dwRead;
387 + DWORD dwNumInput;
388 +
389 + DWORD cchTotal = 0;
390 + DWORD cch = 8;
391 +
392 + DWORD cchRead = 0;
393 + DWORD cchTotalRead = 0;
394 +
395 + DWORD dwIndex = 0;
396 + DWORD er;
397 +
398 + INPUT_RECORD ir;
399 + WCHAR chIn;
400 +
401 + *ppwzBuffer = NULL;
402 + *pcchSize = 0;
403 +
404 + // If we really have a handle to stdin, and not the end of a pipe
405 + if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL))
406 + {
407 + er = ::GetLastError();
408 + if (ERROR_INVALID_HANDLE != er)
409 + {
410 + ExitFunction1(hr = HRESULT_FROM_WIN32(er));
411 + }
412 +
413 + if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead))
414 + {
415 + ExitOnLastError(hr, "failed to peek at console input");
416 + }
417 +
418 + if (0 == dwRead)
419 + {
420 + ExitFunction1(hr = S_FALSE);
421 + }
422 +
423 + for (/* dwRead from num of input events */; dwRead > 0; dwRead--)
424 + {
425 + if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput))
426 + {
427 + ExitOnLastError(hr, "Failed to read input from console");
428 + }
429 +
430 + // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested
431 + if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown)
432 + {
433 + chIn = ir.Event.KeyEvent.uChar.UnicodeChar;
434 +
435 + if (0 == cchTotal)
436 + {
437 + cchTotal = cch;
438 + cch *= 2;
439 + StrAlloc(ppwzBuffer, cch);
440 + }
441 +
442 + (*ppwzBuffer)[dwIndex] = chIn;
443 +
444 + if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex]))
445 + {
446 + *ppwzBuffer[dwIndex - 1] = L'\0';
447 + dwIndex -= 1;
448 + break;
449 + }
450 +
451 + ++dwIndex;
452 + cchTotal--;
453 + }
454 + }
455 +
456 + *pcchSize = dwIndex;
457 + }
458 + else
459 + {
460 + // otherwise, the peek worked, and we have the end of a pipe
461 + if (0 == dwRead)
462 + ExitFunction1(hr = S_FALSE);
463 +
464 + cch = 8;
465 + hr = StrAnsiAlloc(&psz, cch);
466 + ExitOnFailure(hr, "failed to allocate memory to read from console");
467 +
468 + for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--)
469 + {
470 + // read one character at a time, since that seems to be the only way to make this work
471 + if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
472 + {
473 + ExitOnLastError(hr, "failed to read string from console");
474 + }
475 +
476 + cchTotalRead += cchRead;
477 + if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead])
478 + {
479 + psz[cchTotalRead - 1] = '\0'; // chop off the \r\n
480 + cchTotalRead -= 1;
481 + break;
482 + }
483 + else if (0 == cchRead) // nothing more was read
484 + {
485 + psz[cchTotalRead] = '\0'; // null termintate and bail
486 + break;
487 + }
488 +
489 + if (cchTotalRead == cch)
490 + {
491 + cch *= 2; // double everytime we run out of space
492 + hr = StrAnsiAlloc(&psz, cch);
493 + ExitOnFailure(hr, "failed to allocate memory to read from console");
494 + }
495 + }
496 +
497 + *pcchSize = cchTotalRead;
498 + hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP);
499 + }
500 +
501 +LExit:
502 + ReleaseStr(psz);
503 +
504 + return hr;
505 +}
506 +
507 +
508 +/********************************************************************
509 + ConsoleReadStringA - get console input without libc
510 +
511 +*********************************************************************/
512 +extern "C" HRESULT DAPI ConsoleReadStringA(
513 + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer,
514 + CONST DWORD cchCharBuffer,
515 + __out DWORD* pcchNumCharReturn
516 + )
517 +{
518 + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
519 + HRESULT hr = S_OK;
520 + if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2))
521 + {
522 + DWORD iRead = 1;
523 + DWORD iReadCharTotal = 0;
524 + if (ppszCharBuffer && *ppszCharBuffer == NULL)
525 + {
526 + do
527 + {
528 + hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead);
529 + ExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW");
530 + // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
531 + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
532 + {
533 + ExitOnLastError(hr, "failed to read string from console");
534 + }
535 + iReadCharTotal += *pcchNumCharReturn;
536 + iRead += 1;
537 + }
538 + while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13);
539 + *pcchNumCharReturn = iReadCharTotal;
540 + }
541 + else
542 + {
543 + if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) ||
544 + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0)
545 + {
546 + ExitOnLastError(hr, "failed to read string from console");
547 + }
548 + if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
549 + (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13)
550 + {
551 + // need read more
552 + hr = ERROR_MORE_DATA;
553 + }
554 + }
555 + }
556 + else
557 + {
558 + hr = E_INVALIDARG;
559 + }
560 +
561 +LExit:
562 + return hr;
563 +}
564 +
565 +/********************************************************************
566 + ConsoleReadStringW - get console input without libc
567 +
568 +*********************************************************************/
569 +extern "C" HRESULT DAPI ConsoleReadStringW(
570 + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer,
571 + const DWORD cchCharBuffer,
572 + __out DWORD* pcchNumCharReturn
573 + )
574 +{
575 + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
576 + HRESULT hr = S_OK;
577 + if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2))
578 + {
579 + DWORD iRead = 1;
580 + DWORD iReadCharTotal = 0;
581 + if (*ppwzCharBuffer == NULL)
582 + {
583 + do
584 + {
585 + hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead);
586 + ExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW");
587 + // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
588 + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
589 + {
590 + ExitOnLastError(hr, "failed to read string from console");
591 + }
592 + iReadCharTotal += *pcchNumCharReturn;
593 + iRead += 1;
594 + }
595 + while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13);
596 + *pcchNumCharReturn = iReadCharTotal;
597 + }
598 + else
599 + {
600 + if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) ||
601 + *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0)
602 + {
603 + ExitOnLastError(hr, "failed to read string from console");
604 + }
605 + if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
606 + (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13)
607 + {
608 + // need read more
609 + hr = ERROR_MORE_DATA;
610 + }
611 + }
612 + }
613 + else
614 + {
615 + hr = E_INVALIDARG;
616 + }
617 +
618 +LExit:
619 + return hr;
620 +}
621 +
622 +/********************************************************************
623 + ConsoleSetReadHidden - set console input no echo
624 +
625 +*********************************************************************/
626 +extern "C" HRESULT DAPI ConsoleSetReadHidden(void)
627 +{
628 + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
629 + HRESULT hr = S_OK;
630 + ::FlushConsoleInputBuffer(vhStdIn);
631 + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT))
632 + {
633 + ExitOnLastError(hr, "failed to set console input mode to be hidden");
634 + }
635 +
636 +LExit:
637 + return hr;
638 +}
639 +
640 +/********************************************************************
641 + ConsoleSetReadNormal - reset to echo
642 +
643 +*********************************************************************/
644 +extern "C" HRESULT DAPI ConsoleSetReadNormal(void)
645 +{
646 + AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
647 + HRESULT hr = S_OK;
648 + if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT))
649 + {
650 + ExitOnLastError(hr, "failed to set console input mode to be normal");
651 + }
652 +
653 +LExit:
654 + return hr;
655 +}
656 +
src/dutil/cryputil.cpp new
+379
@@ -0,0 +1,379 @@
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 +
5 +static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL;
6 +static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL;
7 +static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL;
8 +static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL;
9 +
10 +static HMODULE vhAdvApi32Dll = NULL;
11 +static HMODULE vhCrypt32Dll = NULL;
12 +static BOOL vfCrypInitialized = FALSE;
13 +
14 +// function definitions
15 +
16 +/********************************************************************
17 + CrypInitialize - initializes cryputil
18 +
19 +*********************************************************************/
20 +extern "C" HRESULT DAPI CrypInitialize(
21 + )
22 +{
23 + HRESULT hr = S_OK;
24 +
25 + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll);
26 + if (SUCCEEDED(hr))
27 + {
28 + // Ignore failures - if these don't exist, we'll try the Crypt methods.
29 + vpfnRtlEncryptMemory = reinterpret_cast<PFN_RTLENCRYPTMEMORY>(::GetProcAddress(vhAdvApi32Dll, "SystemFunction040"));
30 + vpfnRtlDecryptMemory = reinterpret_cast<PFN_RTLDECRYPTMEMORY>(::GetProcAddress(vhAdvApi32Dll, "SystemFunction041"));
31 + }
32 + if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory)
33 + {
34 + hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll);
35 + ExitOnFailure(hr, "Failed to load Crypt32.dll");
36 +
37 + vpfnCryptProtectMemory = reinterpret_cast<PFN_CRYPTPROTECTMEMORY>(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory"));
38 + if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory)
39 + {
40 + ExitWithLastError(hr, "Failed to load an encryption method");
41 + }
42 + vpfnCryptUnprotectMemory = reinterpret_cast<PFN_CRYPTUNPROTECTMEMORY>(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory"));
43 + if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory)
44 + {
45 + ExitWithLastError(hr, "Failed to load a decryption method");
46 + }
47 + }
48 +
49 + vfCrypInitialized = TRUE;
50 +
51 +LExit:
52 + return hr;
53 +}
54 +
55 +
56 +/********************************************************************
57 + CrypUninitialize - uninitializes cryputil
58 +
59 +*********************************************************************/
60 +extern "C" void DAPI CrypUninitialize(
61 + )
62 +{
63 + if (vhAdvApi32Dll)
64 + {
65 + ::FreeLibrary(vhAdvApi32Dll);
66 + vhAdvApi32Dll = NULL;
67 + vpfnRtlEncryptMemory = NULL;
68 + vpfnRtlDecryptMemory = NULL;
69 + }
70 +
71 + if (vhCrypt32Dll)
72 + {
73 + ::FreeLibrary(vhCrypt32Dll);
74 + vhCrypt32Dll = NULL;
75 + vpfnCryptProtectMemory = NULL;
76 + vpfnCryptUnprotectMemory = NULL;
77 + }
78 +
79 + vfCrypInitialized = FALSE;
80 +}
81 +
82 +extern "C" HRESULT DAPI CrypDecodeObject(
83 + __in_z LPCSTR szStructType,
84 + __in_ecount(cbData) const BYTE* pbData,
85 + __in DWORD cbData,
86 + __in DWORD dwFlags,
87 + __out LPVOID* ppvObject,
88 + __out_opt DWORD* pcbObject
89 + )
90 +{
91 + HRESULT hr = S_OK;
92 + LPVOID pvObject = NULL;
93 + DWORD cbObject = 0;
94 +
95 + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject))
96 + {
97 + ExitWithLastError(hr, "Failed to decode object to determine size.");
98 + }
99 +
100 + pvObject = MemAlloc(cbObject, TRUE);
101 + ExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object.");
102 +
103 + if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject))
104 + {
105 + ExitWithLastError(hr, "Failed to decode object.");
106 + }
107 +
108 + *ppvObject = pvObject;
109 + pvObject = NULL;
110 +
111 + if (pcbObject)
112 + {
113 + *pcbObject = cbObject;
114 + }
115 +
116 +LExit:
117 + ReleaseMem(pvObject);
118 +
119 + return hr;
120 +}
121 +
122 +
123 +extern "C" HRESULT DAPI CrypMsgGetParam(
124 + __in HCRYPTMSG hCryptMsg,
125 + __in DWORD dwType,
126 + __in DWORD dwIndex,
127 + __out LPVOID* ppvData,
128 + __out_opt DWORD* pcbData
129 + )
130 +{
131 + HRESULT hr = S_OK;
132 + LPVOID pv = NULL;
133 + DWORD cb = 0;
134 +
135 + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb))
136 + {
137 + ExitWithLastError(hr, "Failed to get crypt message parameter data size.");
138 + }
139 +
140 + pv = MemAlloc(cb, TRUE);
141 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter.");
142 +
143 + if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb))
144 + {
145 + ExitWithLastError(hr, "Failed to get crypt message parameter.");
146 + }
147 +
148 + *ppvData = pv;
149 + pv = NULL;
150 +
151 + if (pcbData)
152 + {
153 + *pcbData = cb;
154 + }
155 +
156 +LExit:
157 + ReleaseMem(pv);
158 +
159 + return hr;
160 +}
161 +
162 +
163 +extern "C" HRESULT DAPI CrypHashFile(
164 + __in LPCWSTR wzFilePath,
165 + __in DWORD dwProvType,
166 + __in ALG_ID algid,
167 + __out_bcount(cbHash) BYTE* pbHash,
168 + __in DWORD cbHash,
169 + __out_opt DWORD64* pqwBytesHashed
170 + )
171 +{
172 + HRESULT hr = S_OK;
173 + HANDLE hFile = INVALID_HANDLE_VALUE;
174 +
175 + // open input file
176 + hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
177 + if (INVALID_HANDLE_VALUE == hFile)
178 + {
179 + ExitWithLastError(hr, "Failed to open input file.");
180 + }
181 +
182 + hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed);
183 + ExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath);
184 +
185 +LExit:
186 + ReleaseFileHandle(hFile);
187 +
188 + return hr;
189 +}
190 +
191 +
192 +extern "C" HRESULT DAPI CrypHashFileHandle(
193 + __in HANDLE hFile,
194 + __in DWORD dwProvType,
195 + __in ALG_ID algid,
196 + __out_bcount(cbHash) BYTE* pbHash,
197 + __in DWORD cbHash,
198 + __out_opt DWORD64* pqwBytesHashed
199 + )
200 +{
201 + HRESULT hr = S_OK;
202 + HCRYPTPROV hProv = NULL;
203 + HCRYPTHASH hHash = NULL;
204 + DWORD cbRead = 0;
205 + BYTE rgbBuffer[4096] = { };
206 + const LARGE_INTEGER liZero = { };
207 +
208 + // get handle to the crypto provider
209 + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
210 + {
211 + ExitWithLastError(hr, "Failed to acquire crypto context.");
212 + }
213 +
214 + // initiate hash
215 + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash))
216 + {
217 + ExitWithLastError(hr, "Failed to initiate hash.");
218 + }
219 +
220 + for (;;)
221 + {
222 + // read data block
223 + if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL))
224 + {
225 + ExitWithLastError(hr, "Failed to read data block.");
226 + }
227 +
228 + if (!cbRead)
229 + {
230 + break; // end of file
231 + }
232 +
233 + // hash data block
234 + if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0))
235 + {
236 + ExitWithLastError(hr, "Failed to hash data block.");
237 + }
238 + }
239 +
240 + // get hash value
241 + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0))
242 + {
243 + ExitWithLastError(hr, "Failed to get hash value.");
244 + }
245 +
246 + if (pqwBytesHashed)
247 + {
248 + if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT))
249 + {
250 + ExitWithLastError(hr, "Failed to get file pointer.");
251 + }
252 + }
253 +
254 +LExit:
255 + if (hHash)
256 + {
257 + ::CryptDestroyHash(hHash);
258 + }
259 + if (hProv)
260 + {
261 + ::CryptReleaseContext(hProv, 0);
262 + }
263 +
264 + return hr;
265 +}
266 +
267 +HRESULT DAPI CrypHashBuffer(
268 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
269 + __in SIZE_T cbBuffer,
270 + __in DWORD dwProvType,
271 + __in ALG_ID algid,
272 + __out_bcount(cbHash) BYTE* pbHash,
273 + __in DWORD cbHash
274 + )
275 +{
276 + HRESULT hr = S_OK;
277 + HCRYPTPROV hProv = NULL;
278 + HCRYPTHASH hHash = NULL;
279 +
280 + // get handle to the crypto provider
281 + if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
282 + {
283 + ExitWithLastError(hr, "Failed to acquire crypto context.");
284 + }
285 +
286 + // initiate hash
287 + if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash))
288 + {
289 + ExitWithLastError(hr, "Failed to initiate hash.");
290 + }
291 +
292 + if (!::CryptHashData(hHash, pbBuffer, static_cast<DWORD>(cbBuffer), 0))
293 + {
294 + ExitWithLastError(hr, "Failed to hash data.");
295 + }
296 +
297 + // get hash value
298 + if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0))
299 + {
300 + ExitWithLastError(hr, "Failed to get hash value.");
301 + }
302 +
303 +LExit:
304 + if (hHash)
305 + {
306 + ::CryptDestroyHash(hHash);
307 + }
308 + if (hProv)
309 + {
310 + ::CryptReleaseContext(hProv, 0);
311 + }
312 +
313 + return hr;
314 +}
315 +
316 +HRESULT DAPI CrypEncryptMemory(
317 + __inout LPVOID pData,
318 + __in DWORD cbData,
319 + __in DWORD dwFlags
320 + )
321 +{
322 + HRESULT hr = E_FAIL;
323 +
324 + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE)
325 + {
326 + hr = E_INVALIDARG;
327 + }
328 + else if (vpfnRtlEncryptMemory)
329 + {
330 + hr = static_cast<HRESULT>(vpfnRtlEncryptMemory(pData, cbData, dwFlags));
331 + }
332 + else if (vpfnCryptProtectMemory)
333 + {
334 + if (vpfnCryptProtectMemory(pData, cbData, dwFlags))
335 + {
336 + hr = S_OK;
337 + }
338 + else
339 + {
340 + hr = HRESULT_FROM_WIN32(::GetLastError());
341 + }
342 + }
343 + ExitOnFailure(hr, "Failed to encrypt memory");
344 +LExit:
345 + return hr;
346 +}
347 +
348 +HRESULT DAPI CrypDecryptMemory(
349 + __inout LPVOID pData,
350 + __in DWORD cbData,
351 + __in DWORD dwFlags
352 + )
353 +{
354 + HRESULT hr = E_FAIL;
355 +
356 + if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE)
357 + {
358 + hr = E_INVALIDARG;
359 + }
360 + else if (vpfnRtlDecryptMemory)
361 + {
362 + hr = static_cast<HRESULT>(vpfnRtlDecryptMemory(pData, cbData, dwFlags));
363 + }
364 + else if (vpfnCryptUnprotectMemory)
365 + {
366 + if (vpfnCryptUnprotectMemory(pData, cbData, dwFlags))
367 + {
368 + hr = S_OK;
369 + }
370 + else
371 + {
372 + hr = HRESULT_FROM_WIN32(::GetLastError());
373 + }
374 + }
375 + ExitOnFailure(hr, "Failed to decrypt memory");
376 +LExit:
377 + return hr;
378 +}
379 +
src/dutil/dictutil.cpp new
+769
@@ -0,0 +1,769 @@
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 +
5 +// These should all be primes, and spaced reasonably apart (currently each is about 4x the last)
6 +const DWORD MAX_BUCKET_SIZES[] = {
7 + 503,
8 + 2017,
9 + 7937,
10 + 32779,
11 + 131111,
12 + 524341,
13 + 2097709,
14 + 8390857,
15 + 33563437,
16 + 134253719,
17 + 537014927,
18 + 2148059509
19 + };
20 +
21 +// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions
22 +#define MAX_BUCKETS_TO_ITEMS_RATIO 8
23 +
24 +enum DICT_TYPE
25 +{
26 + DICT_INVALID = 0,
27 + DICT_EMBEDDED_KEY = 1,
28 + DICT_STRING_LIST = 2
29 +};
30 +
31 +struct STRINGDICT_STRUCT
32 +{
33 + DICT_TYPE dtType;
34 +
35 + // Optional flags to control the behavior of the dictionary.
36 + DICT_FLAG dfFlags;
37 +
38 + // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated
39 + DWORD dwBucketSizeIndex;
40 +
41 + // Number of items currently stored in the dict buckets
42 + DWORD dwNumItems;
43 +
44 + // Byte offset of key within bucket value, for collision checking - see
45 + // comments above DictCreateEmbeddedKey() implementation for further details
46 + size_t cByteOffset;
47 +
48 + // The actual stored buckets
49 + void **ppvBuckets;
50 +
51 + // The actual stored items in the order they were added (used for auto freeing or enumerating)
52 + void **ppvItemList;
53 +
54 + // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm
55 + void **ppvValueArray;
56 +};
57 +
58 +const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT);
59 +
60 +static HRESULT StringHash(
61 + __in const STRINGDICT_STRUCT *psd,
62 + __in DWORD dwNumBuckets,
63 + __in_z LPCWSTR pszString,
64 + __out LPDWORD pdwHash
65 + );
66 +static BOOL IsMatchExact(
67 + __in const STRINGDICT_STRUCT *psd,
68 + __in DWORD dwMatchIndex,
69 + __in_z LPCWSTR wzOriginalString
70 + );
71 +static HRESULT GetValue(
72 + __in const STRINGDICT_STRUCT *psd,
73 + __in_z LPCWSTR pszString,
74 + __out_opt void **ppvValue
75 + );
76 +static HRESULT GetInsertIndex(
77 + __in const STRINGDICT_STRUCT *psd,
78 + __in DWORD dwBucketCount,
79 + __in void **ppvBuckets,
80 + __in_z LPCWSTR pszString,
81 + __out DWORD *pdwOutput
82 + );
83 +static HRESULT GetIndex(
84 + __in const STRINGDICT_STRUCT *psd,
85 + __in_z LPCWSTR pszString,
86 + __out DWORD *pdwOutput
87 + );
88 +static LPCWSTR GetKey(
89 + __in const STRINGDICT_STRUCT *psd,
90 + __in void *pvValue
91 + );
92 +static HRESULT GrowDictionary(
93 + __inout STRINGDICT_STRUCT *psd
94 + );
95 +// These 2 helper functions allow us to safely handle dictutil consumers resizing
96 +// the value array by storing "offsets" instead of raw void *'s in our buckets.
97 +static void * TranslateOffsetToValue(
98 + __in const STRINGDICT_STRUCT *psd,
99 + __in void *pvValue
100 + );
101 +static void * TranslateValueToOffset(
102 + __in const STRINGDICT_STRUCT *psd,
103 + __in void *pvValue
104 + );
105 +
106 +// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s).
107 +// However, to support collision checking, the key needs to be represented in the "value" object (pointed to
108 +// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer
109 +// within the "value" object. Use the offsetof() macro to fill this out.
110 +// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter,
111 +// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide
112 +// this parameter to dictutil if it is possible you will realloc the array.
113 +//
114 +// Use DictAddValue() and DictGetValue() with this dictionary type.
115 +extern "C" HRESULT DAPI DictCreateWithEmbeddedKey(
116 + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
117 + __in DWORD dwNumExpectedItems,
118 + __in_opt void **ppvArray,
119 + __in size_t cByteOffset,
120 + __in DICT_FLAG dfFlags
121 + )
122 +{
123 + HRESULT hr = S_OK;
124 +
125 + ExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict");
126 +
127 + // Allocate the handle
128 + *psdHandle = static_cast<STRINGDICT_HANDLE>(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE));
129 + ExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object");
130 +
131 + STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(*psdHandle);
132 +
133 + // Fill out the new handle's values
134 + psd->dtType = DICT_EMBEDDED_KEY;
135 + psd->dfFlags = dfFlags;
136 + psd->cByteOffset = cByteOffset;
137 + psd->dwBucketSizeIndex = 0;
138 + psd->dwNumItems = 0;
139 + psd->ppvItemList = NULL;
140 + psd->ppvValueArray = ppvArray;
141 +
142 + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime
143 + // array based on expected number of items and items to buckets ratio
144 + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end
145 + // this loop past the end of the array!
146 + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) &&
147 + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO)
148 + {
149 + ++psd->dwBucketSizeIndex;
150 + }
151 +
152 + // Finally, allocate our initial buckets
153 + psd->ppvBuckets = static_cast<void**>(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE));
154 + ExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary");
155 +
156 +LExit:
157 + return hr;
158 +}
159 +
160 +// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type.
161 +extern "C" HRESULT DAPI DictCreateStringList(
162 + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
163 + __in DWORD dwNumExpectedItems,
164 + __in DICT_FLAG dfFlags
165 + )
166 +{
167 + HRESULT hr = S_OK;
168 +
169 + ExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict");
170 +
171 + // Allocate the handle
172 + *psdHandle = static_cast<STRINGDICT_HANDLE>(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE));
173 + ExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object");
174 +
175 + STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(*psdHandle);
176 +
177 + // Fill out the new handle's values
178 + psd->dtType = DICT_STRING_LIST;
179 + psd->dfFlags = dfFlags;
180 + psd->cByteOffset = 0;
181 + psd->dwBucketSizeIndex = 0;
182 + psd->dwNumItems = 0;
183 + psd->ppvItemList = NULL;
184 + psd->ppvValueArray = NULL;
185 +
186 + // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime
187 + // array based on expected number of items and items to buckets ratio
188 + // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end
189 + // this loop past the end of the array!
190 + while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) &&
191 + MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO)
192 + {
193 + ++psd->dwBucketSizeIndex;
194 + }
195 +
196 + // Finally, allocate our initial buckets
197 + psd->ppvBuckets = static_cast<void**>(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE));
198 + ExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary");
199 +
200 +LExit:
201 + return hr;
202 +}
203 +
204 +extern "C" HRESULT DAPI DictCreateStringListFromArray(
205 + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
206 + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
207 + __in const DWORD cStringArray,
208 + __in DICT_FLAG dfFlags
209 + )
210 +{
211 + HRESULT hr = S_OK;
212 + STRINGDICT_HANDLE sd = NULL;
213 +
214 + hr = DictCreateStringList(&sd, cStringArray, dfFlags);
215 + ExitOnFailure(hr, "Failed to create the string dictionary.");
216 +
217 + for (DWORD i = 0; i < cStringArray; ++i)
218 + {
219 + const LPCWSTR wzKey = rgwzStringArray[i];
220 +
221 + hr = DictKeyExists(sd, wzKey);
222 + if (E_NOTFOUND != hr)
223 + {
224 + ExitOnFailure(hr, "Failed to check the string dictionary.");
225 + }
226 + else
227 + {
228 + hr = DictAddKey(sd, wzKey);
229 + ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey);
230 + }
231 + }
232 +
233 + *psdHandle = sd;
234 + sd = NULL;
235 +
236 +LExit:
237 + ReleaseDict(sd);
238 +
239 + return hr;
240 +}
241 +
242 +extern "C" HRESULT DAPI DictCompareStringListToArray(
243 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList,
244 + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
245 + __in const DWORD cStringArray
246 + )
247 +{
248 + HRESULT hr = S_OK;
249 +
250 + for (DWORD i = 0; i < cStringArray; ++i)
251 + {
252 + hr = DictKeyExists(sdStringList, rgwzStringArray[i]);
253 + if (E_NOTFOUND != hr)
254 + {
255 + ExitOnFailure(hr, "Failed to check the string dictionary.");
256 + ExitFunction1(hr = S_OK);
257 + }
258 + }
259 +
260 + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH));
261 +
262 +LExit:
263 + return hr;
264 +}
265 +
266 +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
267 +extern "C" HRESULT DAPI DictAddKey(
268 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
269 + __in_z LPCWSTR pszString
270 + )
271 +{
272 + HRESULT hr = S_OK;
273 + DWORD dwIndex = 0;
274 + STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
275 +
276 + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict");
277 + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict");
278 +
279 + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
280 + {
281 + hr = E_INVALIDARG;
282 + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
283 + }
284 +
285 + if (DICT_STRING_LIST != psd->dtType)
286 + {
287 + hr = E_INVALIDARG;
288 + ExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType);
289 + }
290 +
291 + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO)
292 + {
293 + hr = GrowDictionary(psd);
294 + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr)
295 + {
296 + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full
297 + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
298 + {
299 + hr = S_OK;
300 + }
301 + }
302 + ExitOnFailure(hr, "Failed to grow dictionary");
303 + }
304 +
305 + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex);
306 + ExitOnFailure(hr, "Failed to get index to insert into");
307 +
308 + hr = MemEnsureArraySize(reinterpret_cast<void **>(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000);
309 + ExitOnFailure(hr, "Failed to resize list of items in dictionary");
310 + ++psd->dwNumItems;
311 +
312 + hr = StrAllocString(reinterpret_cast<LPWSTR *>(&(psd->ppvBuckets[dwIndex])), pszString, 0);
313 + ExitOnFailure(hr, "Failed to allocate copy of string");
314 +
315 + psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex];
316 +
317 +LExit:
318 + return hr;
319 +}
320 +
321 +// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
322 +extern "C" HRESULT DAPI DictAddValue(
323 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
324 + __in void *pvValue
325 + )
326 +{
327 + HRESULT hr = S_OK;
328 + void *pvOffset = NULL;
329 + LPCWSTR wzKey = NULL;
330 + DWORD dwIndex = 0;
331 + STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
332 +
333 + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict");
334 + ExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict");
335 +
336 + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
337 + {
338 + hr = E_INVALIDARG;
339 + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
340 + }
341 +
342 + if (DICT_EMBEDDED_KEY != psd->dtType)
343 + {
344 + hr = E_INVALIDARG;
345 + ExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType);
346 + }
347 +
348 + wzKey = GetKey(psd, pvValue);
349 + ExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict");
350 +
351 + if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO)
352 + {
353 + hr = GrowDictionary(psd);
354 + if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 )
355 + {
356 + // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full
357 + if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
358 + {
359 + hr = S_OK;
360 + }
361 + }
362 + ExitOnFailure(hr, "Failed to grow dictionary");
363 + }
364 +
365 + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex);
366 + ExitOnFailure(hr, "Failed to get index to insert into");
367 +
368 + hr = MemEnsureArraySize(reinterpret_cast<void **>(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000);
369 + ExitOnFailure(hr, "Failed to resize list of items in dictionary");
370 + ++psd->dwNumItems;
371 +
372 + pvOffset = TranslateValueToOffset(psd, pvValue);
373 + psd->ppvBuckets[dwIndex] = pvOffset;
374 + psd->ppvItemList[psd->dwNumItems-1] = pvOffset;
375 +
376 +LExit:
377 + return hr;
378 +}
379 +
380 +extern "C" HRESULT DAPI DictGetValue(
381 + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
382 + __in_z LPCWSTR pszString,
383 + __out void **ppvValue
384 + )
385 +{
386 + HRESULT hr = S_OK;
387 +
388 + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict");
389 + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
390 +
391 + const STRINGDICT_STRUCT *psd = static_cast<const STRINGDICT_STRUCT *>(sdHandle);
392 +
393 + if (DICT_EMBEDDED_KEY != psd->dtType)
394 + {
395 + hr = E_INVALIDARG;
396 + ExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType);
397 + }
398 +
399 + hr = GetValue(psd, pszString, ppvValue);
400 + if (E_NOTFOUND == hr)
401 + {
402 + ExitFunction();
403 + }
404 + ExitOnFailure(hr, "Failed to call internal GetValue()");
405 +
406 +LExit:
407 + return hr;
408 +}
409 +
410 +extern "C" HRESULT DAPI DictKeyExists(
411 + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
412 + __in_z LPCWSTR pszString
413 + )
414 +{
415 + HRESULT hr = S_OK;
416 +
417 + ExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict");
418 + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
419 +
420 + const STRINGDICT_STRUCT *psd = static_cast<const STRINGDICT_STRUCT *>(sdHandle);
421 +
422 + // This works with either type of dictionary
423 + hr = GetValue(psd, pszString, NULL);
424 + if (E_NOTFOUND == hr)
425 + {
426 + ExitFunction();
427 + }
428 + ExitOnFailure(hr, "Failed to call internal GetValue()");
429 +
430 +LExit:
431 + return hr;
432 +}
433 +
434 +extern "C" void DAPI DictDestroy(
435 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle
436 + )
437 +{
438 + DWORD i;
439 +
440 + STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
441 +
442 + if (DICT_STRING_LIST == psd->dtType)
443 + {
444 + for (i = 0; i < psd->dwNumItems; ++i)
445 + {
446 + ReleaseStr(reinterpret_cast<LPWSTR>(psd->ppvItemList[i]));
447 + }
448 + }
449 +
450 + ReleaseMem(psd->ppvItemList);
451 + ReleaseMem(psd->ppvBuckets);
452 + ReleaseMem(psd);
453 +}
454 +
455 +static HRESULT StringHash(
456 + __in const STRINGDICT_STRUCT *psd,
457 + __in DWORD dwNumBuckets,
458 + __in_z LPCWSTR pszString,
459 + __out DWORD *pdwHash
460 + )
461 +{
462 + HRESULT hr = S_OK;
463 + LPCWSTR wzKey = NULL;
464 + LPWSTR sczNewKey = NULL;
465 + DWORD result = 0;
466 +
467 + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags)
468 + {
469 + hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0);
470 + ExitOnFailure(hr, "Failed to convert the string to upper-case.");
471 +
472 + wzKey = sczNewKey;
473 + }
474 + else
475 + {
476 + wzKey = pszString;
477 + }
478 +
479 + while (*wzKey)
480 + {
481 + result = ~(*wzKey++ * 509) + result * 65599;
482 + }
483 +
484 + *pdwHash = result % dwNumBuckets;
485 +
486 +LExit:
487 + ReleaseStr(sczNewKey);
488 +
489 + return hr;
490 +}
491 +
492 +static BOOL IsMatchExact(
493 + __in const STRINGDICT_STRUCT *psd,
494 + __in DWORD dwMatchIndex,
495 + __in_z LPCWSTR wzOriginalString
496 + )
497 +{
498 + LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex]));
499 + DWORD dwFlags = 0;
500 +
501 + if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags)
502 + {
503 + dwFlags |= NORM_IGNORECASE;
504 + }
505 +
506 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1))
507 + {
508 + return TRUE;
509 + }
510 +
511 + return FALSE;
512 +}
513 +
514 +static HRESULT GetValue(
515 + __in const STRINGDICT_STRUCT *psd,
516 + __in_z LPCWSTR pszString,
517 + __out_opt void **ppvValue
518 + )
519 +{
520 + HRESULT hr = S_OK;
521 + DWORD dwOriginalIndexCandidate = 0;
522 + void *pvCandidateValue = NULL;
523 + DWORD dwIndex = 0;
524 +
525 + ExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict");
526 + ExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
527 +
528 + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
529 + {
530 + hr = E_INVALIDARG;
531 + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
532 + }
533 +
534 + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate);
535 + ExitOnFailure(hr, "Failed to hash the string.");
536 +
537 + DWORD dwIndexCandidate = dwOriginalIndexCandidate;
538 +
539 + pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]);
540 +
541 + // If no match exists in the dict
542 + if (NULL == pvCandidateValue)
543 + {
544 + if (NULL != ppvValue)
545 + {
546 + *ppvValue = NULL;
547 + }
548 + ExitFunction1(hr = E_NOTFOUND);
549 + }
550 +
551 + hr = GetIndex(psd, pszString, &dwIndex);
552 + if (E_NOTFOUND == hr)
553 + {
554 + ExitFunction();
555 + }
556 + ExitOnFailure(hr, "Failed to find index to get");
557 +
558 + if (NULL != ppvValue)
559 + {
560 + *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]);
561 + }
562 +
563 +LExit:
564 + if (FAILED(hr) && NULL != ppvValue)
565 + {
566 + *ppvValue = NULL;
567 + }
568 +
569 + return hr;
570 +}
571 +
572 +static HRESULT GetInsertIndex(
573 + __in const STRINGDICT_STRUCT *psd,
574 + __in DWORD dwBucketCount,
575 + __in void **ppvBuckets,
576 + __in_z LPCWSTR pszString,
577 + __out DWORD *pdwOutput
578 + )
579 +{
580 + HRESULT hr = S_OK;
581 + DWORD dwOriginalIndexCandidate = 0;
582 +
583 + hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate);
584 + ExitOnFailure(hr, "Failed to hash the string.");
585 +
586 + DWORD dwIndexCandidate = dwOriginalIndexCandidate;
587 +
588 + // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket
589 +#pragma prefast(push)
590 +#pragma prefast(disable:26007)
591 + while (NULL != ppvBuckets[dwIndexCandidate])
592 +#pragma prefast(pop)
593 + {
594 + ++dwIndexCandidate;
595 +
596 + // If we got to the end of the array, wrap around to zero index
597 + if (dwIndexCandidate >= dwBucketCount)
598 + {
599 + dwIndexCandidate = 0;
600 + }
601 +
602 + // If we wrapped all the way back around to our original index, the dict is full - throw an error
603 + if (dwIndexCandidate == dwOriginalIndexCandidate)
604 + {
605 + // The dict table is full - this error seems to be a reasonably close match
606 + hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL);
607 + ExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString);
608 + }
609 + }
610 +
611 + *pdwOutput = dwIndexCandidate;
612 +
613 +LExit:
614 + return hr;
615 +}
616 +
617 +static HRESULT GetIndex(
618 + __in const STRINGDICT_STRUCT *psd,
619 + __in_z LPCWSTR pszString,
620 + __out DWORD *pdwOutput
621 + )
622 +{
623 + HRESULT hr = S_OK;
624 + DWORD dwOriginalIndexCandidate = 0;
625 +
626 + if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
627 + {
628 + hr = E_INVALIDARG;
629 + ExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
630 + }
631 +
632 + hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate);
633 + ExitOnFailure(hr, "Failed to hash the string.");
634 +
635 + DWORD dwIndexCandidate = dwOriginalIndexCandidate;
636 +
637 + while (!IsMatchExact(psd, dwIndexCandidate, pszString))
638 + {
639 + ++dwIndexCandidate;
640 +
641 + // If we got to the end of the array, wrap around to zero index
642 + if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
643 + {
644 + dwIndexCandidate = 0;
645 + }
646 +
647 + // If no match exists in the dict
648 + if (NULL == psd->ppvBuckets[dwIndexCandidate])
649 + {
650 + ExitFunction1(hr = E_NOTFOUND);
651 + }
652 +
653 + // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such
654 + if (dwIndexCandidate == dwOriginalIndexCandidate)
655 + {
656 + ExitFunction1(hr = E_NOTFOUND);
657 + }
658 + }
659 +
660 + *pdwOutput = dwIndexCandidate;
661 +
662 +LExit:
663 + return hr;
664 +}
665 +
666 +static LPCWSTR GetKey(
667 + __in const STRINGDICT_STRUCT *psd,
668 + __in void *pvValue
669 + )
670 +{
671 + const BYTE *lpByte = reinterpret_cast<BYTE *>(pvValue);
672 +
673 + if (DICT_EMBEDDED_KEY == psd->dtType)
674 + {
675 + void *pvKey = reinterpret_cast<void *>(reinterpret_cast<BYTE *>(pvValue) + psd->cByteOffset);
676 +
677 +#pragma prefast(push)
678 +#pragma prefast(disable:26010)
679 + return *(reinterpret_cast<LPCWSTR *>(pvKey));
680 +#pragma prefast(pop)
681 + }
682 + else
683 + {
684 + return (reinterpret_cast<LPCWSTR>(lpByte));
685 + }
686 +}
687 +
688 +static HRESULT GrowDictionary(
689 + __inout STRINGDICT_STRUCT *psd
690 + )
691 +{
692 + HRESULT hr = S_OK;
693 + DWORD dwInsertIndex = 0;
694 + LPCWSTR wzKey = NULL;
695 + DWORD dwNewBucketSizeIndex = 0;
696 + size_t cbAllocSize = 0;
697 + void **ppvNewBuckets = NULL;
698 +
699 + dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1;
700 +
701 + if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
702 + {
703 + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL));
704 + }
705 +
706 + hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize);
707 + ExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary");
708 +
709 + ppvNewBuckets = static_cast<void**>(MemAlloc(cbAllocSize, TRUE));
710 + ExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]);
711 +
712 + for (DWORD i = 0; i < psd->dwNumItems; ++i)
713 + {
714 + wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i]));
715 + ExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value");
716 +
717 + hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex);
718 + ExitOnFailure(hr, "Failed to get index to insert into");
719 +
720 + ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i];
721 + }
722 +
723 + psd->dwBucketSizeIndex = dwNewBucketSizeIndex;
724 + ReleaseMem(psd->ppvBuckets);
725 + psd->ppvBuckets = ppvNewBuckets;
726 + ppvNewBuckets = NULL;
727 +
728 +LExit:
729 + ReleaseMem(ppvNewBuckets);
730 +
731 + return hr;
732 +}
733 +
734 +static void * TranslateOffsetToValue(
735 + __in const STRINGDICT_STRUCT *psd,
736 + __in void *pvValue
737 + )
738 +{
739 + if (NULL == pvValue)
740 + {
741 + return NULL;
742 + }
743 +
744 + // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value
745 + if (NULL != psd->ppvValueArray)
746 + {
747 + return reinterpret_cast<void *>(reinterpret_cast<DWORD_PTR>(pvValue) + reinterpret_cast<DWORD_PTR>(*psd->ppvValueArray) - 1);
748 + }
749 + else
750 + {
751 + return pvValue;
752 + }
753 +}
754 +
755 +static void * TranslateValueToOffset(
756 + __in const STRINGDICT_STRUCT *psd,
757 + __in void *pvValue
758 + )
759 +{
760 + if (NULL != psd->ppvValueArray)
761 + {
762 + // 0 has a special meaning - we don't want offset 0 into the array to have NULL for the offset - so add 1 to avoid this issue
763 + return reinterpret_cast<void *>(reinterpret_cast<DWORD_PTR>(pvValue) - reinterpret_cast<DWORD_PTR>(*psd->ppvValueArray) + 1);
764 + }
765 + else
766 + {
767 + return pvValue;
768 + }
769 +}
src/dutil/dirutil.cpp new
+395
@@ -0,0 +1,395 @@
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 +
5 +
6 +/*******************************************************************
7 + DirExists
8 +
9 +*******************************************************************/
10 +extern "C" BOOL DAPI DirExists(
11 + __in_z LPCWSTR wzPath,
12 + __out_opt DWORD *pdwAttributes
13 + )
14 +{
15 + Assert(wzPath);
16 +
17 + BOOL fExists = FALSE;
18 +
19 + DWORD dwAttributes = ::GetFileAttributesW(wzPath);
20 + if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here
21 + {
22 + ExitFunction();
23 + }
24 +
25 + if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
26 + {
27 + if (pdwAttributes)
28 + {
29 + *pdwAttributes = dwAttributes;
30 + }
31 +
32 + fExists = TRUE;
33 + }
34 +
35 +LExit:
36 + return fExists;
37 +}
38 +
39 +
40 +/*******************************************************************
41 + DirCreateTempPath
42 +
43 + *******************************************************************/
44 +extern "C" HRESULT DAPI DirCreateTempPath(
45 + __in_z LPCWSTR wzPrefix,
46 + __out_ecount_z(cchPath) LPWSTR wzPath,
47 + __in DWORD cchPath
48 + )
49 +{
50 + Assert(wzPrefix);
51 + Assert(wzPath);
52 +
53 + HRESULT hr = S_OK;
54 +
55 + WCHAR wzDir[MAX_PATH];
56 + WCHAR wzFile[MAX_PATH];
57 + DWORD cch = 0;
58 +
59 + cch = ::GetTempPathW(countof(wzDir), wzDir);
60 + if (!cch || cch >= countof(wzDir))
61 + {
62 + ExitWithLastError(hr, "Failed to GetTempPath.");
63 + }
64 +
65 + if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile))
66 + {
67 + ExitWithLastError(hr, "Failed to GetTempFileName.");
68 + }
69 +
70 + hr = ::StringCchCopyW(wzPath, cchPath, wzFile);
71 +
72 +LExit:
73 + return hr;
74 +}
75 +
76 +
77 +/*******************************************************************
78 + DirEnsureExists
79 +
80 +*******************************************************************/
81 +extern "C" HRESULT DAPI DirEnsureExists(
82 + __in_z LPCWSTR wzPath,
83 + __in_opt LPSECURITY_ATTRIBUTES psa
84 + )
85 +{
86 + HRESULT hr = S_OK;
87 + UINT er;
88 +
89 + // try to create this directory
90 + if (!::CreateDirectoryW(wzPath, psa))
91 + {
92 + // if the directory already exists, bail
93 + er = ::GetLastError();
94 + if (ERROR_ALREADY_EXISTS == er)
95 + {
96 + ExitFunction1(hr = S_OK);
97 + }
98 + else if (ERROR_PATH_NOT_FOUND != er && DirExists(wzPath, NULL)) // if the directory happens to exist (don't check if CreateDirectory said it doesn't), declare success.
99 + {
100 + ExitFunction1(hr = S_OK);
101 + }
102 +
103 + // get the parent path and try to create it
104 + LPWSTR pwzLastSlash = NULL;
105 + for (LPWSTR pwz = const_cast<LPWSTR>(wzPath); *pwz; ++pwz)
106 + {
107 + if (*pwz == L'\\')
108 + {
109 + pwzLastSlash = pwz;
110 + }
111 + }
112 +
113 + // if there is no parent directory fail
114 + ExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path");
115 +
116 + *pwzLastSlash = L'\0'; // null terminate the parent path
117 + hr = DirEnsureExists(wzPath, psa); // recurse!
118 + *pwzLastSlash = L'\\'; // put the slash back
119 + ExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath);
120 +
121 + // try to create the directory now that all parents are created
122 + if (!::CreateDirectoryW(wzPath, psa))
123 + {
124 + // if the directory already exists for some reason no error
125 + er = ::GetLastError();
126 + if (ERROR_ALREADY_EXISTS == er)
127 + {
128 + hr = S_FALSE;
129 + }
130 + else
131 + {
132 + hr = HRESULT_FROM_WIN32(er);
133 + }
134 + }
135 + else
136 + {
137 + hr = S_OK;
138 + }
139 + }
140 +
141 +LExit:
142 + return hr;
143 +}
144 +
145 +
146 +/*******************************************************************
147 + DirEnsureDelete - removes an entire directory structure
148 +
149 +*******************************************************************/
150 +extern "C" HRESULT DAPI DirEnsureDelete(
151 + __in_z LPCWSTR wzPath,
152 + __in BOOL fDeleteFiles,
153 + __in BOOL fRecurse
154 + )
155 +{
156 + HRESULT hr = S_OK;
157 + DWORD dwDeleteFlags = 0;
158 +
159 + dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0;
160 + dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0;
161 +
162 + hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags);
163 + return hr;
164 +}
165 +
166 +
167 +/*******************************************************************
168 + DirEnsureDeleteEx - removes an entire directory structure
169 +
170 +*******************************************************************/
171 +extern "C" HRESULT DAPI DirEnsureDeleteEx(
172 + __in_z LPCWSTR wzPath,
173 + __in DWORD dwFlags
174 + )
175 +{
176 + Assert(wzPath && *wzPath);
177 +
178 + HRESULT hr = S_OK;
179 + DWORD er;
180 +
181 + DWORD dwAttrib;
182 + HANDLE hFind = INVALID_HANDLE_VALUE;
183 + LPWSTR sczDelete = NULL;
184 + WIN32_FIND_DATAW wfd;
185 +
186 + BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES));
187 + BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE));
188 + BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE));
189 + WCHAR wzTempDirectory[MAX_PATH] = { };
190 + WCHAR wzTempPath[MAX_PATH] = { };
191 +
192 + if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath)))
193 + {
194 + er = ::GetLastError();
195 + if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory.
196 + {
197 + er = ERROR_PATH_NOT_FOUND;
198 + }
199 + hr = HRESULT_FROM_WIN32(er);
200 + ExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath);
201 + }
202 +
203 + if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
204 + {
205 + if (dwAttrib & FILE_ATTRIBUTE_READONLY)
206 + {
207 + if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL))
208 + {
209 + ExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath);
210 + }
211 + }
212 +
213 + // If we're deleting files and/or child directories loop through the contents of the directory.
214 + if (fDeleteFiles || fRecurse)
215 + {
216 + if (fScheduleDelete)
217 + {
218 + if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory))
219 + {
220 + ExitWithLastError(hr, "Failed to get temp directory.");
221 + }
222 + }
223 +
224 + // Delete everything in this directory.
225 + hr = PathConcat(wzPath, L"*.*", &sczDelete);
226 + ExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath);
227 +
228 + hFind = ::FindFirstFileW(sczDelete, &wfd);
229 + if (INVALID_HANDLE_VALUE == hFind)
230 + {
231 + ExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath);
232 + }
233 +
234 + do
235 + {
236 + // Skip the dot directories.
237 + if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2])))
238 + {
239 + continue;
240 + }
241 +
242 + // For extra safety and to silence OACR.
243 + wfd.cFileName[MAX_PATH - 1] = L'\0';
244 +
245 + hr = PathConcat(wzPath, wfd.cFileName, &sczDelete);
246 + ExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath);
247 +
248 + if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
249 + {
250 + hr = PathBackslashTerminate(&sczDelete);
251 + ExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete);
252 +
253 + hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call
254 + if (FAILED(hr))
255 + {
256 + // if we failed to delete a subdirectory, keep trying to finish any remaining files
257 + ExitTrace(hr, "Failed to delete subdirectory; continuing: %ls", sczDelete);
258 + hr = S_OK;
259 + }
260 + }
261 + else if (fDeleteFiles) // this is a file, just delete it
262 + {
263 + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
264 + {
265 + if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL))
266 + {
267 + ExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete);
268 + }
269 + }
270 +
271 + if (!::DeleteFileW(sczDelete))
272 + {
273 + if (fScheduleDelete)
274 + {
275 + if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath))
276 + {
277 + ExitWithLastError(hr, "Failed to get temp file to move to.");
278 + }
279 +
280 + // Try to move the file to the temp directory then schedule for delete,
281 + // otherwise just schedule for delete.
282 + if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING))
283 + {
284 + ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
285 + }
286 + else
287 + {
288 + ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
289 + }
290 + }
291 + else
292 + {
293 + ExitWithLastError(hr, "Failed to delete file: %ls", sczDelete);
294 + }
295 + }
296 + }
297 + } while (::FindNextFileW(hFind, &wfd));
298 +
299 + er = ::GetLastError();
300 + if (ERROR_NO_MORE_FILES == er)
301 + {
302 + hr = S_OK;
303 + }
304 + else
305 + {
306 + ExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath);
307 + }
308 + }
309 +
310 + if (!::RemoveDirectoryW(wzPath))
311 + {
312 + hr = HRESULT_FROM_WIN32(::GetLastError());
313 + if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete)
314 + {
315 + if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT))
316 + {
317 + hr = S_OK;
318 + }
319 + }
320 +
321 + ExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath);
322 + }
323 + }
324 + else
325 + {
326 + hr = E_UNEXPECTED;
327 + ExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath);
328 + }
329 +
330 + Assert(S_OK == hr);
331 +
332 +LExit:
333 + ReleaseFileFindHandle(hFind);
334 + ReleaseStr(sczDelete);
335 +
336 + return hr;
337 +}
338 +
339 +
340 +/*******************************************************************
341 + DirGetCurrent - gets the current directory.
342 +
343 +*******************************************************************/
344 +extern "C" HRESULT DAPI DirGetCurrent(
345 + __deref_out_z LPWSTR* psczCurrentDirectory
346 + )
347 +{
348 + HRESULT hr = S_OK;
349 + DWORD_PTR cch = 0;
350 +
351 + if (psczCurrentDirectory && *psczCurrentDirectory)
352 + {
353 + hr = StrMaxLength(*psczCurrentDirectory, &cch);
354 + ExitOnFailure(hr, "Failed to determine size of current directory.");
355 + }
356 +
357 + DWORD cchRequired = ::GetCurrentDirectoryW(static_cast<DWORD>(cch), 0 == cch ? NULL : *psczCurrentDirectory);
358 + if (0 == cchRequired)
359 + {
360 + ExitWithLastError(hr, "Failed to get current directory.");
361 + }
362 + else if (cch < cchRequired)
363 + {
364 + hr = StrAlloc(psczCurrentDirectory, cchRequired);
365 + ExitOnFailure(hr, "Failed to allocate string for current directory.");
366 +
367 + if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory))
368 + {
369 + ExitWithLastError(hr, "Failed to get current directory using allocated string.");
370 + }
371 + }
372 +
373 +LExit:
374 + return hr;
375 +}
376 +
377 +
378 +/*******************************************************************
379 + DirSetCurrent - sets the current directory.
380 +
381 +*******************************************************************/
382 +extern "C" HRESULT DAPI DirSetCurrent(
383 + __in_z LPCWSTR wzDirectory
384 + )
385 +{
386 + HRESULT hr = S_OK;
387 +
388 + if (!::SetCurrentDirectoryW(wzDirectory))
389 + {
390 + ExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory);
391 + }
392 +
393 +LExit:
394 + return hr;
395 +}
src/dutil/dlutil.cpp new
+783
@@ -0,0 +1,783 @@
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 +
5 +#include <inetutil.h>
6 +#include <uriutil.h>
7 +
8 +static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024;
9 +static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL };
10 +
11 +// internal function declarations
12 +
13 +static HRESULT InitializeResume(
14 + __in LPCWSTR wzDestinationPath,
15 + __out LPWSTR* psczResumePath,
16 + __out HANDLE* phResumeFile,
17 + __out DWORD64* pdw64ResumeOffset
18 + );
19 +static HRESULT GetResourceMetadata(
20 + __in HINTERNET hSession,
21 + __inout_z LPWSTR* psczUrl,
22 + __in_z_opt LPCWSTR wzUser,
23 + __in_z_opt LPCWSTR wzPassword,
24 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
25 + __out DWORD64* pdw64ResourceSize,
26 + __out FILETIME* pftResourceCreated
27 + );
28 +static HRESULT DownloadResource(
29 + __in HINTERNET hSession,
30 + __inout_z LPWSTR* psczUrl,
31 + __in_z_opt LPCWSTR wzUser,
32 + __in_z_opt LPCWSTR wzPassword,
33 + __in_z LPCWSTR wzDestinationPath,
34 + __in DWORD64 dw64AuthoredResourceLength,
35 + __in DWORD64 dw64ResourceLength,
36 + __in DWORD64 dw64ResumeOffset,
37 + __in HANDLE hResumeFile,
38 + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
39 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
40 + );
41 +static HRESULT AllocateRangeRequestHeader(
42 + __in DWORD64 dw64ResumeOffset,
43 + __in DWORD64 dw64ResourceLength,
44 + __deref_out_z LPWSTR* psczHeader
45 + );
46 +static HRESULT WriteToFile(
47 + __in HINTERNET hUrl,
48 + __in HANDLE hPayloadFile,
49 + __inout DWORD64* pdw64ResumeOffset,
50 + __in HANDLE hResumeFile,
51 + __in DWORD64 dw64ResourceLength,
52 + __in LPBYTE pbData,
53 + __in DWORD cbData,
54 + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback
55 + );
56 +static HRESULT UpdateResumeOffset(
57 + __inout DWORD64* pdw64ResumeOffset,
58 + __in HANDLE hResumeFile,
59 + __in DWORD cbData
60 + );
61 +static HRESULT MakeRequest(
62 + __in HINTERNET hSession,
63 + __inout_z LPWSTR* psczSourceUrl,
64 + __in_z_opt LPCWSTR wzMethod,
65 + __in_z_opt LPCWSTR wzHeaders,
66 + __in_z_opt LPCWSTR wzUser,
67 + __in_z_opt LPCWSTR wzPassword,
68 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
69 + __out HINTERNET* phConnect,
70 + __out HINTERNET* phUrl,
71 + __out BOOL* pfRangeRequestsAccepted
72 + );
73 +static HRESULT OpenRequest(
74 + __in HINTERNET hConnect,
75 + __in_z_opt LPCWSTR wzMethod,
76 + __in INTERNET_SCHEME scheme,
77 + __in_z LPCWSTR wzResource,
78 + __in_z_opt LPCWSTR wzQueryString,
79 + __in_z_opt LPCWSTR wzHeader,
80 + __out HINTERNET* phUrl
81 + );
82 +static HRESULT SendRequest(
83 + __in HINTERNET hUrl,
84 + __inout_z LPWSTR* psczUrl,
85 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
86 + __out BOOL* pfRetry,
87 + __out BOOL* pfRangesAccepted
88 + );
89 +static HRESULT AuthenticationRequired(
90 + __in HINTERNET hUrl,
91 + __in long lHttpCode,
92 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
93 + __out BOOL* pfRetrySend,
94 + __out BOOL* pfRetry
95 + );
96 +static HRESULT DownloadGetResumePath(
97 + __in_z LPCWSTR wzPayloadWorkingPath,
98 + __deref_out_z LPWSTR* psczResumePath
99 + );
100 +static HRESULT DownloadSendProgressCallback(
101 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
102 + __in DWORD64 dw64Progress,
103 + __in DWORD64 dw64Total,
104 + __in HANDLE hDestinationFile
105 + );
106 +// function definitions
107 +
108 +extern "C" HRESULT DAPI DownloadUrl(
109 + __in DOWNLOAD_SOURCE* pDownloadSource,
110 + __in DWORD64 dw64AuthoredDownloadSize,
111 + __in LPCWSTR wzDestinationPath,
112 + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
113 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
114 + )
115 +{
116 + HRESULT hr = S_OK;
117 + LPWSTR sczUrl = NULL;
118 + HINTERNET hSession = NULL;
119 + DWORD dwTimeout = 0;
120 + LPWSTR sczResumePath = NULL;
121 + HANDLE hResumeFile = INVALID_HANDLE_VALUE;
122 + DWORD64 dw64ResumeOffset = 0;
123 + DWORD64 dw64Size = 0;
124 + FILETIME ftCreated = { };
125 +
126 + // Copy the download source into a working variable to handle redirects then
127 + // open the internet session.
128 + hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0);
129 + ExitOnFailure(hr, "Failed to copy download source URL.");
130 +
131 + hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
132 + ExitOnNullWithLastError(hSession, hr, "Failed to open internet session");
133 +
134 + // Make a best effort to set the download timeouts to 2 minutes or whatever policy says.
135 + PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout);
136 + if (0 < dwTimeout)
137 + {
138 + dwTimeout *= 1000; // convert to milliseconds.
139 + ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout));
140 + ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout));
141 + ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout));
142 + }
143 +
144 + // Get the resource size and creation time from the internet.
145 + hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated);
146 + ExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl);
147 +
148 + // Ignore failure to initialize resume because we will fall back to full download then
149 + // download.
150 + InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset);
151 +
152 + hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate);
153 + ExitOnFailure(hr, "Failed to download URL: %ls", sczUrl);
154 +
155 + // Cleanup the resume file because we successfully downloaded the whole file.
156 + if (sczResumePath && *sczResumePath)
157 + {
158 + ::DeleteFileW(sczResumePath);
159 + }
160 +
161 +LExit:
162 + ReleaseFileHandle(hResumeFile);
163 + ReleaseStr(sczResumePath);
164 + ReleaseInternet(hSession);
165 + ReleaseStr(sczUrl);
166 +
167 + return hr;
168 +}
169 +
170 +
171 +// internal helper functions
172 +
173 +static HRESULT InitializeResume(
174 + __in LPCWSTR wzDestinationPath,
175 + __out LPWSTR* psczResumePath,
176 + __out HANDLE* phResumeFile,
177 + __out DWORD64* pdw64ResumeOffset
178 + )
179 +{
180 + HRESULT hr = S_OK;
181 + HANDLE hResumeFile = INVALID_HANDLE_VALUE;
182 + DWORD cbTotalReadResumeData = 0;
183 + DWORD cbReadData = 0;
184 +
185 + *pdw64ResumeOffset = 0;
186 +
187 + hr = DownloadGetResumePath(wzDestinationPath, psczResumePath);
188 + ExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath);
189 +
190 + hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
191 + if (INVALID_HANDLE_VALUE == hResumeFile)
192 + {
193 + ExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath);
194 + }
195 +
196 + do
197 + {
198 + if (!::ReadFile(hResumeFile, reinterpret_cast<BYTE*>(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL))
199 + {
200 + ExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath);
201 + }
202 + cbTotalReadResumeData += cbReadData;
203 + } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData);
204 +
205 + // Start over if we couldn't get a resume offset.
206 + if (cbTotalReadResumeData != sizeof(DWORD64))
207 + {
208 + *pdw64ResumeOffset = 0;
209 + }
210 +
211 + *phResumeFile = hResumeFile;
212 + hResumeFile = INVALID_HANDLE_VALUE;
213 +
214 +LExit:
215 + ReleaseFileHandle(hResumeFile);
216 + return hr;
217 +}
218 +
219 +static HRESULT GetResourceMetadata(
220 + __in HINTERNET hSession,
221 + __inout_z LPWSTR* psczUrl,
222 + __in_z_opt LPCWSTR wzUser,
223 + __in_z_opt LPCWSTR wzPassword,
224 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
225 + __out DWORD64* pdw64ResourceSize,
226 + __out FILETIME* pftResourceCreated
227 + )
228 +{
229 + HRESULT hr = S_OK;
230 + BOOL fRangeRequestsAccepted = TRUE;
231 + HINTERNET hConnect = NULL;
232 + HINTERNET hUrl = NULL;
233 + LONGLONG llLength = 0;
234 +
235 + hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted);
236 + ExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl);
237 +
238 + hr = InternetGetSizeByHandle(hUrl, &llLength);
239 + if (FAILED(hr))
240 + {
241 + llLength = 0;
242 + hr = S_OK;
243 + }
244 +
245 + *pdw64ResourceSize = llLength;
246 +
247 + // Get the last modified time from the server, we'll use that as our downloaded time here. If
248 + // the server time isn't available then use the local system time.
249 + hr = InternetGetCreateTimeByHandle(hUrl, pftResourceCreated);
250 + if (FAILED(hr))
251 + {
252 + ::GetSystemTimeAsFileTime(pftResourceCreated);
253 + hr = S_OK;
254 + }
255 +
256 +LExit:
257 + ReleaseInternet(hUrl);
258 + ReleaseInternet(hConnect);
259 + return hr;
260 +}
261 +
262 +static HRESULT DownloadResource(
263 + __in HINTERNET hSession,
264 + __inout_z LPWSTR* psczUrl,
265 + __in_z_opt LPCWSTR wzUser,
266 + __in_z_opt LPCWSTR wzPassword,
267 + __in_z LPCWSTR wzDestinationPath,
268 + __in DWORD64 dw64AuthoredResourceLength,
269 + __in DWORD64 dw64ResourceLength,
270 + __in DWORD64 dw64ResumeOffset,
271 + __in HANDLE hResumeFile,
272 + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
273 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
274 + )
275 +{
276 + HRESULT hr = S_OK;
277 + HANDLE hPayloadFile = INVALID_HANDLE_VALUE;
278 + DWORD cbMaxData = 64 * 1024; // 64 KB
279 + BYTE* pbData = NULL;
280 + BOOL fRangeRequestsAccepted = TRUE;
281 + LPWSTR sczRangeRequestHeader = NULL;
282 + HINTERNET hConnect = NULL;
283 + HINTERNET hUrl = NULL;
284 + LONGLONG llLength = 0;
285 +
286 + hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
287 + if (INVALID_HANDLE_VALUE == hPayloadFile)
288 + {
289 + ExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath);
290 + }
291 +
292 + // Allocate a memory block on a page boundary in case we want to do optimal writing.
293 + pbData = static_cast<BYTE*>(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
294 + ExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into.");
295 +
296 + // Let's try downloading the file assuming that range requests are accepted. If range requests
297 + // are not supported we'll have to start over and accept the fact that we only get one shot
298 + // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't
299 + // like files that big.
300 + while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength))
301 + {
302 + hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader);
303 + ExitOnFailure(hr, "Failed to allocate range request header.");
304 +
305 + ReleaseNullInternet(hConnect);
306 + ReleaseNullInternet(hUrl);
307 +
308 + hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted);
309 + ExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl);
310 +
311 + // If we didn't get the size of the resource from the initial "HEAD" request
312 + // then let's try to get the size from this "GET" request.
313 + if (0 == dw64ResourceLength)
314 + {
315 + hr = InternetGetSizeByHandle(hUrl, &llLength);
316 + if (SUCCEEDED(hr))
317 + {
318 + dw64ResourceLength = llLength;
319 + }
320 + else // server didn't tell us the resource length.
321 + {
322 + // Fallback to the authored size of the resource. However, since we
323 + // don't really know the size on the server, don't try to use
324 + // range requests either.
325 + dw64ResourceLength = dw64AuthoredResourceLength;
326 + fRangeRequestsAccepted = FALSE;
327 + }
328 + }
329 +
330 + // If we just tried to do a range request and found out that it isn't supported, start over.
331 + if (!fRangeRequestsAccepted)
332 + {
333 + // TODO: log a message that the server did not accept range requests.
334 + dw64ResumeOffset = 0;
335 + }
336 +
337 + hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache);
338 + ExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath);
339 + }
340 +
341 +LExit:
342 + ReleaseInternet(hUrl);
343 + ReleaseInternet(hConnect);
344 + ReleaseStr(sczRangeRequestHeader);
345 + if (pbData)
346 + {
347 + ::VirtualFree(pbData, 0, MEM_RELEASE);
348 + }
349 + ReleaseFileHandle(hPayloadFile);
350 +
351 + return hr;
352 +}
353 +
354 +static HRESULT AllocateRangeRequestHeader(
355 + __in DWORD64 dw64ResumeOffset,
356 + __in DWORD64 dw64ResourceLength,
357 + __deref_out_z LPWSTR* psczHeader
358 + )
359 +{
360 + HRESULT hr = S_OK;
361 +
362 + // If the remaining length is less that 2GB we'll be able to ask for everything.
363 + DWORD64 dw64RemainingLength = dw64ResourceLength - dw64ResumeOffset;
364 + if (DOWNLOAD_ENGINE_TWO_GIGABYTES > dw64RemainingLength)
365 + {
366 + // If we have a resume offset, let's download everything from there. Otherwise, we'll
367 + // just get everything with no headers in the way.
368 + if (0 < dw64ResumeOffset)
369 + {
370 + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset);
371 + ExitOnFailure(hr, "Failed to add range read header.");
372 + }
373 + else
374 + {
375 + ReleaseNullStr(*psczHeader);
376 + }
377 + }
378 + else // we'll have to download in chunks.
379 + {
380 + hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1);
381 + ExitOnFailure(hr, "Failed to add range read header.");
382 + }
383 +
384 +LExit:
385 + return hr;
386 +}
387 +
388 +static HRESULT WriteToFile(
389 + __in HINTERNET hUrl,
390 + __in HANDLE hPayloadFile,
391 + __inout DWORD64* pdw64ResumeOffset,
392 + __in HANDLE hResumeFile,
393 + __in DWORD64 dw64ResourceLength,
394 + __in LPBYTE pbData,
395 + __in DWORD cbData,
396 + __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback
397 + )
398 +{
399 + HRESULT hr = S_OK;
400 + DWORD cbReadData = 0;
401 +
402 + hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN);
403 + ExitOnFailure(hr, "Failed to seek to start point in file.");
404 +
405 + do
406 + {
407 + // Read bits from the internet.
408 + if (!::InternetReadFile(hUrl, static_cast<void*>(pbData), cbData, &cbReadData))
409 + {
410 + ExitWithLastError(hr, "Failed while reading from internet.");
411 + }
412 +
413 + // Write bits to disk (if there are any).
414 + if (cbReadData)
415 + {
416 + DWORD cbTotalWritten = 0;
417 + DWORD cbWritten = 0;
418 + do
419 + {
420 + if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL))
421 + {
422 + ExitWithLastError(hr, "Failed to write data from internet.");
423 + }
424 +
425 + cbTotalWritten += cbWritten;
426 + } while (cbWritten && cbTotalWritten < cbReadData);
427 +
428 + // Ignore failure from updating resume file as this doesn't mean the download cannot succeed.
429 + UpdateResumeOffset(pdw64ResumeOffset, hResumeFile, cbTotalWritten);
430 +
431 + if (pCallback && pCallback->pfnProgress)
432 + {
433 + hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile);
434 + ExitOnFailure(hr, "UX aborted on cache progress.");
435 + }
436 + }
437 + } while (cbReadData);
438 +
439 +LExit:
440 + return hr;
441 +}
442 +
443 +static HRESULT UpdateResumeOffset(
444 + __inout DWORD64* pdw64ResumeOffset,
445 + __in HANDLE hResumeFile,
446 + __in DWORD cbData
447 + )
448 +{
449 + HRESULT hr = S_OK;
450 +
451 + *pdw64ResumeOffset += cbData;
452 +
453 + if (INVALID_HANDLE_VALUE != hResumeFile)
454 + {
455 + DWORD cbTotalWrittenResumeData = 0;
456 + DWORD cbWrittenResumeData = 0;
457 +
458 + hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN);
459 + ExitOnFailure(hr, "Failed to seek to start point in file.");
460 +
461 + do
462 + {
463 + // Ignore failure to write to the resume file as that should not prevent the download from happening.
464 + if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL))
465 + {
466 + ExitOnFailure(hr, "Failed to seek to write to file.");
467 + }
468 +
469 + cbTotalWrittenResumeData += cbWrittenResumeData;
470 + } while (cbWrittenResumeData && sizeof(DWORD64) > cbTotalWrittenResumeData);
471 + }
472 +
473 +LExit:
474 + return hr;
475 +}
476 +
477 +static HRESULT MakeRequest(
478 + __in HINTERNET hSession,
479 + __inout_z LPWSTR* psczSourceUrl,
480 + __in_z_opt LPCWSTR wzMethod,
481 + __in_z_opt LPCWSTR wzHeaders,
482 + __in_z_opt LPCWSTR wzUser,
483 + __in_z_opt LPCWSTR wzPassword,
484 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
485 + __out HINTERNET* phConnect,
486 + __out HINTERNET* phUrl,
487 + __out BOOL* pfRangeRequestsAccepted
488 + )
489 +{
490 + HRESULT hr = S_OK;
491 + HINTERNET hConnect = NULL;
492 + HINTERNET hUrl = NULL;
493 + URI_INFO uri = { };
494 +
495 + // Try to open the URL.
496 + BOOL fRetry;
497 + do
498 + {
499 + fRetry = FALSE;
500 +
501 + // If the URL was opened close it, so we can reopen it again.
502 + ReleaseInternet(hUrl);
503 + ReleaseInternet(hConnect);
504 +
505 + // Open the url.
506 + hr = UriCrackEx(*psczSourceUrl, &uri);
507 + ExitOnFailure(hr, "Failed to break URL into server and resource parts.");
508 +
509 + hConnect = ::InternetConnectW(hSession, uri.sczHostName, uri.port, (wzUser && *wzUser) ? wzUser : uri.sczUser, (wzPassword && *wzPassword) ? wzPassword : uri.sczPassword, INTERNET_SCHEME_FTP == uri.scheme ? INTERNET_SERVICE_FTP : INTERNET_SERVICE_HTTP, 0, 0);
510 + ExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl);
511 +
512 + // Best effort set the proxy username and password, if they were provided.
513 + if ((wzUser && *wzUser) && (wzPassword && *wzPassword))
514 + {
515 + if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser)))
516 + {
517 + ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword));
518 + }
519 + }
520 +
521 + hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl);
522 + ExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl);
523 +
524 + hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted);
525 + ExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl);
526 + } while (fRetry);
527 +
528 + // Okay, we're all ready to start downloading. Update the connection information.
529 + *phConnect = hConnect;
530 + hConnect = NULL;
531 + *phUrl = hUrl;
532 + hUrl = NULL;
533 +
534 +LExit:
535 + UriInfoUninitialize(&uri);
536 + ReleaseInternet(hUrl);
537 + ReleaseInternet(hConnect);
538 +
539 + return hr;
540 +}
541 +
542 +static HRESULT OpenRequest(
543 + __in HINTERNET hConnect,
544 + __in_z_opt LPCWSTR wzMethod,
545 + __in INTERNET_SCHEME scheme,
546 + __in_z LPCWSTR wzResource,
547 + __in_z_opt LPCWSTR wzQueryString,
548 + __in_z_opt LPCWSTR wzHeader,
549 + __out HINTERNET* phUrl
550 + )
551 +{
552 + HRESULT hr = S_OK;
553 + DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD;
554 + LPWSTR sczResource = NULL;
555 + HINTERNET hUrl = NULL;
556 +
557 + if (INTERNET_SCHEME_HTTPS == scheme)
558 + {
559 + dwRequestFlags |= INTERNET_FLAG_SECURE;
560 + }
561 +
562 + // Allocate the resource name.
563 + hr = StrAllocString(&sczResource, wzResource, 0);
564 + ExitOnFailure(hr, "Failed to allocate string for resource URI.");
565 +
566 + if (wzQueryString && *wzQueryString)
567 + {
568 + hr = StrAllocConcat(&sczResource, wzQueryString, 0);
569 + ExitOnFailure(hr, "Failed to append query strong to resource from URI.");
570 + }
571 +
572 + // Open the request and add the header if provided.
573 + hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL);
574 + ExitOnNullWithLastError(hUrl, hr, "Failed to open internet request.");
575 +
576 + if (wzHeader && *wzHeader)
577 + {
578 + if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast<DWORD>(-1), HTTP_ADDREQ_FLAG_COALESCE))
579 + {
580 + ExitWithLastError(hr, "Failed to add header to HTTP request.");
581 + }
582 + }
583 +
584 + *phUrl = hUrl;
585 + hUrl = NULL;
586 +
587 +LExit:
588 + ReleaseInternet(hUrl);
589 + ReleaseStr(sczResource);
590 + return hr;
591 +}
592 +
593 +static HRESULT SendRequest(
594 + __in HINTERNET hUrl,
595 + __inout_z LPWSTR* psczUrl,
596 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
597 + __out BOOL* pfRetry,
598 + __out BOOL* pfRangesAccepted
599 + )
600 +{
601 + HRESULT hr = S_OK;
602 + BOOL fRetrySend = FALSE;
603 + LONG lCode = 0;
604 +
605 + do
606 + {
607 + fRetrySend = FALSE;
608 +
609 + if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0))
610 + {
611 + hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it.
612 + LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl);
613 +
614 + // Try to get the HTTP status code and, if good, handle via the switch statement below but if it
615 + // fails return the error code from the send request above as the result of the function.
616 + HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode);
617 + ExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl);
618 + }
619 + else // get the http status code.
620 + {
621 + hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode);
622 + ExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl);
623 + }
624 +
625 + switch (lCode)
626 + {
627 + case 200: // OK but range requests don't work.
628 + *pfRangesAccepted = FALSE;
629 + hr = S_OK;
630 + break;
631 +
632 + case 206: // Partial content means that range requests work!
633 + *pfRangesAccepted = TRUE;
634 + hr = S_OK;
635 + break;
636 +
637 + // redirection cases
638 + case 301: __fallthrough; // file moved
639 + case 302: __fallthrough; // temporary
640 + case 303: // redirect method
641 + hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl);
642 + ExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl);
643 +
644 + *pfRetry = TRUE;
645 + break;
646 +
647 + // error cases
648 + case 400: // bad request
649 + hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
650 + break;
651 +
652 + case 401: __fallthrough; // unauthorized
653 + case 407: __fallthrough; // proxy unauthorized
654 + hr = AuthenticationRequired(hUrl, lCode, pAuthenticate, &fRetrySend, pfRetry);
655 + break;
656 +
657 + case 403: // forbidden
658 + hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
659 + break;
660 +
661 + case 404: // file not found
662 + case 410: // gone
663 + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
664 + break;
665 +
666 + case 405: // method not allowed
667 + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
668 + break;
669 +
670 + case 408: __fallthrough; // request timedout
671 + case 504: // gateway timeout
672 + hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT);
673 + break;
674 +
675 + case 414: // request URI too long
676 + hr = CO_E_PATHTOOLONG;
677 + break;
678 +
679 + case 502: __fallthrough; // server (through a gateway) was not found
680 + case 503: // server unavailable
681 + hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
682 + break;
683 +
684 + case 418: // I'm a teapot.
685 + default:
686 + // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway)
687 + // do not overwrite the error code from the failed request. Otherwise, the error was unexpected.
688 + if (SUCCEEDED(hr))
689 + {
690 + hr = E_UNEXPECTED;
691 + }
692 +
693 + LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl);
694 + break;
695 + }
696 + } while (fRetrySend);
697 +
698 +LExit:
699 + return hr;
700 +}
701 +
702 +static HRESULT AuthenticationRequired(
703 + __in HINTERNET hUrl,
704 + __in long lHttpCode,
705 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
706 + __out BOOL* pfRetrySend,
707 + __out BOOL* pfRetry
708 + )
709 +{
710 + Assert(401 == lHttpCode || 407 == lHttpCode);
711 +
712 + HRESULT hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
713 + *pfRetrySend = FALSE;
714 + *pfRetry = FALSE;
715 +
716 + if (pAuthenticate && pAuthenticate->pfnAuthenticate)
717 + {
718 + hr = (*pAuthenticate->pfnAuthenticate)(pAuthenticate->pv, hUrl, lHttpCode, pfRetrySend, pfRetry);
719 + }
720 +
721 + return hr;
722 +}
723 +
724 +
725 +static HRESULT DownloadGetResumePath(
726 + __in_z LPCWSTR wzPayloadWorkingPath,
727 + __deref_out_z LPWSTR* psczResumePath
728 + )
729 +{
730 + HRESULT hr = S_OK;
731 +
732 + hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath);
733 + ExitOnFailure(hr, "Failed to create resume path.");
734 +
735 +LExit:
736 + return hr;
737 +}
738 +
739 +static HRESULT DownloadSendProgressCallback(
740 + __in DOWNLOAD_CACHE_CALLBACK* pCallback,
741 + __in DWORD64 dw64Progress,
742 + __in DWORD64 dw64Total,
743 + __in HANDLE hDestinationFile
744 + )
745 +{
746 + static LARGE_INTEGER LARGE_INTEGER_ZERO = { };
747 +
748 + HRESULT hr = S_OK;
749 + DWORD dwResult = PROGRESS_CONTINUE;
750 + LARGE_INTEGER liTotalSize = { };
751 + LARGE_INTEGER liTotalTransferred = { };
752 +
753 + if (pCallback->pfnProgress)
754 + {
755 + liTotalSize.QuadPart = dw64Total;
756 + liTotalTransferred.QuadPart = dw64Progress;
757 +
758 + dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv);
759 + switch (dwResult)
760 + {
761 + case PROGRESS_CONTINUE:
762 + hr = S_OK;
763 + break;
764 +
765 + case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently?
766 + case PROGRESS_STOP:
767 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
768 + ExitOnRootFailure(hr, "UX aborted on download progress.");
769 +
770 + case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress.
771 + pCallback->pfnProgress = NULL;
772 + hr = S_OK;
773 + break;
774 +
775 + default:
776 + hr = E_UNEXPECTED;
777 + ExitOnRootFailure(hr, "Invalid return code from progress routine.");
778 + }
779 + }
780 +
781 +LExit:
782 + return hr;
783 +}
src/dutil/dutil.cpp new
+462
@@ -0,0 +1,462 @@
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 +
5 +// No need for OACR to warn us about using non-unicode APIs in this file.
6 +#pragma prefast(disable:25068)
7 +
8 +// Asserts & Tracing
9 +
10 +const int DUTIL_STRING_BUFFER = 1024;
11 +static HMODULE Dutil_hAssertModule = NULL;
12 +static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL;
13 +static BOOL Dutil_fNoAsserts = FALSE;
14 +static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD;
15 +static BOOL Dutil_fTraceFilenames = FALSE;
16 +
17 +
18 +/*******************************************************************
19 +Dutil_SetAssertModule
20 +
21 +*******************************************************************/
22 +extern "C" void DAPI Dutil_SetAssertModule(
23 + __in HMODULE hAssertModule
24 + )
25 +{
26 + Dutil_hAssertModule = hAssertModule;
27 +}
28 +
29 +
30 +/*******************************************************************
31 +Dutil_SetAssertDisplayFunction
32 +
33 +*******************************************************************/
34 +extern "C" void DAPI Dutil_SetAssertDisplayFunction(
35 + __in DUTIL_ASSERTDISPLAYFUNCTION pfn
36 + )
37 +{
38 + Dutil_pfnDisplayAssert = pfn;
39 +}
40 +
41 +
42 +/*******************************************************************
43 +Dutil_AssertMsg
44 +
45 +*******************************************************************/
46 +extern "C" void DAPI Dutil_AssertMsg(
47 + __in_z LPCSTR szMessage
48 + )
49 +{
50 + static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts)
51 +
52 + HRESULT hr = S_OK;
53 + DWORD er = ERROR_SUCCESS;
54 +
55 + int id = IDRETRY;
56 + HKEY hkDebug = NULL;
57 + HANDLE hAssertFile = INVALID_HANDLE_VALUE;
58 + char szPath[MAX_PATH] = { };
59 + DWORD cch = 0;
60 +
61 + if (fInAssert)
62 + {
63 + return;
64 + }
65 + fInAssert = TRUE;
66 +
67 + char szMsg[DUTIL_STRING_BUFFER];
68 + hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage);
69 + ExitOnFailure(hr, "failed to copy message while building assert message");
70 +
71 + if (Dutil_pfnDisplayAssert)
72 + {
73 + // call custom function to display the assert string
74 + if (!Dutil_pfnDisplayAssert(szMsg))
75 + {
76 + ExitFunction();
77 + }
78 + }
79 + else
80 + {
81 + OutputDebugStringA(szMsg);
82 + }
83 +
84 + if (!Dutil_fNoAsserts)
85 + {
86 + er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug);
87 + if (ERROR_SUCCESS == er)
88 + {
89 + cch = countof(szPath);
90 + er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast<BYTE*>(szPath), &cch);
91 + szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that.
92 + if (ERROR_SUCCESS == er)
93 + {
94 + hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
95 + if (INVALID_HANDLE_VALUE != hAssertFile)
96 + {
97 + ::SetFilePointer(hAssertFile, 0, 0, FILE_END);
98 + ::StringCchCatA(szMsg, countof(szMsg), "\r\n");
99 + ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL);
100 + }
101 + }
102 + }
103 +
104 + // if anything went wrong while fooling around with the registry, just show the usual assert dialog box
105 + if (ERROR_SUCCESS != er)
106 + {
107 + hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all");
108 + ExitOnFailure(hr, "failed to concat string while building assert message");
109 +
110 + id = ::MessageBoxA(0, szMsg, "Debug Assert Message",
111 + MB_SERVICE_NOTIFICATION | MB_TOPMOST |
112 + MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE);
113 + }
114 + }
115 +
116 + if (id == IDABORT)
117 + {
118 + if (Dutil_hAssertModule)
119 + {
120 + ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath));
121 +
122 + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "Module is running from: %s\nIf you are not using pdb-stamping, place your PDB near the module and attach to process id: %d (0x%x)", szPath, ::GetCurrentProcessId(), ::GetCurrentProcessId());
123 + if (SUCCEEDED(hr))
124 + {
125 + ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK);
126 + }
127 + }
128 +
129 + ::DebugBreak();
130 + }
131 + else if (id == IDIGNORE)
132 + {
133 + Dutil_fNoAsserts = TRUE;
134 + }
135 +
136 +LExit:
137 + ReleaseFileHandle(hAssertFile);
138 + ReleaseRegKey(hkDebug);
139 + fInAssert = FALSE;
140 +}
141 +
142 +
143 +/*******************************************************************
144 +Dutil_Assert
145 +
146 +*******************************************************************/
147 +extern "C" void DAPI Dutil_Assert(
148 + __in_z LPCSTR szFile,
149 + __in int iLine
150 + )
151 +{
152 + HRESULT hr = S_OK;
153 + char szMessage[DUTIL_STRING_BUFFER] = { };
154 + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine);
155 + if (SUCCEEDED(hr))
156 + {
157 + Dutil_AssertMsg(szMessage);
158 + }
159 + else
160 + {
161 + Dutil_AssertMsg("Assert failed to build string");
162 + }
163 +}
164 +
165 +
166 +/*******************************************************************
167 +Dutil_AssertSz
168 +
169 +*******************************************************************/
170 +extern "C" void DAPI Dutil_AssertSz(
171 + __in_z LPCSTR szFile,
172 + __in int iLine,
173 + __in_z __format_string LPCSTR szMsg
174 + )
175 +{
176 + HRESULT hr = S_OK;
177 + char szMessage[DUTIL_STRING_BUFFER] = { };
178 +
179 + hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg);
180 + if (SUCCEEDED(hr))
181 + {
182 + Dutil_AssertMsg(szMessage);
183 + }
184 + else
185 + {
186 + Dutil_AssertMsg("Assert failed to build string");
187 + }
188 +}
189 +
190 +
191 +/*******************************************************************
192 +Dutil_TraceSetLevel
193 +
194 +*******************************************************************/
195 +extern "C" void DAPI Dutil_TraceSetLevel(
196 + __in REPORT_LEVEL rl,
197 + __in BOOL fTraceFilenames
198 + )
199 +{
200 + Dutil_rlCurrentTrace = rl;
201 + Dutil_fTraceFilenames = fTraceFilenames;
202 +}
203 +
204 +
205 +/*******************************************************************
206 +Dutil_TraceGetLevel
207 +
208 +*******************************************************************/
209 +extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel()
210 +{
211 + return Dutil_rlCurrentTrace;
212 +}
213 +
214 +
215 +/*******************************************************************
216 +Dutil_Trace
217 +
218 +*******************************************************************/
219 +extern "C" void DAPI Dutil_Trace(
220 + __in_z LPCSTR szFile,
221 + __in int iLine,
222 + __in REPORT_LEVEL rl,
223 + __in_z __format_string LPCSTR szFormat,
224 + ...
225 + )
226 +{
227 + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level");
228 +
229 + HRESULT hr = S_OK;
230 + char szOutput[DUTIL_STRING_BUFFER] = { };
231 + char szMsg[DUTIL_STRING_BUFFER] = { };
232 +
233 + if (Dutil_rlCurrentTrace < rl)
234 + {
235 + return;
236 + }
237 +
238 + va_list args;
239 + va_start(args, szFormat);
240 + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args);
241 + va_end(args);
242 +
243 + if (SUCCEEDED(hr))
244 + {
245 + LPCSTR szPrefix = "Trace/u";
246 + switch (rl)
247 + {
248 + case REPORT_STANDARD:
249 + szPrefix = "Trace/s";
250 + break;
251 + case REPORT_VERBOSE:
252 + szPrefix = "Trace/v";
253 + break;
254 + case REPORT_DEBUG:
255 + szPrefix = "Trace/d";
256 + break;
257 + }
258 +
259 + if (Dutil_fTraceFilenames)
260 + {
261 + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput);
262 + }
263 + else
264 + {
265 + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput);
266 + }
267 +
268 + if (SUCCEEDED(hr))
269 + {
270 + OutputDebugStringA(szMsg);
271 + }
272 + // else fall through to the case below
273 + }
274 +
275 + if (FAILED(hr))
276 + {
277 + if (Dutil_fTraceFilenames)
278 + {
279 + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine);
280 + }
281 + else
282 + {
283 + ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n");
284 + }
285 +
286 + szMsg[countof(szMsg)-1] = '\0';
287 + OutputDebugStringA(szMsg);
288 + }
289 +}
290 +
291 +
292 +/*******************************************************************
293 +Dutil_TraceError
294 +
295 +*******************************************************************/
296 +extern "C" void DAPI Dutil_TraceError(
297 + __in_z LPCSTR szFile,
298 + __in int iLine,
299 + __in REPORT_LEVEL rl,
300 + __in HRESULT hrError,
301 + __in_z __format_string LPCSTR szFormat,
302 + ...
303 + )
304 +{
305 + HRESULT hr = S_OK;
306 + char szOutput[DUTIL_STRING_BUFFER] = { };
307 + char szMsg[DUTIL_STRING_BUFFER] = { };
308 +
309 + // if this is NOT an error report and we're not logging at this level, bail
310 + if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl)
311 + {
312 + return;
313 + }
314 +
315 + va_list args;
316 + va_start(args, szFormat);
317 + hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args);
318 + va_end(args);
319 +
320 + if (SUCCEEDED(hr))
321 + {
322 + if (Dutil_fTraceFilenames)
323 + {
324 + if (FAILED(hrError))
325 + {
326 + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput);
327 + }
328 + else
329 + {
330 + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput);
331 + }
332 + }
333 + else
334 + {
335 + if (FAILED(hrError))
336 + {
337 + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput);
338 + }
339 + else
340 + {
341 + hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput);
342 + }
343 + }
344 +
345 + if (SUCCEEDED(hr))
346 + {
347 + OutputDebugStringA(szMsg);
348 + }
349 + // else fall through to the failure case below
350 + }
351 +
352 + if (FAILED(hr))
353 + {
354 + if (Dutil_fTraceFilenames)
355 + {
356 + if (FAILED(hrError))
357 + {
358 + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine);
359 + }
360 + else
361 + {
362 + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine);
363 + }
364 + }
365 + else
366 + {
367 + if (FAILED(hrError))
368 + {
369 + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError);
370 + }
371 + else
372 + {
373 + ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n");
374 + }
375 + }
376 +
377 + szMsg[countof(szMsg)-1] = '\0';
378 + OutputDebugStringA(szMsg);
379 + }
380 +}
381 +
382 +
383 +
384 +/*******************************************************************
385 +Dutil_RootFailure
386 +
387 +*******************************************************************/
388 +extern "C" void DAPI Dutil_RootFailure(
389 + __in_z LPCSTR szFile,
390 + __in int iLine,
391 + __in HRESULT hrError
392 + )
393 +{
394 +#ifndef DEBUG
395 + UNREFERENCED_PARAMETER(szFile);
396 + UNREFERENCED_PARAMETER(iLine);
397 + UNREFERENCED_PARAMETER(hrError);
398 +#endif // DEBUG
399 +
400 + TraceError(hrError, "Root failure at %s:%d", szFile, iLine);
401 +}
402 +
403 +/*******************************************************************
404 + LoadSystemLibrary - Fully qualifies the path to a module in the
405 + Windows system directory and loads it.
406 +
407 + Returns
408 + E_MODNOTFOUND - The module could not be found.
409 + * - Another error occured.
410 +********************************************************************/
411 +extern "C" HRESULT DAPI LoadSystemLibrary(
412 + __in_z LPCWSTR wzModuleName,
413 + __out HMODULE *phModule
414 + )
415 +{
416 + HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL);
417 + return hr;
418 +}
419 +
420 +/*******************************************************************
421 + LoadSystemLibraryWithPath - Fully qualifies the path to a module in
422 + the Windows system directory and loads it
423 + and returns the path
424 +
425 + Returns
426 + E_MODNOTFOUND - The module could not be found.
427 + * - Another error occured.
428 +********************************************************************/
429 +extern "C" HRESULT DAPI LoadSystemLibraryWithPath(
430 + __in_z LPCWSTR wzModuleName,
431 + __out HMODULE *phModule,
432 + __deref_out_z_opt LPWSTR* psczPath
433 + )
434 +{
435 + HRESULT hr = S_OK;
436 + DWORD cch = 0;
437 + WCHAR wzPath[MAX_PATH] = { };
438 +
439 + cch = ::GetSystemDirectoryW(wzPath, MAX_PATH);
440 + ExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory.");
441 +
442 + if (L'\\' != wzPath[cch - 1])
443 + {
444 + hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1);
445 + ExitOnRootFailure(hr, "Failed to terminate the string with a backslash.");
446 + }
447 +
448 + hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName);
449 + ExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName);
450 +
451 + *phModule = ::LoadLibraryW(wzPath);
452 + ExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName);
453 +
454 + if (psczPath)
455 + {
456 + hr = StrAllocString(psczPath, wzPath, MAX_PATH);
457 + ExitOnFailure(hr, "Failed to copy the path to library.");
458 + }
459 +
460 +LExit:
461 + return hr;
462 +}
src/dutil/dutil.vcxproj new
+300
@@ -0,0 +1,300 @@
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 + <ProjectConfiguration Include="Debug|x64">
15 + <Configuration>Debug</Configuration>
16 + <Platform>x64</Platform>
17 + </ProjectConfiguration>
18 + <ProjectConfiguration Include="Release|x64">
19 + <Configuration>Release</Configuration>
20 + <Platform>x64</Platform>
21 + </ProjectConfiguration>
22 + <ProjectConfiguration Include="Debug|Itanium">
23 + <Configuration>Debug</Configuration>
24 + <Platform>Itanium</Platform>
25 + </ProjectConfiguration>
26 + <ProjectConfiguration Include="Release|Itanium">
27 + <Configuration>Release</Configuration>
28 + <Platform>Itanium</Platform>
29 + </ProjectConfiguration>
30 + <ProjectConfiguration Include="Debug|ARM">
31 + <Configuration>Debug</Configuration>
32 + <Platform>ARM</Platform>
33 + </ProjectConfiguration>
34 + <ProjectConfiguration Include="Release|ARM">
35 + <Configuration>Release</Configuration>
36 + <Platform>ARM</Platform>
37 + </ProjectConfiguration>
38 + </ItemGroup>
39 +
40 + <PropertyGroup Label="Globals">
41 + <ProjectGuid>{1244E671-F108-4334-BA52-8A7517F26ECD}</ProjectGuid>
42 + <ConfigurationType>StaticLibrary</ConfigurationType>
43 + <TargetName>dutil</TargetName>
44 + <MultiTargetLibrary>true</MultiTargetLibrary>
45 + <PlatformToolset>v141_xp</PlatformToolset>
46 + <CharacterSet>MultiByte</CharacterSet>
47 + </PropertyGroup>
48 +
49 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
50 +
51 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
52 + <UseDebugLibraries>true</UseDebugLibraries>
53 + </PropertyGroup>
54 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
55 + <UseDebugLibraries>false</UseDebugLibraries>
56 + <WholeProgramOptimization>true</WholeProgramOptimization>
57 + </PropertyGroup>
58 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
59 + <UseDebugLibraries>true</UseDebugLibraries>
60 + </PropertyGroup>
61 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
62 + <UseDebugLibraries>false</UseDebugLibraries>
63 + <WholeProgramOptimization>true</WholeProgramOptimization>
64 + </PropertyGroup>
65 +
66 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
67 + <ImportGroup Label="ExtensionSettings">
68 + </ImportGroup>
69 +
70 + <ImportGroup Label="Shared">
71 + </ImportGroup>
72 +
73 + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
74 + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
75 + </ImportGroup>
76 + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
77 + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
78 + </ImportGroup>
79 + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
80 + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
81 + </ImportGroup>
82 + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
83 + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
84 + </ImportGroup>
85 +
86 + <PropertyGroup Label="UserMacros" />
87 +
88 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
89 + <LinkIncremental>true</LinkIncremental>
90 + </PropertyGroup>
91 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
92 + <LinkIncremental>true</LinkIncremental>
93 + </PropertyGroup>
94 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
95 + <LinkIncremental>false</LinkIncremental>
96 + </PropertyGroup>
97 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
98 + <LinkIncremental>false</LinkIncremental>
99 + </PropertyGroup>
100 +
101 + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
102 + <ClCompile>
103 + <PrecompiledHeader>Use</PrecompiledHeader>
104 + <WarningLevel>Level3</WarningLevel>
105 + <Optimization>Disabled</Optimization>
106 + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
107 + <AdditionalIncludeDirectories>$(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
108 + <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
109 + <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
110 + </ClCompile>
111 + <Link>
112 + <SubSystem>Console</SubSystem>
113 + <AdditionalDependencies>advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
114 + </Link>
115 + </ItemDefinitionGroup>
116 + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
117 + <ClCompile>
118 + <PrecompiledHeader>Use</PrecompiledHeader>
119 + <WarningLevel>Level3</WarningLevel>
120 + <Optimization>Disabled</Optimization>
121 + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
122 + <AdditionalIncludeDirectories>$(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
123 + <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
124 + <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
125 + </ClCompile>
126 + <Link>
127 + <SubSystem>Console</SubSystem>
128 + <AdditionalDependencies>advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
129 + </Link>
130 + </ItemDefinitionGroup>
131 + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
132 + <ClCompile>
133 + <WarningLevel>Level3</WarningLevel>
134 + <PrecompiledHeader>Use</PrecompiledHeader>
135 + <Optimization>MaxSpeed</Optimization>
136 + <FunctionLevelLinking>true</FunctionLevelLinking>
137 + <IntrinsicFunctions>true</IntrinsicFunctions>
138 + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
139 + <AdditionalIncludeDirectories>$(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
140 + <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
141 + <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
142 + </ClCompile>
143 + <Link>
144 + <SubSystem>Console</SubSystem>
145 + <EnableCOMDATFolding>true</EnableCOMDATFolding>
146 + <OptimizeReferences>true</OptimizeReferences>
147 + <AdditionalDependencies>advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
148 + </Link>
149 + </ItemDefinitionGroup>
150 + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
151 + <ClCompile>
152 + <WarningLevel>Level3</WarningLevel>
153 + <PrecompiledHeader>Use</PrecompiledHeader>
154 + <Optimization>MaxSpeed</Optimization>
155 + <FunctionLevelLinking>true</FunctionLevelLinking>
156 + <IntrinsicFunctions>true</IntrinsicFunctions>
157 + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
158 + <AdditionalIncludeDirectories>$(MSBuildProjectDirectory)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
159 + <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
160 + <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
161 + </ClCompile>
162 + <Link>
163 + <SubSystem>Console</SubSystem>
164 + <EnableCOMDATFolding>true</EnableCOMDATFolding>
165 + <OptimizeReferences>true</OptimizeReferences>
166 + <AdditionalDependencies>advapi32.lib;oleaut32.lib;shell32.lib;ole32.lib;version.lib;activeds.lib;adsiid.lib;crypt32.lib;msi.lib;netapi32.lib;Ws2_32.lib;cabinet.lib;shlwapi.lib;gdiplus.lib;wininet.lib;ESENT.lib;Userenv.lib;Wtsapi32.lib;Comctl32.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
167 + </Link>
168 + </ItemDefinitionGroup>
169 +
170 + <ItemGroup>
171 + <ClCompile Include="acl2util.cpp" />
172 + <ClCompile Include="aclutil.cpp" />
173 + <ClCompile Include="apputil.cpp" />
174 + <ClCompile Include="apuputil.cpp" />
175 + <ClCompile Include="atomutil.cpp" />
176 + <ClCompile Include="butil.cpp" />
177 + <ClCompile Include="buffutil.cpp" />
178 + <ClCompile Include="cabcutil.cpp" />
179 + <ClCompile Include="cabutil.cpp" />
180 + <ClCompile Include="certutil.cpp" />
181 + <ClCompile Include="condutil.cpp" />
182 + <ClCompile Include="conutil.cpp" />
183 + <ClCompile Include="cryputil.cpp" />
184 + <ClCompile Include="dictutil.cpp" />
185 + <ClCompile Include="dirutil.cpp" />
186 + <ClCompile Include="dlutil.cpp" />
187 + <ClCompile Include="dutil.cpp">
188 + <PrecompiledHeader>Create</PrecompiledHeader>
189 + <!-- turn off typedef warning in shlobj.h -->
190 + <DisableSpecificWarnings>4091</DisableSpecificWarnings>
191 + </ClCompile>
192 + <ClCompile Include="eseutil.cpp" />
193 + <ClCompile Include="fileutil.cpp" />
194 + <ClCompile Include="gdiputil.cpp" />
195 + <ClCompile Include="guidutil.cpp" />
196 + <ClCompile Include="iis7util.cpp" />
197 + <ClCompile Include="inetutil.cpp" />
198 + <ClCompile Include="iniutil.cpp" />
199 + <ClCompile Include="jsonutil.cpp" />
200 + <ClCompile Include="locutil.cpp" />
201 + <ClCompile Include="logutil.cpp" />
202 + <ClCompile Include="memutil.cpp" />
203 + <ClCompile Include="metautil.cpp" />
204 + <ClCompile Include="monutil.cpp" />
205 + <ClCompile Include="osutil.cpp">
206 + <!-- turn off deprecation warning -->
207 + <DisableSpecificWarnings>4996</DisableSpecificWarnings>
208 + </ClCompile>
209 + <ClCompile Include="path2utl.cpp" />
210 + <ClCompile Include="pathutil.cpp" />
211 + <ClCompile Include="perfutil.cpp" />
212 + <ClCompile Include="polcutil.cpp" />
213 + <ClCompile Include="proc2utl.cpp" />
214 + <ClCompile Include="proc3utl.cpp" />
215 + <ClCompile Include="procutil.cpp" />
216 + <ClCompile Include="regutil.cpp" />
217 + <ClCompile Include="resrutil.cpp" />
218 + <ClCompile Include="reswutil.cpp" />
219 + <ClCompile Include="rexutil.cpp" />
220 + <ClCompile Include="rmutil.cpp" />
221 + <ClCompile Include="rssutil.cpp" />
222 + <ClCompile Include="sceutil.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
223 + <ClCompile Include="shelutil.cpp" />
224 + <ClCompile Include="sqlutil.cpp" />
225 + <ClCompile Include="srputil.cpp" />
226 + <ClCompile Include="strutil.cpp" />
227 + <ClCompile Include="svcutil.cpp" />
228 + <ClCompile Include="thmutil.cpp" />
229 + <ClCompile Include="timeutil.cpp" />
230 + <ClCompile Include="uncutil.cpp" />
231 + <ClCompile Include="uriutil.cpp" />
232 + <ClCompile Include="userutil.cpp" />
233 + <ClCompile Include="varutil.cpp" />
234 + <ClCompile Include="wiutil.cpp" />
235 + <ClCompile Include="wuautil.cpp" />
236 + <ClCompile Include="xmlutil.cpp" />
237 + </ItemGroup>
238 +
239 + <ItemGroup>
240 + <ClInclude Include="inc\aclutil.h" />
241 + <ClInclude Include="inc\apputil.h" />
242 + <ClInclude Include="inc\apuputil.h" />
243 + <ClInclude Include="inc\atomutil.h" />
244 + <ClInclude Include="inc\buffutil.h" />
245 + <ClInclude Include="inc\butil.h" />
246 + <ClInclude Include="inc\cabcutil.h" />
247 + <ClInclude Include="inc\cabutil.h" />
248 + <ClInclude Include="inc\certutil.h" />
249 + <ClInclude Include="inc\condutil.h" />
250 + <ClInclude Include="inc\conutil.h" />
251 + <ClInclude Include="inc\cryputil.h" />
252 + <ClInclude Include="inc\dictutil.h" />
253 + <ClInclude Include="inc\dirutil.h" />
254 + <ClInclude Include="inc\dlutil.h" />
255 + <ClInclude Include="inc\dutil.h" />
256 + <ClInclude Include="inc\eseutil.h" />
257 + <ClInclude Include="inc\fileutil.h" />
258 + <ClInclude Include="inc\gdiputil.h" />
259 + <ClInclude Include="inc\guidutil.h" />
260 + <ClInclude Include="inc\inetutil.h" />
261 + <ClInclude Include="inc\iniutil.h" />
262 + <ClInclude Include="inc\jsonutil.h" />
263 + <ClInclude Include="inc\locutil.h" />
264 + <ClInclude Include="inc\logutil.h" />
265 + <ClInclude Include="inc\memutil.h" />
266 + <ClInclude Include="inc\metautil.h" />
267 + <ClInclude Include="inc\monutil.h" />
268 + <ClInclude Include="inc\osutil.h" />
269 + <ClInclude Include="inc\pathutil.h" />
270 + <ClInclude Include="inc\perfutil.h" />
271 + <ClInclude Include="inc\polcutil.h" />
272 + <ClInclude Include="inc\procutil.h" />
273 + <ClInclude Include="inc\regutil.h" />
274 + <ClInclude Include="inc\resrutil.h" />
275 + <ClInclude Include="inc\reswutil.h" />
276 + <ClInclude Include="inc\rexutil.h" />
277 + <ClInclude Include="inc\rssutil.h" />
278 + <ClInclude Include="inc\sceutil.h" />
279 + <ClInclude Include="inc\shelutil.h" />
280 + <ClInclude Include="inc\sqlutil.h" />
281 + <ClInclude Include="inc\srputil.h" />
282 + <ClInclude Include="inc\strutil.h" />
283 + <ClInclude Include="inc\svcutil.h" />
284 + <ClInclude Include="inc\thmutil.h" />
285 + <ClInclude Include="inc\timeutil.h" />
286 + <ClInclude Include="inc\uriutil.h" />
287 + <ClInclude Include="inc\userutil.h" />
288 + <ClInclude Include="inc\varutil.h" />
289 + <ClInclude Include="inc\wiutil.h" />
290 + <ClInclude Include="inc\wuautil.h" />
291 + <ClInclude Include="inc\xmlutil.h" />
292 + <ClInclude Include="precomp.h" />
293 + </ItemGroup>
294 +
295 + <ItemGroup>
296 + <None Include="xsd\thmutil.xsd" />
297 + </ItemGroup>
298 +
299 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
300 +</Project>
src/dutil/dutil.vcxproj.filters new
+365
@@ -0,0 +1,365 @@
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;mfcribbon-ms</Extensions>
15 + </Filter>
16 + </ItemGroup>
17 + <ItemGroup>
18 + <ClCompile Include="acl2util.cpp">
19 + <Filter>Source Files</Filter>
20 + </ClCompile>
21 + <ClCompile Include="aclutil.cpp">
22 + <Filter>Source Files</Filter>
23 + </ClCompile>
24 + <ClCompile Include="apputil.cpp">
25 + <Filter>Source Files</Filter>
26 + </ClCompile>
27 + <ClCompile Include="apuputil.cpp">
28 + <Filter>Source Files</Filter>
29 + </ClCompile>
30 + <ClCompile Include="atomutil.cpp">
31 + <Filter>Source Files</Filter>
32 + </ClCompile>
33 + <ClCompile Include="buffutil.cpp">
34 + <Filter>Source Files</Filter>
35 + </ClCompile>
36 + <ClCompile Include="butil.cpp">
37 + <Filter>Source Files</Filter>
38 + </ClCompile>
39 + <ClCompile Include="cabcutil.cpp">
40 + <Filter>Source Files</Filter>
41 + </ClCompile>
42 + <ClCompile Include="cabutil.cpp">
43 + <Filter>Source Files</Filter>
44 + </ClCompile>
45 + <ClCompile Include="certutil.cpp">
46 + <Filter>Source Files</Filter>
47 + </ClCompile>
48 + <ClCompile Include="condutil.cpp">
49 + <Filter>Source Files</Filter>
50 + </ClCompile>
51 + <ClCompile Include="conutil.cpp">
52 + <Filter>Source Files</Filter>
53 + </ClCompile>
54 + <ClCompile Include="cryputil.cpp">
55 + <Filter>Source Files</Filter>
56 + </ClCompile>
57 + <ClCompile Include="dictutil.cpp">
58 + <Filter>Source Files</Filter>
59 + </ClCompile>
60 + <ClCompile Include="dirutil.cpp">
61 + <Filter>Source Files</Filter>
62 + </ClCompile>
63 + <ClCompile Include="dlutil.cpp">
64 + <Filter>Source Files</Filter>
65 + </ClCompile>
66 + <ClCompile Include="dutil.cpp">
67 + <Filter>Source Files</Filter>
68 + </ClCompile>
69 + <ClCompile Include="eseutil.cpp">
70 + <Filter>Source Files</Filter>
71 + </ClCompile>
72 + <ClCompile Include="fileutil.cpp">
73 + <Filter>Source Files</Filter>
74 + </ClCompile>
75 + <ClCompile Include="gdiputil.cpp">
76 + <Filter>Source Files</Filter>
77 + </ClCompile>
78 + <ClCompile Include="guidutil.cpp">
79 + <Filter>Source Files</Filter>
80 + </ClCompile>
81 + <ClCompile Include="iis7util.cpp">
82 + <Filter>Source Files</Filter>
83 + </ClCompile>
84 + <ClCompile Include="inetutil.cpp">
85 + <Filter>Source Files</Filter>
86 + </ClCompile>
87 + <ClCompile Include="jsonutil.cpp">
88 + <Filter>Source Files</Filter>
89 + </ClCompile>
90 + <ClCompile Include="locutil.cpp">
91 + <Filter>Source Files</Filter>
92 + </ClCompile>
93 + <ClCompile Include="logutil.cpp">
94 + <Filter>Source Files</Filter>
95 + </ClCompile>
96 + <ClCompile Include="memutil.cpp">
97 + <Filter>Source Files</Filter>
98 + </ClCompile>
99 + <ClCompile Include="metautil.cpp">
100 + <Filter>Source Files</Filter>
101 + </ClCompile>
102 + <ClCompile Include="monutil.cpp">
103 + <Filter>Source Files</Filter>
104 + </ClCompile>
105 + <ClCompile Include="osutil.cpp">
106 + <Filter>Source Files</Filter>
107 + </ClCompile>
108 + <ClCompile Include="path2utl.cpp">
109 + <Filter>Source Files</Filter>
110 + </ClCompile>
111 + <ClCompile Include="pathutil.cpp">
112 + <Filter>Source Files</Filter>
113 + </ClCompile>
114 + <ClCompile Include="perfutil.cpp">
115 + <Filter>Source Files</Filter>
116 + </ClCompile>
117 + <ClCompile Include="proc2utl.cpp">
118 + <Filter>Source Files</Filter>
119 + </ClCompile>
120 + <ClCompile Include="procutil.cpp">
121 + <Filter>Source Files</Filter>
122 + </ClCompile>
123 + <ClCompile Include="resrutil.cpp">
124 + <Filter>Source Files</Filter>
125 + </ClCompile>
126 + <ClCompile Include="reswutil.cpp">
127 + <Filter>Source Files</Filter>
128 + </ClCompile>
129 + <ClCompile Include="rexutil.cpp">
130 + <Filter>Source Files</Filter>
131 + </ClCompile>
132 + <ClCompile Include="rmutil.cpp">
133 + <Filter>Source Files</Filter>
134 + </ClCompile>
135 + <ClCompile Include="rssutil.cpp">
136 + <Filter>Source Files</Filter>
137 + </ClCompile>
138 + <ClCompile Include="sceutil.cpp">
139 + <Filter>Source Files</Filter>
140 + </ClCompile>
141 + <ClCompile Include="shelutil.cpp">
142 + <Filter>Source Files</Filter>
143 + </ClCompile>
144 + <ClCompile Include="sqlutil.cpp">
145 + <Filter>Source Files</Filter>
146 + </ClCompile>
147 + <ClCompile Include="strutil.cpp">
148 + <Filter>Source Files</Filter>
149 + </ClCompile>
150 + <ClCompile Include="thmutil.cpp">
151 + <Filter>Source Files</Filter>
152 + </ClCompile>
153 + <ClCompile Include="timeutil.cpp">
154 + <Filter>Source Files</Filter>
155 + </ClCompile>
156 + <ClCompile Include="uncutil.cpp">
157 + <Filter>Source Files</Filter>
158 + </ClCompile>
159 + <ClCompile Include="uriutil.cpp">
160 + <Filter>Source Files</Filter>
161 + </ClCompile>
162 + <ClCompile Include="userutil.cpp">
163 + <Filter>Source Files</Filter>
164 + </ClCompile>
165 + <ClCompile Include="varutil.cpp">
166 + <Filter>Source Files</Filter>
167 + </ClCompile>
168 + <ClCompile Include="wiutil.cpp">
169 + <Filter>Source Files</Filter>
170 + </ClCompile>
171 + <ClCompile Include="xmlutil.cpp">
172 + <Filter>Source Files</Filter>
173 + </ClCompile>
174 + <ClCompile Include="svcutil.cpp">
175 + <Filter>Source Files</Filter>
176 + </ClCompile>
177 + <ClCompile Include="regutil.cpp">
178 + <Filter>Source Files</Filter>
179 + </ClCompile>
180 + <ClCompile Include="iniutil.cpp">
181 + <Filter>Source Files</Filter>
182 + </ClCompile>
183 + <ClCompile Include="proc3utl.cpp">
184 + <Filter>Source Files</Filter>
185 + </ClCompile>
186 + <ClCompile Include="wuautil.cpp">
187 + <Filter>Source Files</Filter>
188 + </ClCompile>
189 + <ClCompile Include="srputil.cpp">
190 + <Filter>Source Files</Filter>
191 + </ClCompile>
192 + <ClCompile Include="polcutil.cpp">
193 + <Filter>Source Files</Filter>
194 + </ClCompile>
195 + </ItemGroup>
196 + <ItemGroup>
197 + <ClInclude Include="inc\aclutil.h">
198 + <Filter>Header Files</Filter>
199 + </ClInclude>
200 + <ClInclude Include="inc\apputil.h">
201 + <Filter>Header Files</Filter>
202 + </ClInclude>
203 + <ClInclude Include="inc\apuputil.h">
204 + <Filter>Header Files</Filter>
205 + </ClInclude>
206 + <ClInclude Include="inc\atomutil.h">
207 + <Filter>Header Files</Filter>
208 + </ClInclude>
209 + <ClInclude Include="inc\buffutil.h">
210 + <Filter>Header Files</Filter>
211 + </ClInclude>
212 + <ClInclude Include="inc\butil.h">
213 + <Filter>Header Files</Filter>
214 + </ClInclude>
215 + <ClInclude Include="inc\cabcutil.h">
216 + <Filter>Header Files</Filter>
217 + </ClInclude>
218 + <ClInclude Include="inc\cabutil.h">
219 + <Filter>Header Files</Filter>
220 + </ClInclude>
221 + <ClInclude Include="inc\certutil.h">
222 + <Filter>Header Files</Filter>
223 + </ClInclude>
224 + <ClInclude Include="inc\condutil.h">
225 + <Filter>Header Files</Filter>
226 + </ClInclude>
227 + <ClInclude Include="inc\conutil.h">
228 + <Filter>Header Files</Filter>
229 + </ClInclude>
230 + <ClInclude Include="inc\cryputil.h">
231 + <Filter>Header Files</Filter>
232 + </ClInclude>
233 + <ClInclude Include="inc\dictutil.h">
234 + <Filter>Header Files</Filter>
235 + </ClInclude>
236 + <ClInclude Include="inc\dirutil.h">
237 + <Filter>Header Files</Filter>
238 + </ClInclude>
239 + <ClInclude Include="inc\dlutil.h">
240 + <Filter>Header Files</Filter>
241 + </ClInclude>
242 + <ClInclude Include="inc\dutil.h">
243 + <Filter>Header Files</Filter>
244 + </ClInclude>
245 + <ClInclude Include="inc\eseutil.h">
246 + <Filter>Header Files</Filter>
247 + </ClInclude>
248 + <ClInclude Include="inc\fileutil.h">
249 + <Filter>Header Files</Filter>
250 + </ClInclude>
251 + <ClInclude Include="inc\gdiputil.h">
252 + <Filter>Header Files</Filter>
253 + </ClInclude>
254 + <ClInclude Include="inc\guidutil.h">
255 + <Filter>Header Files</Filter>
256 + </ClInclude>
257 + <ClInclude Include="inc\inetutil.h">
258 + <Filter>Header Files</Filter>
259 + </ClInclude>
260 + <ClInclude Include="inc\jsonutil.h">
261 + <Filter>Header Files</Filter>
262 + </ClInclude>
263 + <ClInclude Include="inc\locutil.h">
264 + <Filter>Header Files</Filter>
265 + </ClInclude>
266 + <ClInclude Include="inc\logutil.h">
267 + <Filter>Header Files</Filter>
268 + </ClInclude>
269 + <ClInclude Include="inc\memutil.h">
270 + <Filter>Header Files</Filter>
271 + </ClInclude>
272 + <ClInclude Include="inc\metautil.h">
273 + <Filter>Header Files</Filter>
274 + </ClInclude>
275 + <ClInclude Include="inc\monutil.h">
276 + <Filter>Header Files</Filter>
277 + </ClInclude>
278 + <ClInclude Include="inc\osutil.h">
279 + <Filter>Header Files</Filter>
280 + </ClInclude>
281 + <ClInclude Include="inc\pathutil.h">
282 + <Filter>Header Files</Filter>
283 + </ClInclude>
284 + <ClInclude Include="inc\perfutil.h">
285 + <Filter>Header Files</Filter>
286 + </ClInclude>
287 + <ClInclude Include="inc\procutil.h">
288 + <Filter>Header Files</Filter>
289 + </ClInclude>
290 + <ClInclude Include="inc\regutil.h">
291 + <Filter>Header Files</Filter>
292 + </ClInclude>
293 + <ClInclude Include="inc\resrutil.h">
294 + <Filter>Header Files</Filter>
295 + </ClInclude>
296 + <ClInclude Include="inc\reswutil.h">
297 + <Filter>Header Files</Filter>
298 + </ClInclude>
299 + <ClInclude Include="inc\rexutil.h">
300 + <Filter>Header Files</Filter>
301 + </ClInclude>
302 + <ClInclude Include="inc\rssutil.h">
303 + <Filter>Header Files</Filter>
304 + </ClInclude>
305 + <ClInclude Include="inc\sceutil.h">
306 + <Filter>Header Files</Filter>
307 + </ClInclude>
308 + <ClInclude Include="inc\shelutil.h">
309 + <Filter>Header Files</Filter>
310 + </ClInclude>
311 + <ClInclude Include="inc\sqlutil.h">
312 + <Filter>Header Files</Filter>
313 + </ClInclude>
314 + <ClInclude Include="inc\strutil.h">
315 + <Filter>Header Files</Filter>
316 + </ClInclude>
317 + <ClInclude Include="inc\thmutil.h">
318 + <Filter>Header Files</Filter>
319 + </ClInclude>
320 + <ClInclude Include="inc\timeutil.h">
321 + <Filter>Header Files</Filter>
322 + </ClInclude>
323 + <ClInclude Include="inc\uncutil.h">
324 + <Filter>Header Files</Filter>
325 + </ClInclude>
326 + <ClInclude Include="inc\uriutil.h">
327 + <Filter>Header Files</Filter>
328 + </ClInclude>
329 + <ClInclude Include="inc\userutil.h">
330 + <Filter>Header Files</Filter>
331 + </ClInclude>
332 + <ClInclude Include="inc\varutil.h">
333 + <Filter>Header Files</Filter>
334 + </ClInclude>
335 + <ClInclude Include="inc\wiutil.h">
336 + <Filter>Header Files</Filter>
337 + </ClInclude>
338 + <ClInclude Include="inc\xmlutil.h">
339 + <Filter>Header Files</Filter>
340 + </ClInclude>
341 + <ClInclude Include="precomp.h">
342 + <Filter>Header Files</Filter>
343 + </ClInclude>
344 + <ClInclude Include="inc\svcutil.h">
345 + <Filter>Header Files</Filter>
346 + </ClInclude>
347 + <ClInclude Include="inc\iniutil.h">
348 + <Filter>Header Files</Filter>
349 + </ClInclude>
350 + <ClInclude Include="inc\wuautil.h">
351 + <Filter>Header Files</Filter>
352 + </ClInclude>
353 + <ClInclude Include="inc\srputil.h">
354 + <Filter>Header Files</Filter>
355 + </ClInclude>
356 + <ClInclude Include="inc\polcutil.h">
357 + <Filter>Header Files</Filter>
358 + </ClInclude>
359 + </ItemGroup>
360 + <ItemGroup>
361 + <None Include="xsd\thmutil.xsd">
362 + <Filter>Header Files</Filter>
363 + </None>
364 + </ItemGroup>
365 +</Project>
\ No newline at end of file
src/dutil/eseutil.cpp new
+1308
@@ -0,0 +1,1308 @@
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 +
5 +struct ESE_QUERY
6 +{
7 + ESE_QUERY_TYPE qtQueryType;
8 + BOOL fIndexRangeSet;
9 +
10 + JET_SESID jsSession;
11 + JET_TABLEID jtTable;
12 +
13 + DWORD dwColumns;
14 + void *pvData[10]; // The data queried for for this column
15 + DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData
16 +};
17 +
18 +// Todo: convert more JET_ERR to HRESULTS here
19 +HRESULT HresultFromJetError(JET_ERR jEr)
20 +{
21 + HRESULT hr = S_OK;
22 +
23 + switch (jEr)
24 + {
25 + case JET_errSuccess:
26 + return S_OK;
27 +
28 + case JET_wrnNyi:
29 + return E_NOTIMPL;
30 + break;
31 +
32 + case JET_errOutOfMemory:
33 + hr = E_OUTOFMEMORY;
34 + break;
35 +
36 + case JET_errInvalidParameter: __fallthrough;
37 + case JET_errInvalidInstance:
38 + hr = E_INVALIDARG;
39 + break;
40 +
41 + case JET_errDatabaseInUse:
42 + hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE);
43 + break;
44 +
45 + case JET_errKeyDuplicate:
46 + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
47 + break;
48 +
49 + case JET_errInvalidSystemPath: __fallthrough;
50 + case JET_errInvalidLogDirectory: __fallthrough;
51 + case JET_errInvalidPath: __fallthrough;
52 + case JET_errDatabaseInvalidPath:
53 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
54 + break;
55 +
56 + case JET_errDatabaseLocked:
57 + hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT);
58 + break;
59 +
60 + case JET_errInvalidDatabase:
61 + hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION);
62 + break;
63 +
64 + case JET_errCallbackNotResolved:
65 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
66 + break;
67 +
68 + case JET_errNoCurrentRecord: __fallthrough;
69 + case JET_errRecordNotFound: __fallthrough;
70 + case JET_errFileNotFound: __fallthrough;
71 + case JET_errObjectNotFound:
72 + hr = E_NOTFOUND;
73 + break;
74 +
75 + case JET_wrnBufferTruncated:
76 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
77 + break;
78 +
79 + case JET_errFileAccessDenied:
80 + hr = E_ACCESSDENIED;
81 + break;
82 +
83 + default:
84 + hr = E_FAIL;
85 + }
86 +
87 + // Log the actual Jet error code so we have record of it before it's morphed into an HRESULT to be compatible with the rest of our code
88 + ExitTrace(hr, "Encountered Jet Error: 0x%08x", jEr);
89 +
90 + return hr;
91 +}
92 +
93 +#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTrace(x, s, __VA_ARGS__); goto LExit; }}
94 +#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }}
95 +
96 +HRESULT DAPI EseBeginSession(
97 + __out JET_INSTANCE *pjiInstance,
98 + __out JET_SESID *pjsSession,
99 + __in_z LPCWSTR pszInstance,
100 + __in_z LPCWSTR pszPath
101 + )
102 +{
103 + HRESULT hr = S_OK;
104 + JET_ERR jEr = JET_errSuccess;
105 + LPSTR pszAnsiInstance = NULL;
106 + LPSTR pszAnsiPath = NULL;
107 +
108 + hr = DirEnsureExists(pszPath, NULL);
109 + ExitOnFailure(hr, "Failed to ensure database directory exists");
110 +
111 + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling,
112 + // likely breaking everyone with unicode characters in their path.
113 + hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP);
114 + ExitOnFailure(hr, "Failed converting instance name to ansi");
115 +
116 + hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP);
117 + ExitOnFailure(hr, "Failed converting session path name to ansi");
118 +
119 + jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance);
120 + ExitOnJetFailure(jEr, hr, "Failed to create instance");
121 +
122 + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath);
123 + ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath);
124 +
125 + // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution)
126 + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath);
127 + ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath);
128 +
129 + jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL);
130 + ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter");
131 +
132 + // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too)
133 +
134 + jEr = JetInit(pjiInstance);
135 + ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance");
136 +
137 + jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL);
138 + ExitOnJetFailure(jEr, hr, "Failed to begin jet session");
139 +
140 +LExit:
141 + ReleaseStr(pszAnsiInstance);
142 + ReleaseStr(pszAnsiPath);
143 +
144 + return hr;
145 +}
146 +
147 +HRESULT DAPI EseEndSession(
148 + __in JET_INSTANCE jiInstance,
149 + __in JET_SESID jsSession
150 + )
151 +{
152 + HRESULT hr = S_OK;
153 + JET_ERR jEr = JET_errSuccess;
154 +
155 + jEr = JetEndSession(jsSession, 0);
156 + ExitOnJetFailure(jEr, hr, "Failed to end jet session");
157 +
158 + jEr = JetTerm(jiInstance);
159 + ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance");
160 +
161 +LExit:
162 + return hr;
163 +}
164 +
165 +// Utility function used by EnsureSchema()
166 +HRESULT AllocColumnCreateStruct(
167 + __in const ESE_TABLE_SCHEMA *ptsSchema,
168 + __deref_out JET_COLUMNCREATE **ppjccColumnCreate
169 + )
170 +{
171 + HRESULT hr = S_OK;
172 + DWORD_PTR i;
173 + size_t cbAllocSize = 0;
174 +
175 + hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize));
176 + ExitOnFailure(hr, "Maximum allocation exceeded.");
177 +
178 + *ppjccColumnCreate = static_cast<JET_COLUMNCREATE*>(MemAlloc(cbAllocSize, TRUE));
179 + ExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database");
180 +
181 + for (i = 0; i < ptsSchema->dwColumns; ++i)
182 + {
183 + (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE);
184 +
185 + hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP);
186 + ExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName);
187 +
188 + (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType;
189 +
190 + if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp)
191 + {
192 + (*ppjccColumnCreate)[i].cbMax = 256;
193 + }
194 + else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp)
195 + {
196 + (*ppjccColumnCreate)[i].cbMax = 2147483648;
197 + (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged
198 + ptsSchema->pcsColumns[i].fNullable = TRUE;
199 + }
200 + else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp)
201 + {
202 + (*ppjccColumnCreate)[i].cbMax = 4;
203 +
204 + if (ptsSchema->pcsColumns[i].fAutoIncrement)
205 + {
206 + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement;
207 + }
208 + }
209 +
210 + if (!(ptsSchema->pcsColumns[i].fNullable))
211 + {
212 + (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL;
213 + }
214 +
215 + (*ppjccColumnCreate)[i].pvDefault = NULL;
216 + (*ppjccColumnCreate)[i].cbDefault = 0;
217 + (*ppjccColumnCreate)[i].cp = 1200;
218 + (*ppjccColumnCreate)[i].columnid = 0;
219 + (*ppjccColumnCreate)[i].err = 0;
220 + }
221 +
222 +LExit:
223 + return hr;
224 +}
225 +
226 +HRESULT FreeColumnCreateStruct(
227 + __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate,
228 + __in DWORD dwColumns
229 + )
230 +{
231 + HRESULT hr = S_OK;
232 + DWORD i;
233 +
234 + for (i = 0; i < dwColumns; ++i)
235 + {
236 + ReleaseStr((pjccColumnCreate[i]).szColumnName);
237 + }
238 +
239 + hr = MemFree(pjccColumnCreate);
240 + ExitOnFailure(hr, "Failed to release core column create struct");
241 +
242 +LExit:
243 + return hr;
244 +}
245 +
246 +// Utility function used by EnsureSchema()
247 +HRESULT AllocIndexCreateStruct(
248 + __in const ESE_TABLE_SCHEMA *ptsSchema,
249 + __deref_out JET_INDEXCREATE **ppjicIndexCreate
250 + )
251 +{
252 + HRESULT hr = S_OK;
253 + LPSTR pszMultiSzKeys = NULL;
254 + LPSTR pszIndexName = NULL;
255 + LPSTR pszTempString = NULL;
256 + BOOL fKeyColumns = FALSE;
257 + DWORD_PTR i;
258 +
259 + for (i=0; i < ptsSchema->dwColumns; ++i)
260 + {
261 + if (ptsSchema->pcsColumns[i].fKey)
262 + {
263 + hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP);
264 + ExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName);
265 +
266 + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0);
267 + ExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString);
268 +
269 + hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0);
270 + ExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString);
271 +
272 + ReleaseNullStr(pszTempString);
273 +
274 + // All question marks will be converted to null characters later; this is just to trick dutil
275 + // into letting us create an ansi, double-null-terminated list of single-null-terminated strings
276 + hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0);
277 + ExitOnFailure(hr, "Failed to append placeholder character to multisz string: %ls", pszMultiSzKeys);
278 +
279 + // Record that at least one key column was found
280 + fKeyColumns = TRUE;
281 + }
282 + }
283 +
284 + // If no key columns were found, don't create an index - just return
285 + if (!fKeyColumns)
286 + {
287 + ExitFunction1(hr = S_OK);
288 + }
289 +
290 + hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP);
291 + ExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName);
292 +
293 + hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0);
294 + ExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName);
295 +
296 + *ppjicIndexCreate = static_cast<JET_INDEXCREATE*>(MemAlloc(sizeof(JET_INDEXCREATE), TRUE));
297 + ExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database");
298 +
299 + // Record the size including both null terminators - the struct requires this
300 + DWORD dwSize = 0;
301 + dwSize = lstrlen(pszMultiSzKeys) + 1; // add 1 to include null character at the end
302 + ExitOnFailure(hr, "Failed to get size of keys string");
303 +
304 + // At this point convert all question marks to null characters
305 + for (i = 0; i < dwSize; ++i)
306 + {
307 + if ('?' == pszMultiSzKeys[i])
308 + {
309 + pszMultiSzKeys[i] = '\0';
310 + }
311 + }
312 +
313 + (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE);
314 + (*ppjicIndexCreate)->szIndexName = pszIndexName;
315 + (*ppjicIndexCreate)->szKey = pszMultiSzKeys;
316 + (*ppjicIndexCreate)->cbKey = dwSize;
317 + (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary;
318 + (*ppjicIndexCreate)->ulDensity = 80;
319 + (*ppjicIndexCreate)->lcid = 1033;
320 + (*ppjicIndexCreate)->pidxunicode = NULL;
321 + (*ppjicIndexCreate)->cbVarSegMac = 0;
322 + (*ppjicIndexCreate)->rgconditionalcolumn = NULL;
323 + (*ppjicIndexCreate)->cConditionalColumn = 0;
324 + (*ppjicIndexCreate)->err = 0;
325 +
326 +LExit:
327 + ReleaseStr(pszTempString);
328 +
329 + return hr;
330 +}
331 +
332 +HRESULT EnsureSchema(
333 + __in JET_DBID jdbDb,
334 + __in JET_SESID jsSession,
335 + __in ESE_DATABASE_SCHEMA *pdsSchema
336 + )
337 +{
338 + HRESULT hr = S_OK;
339 + JET_ERR jEr = JET_errSuccess;
340 + BOOL fTransaction = FALSE;
341 + DWORD dwTable;
342 + DWORD dwColumn;
343 + JET_TABLECREATE jtTableCreate = { };
344 +
345 + // Set parameters which apply to all tables here
346 + jtTableCreate.cbStruct = sizeof(jtTableCreate);
347 + jtTableCreate.ulPages = 100;
348 + jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value"
349 + jtTableCreate.cIndexes = 1;
350 +
351 + hr = EseBeginTransaction(jsSession);
352 + ExitOnFailure(hr, "Failed to begin transaction to create tables");
353 + fTransaction = TRUE;
354 +
355 + for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable)
356 + {
357 + // Don't free this pointer - it's just a shortcut to the current table's name within the struct
358 + LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName;
359 +
360 + // Ensure table exists
361 + hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable);
362 + if (E_NOTFOUND == hr) // if the table is missing, create it
363 + {
364 + // Fill out the JET_TABLECREATE struct
365 + hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP);
366 + ExitOnFailure(hr, "Failed converting table name to ansi");
367 +
368 + hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate);
369 + ExitOnFailure(hr, "Failed to allocate column create struct");
370 +
371 + hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate);
372 + ExitOnFailure(hr, "Failed to allocate index create struct");
373 +
374 + jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns;
375 + jtTableCreate.tableid = NULL;
376 +
377 + // TODO: Investigate why we can't create a table without a key column?
378 + // Actually create the table using our JET_TABLECREATE struct
379 + jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate);
380 + ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName);
381 +
382 + // Record the table ID in our cache
383 + pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid;
384 +
385 + // Record the column IDs in our cache
386 + for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn)
387 + {
388 + pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid;
389 + }
390 +
391 + // Free and NULL things we allocated in this struct
392 + ReleaseNullStr(jtTableCreate.szTableName);
393 +
394 + hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns);
395 + ExitOnFailure(hr, "Failed to free column create struct");
396 + jtTableCreate.rgcolumncreate = NULL;
397 + }
398 + else
399 + {
400 + // If the table already exists, grab the column ids and put them into our cache
401 + for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn)
402 + {
403 + // Don't free this - it's just a shortcut to the current column within the struct
404 + ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]);
405 + ULONG ulColumnSize = 0;
406 + BOOL fNullable = pcsColumn->fNullable;
407 +
408 + // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out!
409 + if (JET_coltypText == pcsColumn->jcColumnType)
410 + {
411 + ulColumnSize = 256;
412 + }
413 + else if (JET_coltypLongText == pcsColumn->jcColumnType)
414 + {
415 + ulColumnSize = 2147483648;
416 + fNullable = TRUE;
417 + }
418 + else if (JET_coltypLong == pcsColumn->jcColumnType)
419 + {
420 + ulColumnSize = 4;
421 + fNullable = TRUE;
422 + }
423 +
424 + hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn);
425 + ExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName);
426 + }
427 + }
428 + }
429 +
430 +LExit:
431 + ReleaseStr(jtTableCreate.szTableName);
432 +
433 + if (NULL != jtTableCreate.rgcolumncreate)
434 + {
435 + // Don't record the HRESULT here or it will override the return value of this function
436 + FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns);
437 + }
438 +
439 + if (fTransaction)
440 + {
441 + EseCommitTransaction(jsSession);
442 + }
443 +
444 + return hr;
445 +}
446 +
447 +// Todo: support overwrite flag? Unfortunately, requires WinXP and up
448 +// Todo: Add version parameter, and a built-in dutil table that stores the version of the database schema on disk - then allow overriding the "migrate to new schema" functionality with a callback
449 +HRESULT DAPI EseEnsureDatabase(
450 + __in JET_SESID jsSession,
451 + __in_z LPCWSTR pszFile,
452 + __in ESE_DATABASE_SCHEMA *pdsSchema,
453 + __out JET_DBID* pjdbDb,
454 + __in BOOL fExclusive,
455 + __in BOOL fReadonly
456 + )
457 +{
458 + HRESULT hr = S_OK;
459 + JET_ERR jEr = JET_errSuccess;
460 + JET_GRBIT jgrOptions = 0;
461 + LPWSTR pszDir = NULL;
462 + LPSTR pszAnsiFile = NULL;
463 +
464 + // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling,
465 + // likely breaking all those with unicode characters in their path.
466 + hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP);
467 + ExitOnFailure(hr, "Failed converting database name to ansi");
468 +
469 + hr = PathGetDirectory(pszFile, &pszDir);
470 + ExitOnFailure(hr, "Failed to get directory that will contain database file");
471 +
472 + hr = DirEnsureExists(pszDir, NULL);
473 + ExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir);
474 +
475 + if (FileExistsEx(pszFile, NULL))
476 + {
477 + if (fReadonly)
478 + {
479 + jgrOptions = jgrOptions | JET_bitDbReadOnly;
480 + }
481 +
482 + jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions);
483 + ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile);
484 +
485 + // This flag doesn't apply to attach, only applies to Open, so only set it after the attach
486 + if (fExclusive)
487 + {
488 + jgrOptions = jgrOptions | JET_bitDbExclusive;
489 + }
490 +
491 + jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions);
492 + ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile);
493 + }
494 + else
495 + {
496 + jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0);
497 + ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile);
498 + }
499 +
500 + hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema);
501 + ExitOnFailure(hr, "Failed to ensure database schema matches expectations");
502 +
503 +LExit:
504 + ReleaseStr(pszDir);
505 + ReleaseStr(pszAnsiFile);
506 +
507 + return hr;
508 +}
509 +
510 +HRESULT DAPI EseCloseDatabase(
511 + __in JET_SESID jsSession,
512 + __in JET_DBID jdbDb
513 + )
514 +{
515 + HRESULT hr = S_OK;
516 + JET_ERR jEr = JET_errSuccess;
517 + JET_GRBIT jgrOptions = 0;
518 +
519 + jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions);
520 + ExitOnJetFailure(jEr, hr, "Failed to close database");
521 +
522 +LExit:
523 + return hr;
524 +}
525 +
526 +HRESULT DAPI EseCreateTable(
527 + __in JET_SESID jsSession,
528 + __in JET_DBID jdbDb,
529 + __in_z LPCWSTR pszTable,
530 + __out JET_TABLEID *pjtTable
531 + )
532 +{
533 + HRESULT hr = S_OK;
534 + JET_ERR jEr = JET_errSuccess;
535 + LPSTR pszAnsiTable = NULL;
536 +
537 + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP);
538 + ExitOnFailure(hr, "Failed converting table name to ansi");
539 +
540 + jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable);
541 + ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable);
542 +
543 +LExit:
544 + ReleaseStr(pszAnsiTable);
545 +
546 + return hr;
547 +}
548 +
549 +HRESULT DAPI EseOpenTable(
550 + __in JET_SESID jsSession,
551 + __in JET_DBID jdbDb,
552 + __in_z LPCWSTR pszTable,
553 + __out JET_TABLEID *pjtTable
554 + )
555 +{
556 + HRESULT hr = S_OK;
557 + JET_ERR jEr = JET_errSuccess;
558 + LPSTR pszAnsiTable = NULL;
559 +
560 + hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP);
561 + ExitOnFailure(hr, "Failed converting table name to ansi");
562 +
563 + jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable);
564 + ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable);
565 +
566 +LExit:
567 + ReleaseStr(pszAnsiTable);
568 +
569 + return hr;
570 +}
571 +
572 +HRESULT DAPI EseCloseTable(
573 + __in JET_SESID jsSession,
574 + __in JET_TABLEID jtTable
575 + )
576 +{
577 + HRESULT hr = S_OK;
578 + JET_ERR jEr = JET_errSuccess;
579 +
580 + jEr = JetCloseTable(jsSession, jtTable);
581 + ExitOnJetFailure(jEr, hr, "Failed to close table");
582 +
583 +LExit:
584 + return hr;
585 +}
586 +
587 +HRESULT DAPI EseEnsureColumn(
588 + __in JET_SESID jsSession,
589 + __in JET_TABLEID jtTable,
590 + __in_z LPCWSTR pszColumnName,
591 + __in JET_COLTYP jcColumnType,
592 + __in ULONG ulColumnSize,
593 + __in BOOL fFixed,
594 + __in BOOL fNullable,
595 + __out_opt JET_COLUMNID *pjcColumn
596 + )
597 +{
598 + HRESULT hr = S_OK;
599 + JET_ERR jEr = JET_errSuccess;
600 + LPSTR pszAnsiColumnName = NULL;
601 + JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) };
602 + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) };
603 +
604 + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP);
605 + ExitOnFailure(hr, "Failed converting column name to ansi");
606 +
607 + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
608 + if (JET_errSuccess == jEr)
609 + {
610 + // Return the found columnID
611 + if (NULL != pjcColumn)
612 + {
613 + *pjcColumn = jcdTempBase.columnid;
614 + }
615 +
616 + ExitFunction1(hr = S_OK);
617 + }
618 + else if (JET_errColumnNotFound == jEr)
619 + {
620 + jEr = JET_errSuccess;
621 + }
622 + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName);
623 +
624 + jcdColumnDef.columnid = 0;
625 + jcdColumnDef.coltyp = jcColumnType;
626 + jcdColumnDef.wCountry = 0;
627 + jcdColumnDef.langid = 0;
628 + jcdColumnDef.cp = 1200;
629 + jcdColumnDef.wCollate = 0;
630 + jcdColumnDef.cbMax = ulColumnSize;
631 + jcdColumnDef.grbit = 0;
632 +
633 + if (fFixed)
634 + {
635 + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed;
636 + }
637 + if (!fNullable)
638 + {
639 + jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL;
640 + }
641 +
642 + jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn);
643 + ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName);
644 +
645 +LExit:
646 + ReleaseStr(pszAnsiColumnName);
647 +
648 + return hr;
649 +}
650 +
651 +HRESULT DAPI EseGetColumn(
652 + __in JET_SESID jsSession,
653 + __in JET_TABLEID jtTable,
654 + __in_z LPCWSTR pszColumnName,
655 + __out JET_COLUMNID *pjcColumn
656 + )
657 +{
658 + HRESULT hr = S_OK;
659 + JET_ERR jEr = JET_errSuccess;
660 + LPSTR pszAnsiColumnName = NULL;
661 + JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) };
662 +
663 + hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP);
664 + ExitOnFailure(hr, "Failed converting column name to ansi");
665 +
666 + jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
667 + if (JET_errSuccess == jEr)
668 + {
669 + // Return the found columnID
670 + if (NULL != pjcColumn)
671 + {
672 + *pjcColumn = jcdTempBase.columnid;
673 + }
674 +
675 + ExitFunction1(hr = S_OK);
676 + }
677 + ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName);
678 +
679 +LExit:
680 + ReleaseStr(pszAnsiColumnName);
681 +
682 + return hr;
683 +}
684 +
685 +HRESULT DAPI EseMoveCursor(
686 + __in JET_SESID jsSession,
687 + __in JET_TABLEID jtTable,
688 + __in LONG lRow
689 + )
690 +{
691 + HRESULT hr = S_OK;
692 + JET_ERR jEr = JET_errSuccess;
693 +
694 + jEr = JetMove(jsSession, jtTable, lRow, 0);
695 + ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow);
696 +
697 +LExit:
698 + return hr;
699 +}
700 +
701 +HRESULT DAPI EseDeleteRow(
702 + __in JET_SESID jsSession,
703 + __in JET_TABLEID jtTable
704 + )
705 +{
706 + HRESULT hr = S_OK;
707 + JET_ERR jEr = JET_errSuccess;
708 +
709 + jEr = JetDelete(jsSession, jtTable);
710 + ExitOnJetFailure(jEr, hr, "Failed to delete row");
711 +
712 +LExit:
713 + return hr;
714 +}
715 +
716 +HRESULT DAPI EseBeginTransaction(
717 + __in JET_SESID jsSession
718 + )
719 +{
720 + HRESULT hr = S_OK;
721 + JET_ERR jEr = JET_errSuccess;
722 +
723 + jEr = JetBeginTransaction(jsSession);
724 + ExitOnJetFailure(jEr, hr, "Failed to begin transaction");
725 +
726 +LExit:
727 + return hr;
728 +}
729 +
730 +HRESULT DAPI EseRollbackTransaction(
731 + __in JET_SESID jsSession,
732 + __in BOOL fAll
733 + )
734 +{
735 + HRESULT hr = S_OK;
736 + JET_ERR jEr = JET_errSuccess;
737 +
738 + jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0);
739 + ExitOnJetFailure(jEr, hr, "Failed to rollback transaction");
740 +
741 +LExit:
742 + return hr;
743 +}
744 +
745 +HRESULT DAPI EseCommitTransaction(
746 + __in JET_SESID jsSession
747 + )
748 +{
749 + HRESULT hr = S_OK;
750 + JET_ERR jEr = JET_errSuccess;
751 +
752 + jEr = JetCommitTransaction(jsSession, 0);
753 + ExitOnJetFailure(jEr, hr, "Failed to commit transaction");
754 +
755 +LExit:
756 + return hr;
757 +}
758 +
759 +HRESULT DAPI EsePrepareUpdate(
760 + __in JET_SESID jsSession,
761 + __in JET_TABLEID jtTable,
762 + __in ULONG ulPrep
763 + )
764 +{
765 + HRESULT hr = S_OK;
766 + JET_ERR jEr = JET_errSuccess;
767 +
768 + jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep);
769 + ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep);
770 +
771 +LExit:
772 + return hr;
773 +}
774 +
775 +HRESULT DAPI EseFinishUpdate(
776 + __in JET_SESID jsSession,
777 + __in JET_TABLEID jtTable,
778 + __in BOOL fSeekToInsertedRecord
779 + )
780 +{
781 + HRESULT hr = S_OK;
782 + JET_ERR jEr = JET_errSuccess;
783 + unsigned char rgbBookmark[JET_cbBookmarkMost + 1];
784 + DWORD cbBookmark;
785 +
786 + if (fSeekToInsertedRecord)
787 + {
788 + jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark);
789 + ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark");
790 +
791 + jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark);
792 + ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark");
793 + }
794 + else
795 + {
796 + jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL);
797 + ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)");
798 + }
799 +
800 +LExit:
801 + // If we fail, the caller won't expect that the update wasn't finished, so we'll cancel their entire update to leave them in a good state
802 + if (FAILED(hr))
803 + {
804 + JetPrepareUpdate(jsSession, jtTable, JET_prepCancel);
805 + }
806 +
807 + return hr;
808 +}
809 +
810 +HRESULT DAPI EseSetColumnBinary(
811 + __in JET_SESID jsSession,
812 + __in ESE_TABLE_SCHEMA tsTable,
813 + __in DWORD dwColumn,
814 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
815 + __in SIZE_T cbBuffer
816 + )
817 +{
818 + HRESULT hr = S_OK;
819 + JET_ERR jEr = JET_errSuccess;
820 +
821 + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast<unsigned long>(cbBuffer), 0, NULL);
822 + ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database");
823 +
824 +LExit:
825 + return hr;
826 +}
827 +
828 +HRESULT DAPI EseSetColumnDword(
829 + __in JET_SESID jsSession,
830 + __in ESE_TABLE_SCHEMA tsTable,
831 + __in DWORD dwColumn,
832 + __in DWORD dwValue
833 + )
834 +{
835 + HRESULT hr = S_OK;
836 + JET_ERR jEr = JET_errSuccess;
837 +
838 + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL);
839 + ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue);
840 +
841 +LExit:
842 + return hr;
843 +}
844 +
845 +HRESULT DAPI EseSetColumnBool(
846 + __in JET_SESID jsSession,
847 + __in ESE_TABLE_SCHEMA tsTable,
848 + __in DWORD dwColumn,
849 + __in BOOL fValue
850 + )
851 +{
852 + HRESULT hr = S_OK;
853 + JET_ERR jEr = JET_errSuccess;
854 + BYTE bValue = fValue ? 0xFF : 0x00;
855 +
856 + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL);
857 + ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database");
858 +
859 +LExit:
860 + return hr;
861 +}
862 +
863 +HRESULT DAPI EseSetColumnString(
864 + __in JET_SESID jsSession,
865 + __in ESE_TABLE_SCHEMA tsTable,
866 + __in DWORD dwColumn,
867 + __in_z LPCWSTR pwzValue
868 + )
869 +{
870 + HRESULT hr = S_OK;
871 + JET_ERR jEr = JET_errSuccess;
872 + ULONG cbValueSize = static_cast<ULONG>((wcslen(pwzValue) + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes
873 +
874 + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL);
875 + ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue);
876 +
877 +LExit:
878 + return hr;
879 +}
880 +
881 +HRESULT DAPI EseSetColumnEmpty(
882 + __in JET_SESID jsSession,
883 + __in ESE_TABLE_SCHEMA tsTable,
884 + __in DWORD dwColumn
885 + )
886 +{
887 + HRESULT hr = S_OK;
888 + JET_ERR jEr = JET_errSuccess;
889 +
890 + jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL);
891 + ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database");
892 +
893 +LExit:
894 + return hr;
895 +}
896 +
897 +HRESULT DAPI EseGetColumnBinary(
898 + __in JET_SESID jsSession,
899 + __in ESE_TABLE_SCHEMA tsTable,
900 + __in DWORD dwColumn,
901 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
902 + __inout SIZE_T* piBuffer
903 + )
904 +{
905 + HRESULT hr = S_OK;
906 + JET_ERR jEr = JET_errSuccess;
907 + ULONG ulActualSize = 0;
908 +
909 + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL);
910 + if (JET_wrnBufferTruncated == jEr)
911 + {
912 + jEr = JET_errSuccess;
913 + }
914 + ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record");
915 +
916 + if (NULL == *ppbBuffer)
917 + {
918 + *ppbBuffer = reinterpret_cast<BYTE *>(MemAlloc(ulActualSize, FALSE));
919 + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column");
920 + }
921 + else
922 + {
923 + *ppbBuffer = reinterpret_cast<BYTE *>(MemReAlloc(*ppbBuffer, ulActualSize, FALSE));
924 + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column");
925 + }
926 +
927 + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL);
928 + ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record");
929 +
930 + *piBuffer = static_cast<SIZE_T>(ulActualSize);
931 +
932 +LExit:
933 + if (FAILED(hr))
934 + {
935 + ReleaseNullMem(*ppbBuffer);
936 + }
937 +
938 + return hr;
939 +}
940 +
941 +HRESULT DAPI EseGetColumnDword(
942 + __in JET_SESID jsSession,
943 + __in ESE_TABLE_SCHEMA tsTable,
944 + __in DWORD dwColumn,
945 + __out DWORD *pdwValue
946 + )
947 +{
948 + HRESULT hr = S_OK;
949 + JET_ERR jEr = JET_errSuccess;
950 +
951 + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL);
952 + ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record");
953 +
954 +LExit:
955 + return hr;
956 +}
957 +
958 +HRESULT DAPI EseGetColumnBool(
959 + __in JET_SESID jsSession,
960 + __in ESE_TABLE_SCHEMA tsTable,
961 + __in DWORD dwColumn,
962 + __out BOOL *pfValue
963 + )
964 +{
965 + HRESULT hr = S_OK;
966 + JET_ERR jEr = JET_errSuccess;
967 + BYTE bValue = 0;
968 +
969 + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL);
970 + ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record");
971 +
972 + if (bValue == 0)
973 + {
974 + *pfValue = FALSE;
975 + }
976 + else
977 + {
978 + *pfValue = TRUE;
979 + }
980 +
981 +LExit:
982 + return hr;
983 +}
984 +
985 +HRESULT DAPI EseGetColumnString(
986 + __in JET_SESID jsSession,
987 + __in ESE_TABLE_SCHEMA tsTable,
988 + __in DWORD dwColumn,
989 + __out LPWSTR *ppszValue
990 + )
991 +{
992 + HRESULT hr = S_OK;
993 + JET_ERR jEr = JET_errSuccess;
994 + ULONG ulActualSize = 0;
995 +
996 + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL);
997 + if (JET_wrnBufferTruncated == jEr)
998 + {
999 + jEr = JET_errSuccess;
1000 + }
1001 + ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record");
1002 +
1003 + hr = StrAlloc(ppszValue, ulActualSize);
1004 + ExitOnFailure(hr, "Failed to allocate string while retrieving column value");
1005 +
1006 + jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL);
1007 + ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record");
1008 +
1009 +LExit:
1010 + return hr;
1011 +}
1012 +
1013 +HRESULT DAPI EseBeginQuery(
1014 + __in JET_SESID jsSession,
1015 + __in JET_TABLEID jtTable,
1016 + __in ESE_QUERY_TYPE qtQueryType,
1017 + __out ESE_QUERY_HANDLE *peqhHandle
1018 + )
1019 +{
1020 + UNREFERENCED_PARAMETER(jsSession);
1021 + UNREFERENCED_PARAMETER(jtTable);
1022 +
1023 + HRESULT hr = S_OK;
1024 +
1025 + *peqhHandle = static_cast<ESE_QUERY*>(MemAlloc(sizeof(ESE_QUERY), TRUE));
1026 + ExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query");
1027 +
1028 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(*peqhHandle);
1029 + peqHandle->qtQueryType = qtQueryType;
1030 + peqHandle->jsSession = jsSession;
1031 + peqHandle->jtTable = jtTable;
1032 +
1033 +LExit:
1034 + return hr;
1035 +}
1036 +
1037 +// Utility function used by other functions to set a query column
1038 +HRESULT DAPI SetQueryColumn(
1039 + __in ESE_QUERY_HANDLE eqhHandle,
1040 + __in_bcount(cbData) const void *pvData,
1041 + __in DWORD cbData,
1042 + __in JET_GRBIT jGrb
1043 + )
1044 +{
1045 + HRESULT hr = S_OK;
1046 + JET_ERR jEr = JET_errSuccess;
1047 +
1048 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1049 +
1050 + if (peqHandle->dwColumns == countof(peqHandle->pvData))
1051 + {
1052 + hr = E_NOTIMPL;
1053 + ExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData));
1054 + }
1055 +
1056 + if (0 == peqHandle->dwColumns) // If it's the first column, start a new key
1057 + {
1058 + jGrb = jGrb | JET_bitNewKey;
1059 + }
1060 +
1061 + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb);
1062 + ExitOnJetFailure(jEr, hr, "Failed to begin new query");
1063 +
1064 + // If the query is wildcard, setup the cached copy of pvData
1065 + if (ESE_QUERY_EXACT != peqHandle->qtQueryType)
1066 + {
1067 + peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE);
1068 + ExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory");
1069 +
1070 + memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData);
1071 +
1072 + peqHandle->cbData[peqHandle->dwColumns] = cbData;
1073 + }
1074 +
1075 + // Increment the number of total columns
1076 + ++peqHandle->dwColumns;
1077 +
1078 +LExit:
1079 + return hr;
1080 +}
1081 +
1082 +HRESULT DAPI EseSetQueryColumnBinary(
1083 + __in ESE_QUERY_HANDLE eqhHandle,
1084 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
1085 + __in SIZE_T cbBuffer,
1086 + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
1087 + )
1088 +{
1089 + HRESULT hr = S_OK;
1090 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1091 + JET_GRBIT jGrb = 0;
1092 +
1093 + if (cbBuffer > DWORD_MAX)
1094 + {
1095 + ExitFunction1(hr = E_INVALIDARG);
1096 + }
1097 +
1098 + if (fFinal)
1099 + {
1100 + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1101 + {
1102 + jGrb = jGrb | JET_bitFullColumnStartLimit;
1103 + }
1104 + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1105 + {
1106 + jGrb = jGrb | JET_bitFullColumnEndLimit;
1107 + }
1108 + }
1109 +
1110 + hr = SetQueryColumn(eqhHandle, reinterpret_cast<const void *>(pbBuffer), static_cast<DWORD>(cbBuffer), jGrb);
1111 + ExitOnFailure(hr, "Failed to set value of query colum (as binary) to:");
1112 +
1113 +LExit:
1114 + return hr;
1115 +}
1116 +
1117 +HRESULT DAPI EseSetQueryColumnDword(
1118 + __in ESE_QUERY_HANDLE eqhHandle,
1119 + __in DWORD dwData,
1120 + __in BOOL fFinal
1121 + )
1122 +{
1123 + HRESULT hr = S_OK;
1124 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1125 + JET_GRBIT jGrb = 0;
1126 +
1127 + if (fFinal)
1128 + {
1129 + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1130 + {
1131 + jGrb = jGrb | JET_bitFullColumnStartLimit;
1132 + }
1133 + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1134 + {
1135 + jGrb = jGrb | JET_bitFullColumnEndLimit;
1136 + }
1137 + }
1138 +
1139 + hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb);
1140 + ExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData);
1141 +
1142 +LExit:
1143 + return hr;
1144 +}
1145 +
1146 +HRESULT DAPI EseSetQueryColumnBool(
1147 + __in ESE_QUERY_HANDLE eqhHandle,
1148 + __in BOOL fValue,
1149 + __in BOOL fFinal
1150 + )
1151 +{
1152 + HRESULT hr = S_OK;
1153 + BYTE bByte = fValue ? 0xFF : 0x00;
1154 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1155 + JET_GRBIT jGrb = 0;
1156 +
1157 + if (fFinal)
1158 + {
1159 + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1160 + {
1161 + jGrb = jGrb | JET_bitFullColumnStartLimit;
1162 + }
1163 + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1164 + {
1165 + jGrb = jGrb | JET_bitFullColumnEndLimit;
1166 + }
1167 + }
1168 +
1169 + hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb);
1170 + ExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE");
1171 +
1172 +LExit:
1173 + return hr;
1174 +}
1175 +
1176 +HRESULT DAPI EseSetQueryColumnString(
1177 + __in ESE_QUERY_HANDLE eqhHandle,
1178 + __in_z LPCWSTR pszString,
1179 + __in BOOL fFinal
1180 + )
1181 +{
1182 + HRESULT hr = S_OK;
1183 + DWORD dwStringSize = 0;
1184 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1185 + JET_GRBIT jGrb = 0;
1186 +
1187 + dwStringSize = sizeof(WCHAR) * (lstrlenW(pszString) + 1); // Add 1 for null terminator
1188 +
1189 +
1190 + if (fFinal)
1191 + {
1192 + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1193 + {
1194 + jGrb = jGrb | JET_bitFullColumnStartLimit;
1195 + }
1196 + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1197 + {
1198 + jGrb = jGrb | JET_bitFullColumnEndLimit;
1199 + }
1200 + }
1201 +
1202 + hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb);
1203 + ExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString);
1204 +
1205 +LExit:
1206 + return hr;
1207 +}
1208 +
1209 +HRESULT DAPI EseFinishQuery(
1210 + __in ESE_QUERY_HANDLE eqhHandle
1211 + )
1212 +{
1213 + HRESULT hr = S_OK;
1214 + JET_ERR jEr = JET_errSuccess;
1215 +
1216 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1217 +
1218 + if (peqHandle->fIndexRangeSet)
1219 + {
1220 + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove);
1221 + ExitOnJetFailure(jEr, hr, "Failed to release index range");
1222 +
1223 + peqHandle->fIndexRangeSet = FALSE;
1224 + }
1225 +
1226 + for (int i=0; i < countof(peqHandle->pvData); ++i)
1227 + {
1228 + ReleaseMem(peqHandle->pvData[i]);
1229 + }
1230 +
1231 + ReleaseMem(peqHandle);
1232 +
1233 +LExit:
1234 + return hr;
1235 +}
1236 +
1237 +HRESULT DAPI EseRunQuery(
1238 + __in ESE_QUERY_HANDLE eqhHandle
1239 + )
1240 +{
1241 + HRESULT hr = S_OK;
1242 + JET_ERR jEr = JET_errSuccess;
1243 + JET_GRBIT jGrb = 0;
1244 + JET_GRBIT jGrbSeekType = 0;
1245 + DWORD i;
1246 +
1247 + ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1248 +
1249 + if (ESE_QUERY_EXACT == peqHandle->qtQueryType)
1250 + {
1251 + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ);
1252 + ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table");
1253 + }
1254 + else
1255 + {
1256 + if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1257 + {
1258 + jGrbSeekType = JET_bitSeekGE;
1259 + }
1260 + else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1261 + {
1262 + jGrbSeekType = JET_bitSeekLE;
1263 + }
1264 +
1265 + jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType);
1266 + if (jEr == JET_wrnSeekNotEqual)
1267 + {
1268 + jEr = JET_errSuccess;
1269 + }
1270 +
1271 + // At this point we've already set our cursor to the beginning of the range of records to select.
1272 + // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange()
1273 + // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx
1274 + for (i = 0; i < peqHandle->dwColumns; ++i)
1275 + {
1276 + if (i == 0)
1277 + {
1278 + jGrb = JET_bitNewKey;
1279 + }
1280 + else
1281 + {
1282 + jGrb = 0;
1283 + }
1284 +
1285 + // On the last iteration
1286 + if (i == peqHandle->dwColumns - 1)
1287 + {
1288 + jGrb |= JET_bitFullColumnEndLimit;
1289 + }
1290 +
1291 + jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb);
1292 + ExitOnJetFailure(jEr, hr, "Failed to begin new query");
1293 + }
1294 +
1295 + jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit);
1296 + ExitOnJetFailure(jEr, hr, "Failed to set index range");
1297 +
1298 + peqHandle->fIndexRangeSet = TRUE;
1299 +
1300 + // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does)
1301 + // So, let's check if there is a current record before returning (by reading the first byte of one).
1302 + jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0);
1303 + ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query");
1304 + }
1305 +
1306 +LExit:
1307 + return hr;
1308 +}
src/dutil/fileutil.cpp new
+1860
@@ -0,0 +1,1860 @@
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 +
5 +// constants
6 +
7 +const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF};
8 +const BYTE UTF16BOM[] = {0xFF, 0xFE};
9 +
10 +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
11 +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations";
12 +
13 +/*******************************************************************
14 + FileFromPath - returns a pointer to the file part of the path
15 +
16 +********************************************************************/
17 +extern "C" LPWSTR DAPI FileFromPath(
18 + __in LPCWSTR wzPath
19 + )
20 +{
21 + if (!wzPath)
22 + return NULL;
23 +
24 + LPWSTR wzFile = const_cast<LPWSTR>(wzPath);
25 + for (LPWSTR wz = wzFile; *wz; ++wz)
26 + {
27 + // valid delineators
28 + // \ => Windows path
29 + // / => unix and URL path
30 + // : => relative path from mapped root
31 + if (L'\\' == *wz || L'/' == *wz || L':' == *wz)
32 + wzFile = wz + 1;
33 + }
34 +
35 + return wzFile;
36 +}
37 +
38 +
39 +/*******************************************************************
40 + FileResolvePath - gets the full path to a file resolving environment
41 + variables along the way.
42 +
43 +********************************************************************/
44 +extern "C" HRESULT DAPI FileResolvePath(
45 + __in LPCWSTR wzRelativePath,
46 + __out LPWSTR *ppwzFullPath
47 + )
48 +{
49 + Assert(wzRelativePath && *wzRelativePath);
50 +
51 + HRESULT hr = S_OK;
52 + DWORD cch = 0;
53 + LPWSTR pwzExpandedPath = NULL;
54 + DWORD cchExpandedPath = 0;
55 +
56 + LPWSTR pwzFullPath = NULL;
57 + DWORD cchFullPath = 0;
58 +
59 + LPWSTR wzFileName = NULL;
60 +
61 + //
62 + // First, expand any environment variables.
63 + //
64 + cchExpandedPath = MAX_PATH;
65 + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath);
66 + ExitOnFailure(hr, "Failed to allocate space for expanded path.");
67 +
68 + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath);
69 + if (0 == cch)
70 + {
71 + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
72 + }
73 + else if (cchExpandedPath < cch)
74 + {
75 + cchExpandedPath = cch;
76 + hr = StrAlloc(&pwzExpandedPath, cchExpandedPath);
77 + ExitOnFailure(hr, "Failed to re-allocate more space for expanded path.");
78 +
79 + cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath);
80 + if (0 == cch)
81 + {
82 + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
83 + }
84 + else if (cchExpandedPath < cch)
85 + {
86 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
87 + ExitOnRootFailure(hr, "Failed to allocate buffer for expanded path.");
88 + }
89 + }
90 +
91 + //
92 + // Second, get the full path.
93 + //
94 + cchFullPath = MAX_PATH;
95 + hr = StrAlloc(&pwzFullPath, cchFullPath);
96 + ExitOnFailure(hr, "Failed to allocate space for full path.");
97 +
98 + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName);
99 + if (0 == cch)
100 + {
101 + ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath);
102 + }
103 + else if (cchFullPath < cch)
104 + {
105 + cchFullPath = cch;
106 + hr = StrAlloc(&pwzFullPath, cchFullPath);
107 + ExitOnFailure(hr, "Failed to re-allocate more space for full path.");
108 +
109 + cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName);
110 + if (0 == cch)
111 + {
112 + ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath);
113 + }
114 + else if (cchFullPath < cch)
115 + {
116 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
117 + ExitOnRootFailure(hr, "Failed to allocate buffer for full path.");
118 + }
119 + }
120 +
121 + *ppwzFullPath = pwzFullPath;
122 + pwzFullPath = NULL;
123 +
124 +LExit:
125 + ReleaseStr(pwzFullPath);
126 + ReleaseStr(pwzExpandedPath);
127 +
128 + return hr;
129 +}
130 +
131 +
132 +/*******************************************************************
133 +FileStripExtension - Strip extension from filename
134 +********************************************************************/
135 +extern "C" HRESULT DAPI FileStripExtension(
136 +__in LPCWSTR wzFileName,
137 +__out LPWSTR *ppwzFileNameNoExtension
138 +)
139 +{
140 + Assert(wzFileName && *wzFileName);
141 +
142 + HRESULT hr = S_OK;
143 +
144 + SIZE_T cchFileName = wcslen(wzFileName);
145 +
146 + LPWSTR pwzFileNameNoExtension = NULL;
147 + DWORD cchFileNameNoExtension = 0;
148 +
149 + // Filename without extension can not be longer than _MAX_FNAME
150 + // Filename without extension should also not be longer than filename itself
151 + if (_MAX_FNAME > cchFileName)
152 + {
153 + cchFileNameNoExtension = (DWORD) cchFileName;
154 + }
155 + else
156 + {
157 + cchFileNameNoExtension = _MAX_FNAME;
158 + }
159 +
160 + hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension);
161 + ExitOnFailure(hr, "failed to allocate space for File Name without extension");
162 +
163 + // _wsplitpath_s can handle drive/path/filename/extension
164 + errno_t err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL);
165 + if (0 != err)
166 + {
167 + hr = E_INVALIDARG;
168 + ExitOnFailure(hr, "failed to parse filename: %ls", wzFileName);
169 + }
170 +
171 + *ppwzFileNameNoExtension = pwzFileNameNoExtension;
172 + pwzFileNameNoExtension = NULL;
173 +
174 +LExit:
175 + ReleaseStr(pwzFileNameNoExtension);
176 +
177 + return hr;
178 +}
179 +
180 +
181 +/*******************************************************************
182 +FileChangeExtension - Changes the extension of a filename
183 +********************************************************************/
184 +extern "C" HRESULT DAPI FileChangeExtension(
185 + __in LPCWSTR wzFileName,
186 + __in LPCWSTR wzNewExtension,
187 + __out LPWSTR *ppwzFileNameNewExtension
188 + )
189 +{
190 + Assert(wzFileName && *wzFileName);
191 +
192 + HRESULT hr = S_OK;
193 + LPWSTR sczFileName = NULL;
194 +
195 + hr = FileStripExtension(wzFileName, &sczFileName);
196 + ExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName);
197 +
198 + hr = StrAllocConcat(&sczFileName, wzNewExtension, 0);
199 + ExitOnFailure(hr, "Failed to add new extension.");
200 +
201 + *ppwzFileNameNewExtension = sczFileName;
202 + sczFileName = NULL;
203 +
204 +LExit:
205 + ReleaseStr(sczFileName);
206 +
207 + return hr;
208 +}
209 +
210 +
211 +/*******************************************************************
212 +FileAddSuffixToBaseName - Adds a suffix the base portion of a file
213 +name; e.g., file.ext to fileSuffix.ext.
214 +********************************************************************/
215 +extern "C" HRESULT DAPI FileAddSuffixToBaseName(
216 + __in_z LPCWSTR wzFileName,
217 + __in_z LPCWSTR wzSuffix,
218 + __out_z LPWSTR* psczNewFileName
219 + )
220 +{
221 + Assert(wzFileName && *wzFileName);
222 +
223 + HRESULT hr = S_OK;
224 + LPWSTR sczNewFileName = NULL;
225 +
226 + LPCWSTR wzExtension = wzFileName + lstrlenW(wzFileName);
227 + while (wzFileName < wzExtension && L'.' != *wzExtension)
228 + {
229 + --wzExtension;
230 + }
231 +
232 + if (wzFileName < wzExtension)
233 + {
234 + // found an extension so add the suffix before it
235 + hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast<int>(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension);
236 + }
237 + else
238 + {
239 + // no extension, so add the suffix at the end of the whole name
240 + hr = StrAllocString(&sczNewFileName, wzFileName, 0);
241 + ExitOnFailure(hr, "Failed to allocate new file name.");
242 +
243 + hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0);
244 + }
245 + ExitOnFailure(hr, "Failed to allocate new file name with suffix.");
246 +
247 + *psczNewFileName = sczNewFileName;
248 + sczNewFileName = NULL;
249 +
250 +LExit:
251 + ReleaseStr(sczNewFileName);
252 +
253 + return hr;
254 +}
255 +
256 +
257 +/*******************************************************************
258 + FileVersion
259 +
260 +********************************************************************/
261 +extern "C" HRESULT DAPI FileVersion(
262 + __in LPCWSTR wzFilename,
263 + __out DWORD *pdwVerMajor,
264 + __out DWORD* pdwVerMinor
265 + )
266 +{
267 + HRESULT hr = S_OK;
268 +
269 + DWORD dwHandle = 0;
270 + UINT cbVerBuffer = 0;
271 + LPVOID pVerBuffer = NULL;
272 + VS_FIXEDFILEINFO* pvsFileInfo = NULL;
273 + UINT cbFileInfo = 0;
274 +
275 + if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle)))
276 + {
277 + ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename);
278 + }
279 +
280 + pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer);
281 + ExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename);
282 +
283 + if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer))
284 + {
285 + ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename);
286 + }
287 +
288 + if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo))
289 + {
290 + ExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename);
291 + }
292 +
293 + *pdwVerMajor = pvsFileInfo->dwFileVersionMS;
294 + *pdwVerMinor = pvsFileInfo->dwFileVersionLS;
295 +
296 +LExit:
297 + if (pVerBuffer)
298 + {
299 + ::GlobalFree(pVerBuffer);
300 + }
301 + return hr;
302 +}
303 +
304 +
305 +/*******************************************************************
306 + FileVersionFromString
307 +
308 +*******************************************************************/
309 +extern "C" HRESULT DAPI FileVersionFromString(
310 + __in LPCWSTR wzVersion,
311 + __out DWORD* pdwVerMajor,
312 + __out DWORD* pdwVerMinor
313 + )
314 +{
315 + Assert(pdwVerMajor && pdwVerMinor);
316 +
317 + HRESULT hr = S_OK;
318 + LPCWSTR pwz = wzVersion;
319 + DWORD dw;
320 +
321 + *pdwVerMajor = 0;
322 + *pdwVerMinor = 0;
323 +
324 + if ((L'v' == *pwz) || (L'V' == *pwz))
325 + {
326 + ++pwz;
327 + }
328 +
329 + dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
330 + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
331 + {
332 + *pdwVerMajor = dw << 16;
333 +
334 + if (!*pwz)
335 + {
336 + ExitFunction1(hr = S_OK);
337 + }
338 + ++pwz;
339 + }
340 + else
341 + {
342 + ExitFunction1(hr = S_FALSE);
343 + }
344 +
345 + dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
346 + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
347 + {
348 + *pdwVerMajor |= dw;
349 +
350 + if (!*pwz)
351 + {
352 + ExitFunction1(hr = S_OK);
353 + }
354 + ++pwz;
355 + }
356 + else
357 + {
358 + ExitFunction1(hr = S_FALSE);
359 + }
360 +
361 + dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
362 + if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
363 + {
364 + *pdwVerMinor = dw << 16;
365 +
366 + if (!*pwz)
367 + {
368 + ExitFunction1(hr = S_OK);
369 + }
370 + ++pwz;
371 + }
372 + else
373 + {
374 + ExitFunction1(hr = S_FALSE);
375 + }
376 +
377 + dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
378 + if (pwz && L'\0' == *pwz && dw < 0x10000)
379 + {
380 + *pdwVerMinor |= dw;
381 + }
382 + else
383 + {
384 + ExitFunction1(hr = S_FALSE);
385 + }
386 +
387 +LExit:
388 + return hr;
389 +}
390 +
391 +
392 +/*******************************************************************
393 + FileVersionFromStringEx
394 +
395 +*******************************************************************/
396 +extern "C" HRESULT DAPI FileVersionFromStringEx(
397 + __in LPCWSTR wzVersion,
398 + __in DWORD cchVersion,
399 + __out DWORD64* pqwVersion
400 + )
401 +{
402 + Assert(wzVersion);
403 + Assert(pqwVersion);
404 +
405 + HRESULT hr = S_OK;
406 + LPCWSTR wzEnd = NULL;
407 + LPCWSTR wzPartBegin = wzVersion;
408 + LPCWSTR wzPartEnd = wzVersion;
409 + DWORD iPart = 0;
410 + USHORT us = 0;
411 + DWORD64 qwVersion = 0;
412 +
413 + // get string length if not provided
414 + if (0 >= cchVersion)
415 + {
416 + cchVersion = lstrlenW(wzVersion);
417 + if (0 >= cchVersion)
418 + {
419 + ExitFunction1(hr = E_INVALIDARG);
420 + }
421 + }
422 +
423 + if ((L'v' == *wzVersion) || (L'V' == *wzVersion))
424 + {
425 + ++wzVersion;
426 + --cchVersion;
427 + wzPartBegin = wzVersion;
428 + wzPartEnd = wzVersion;
429 + }
430 +
431 + // save end pointer
432 + wzEnd = wzVersion + cchVersion;
433 +
434 + // loop through parts
435 + for (;;)
436 + {
437 + if (4 <= iPart)
438 + {
439 + // error, too many parts
440 + ExitFunction1(hr = E_INVALIDARG);
441 + }
442 +
443 + // find end of part
444 + while (wzPartEnd < wzEnd && L'.' != *wzPartEnd)
445 + {
446 + ++wzPartEnd;
447 + }
448 + if (wzPartBegin == wzPartEnd)
449 + {
450 + // error, empty part
451 + ExitFunction1(hr = E_INVALIDARG);
452 + }
453 +
454 + DWORD cchPart;
455 + hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart);
456 + ExitOnFailure(hr, "Version number part was too long.");
457 +
458 + // parse version part
459 + hr = StrStringToUInt16(wzPartBegin, cchPart, &us);
460 + ExitOnFailure(hr, "Failed to parse version number part.");
461 +
462 + // add part to qword version
463 + qwVersion |= (DWORD64)us << ((3 - iPart) * 16);
464 +
465 + if (wzPartEnd >= wzEnd)
466 + {
467 + // end of string
468 + break;
469 + }
470 +
471 + wzPartBegin = ++wzPartEnd; // skip over separator
472 + ++iPart;
473 + }
474 +
475 + *pqwVersion = qwVersion;
476 +
477 +LExit:
478 + return hr;
479 +}
480 +
481 +/*******************************************************************
482 + FileVersionFromStringEx - Formats the DWORD64 as a string version.
483 +
484 +*******************************************************************/
485 +extern "C" HRESULT DAPI FileVersionToStringEx(
486 + __in DWORD64 qwVersion,
487 + __out LPWSTR* psczVersion
488 + )
489 +{
490 + HRESULT hr = S_OK;
491 + WORD wMajor = 0;
492 + WORD wMinor = 0;
493 + WORD wBuild = 0;
494 + WORD wRevision = 0;
495 +
496 + // Mask and shift each WORD for each field.
497 + wMajor = (WORD)(qwVersion >> 48 & 0xffff);
498 + wMinor = (WORD)(qwVersion >> 32 & 0xffff);
499 + wBuild = (WORD)(qwVersion >> 16 & 0xffff);
500 + wRevision = (WORD)(qwVersion & 0xffff);
501 +
502 + // Format and return the version string.
503 + hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision);
504 + ExitOnFailure(hr, "Failed to allocate and format the version number.");
505 +
506 +LExit:
507 + return hr;
508 +}
509 +
510 +/*******************************************************************
511 + FileSetPointer - sets the file pointer.
512 +
513 +********************************************************************/
514 +extern "C" HRESULT DAPI FileSetPointer(
515 + __in HANDLE hFile,
516 + __in DWORD64 dw64Move,
517 + __out_opt DWORD64* pdw64NewPosition,
518 + __in DWORD dwMoveMethod
519 + )
520 +{
521 + Assert(INVALID_HANDLE_VALUE != hFile);
522 +
523 + HRESULT hr = S_OK;
524 + LARGE_INTEGER liMove;
525 + LARGE_INTEGER liNewPosition;
526 +
527 + liMove.QuadPart = dw64Move;
528 + if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod))
529 + {
530 + ExitWithLastError(hr, "Failed to set file pointer.");
531 + }
532 +
533 + if (pdw64NewPosition)
534 + {
535 + *pdw64NewPosition = liNewPosition.QuadPart;
536 + }
537 +
538 +LExit:
539 + return hr;
540 +}
541 +
542 +
543 +/*******************************************************************
544 + FileSize
545 +
546 +********************************************************************/
547 +extern "C" HRESULT DAPI FileSize(
548 + __in LPCWSTR pwzFileName,
549 + __out LONGLONG* pllSize
550 + )
551 +{
552 + HRESULT hr = S_OK;
553 + HANDLE hFile = INVALID_HANDLE_VALUE;
554 +
555 + ExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided");
556 +
557 + hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
558 + if (INVALID_HANDLE_VALUE == hFile)
559 + {
560 + ExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName);
561 + }
562 +
563 + hr = FileSizeByHandle(hFile, pllSize);
564 + ExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName);
565 +
566 +LExit:
567 + ReleaseFileHandle(hFile);
568 +
569 + return hr;
570 +}
571 +
572 +
573 +/*******************************************************************
574 + FileSizeByHandle
575 +
576 +********************************************************************/
577 +extern "C" HRESULT DAPI FileSizeByHandle(
578 + __in HANDLE hFile,
579 + __out LONGLONG* pllSize
580 + )
581 +{
582 + Assert(INVALID_HANDLE_VALUE != hFile && pllSize);
583 + HRESULT hr = S_OK;
584 + LARGE_INTEGER li;
585 +
586 + *pllSize = 0;
587 +
588 + if (!::GetFileSizeEx(hFile, &li))
589 + {
590 + ExitWithLastError(hr, "Failed to get size of file.");
591 + }
592 +
593 + *pllSize = li.QuadPart;
594 +
595 +LExit:
596 + return hr;
597 +}
598 +
599 +
600 +/*******************************************************************
601 + FileExistsEx
602 +
603 +********************************************************************/
604 +extern "C" BOOL DAPI FileExistsEx(
605 + __in LPCWSTR wzPath,
606 + __out_opt DWORD *pdwAttributes
607 + )
608 +{
609 + Assert(wzPath && *wzPath);
610 + BOOL fExists = FALSE;
611 +
612 + WIN32_FIND_DATAW fd = { };
613 + HANDLE hff;
614 +
615 + if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd)))
616 + {
617 + ::FindClose(hff);
618 + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
619 + {
620 + if (pdwAttributes)
621 + {
622 + *pdwAttributes = fd.dwFileAttributes;
623 + }
624 +
625 + fExists = TRUE;
626 + }
627 + }
628 +
629 + return fExists;
630 +}
631 +
632 +
633 +/*******************************************************************
634 + FileExistsAfterRestart - checks that a file exists and will continue
635 + to exist after restart.
636 +
637 +********************************************************************/
638 +extern "C" BOOL DAPI FileExistsAfterRestart(
639 + __in_z LPCWSTR wzPath,
640 + __out_opt DWORD *pdwAttributes
641 + )
642 +{
643 + HRESULT hr = S_OK;
644 + BOOL fExists = FALSE;
645 + HKEY hkPendingFileRename = NULL;
646 + LPWSTR* rgsczRenames = NULL;
647 + DWORD cRenames = 0;
648 + int nCompare = 0;
649 +
650 + fExists = FileExistsEx(wzPath, pdwAttributes);
651 + if (fExists)
652 + {
653 + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename);
654 + if (E_FILENOTFOUND == hr)
655 + {
656 + ExitFunction1(hr = S_OK);
657 + }
658 + ExitOnFailure(hr, "Failed to open pending file rename registry key.");
659 +
660 + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
661 + if (E_FILENOTFOUND == hr)
662 + {
663 + ExitFunction1(hr = S_OK);
664 + }
665 + ExitOnFailure(hr, "Failed to read pending file renames.");
666 +
667 + // The pending file renames array is pairs of source and target paths. We only care
668 + // about checking the source paths so skip the target paths (i += 2).
669 + for (DWORD i = 0; i < cRenames; i += 2)
670 + {
671 + LPWSTR wzRename = rgsczRenames[i];
672 + if (wzRename && *wzRename)
673 + {
674 + // Skip the long path designator if present.
675 + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
676 + {
677 + wzRename += 4;
678 + }
679 +
680 + hr = PathCompare(wzPath, wzRename, &nCompare);
681 + ExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
682 +
683 + if (CSTR_EQUAL == nCompare)
684 + {
685 + fExists = FALSE;
686 + break;
687 + }
688 + }
689 + }
690 + }
691 +
692 +LExit:
693 + ReleaseStrArray(rgsczRenames, cRenames);
694 + ReleaseRegKey(hkPendingFileRename);
695 +
696 + return fExists;
697 +}
698 +
699 +
700 +/*******************************************************************
701 + FileRemoveFromPendingRename - removes the file path from the pending
702 + file rename list.
703 +
704 +********************************************************************/
705 +extern "C" HRESULT DAPI FileRemoveFromPendingRename(
706 + __in_z LPCWSTR wzPath
707 + )
708 +{
709 + HRESULT hr = S_OK;
710 + HKEY hkPendingFileRename = NULL;
711 + LPWSTR* rgsczRenames = NULL;
712 + DWORD cRenames = 0;
713 + int nCompare = 0;
714 + BOOL fRemoved = FALSE;
715 + DWORD cNewRenames = 0;
716 +
717 + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename);
718 + if (E_FILENOTFOUND == hr)
719 + {
720 + ExitFunction1(hr = S_OK);
721 + }
722 + ExitOnFailure(hr, "Failed to open pending file rename registry key.");
723 +
724 + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
725 + if (E_FILENOTFOUND == hr)
726 + {
727 + ExitFunction1(hr = S_OK);
728 + }
729 + ExitOnFailure(hr, "Failed to read pending file renames.");
730 +
731 + // The pending file renames array is pairs of source and target paths. We only care
732 + // about checking the source paths so skip the target paths (i += 2).
733 + for (DWORD i = 0; i < cRenames; i += 2)
734 + {
735 + LPWSTR wzRename = rgsczRenames[i];
736 + if (wzRename && *wzRename)
737 + {
738 + // Skip the long path designator if present.
739 + if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
740 + {
741 + wzRename += 4;
742 + }
743 +
744 + hr = PathCompare(wzPath, wzRename, &nCompare);
745 + ExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
746 +
747 + // If we find our path in the list, null out the source and target slot and
748 + // we'll compact the array next.
749 + if (CSTR_EQUAL == nCompare)
750 + {
751 + ReleaseNullStr(rgsczRenames[i]);
752 + ReleaseNullStr(rgsczRenames[i + 1]);
753 + fRemoved = TRUE;
754 + }
755 + }
756 + }
757 +
758 + if (fRemoved)
759 + {
760 + // Compact the array by removing any nulls.
761 + for (DWORD i = 0; i < cRenames; ++i)
762 + {
763 + LPWSTR wzRename = rgsczRenames[i];
764 + if (wzRename)
765 + {
766 + rgsczRenames[cNewRenames] = wzRename;
767 + ++cNewRenames;
768 + }
769 + }
770 +
771 + cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already.
772 +
773 + // Write the new array back to the pending file rename key.
774 + hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames);
775 + ExitOnFailure(hr, "Failed to update pending file renames.");
776 + }
777 +
778 +LExit:
779 + ReleaseStrArray(rgsczRenames, cRenames);
780 + ReleaseRegKey(hkPendingFileRename);
781 +
782 + return hr;
783 +}
784 +
785 +
786 +/*******************************************************************
787 + FileRead - read a file into memory
788 +
789 +********************************************************************/
790 +extern "C" HRESULT DAPI FileRead(
791 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
792 + __out DWORD* pcbDest,
793 + __in LPCWSTR wzSrcPath
794 + )
795 +{
796 + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE);
797 + return hr;
798 +}
799 +
800 +/*******************************************************************
801 + FileRead - read a file into memory with specified share mode
802 +
803 +********************************************************************/
804 +extern "C" HRESULT DAPI FileReadEx(
805 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
806 + __out DWORD* pcbDest,
807 + __in_z LPCWSTR wzSrcPath,
808 + __in DWORD dwShareMode
809 + )
810 +{
811 + HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode);
812 + return hr;
813 +}
814 +
815 +/*******************************************************************
816 + FileReadUntil - read a file into memory with a maximum size
817 +
818 +********************************************************************/
819 +extern "C" HRESULT DAPI FileReadUntil(
820 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
821 + __out_range(<=, cbMaxRead) DWORD* pcbDest,
822 + __in LPCWSTR wzSrcPath,
823 + __in DWORD cbMaxRead
824 + )
825 +{
826 + HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE);
827 + return hr;
828 +}
829 +
830 +
831 +/*******************************************************************
832 + FileReadPartial - read a portion of a file into memory
833 +
834 +********************************************************************/
835 +extern "C" HRESULT DAPI FileReadPartial(
836 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
837 + __out_range(<=, cbMaxRead) DWORD* pcbDest,
838 + __in LPCWSTR wzSrcPath,
839 + __in BOOL fSeek,
840 + __in DWORD cbStartPosition,
841 + __in DWORD cbMaxRead,
842 + __in BOOL fPartialOK
843 + )
844 +{
845 + return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE);
846 +}
847 +
848 +/*******************************************************************
849 + FileReadPartial - read a portion of a file into memory
850 + (with specified share mode)
851 +********************************************************************/
852 +extern "C" HRESULT DAPI FileReadPartialEx(
853 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
854 + __out_range(<=, cbMaxRead) DWORD* pcbDest,
855 + __in_z LPCWSTR wzSrcPath,
856 + __in BOOL fSeek,
857 + __in DWORD cbStartPosition,
858 + __in DWORD cbMaxRead,
859 + __in BOOL fPartialOK,
860 + __in DWORD dwShareMode
861 + )
862 +{
863 + HRESULT hr = S_OK;
864 +
865 + UINT er = ERROR_SUCCESS;
866 + HANDLE hFile = INVALID_HANDLE_VALUE;
867 + LARGE_INTEGER liFileSize = { };
868 + DWORD cbData = 0;
869 + BYTE* pbData = NULL;
870 +
871 + ExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest");
872 + ExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest");
873 + ExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath");
874 + ExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null");
875 +
876 + hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
877 + if (INVALID_HANDLE_VALUE == hFile)
878 + {
879 + er = ::GetLastError();
880 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
881 + {
882 + ExitFunction1(hr = E_FILENOTFOUND);
883 + }
884 + ExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath);
885 + }
886 +
887 + if (!::GetFileSizeEx(hFile, &liFileSize))
888 + {
889 + ExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath);
890 + }
891 +
892 + if (fSeek)
893 + {
894 + if (cbStartPosition > liFileSize.QuadPart)
895 + {
896 + hr = E_INVALIDARG;
897 + ExitOnFailure(hr, "Start position %d bigger than file '%ls' size %d", cbStartPosition, wzSrcPath, liFileSize.QuadPart);
898 + }
899 +
900 + DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT);
901 + if (INVALID_SET_FILE_POINTER == dwErr)
902 + {
903 + ExitOnLastError(hr, "Failed to seek position %d", cbStartPosition);
904 + }
905 + }
906 + else
907 + {
908 + cbStartPosition = 0;
909 + }
910 +
911 + if (fPartialOK)
912 + {
913 + cbData = cbMaxRead;
914 + }
915 + else
916 + {
917 + cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD
918 + if (cbMaxRead < liFileSize.QuadPart - cbStartPosition)
919 + {
920 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
921 + ExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath);
922 + }
923 + }
924 +
925 + if (*ppbDest)
926 + {
927 + if (0 == cbData)
928 + {
929 + ReleaseNullMem(*ppbDest);
930 + *pcbDest = 0;
931 + ExitFunction1(hr = S_OK);
932 + }
933 +
934 + LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE);
935 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath);
936 +
937 + pbData = static_cast<BYTE*>(pv);
938 + }
939 + else
940 + {
941 + if (0 == cbData)
942 + {
943 + *pcbDest = 0;
944 + ExitFunction1(hr = S_OK);
945 + }
946 +
947 + pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
948 + ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath);
949 + }
950 +
951 + DWORD cbTotalRead = 0;
952 + DWORD cbRead = 0;
953 + do
954 + {
955 + DWORD cbRemaining = 0;
956 + hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining);
957 + ExitOnFailure(hr, "Underflow calculating remaining buffer size.");
958 +
959 + if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL))
960 + {
961 + ExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath);
962 + }
963 +
964 + cbTotalRead += cbRead;
965 + } while (cbRead);
966 +
967 + if (cbTotalRead != cbData)
968 + {
969 + hr = E_UNEXPECTED;
970 + ExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath);
971 + }
972 +
973 + *ppbDest = pbData;
974 + pbData = NULL;
975 + *pcbDest = cbData;
976 +
977 +LExit:
978 + ReleaseMem(pbData);
979 + ReleaseFile(hFile);
980 +
981 + return hr;
982 +}
983 +
984 +
985 +/*******************************************************************
986 + FileWrite - write a file from memory
987 +
988 +********************************************************************/
989 +extern "C" HRESULT DAPI FileWrite(
990 + __in_z LPCWSTR pwzFileName,
991 + __in DWORD dwFlagsAndAttributes,
992 + __in_bcount_opt(cbData) LPCBYTE pbData,
993 + __in DWORD cbData,
994 + __out_opt HANDLE* pHandle
995 + )
996 +{
997 + HRESULT hr = S_OK;
998 + HANDLE hFile = INVALID_HANDLE_VALUE;
999 +
1000 + // Open the file
1001 + hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL);
1002 + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName);
1003 +
1004 + hr = FileWriteHandle(hFile, pbData, cbData);
1005 + ExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName);
1006 +
1007 + if (pHandle)
1008 + {
1009 + *pHandle = hFile;
1010 + hFile = INVALID_HANDLE_VALUE;
1011 + }
1012 +
1013 +LExit:
1014 + ReleaseFile(hFile);
1015 +
1016 + return hr;
1017 +}
1018 +
1019 +
1020 +/*******************************************************************
1021 + FileWriteHandle - write to a file handle from memory
1022 +
1023 +********************************************************************/
1024 +extern "C" HRESULT DAPI FileWriteHandle(
1025 + __in HANDLE hFile,
1026 + __in_bcount_opt(cbData) LPCBYTE pbData,
1027 + __in DWORD cbData
1028 + )
1029 +{
1030 + HRESULT hr = S_OK;
1031 + DWORD cbDataWritten = 0;
1032 + DWORD cbTotal = 0;
1033 +
1034 + // Write out all of the data.
1035 + do
1036 + {
1037 + if (!::WriteFile(hFile, pbData + cbTotal, cbData - cbTotal, &cbDataWritten, NULL))
1038 + {
1039 + ExitOnLastError(hr, "Failed to write data to file handle.");
1040 + }
1041 +
1042 + cbTotal += cbDataWritten;
1043 + } while (cbTotal < cbData);
1044 +
1045 +LExit:
1046 + return hr;
1047 +}
1048 +
1049 +
1050 +/*******************************************************************
1051 + FileCopyUsingHandles
1052 +
1053 +*******************************************************************/
1054 +extern "C" HRESULT DAPI FileCopyUsingHandles(
1055 + __in HANDLE hSource,
1056 + __in HANDLE hTarget,
1057 + __in DWORD64 cbCopy,
1058 + __out_opt DWORD64* pcbCopied
1059 + )
1060 +{
1061 + HRESULT hr = S_OK;
1062 + DWORD64 cbTotalCopied = 0;
1063 + BYTE rgbData[4 * 1024];
1064 + DWORD cbRead = 0;
1065 +
1066 + do
1067 + {
1068 + cbRead = static_cast<DWORD>((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied));
1069 + if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL))
1070 + {
1071 + ExitWithLastError(hr, "Failed to read from source.");
1072 + }
1073 +
1074 + if (cbRead)
1075 + {
1076 + hr = FileWriteHandle(hTarget, rgbData, cbRead);
1077 + ExitOnFailure(hr, "Failed to write to target.");
1078 + }
1079 +
1080 + cbTotalCopied += cbRead;
1081 + } while (cbTotalCopied < cbCopy && 0 != cbRead);
1082 +
1083 + if (pcbCopied)
1084 + {
1085 + *pcbCopied = cbTotalCopied;
1086 + }
1087 +
1088 +LExit:
1089 + return hr;
1090 +}
1091 +
1092 +
1093 +/*******************************************************************
1094 + FileEnsureCopy
1095 +
1096 +*******************************************************************/
1097 +extern "C" HRESULT DAPI FileEnsureCopy(
1098 + __in LPCWSTR wzSource,
1099 + __in LPCWSTR wzTarget,
1100 + __in BOOL fOverwrite
1101 + )
1102 +{
1103 + HRESULT hr = S_OK;
1104 + DWORD er;
1105 +
1106 + // try to copy the file first
1107 + if (::CopyFileW(wzSource, wzTarget, !fOverwrite))
1108 + {
1109 + ExitFunction(); // we're done
1110 + }
1111 +
1112 + er = ::GetLastError(); // check the error and do the right thing below
1113 + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er))
1114 + {
1115 + // if not overwriting this is an expected error
1116 + ExitFunction1(hr = S_FALSE);
1117 + }
1118 + else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist
1119 + {
1120 + // try to create the directory then do the copy
1121 + LPWSTR pwzLastSlash = NULL;
1122 + for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz)
1123 + {
1124 + if (*pwz == L'\\')
1125 + {
1126 + pwzLastSlash = pwz;
1127 + }
1128 + }
1129 +
1130 + if (pwzLastSlash)
1131 + {
1132 + *pwzLastSlash = L'\0'; // null terminate
1133 + hr = DirEnsureExists(wzTarget, NULL);
1134 + *pwzLastSlash = L'\\'; // now put the slash back
1135 + ExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget);
1136 +
1137 + // try to copy again
1138 + if (!::CopyFileW(wzSource, wzTarget, fOverwrite))
1139 + {
1140 + ExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget);
1141 + }
1142 + }
1143 + else // no path was specified so just return the error
1144 + {
1145 + hr = HRESULT_FROM_WIN32(er);
1146 + }
1147 + }
1148 + else // unexpected error
1149 + {
1150 + hr = HRESULT_FROM_WIN32(er);
1151 + }
1152 +
1153 +LExit:
1154 + return hr;
1155 +}
1156 +
1157 +
1158 +/*******************************************************************
1159 + FileEnsureCopyWithRetry
1160 +
1161 +*******************************************************************/
1162 +extern "C" HRESULT DAPI FileEnsureCopyWithRetry(
1163 + __in LPCWSTR wzSource,
1164 + __in LPCWSTR wzTarget,
1165 + __in BOOL fOverwrite,
1166 + __in DWORD cRetry,
1167 + __in DWORD dwWaitMilliseconds
1168 + )
1169 +{
1170 + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry.");
1171 +
1172 + HRESULT hr = E_FAIL;
1173 + DWORD i = 0;
1174 +
1175 + for (i = 0; FAILED(hr) && i <= cRetry; ++i)
1176 + {
1177 + if (0 < i)
1178 + {
1179 + ::Sleep(dwWaitMilliseconds);
1180 + }
1181 +
1182 + hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite);
1183 + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr
1184 + || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr)
1185 + {
1186 + break; // no reason to retry these errors.
1187 + }
1188 + }
1189 + ExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i);
1190 +
1191 +LExit:
1192 + return hr;
1193 +}
1194 +
1195 +
1196 +/*******************************************************************
1197 + FileEnsureMove
1198 +
1199 +*******************************************************************/
1200 +extern "C" HRESULT DAPI FileEnsureMove(
1201 + __in LPCWSTR wzSource,
1202 + __in LPCWSTR wzTarget,
1203 + __in BOOL fOverwrite,
1204 + __in BOOL fAllowCopy
1205 + )
1206 +{
1207 + HRESULT hr = S_OK;
1208 + DWORD er;
1209 +
1210 + DWORD dwFlags = 0;
1211 +
1212 + if (fOverwrite)
1213 + {
1214 + dwFlags |= MOVEFILE_REPLACE_EXISTING;
1215 + }
1216 + if (fAllowCopy)
1217 + {
1218 + dwFlags |= MOVEFILE_COPY_ALLOWED;
1219 + }
1220 +
1221 + // try to move the file first
1222 + if (::MoveFileExW(wzSource, wzTarget, dwFlags))
1223 + {
1224 + ExitFunction(); // we're done
1225 + }
1226 +
1227 + er = ::GetLastError(); // check the error and do the right thing below
1228 + if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er))
1229 + {
1230 + // if not overwriting this is an expected error
1231 + ExitFunction1(hr = S_FALSE);
1232 + }
1233 + else if (ERROR_FILE_NOT_FOUND == er)
1234 + {
1235 + // We are seeing some cases where ::MoveFileEx() says a file was not found
1236 + // but the source file is actually present. In that case, return path not
1237 + // found so we try to create the target path since that is most likely
1238 + // what is missing. Otherwise, the source file is missing and we're obviously
1239 + // not going to be recovering from that.
1240 + if (FileExistsEx(wzSource, NULL))
1241 + {
1242 + er = ERROR_PATH_NOT_FOUND;
1243 + }
1244 + }
1245 +
1246 + // If the path doesn't exist, try to create the directory tree then do the move.
1247 + if (ERROR_PATH_NOT_FOUND == er)
1248 + {
1249 + LPWSTR pwzLastSlash = NULL;
1250 + for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz)
1251 + {
1252 + if (*pwz == L'\\')
1253 + {
1254 + pwzLastSlash = pwz;
1255 + }
1256 + }
1257 +
1258 + if (pwzLastSlash)
1259 + {
1260 + *pwzLastSlash = L'\0'; // null terminate
1261 + hr = DirEnsureExists(wzTarget, NULL);
1262 + *pwzLastSlash = L'\\'; // now put the slash back
1263 + ExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget);
1264 +
1265 + // try to move again
1266 + if (!::MoveFileExW(wzSource, wzTarget, dwFlags))
1267 + {
1268 + ExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget);
1269 + }
1270 + }
1271 + else // no path was specified so just return the error
1272 + {
1273 + hr = HRESULT_FROM_WIN32(er);
1274 + }
1275 + }
1276 + else // unexpected error
1277 + {
1278 + hr = HRESULT_FROM_WIN32(er);
1279 + }
1280 +
1281 +LExit:
1282 + return hr;
1283 +}
1284 +
1285 +
1286 +/*******************************************************************
1287 + FileEnsureMoveWithRetry
1288 +
1289 +*******************************************************************/
1290 +extern "C" HRESULT DAPI FileEnsureMoveWithRetry(
1291 + __in LPCWSTR wzSource,
1292 + __in LPCWSTR wzTarget,
1293 + __in BOOL fOverwrite,
1294 + __in BOOL fAllowCopy,
1295 + __in DWORD cRetry,
1296 + __in DWORD dwWaitMilliseconds
1297 + )
1298 +{
1299 + AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry.");
1300 +
1301 + HRESULT hr = E_FAIL;
1302 + DWORD i = 0;
1303 +
1304 + for (i = 0; FAILED(hr) && i < cRetry + 1; ++i)
1305 + {
1306 + if (0 < i)
1307 + {
1308 + ::Sleep(dwWaitMilliseconds);
1309 + }
1310 +
1311 + hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy);
1312 + }
1313 + ExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i);
1314 +
1315 +LExit:
1316 + return hr;
1317 +}
1318 +
1319 +
1320 +/*******************************************************************
1321 + FileCreateTemp - creates an empty temp file
1322 +
1323 + NOTE: uses ANSI functions internally so it is Win9x safe
1324 +********************************************************************/
1325 +extern "C" HRESULT DAPI FileCreateTemp(
1326 + __in LPCWSTR wzPrefix,
1327 + __in LPCWSTR wzExtension,
1328 + __deref_opt_out_z LPWSTR* ppwzTempFile,
1329 + __out_opt HANDLE* phTempFile
1330 + )
1331 +{
1332 + Assert(wzPrefix && *wzPrefix);
1333 + HRESULT hr = S_OK;
1334 + LPSTR pszTempPath = NULL;
1335 + DWORD cchTempPath = MAX_PATH;
1336 +
1337 + HANDLE hTempFile = INVALID_HANDLE_VALUE;
1338 + LPSTR pszTempFile = NULL;
1339 +
1340 + int i = 0;
1341 +
1342 + hr = StrAnsiAlloc(&pszTempPath, cchTempPath);
1343 + ExitOnFailure(hr, "failed to allocate memory for the temp path");
1344 + ::GetTempPathA(cchTempPath, pszTempPath);
1345 +
1346 + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
1347 + {
1348 + hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension);
1349 + ExitOnFailure(hr, "failed to allocate memory for log file");
1350 +
1351 + hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1352 + if (INVALID_HANDLE_VALUE == hTempFile)
1353 + {
1354 + // if the file already exists, just try again
1355 + hr = HRESULT_FROM_WIN32(::GetLastError());
1356 + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
1357 + {
1358 + hr = S_OK;
1359 + continue;
1360 + }
1361 + ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pszTempFile);
1362 + }
1363 + }
1364 +
1365 + if (ppwzTempFile)
1366 + {
1367 + hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8);
1368 + }
1369 +
1370 + if (phTempFile)
1371 + {
1372 + *phTempFile = hTempFile;
1373 + hTempFile = INVALID_HANDLE_VALUE;
1374 + }
1375 +
1376 +LExit:
1377 + ReleaseFile(hTempFile);
1378 + ReleaseStr(pszTempFile);
1379 + ReleaseStr(pszTempPath);
1380 +
1381 + return hr;
1382 +}
1383 +
1384 +
1385 +/*******************************************************************
1386 + FileCreateTempW - creates an empty temp file
1387 +
1388 +*******************************************************************/
1389 +extern "C" HRESULT DAPI FileCreateTempW(
1390 + __in LPCWSTR wzPrefix,
1391 + __in LPCWSTR wzExtension,
1392 + __deref_opt_out_z LPWSTR* ppwzTempFile,
1393 + __out_opt HANDLE* phTempFile
1394 + )
1395 +{
1396 + Assert(wzPrefix && *wzPrefix);
1397 + HRESULT hr = E_FAIL;
1398 +
1399 + WCHAR wzTempPath[MAX_PATH];
1400 + DWORD cchTempPath = countof(wzTempPath);
1401 + LPWSTR pwzTempFile = NULL;
1402 +
1403 + HANDLE hTempFile = INVALID_HANDLE_VALUE;
1404 + int i = 0;
1405 +
1406 + if (!::GetTempPathW(cchTempPath, wzTempPath))
1407 + {
1408 + ExitOnLastError(hr, "failed to get temp path");
1409 + }
1410 +
1411 + for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
1412 + {
1413 + hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension);
1414 + ExitOnFailure(hr, "failed to allocate memory for temp filename");
1415 +
1416 + hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1417 + if (INVALID_HANDLE_VALUE == hTempFile)
1418 + {
1419 + // if the file already exists, just try again
1420 + hr = HRESULT_FROM_WIN32(::GetLastError());
1421 + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
1422 + {
1423 + hr = S_OK;
1424 + continue;
1425 + }
1426 + ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile);
1427 + }
1428 + }
1429 +
1430 + if (phTempFile)
1431 + {
1432 + *phTempFile = hTempFile;
1433 + hTempFile = INVALID_HANDLE_VALUE;
1434 + }
1435 +
1436 + if (ppwzTempFile)
1437 + {
1438 + *ppwzTempFile = pwzTempFile;
1439 + pwzTempFile = NULL;
1440 + }
1441 +
1442 +LExit:
1443 + ReleaseFile(hTempFile);
1444 + ReleaseStr(pwzTempFile);
1445 +
1446 + return hr;
1447 +}
1448 +
1449 +
1450 +/*******************************************************************
1451 + FileIsSame
1452 +
1453 +********************************************************************/
1454 +extern "C" HRESULT DAPI FileIsSame(
1455 + __in LPCWSTR wzFile1,
1456 + __in LPCWSTR wzFile2,
1457 + __out LPBOOL lpfSameFile
1458 + )
1459 +{
1460 + HRESULT hr = S_OK;
1461 + HANDLE hFile1 = NULL;
1462 + HANDLE hFile2 = NULL;
1463 + BY_HANDLE_FILE_INFORMATION fileInfo1 = { };
1464 + BY_HANDLE_FILE_INFORMATION fileInfo2 = { };
1465 +
1466 + hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1467 + ExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1);
1468 +
1469 + hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1470 + ExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2);
1471 +
1472 + if (!::GetFileInformationByHandle(hFile1, &fileInfo1))
1473 + {
1474 + ExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1);
1475 + }
1476 +
1477 + if (!::GetFileInformationByHandle(hFile2, &fileInfo2))
1478 + {
1479 + ExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2);
1480 + }
1481 +
1482 + *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
1483 + fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
1484 + fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE;
1485 +
1486 +LExit:
1487 + ReleaseFile(hFile1);
1488 + ReleaseFile(hFile2);
1489 +
1490 + return hr;
1491 +}
1492 +
1493 +/*******************************************************************
1494 + FileEnsureDelete - deletes a file, first removing read-only,
1495 + hidden, or system attributes if necessary.
1496 +********************************************************************/
1497 +extern "C" HRESULT DAPI FileEnsureDelete(
1498 + __in LPCWSTR wzFile
1499 + )
1500 +{
1501 + HRESULT hr = S_OK;
1502 +
1503 + DWORD dwAttrib = INVALID_FILE_ATTRIBUTES;
1504 + if (FileExistsEx(wzFile, &dwAttrib))
1505 + {
1506 + if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM)
1507 + {
1508 + if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL))
1509 + {
1510 + ExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile);
1511 + }
1512 + }
1513 +
1514 + if (!::DeleteFileW(wzFile))
1515 + {
1516 + ExitOnLastError(hr, "Failed to delete file: %ls", wzFile);
1517 + }
1518 + }
1519 +
1520 +LExit:
1521 + return hr;
1522 +}
1523 +
1524 +/*******************************************************************
1525 + FileGetTime - Gets the file time of a specified file
1526 +********************************************************************/
1527 +extern "C" HRESULT DAPI FileGetTime(
1528 + __in LPCWSTR wzFile,
1529 + __out_opt LPFILETIME lpCreationTime,
1530 + __out_opt LPFILETIME lpLastAccessTime,
1531 + __out_opt LPFILETIME lpLastWriteTime
1532 + )
1533 +{
1534 + HRESULT hr = S_OK;
1535 + HANDLE hFile = NULL;
1536 +
1537 + hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
1538 + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1539 +
1540 + if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime))
1541 + {
1542 + ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile);
1543 + }
1544 +
1545 +LExit:
1546 + ReleaseFile(hFile);
1547 + return hr;
1548 +}
1549 +
1550 +/*******************************************************************
1551 + FileSetTime - Sets the file time of a specified file
1552 +********************************************************************/
1553 +extern "C" HRESULT DAPI FileSetTime(
1554 + __in LPCWSTR wzFile,
1555 + __in_opt const FILETIME *lpCreationTime,
1556 + __in_opt const FILETIME *lpLastAccessTime,
1557 + __in_opt const FILETIME *lpLastWriteTime
1558 + )
1559 +{
1560 + HRESULT hr = S_OK;
1561 + HANDLE hFile = NULL;
1562 +
1563 + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
1564 + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1565 +
1566 + if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime))
1567 + {
1568 + ExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile);
1569 + }
1570 +
1571 +LExit:
1572 + ReleaseFile(hFile);
1573 + return hr;
1574 +}
1575 +
1576 +/*******************************************************************
1577 + FileReSetTime - ReSets a file's last acess and modified time to the
1578 + creation time of the file
1579 +********************************************************************/
1580 +extern "C" HRESULT DAPI FileResetTime(
1581 + __in LPCWSTR wzFile
1582 + )
1583 +{
1584 + HRESULT hr = S_OK;
1585 + HANDLE hFile = NULL;
1586 + FILETIME ftCreateTime;
1587 +
1588 + hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1589 + ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1590 +
1591 + if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL))
1592 + {
1593 + ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile);
1594 + }
1595 +
1596 + if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime))
1597 + {
1598 + ExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile);
1599 + }
1600 +
1601 +LExit:
1602 + ReleaseFile(hFile);
1603 + return hr;
1604 +}
1605 +
1606 +
1607 +/*******************************************************************
1608 + FileExecutableArchitecture
1609 +
1610 +*******************************************************************/
1611 +extern "C" HRESULT DAPI FileExecutableArchitecture(
1612 + __in LPCWSTR wzFile,
1613 + __out FILE_ARCHITECTURE *pArchitecture
1614 + )
1615 +{
1616 + HRESULT hr = S_OK;
1617 +
1618 + HANDLE hFile = INVALID_HANDLE_VALUE;
1619 + DWORD cbRead = 0;
1620 + IMAGE_DOS_HEADER DosImageHeader = { };
1621 + IMAGE_NT_HEADERS NtImageHeader = { };
1622 +
1623 + hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1624 + if (hFile == INVALID_HANDLE_VALUE)
1625 + {
1626 + ExitWithLastError(hr, "Failed to open file: %ls", wzFile);
1627 + }
1628 +
1629 + if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL))
1630 + {
1631 + ExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile);
1632 + }
1633 +
1634 + if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE)
1635 + {
1636 + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1637 + ExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile);
1638 + }
1639 +
1640 + if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN))
1641 + {
1642 + ExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile);
1643 + }
1644 +
1645 + if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL))
1646 + {
1647 + ExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile);
1648 + }
1649 +
1650 + if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE)
1651 + {
1652 + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1653 + ExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile);
1654 + }
1655 +
1656 + if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem ||
1657 + IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem ||
1658 + IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem)
1659 + {
1660 + switch (NtImageHeader.FileHeader.Machine)
1661 + {
1662 + case IMAGE_FILE_MACHINE_I386:
1663 + *pArchitecture = FILE_ARCHITECTURE_X86;
1664 + break;
1665 + case IMAGE_FILE_MACHINE_IA64:
1666 + *pArchitecture = FILE_ARCHITECTURE_IA64;
1667 + break;
1668 + case IMAGE_FILE_MACHINE_AMD64:
1669 + *pArchitecture = FILE_ARCHITECTURE_X64;
1670 + break;
1671 + default:
1672 + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1673 + break;
1674 + }
1675 + }
1676 + else
1677 + {
1678 + hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1679 + }
1680 + ExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile);
1681 +
1682 +LExit:
1683 + if (hFile != INVALID_HANDLE_VALUE)
1684 + {
1685 + ::CloseHandle(hFile);
1686 + }
1687 +
1688 + return hr;
1689 +}
1690 +
1691 +/*******************************************************************
1692 + FileToString
1693 +
1694 +*******************************************************************/
1695 +extern "C" HRESULT DAPI FileToString(
1696 + __in_z LPCWSTR wzFile,
1697 + __out LPWSTR *psczString,
1698 + __out_opt FILE_ENCODING *pfeEncoding
1699 + )
1700 +{
1701 + HRESULT hr = S_OK;
1702 + BYTE *pbFullFileBuffer = NULL;
1703 + DWORD cbFullFileBuffer = 0;
1704 + BOOL fNullCharFound = FALSE;
1705 + LPWSTR sczFileText = NULL;
1706 +
1707 + // Check if the file is ANSI
1708 + hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile);
1709 + ExitOnFailure(hr, "Failed to read file: %ls", wzFile);
1710 +
1711 + if (0 == cbFullFileBuffer)
1712 + {
1713 + *psczString = NULL;
1714 + ExitFunction1(hr = S_OK);
1715 + }
1716 +
1717 + // UTF-8 BOM
1718 + if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM)))
1719 + {
1720 + if (pfeEncoding)
1721 + {
1722 + *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM;
1723 + }
1724 +
1725 + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8);
1726 + ExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile);
1727 +
1728 + *psczString = sczFileText;
1729 + sczFileText = NULL;
1730 + }
1731 + // UTF-16 BOM, little endian (windows regular UTF-16)
1732 + else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM)))
1733 + {
1734 + if (pfeEncoding)
1735 + {
1736 + *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM;
1737 + }
1738 +
1739 + hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR));
1740 + ExitOnFailure(hr, "Failed to allocate copy of string");
1741 + }
1742 + // No BOM, let's try to detect
1743 + else
1744 + {
1745 + for (DWORD i = 0; i < cbFullFileBuffer; ++i)
1746 + {
1747 + if (pbFullFileBuffer[i] == '\0')
1748 + {
1749 + fNullCharFound = TRUE;
1750 + break;
1751 + }
1752 + }
1753 +
1754 + if (!fNullCharFound)
1755 + {
1756 + if (pfeEncoding)
1757 + {
1758 + *pfeEncoding = FILE_ENCODING_UTF8;
1759 + }
1760 +
1761 + hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8);
1762 + if (FAILED(hr))
1763 + {
1764 + if (E_OUTOFMEMORY == hr)
1765 + {
1766 + ExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile);
1767 + }
1768 + }
1769 + else
1770 + {
1771 + *psczString = sczFileText;
1772 + sczFileText = NULL;
1773 + }
1774 + }
1775 + else if (NULL == *psczString)
1776 + {
1777 + if (pfeEncoding)
1778 + {
1779 + *pfeEncoding = FILE_ENCODING_UTF16;
1780 + }
1781 +
1782 + hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR));
1783 + ExitOnFailure(hr, "Failed to allocate copy of string");
1784 + }
1785 + }
1786 +
1787 +LExit:
1788 + ReleaseStr(sczFileText);
1789 + ReleaseMem(pbFullFileBuffer);
1790 +
1791 + return hr;
1792 +}
1793 +
1794 +/*******************************************************************
1795 + FileFromString
1796 +
1797 +*******************************************************************/
1798 +extern "C" HRESULT DAPI FileFromString(
1799 + __in_z LPCWSTR wzFile,
1800 + __in DWORD dwFlagsAndAttributes,
1801 + __in_z LPCWSTR sczString,
1802 + __in FILE_ENCODING feEncoding
1803 + )
1804 +{
1805 + HRESULT hr = S_OK;
1806 + LPSTR sczUtf8String = NULL;
1807 + BYTE *pbFullFileBuffer = NULL;
1808 + const BYTE *pcbFullFileBuffer = NULL;
1809 + DWORD cbFullFileBuffer = 0;
1810 + DWORD cbStrLen = 0;
1811 +
1812 + switch (feEncoding)
1813 + {
1814 + case FILE_ENCODING_UTF8:
1815 + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8);
1816 + ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file");
1817 +
1818 + cbFullFileBuffer = lstrlenA(sczUtf8String);
1819 + pcbFullFileBuffer = reinterpret_cast<BYTE *>(sczUtf8String);
1820 + break;
1821 + case FILE_ENCODING_UTF8_WITH_BOM:
1822 + hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8);
1823 + ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file");
1824 +
1825 + cbStrLen = lstrlenA(sczUtf8String);
1826 + cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen;
1827 +
1828 + pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE));
1829 + ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer");
1830 +
1831 + memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM));
1832 + memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen);
1833 + pcbFullFileBuffer = pbFullFileBuffer;
1834 + break;
1835 + case FILE_ENCODING_UTF16:
1836 + cbFullFileBuffer = lstrlenW(sczString) * sizeof(WCHAR);
1837 + pcbFullFileBuffer = reinterpret_cast<const BYTE *>(sczString);
1838 + break;
1839 + case FILE_ENCODING_UTF16_WITH_BOM:
1840 + cbStrLen = lstrlenW(sczString) * sizeof(WCHAR);
1841 + cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen;
1842 +
1843 + pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE));
1844 + ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer");
1845 +
1846 + memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM));
1847 + memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen);
1848 + pcbFullFileBuffer = pbFullFileBuffer;
1849 + break;
1850 + }
1851 +
1852 + hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL);
1853 + ExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile);
1854 +
1855 +LExit:
1856 + ReleaseStr(sczUtf8String);
1857 + ReleaseMem(pbFullFileBuffer);
1858 +
1859 + return hr;
1860 +}
src/dutil/gdiputil.cpp new
+212
@@ -0,0 +1,212 @@
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 +
5 +using namespace Gdiplus;
6 +
7 +/********************************************************************
8 + GdipInitialize - initializes GDI+.
9 +
10 + Note: pOutput must be non-NULL if pInput->SuppressBackgroundThread
11 + is TRUE. See GdiplusStartup() for more information.
12 +********************************************************************/
13 +extern "C" HRESULT DAPI GdipInitialize(
14 + __in const Gdiplus::GdiplusStartupInput* pInput,
15 + __out ULONG_PTR* pToken,
16 + __out_opt Gdiplus::GdiplusStartupOutput *pOutput
17 + )
18 +{
19 + AssertSz(!pInput->SuppressBackgroundThread || pOutput, "pOutput required if background thread suppressed.");
20 +
21 + HRESULT hr = S_OK;
22 + Status status = Ok;
23 +
24 + status = GdiplusStartup(pToken, pInput, pOutput);
25 + ExitOnGdipFailure(status, hr, "Failed to initialize GDI+.");
26 +
27 +LExit:
28 + return hr;
29 +}
30 +
31 +/********************************************************************
32 + GdipUninitialize - uninitializes GDI+.
33 +
34 +********************************************************************/
35 +extern "C" void DAPI GdipUninitialize(
36 + __in ULONG_PTR token
37 + )
38 +{
39 + GdiplusShutdown(token);
40 +}
41 +
42 +/********************************************************************
43 + GdipBitmapFromResource - read a GDI+ image out of a resource stream
44 +
45 +********************************************************************/
46 +extern "C" HRESULT DAPI GdipBitmapFromResource(
47 + __in_opt HINSTANCE hinst,
48 + __in_z LPCSTR szId,
49 + __out Bitmap **ppBitmap
50 + )
51 +{
52 + HRESULT hr = S_OK;
53 + LPVOID pvData = NULL;
54 + DWORD cbData = 0;
55 + HGLOBAL hGlobal = NULL;;
56 + LPVOID pv = NULL;
57 + IStream *pStream = NULL;
58 + Bitmap *pBitmap = NULL;
59 + Status gs = Ok;
60 +
61 + hr = ResReadData(hinst, szId, &pvData, &cbData);
62 + ExitOnFailure(hr, "Failed to load GDI+ bitmap from resource.");
63 +
64 + // Have to copy the fixed resource data into moveable (heap) memory
65 + // since that's what GDI+ expects.
66 + hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData);
67 + ExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory.");
68 +
69 + pv = ::GlobalLock(hGlobal);
70 + ExitOnNullWithLastError(pv, hr, "Failed to lock global memory.");
71 +
72 + memcpy(pv, pvData, cbData);
73 +
74 + ::GlobalUnlock(pv); // no point taking any more memory than we have already
75 + pv = NULL;
76 +
77 + hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
78 + ExitOnFailure(hr, "Failed to allocate stream from global memory.");
79 +
80 + hGlobal = NULL; // we gave the global memory to the stream object so it will close it
81 +
82 + pBitmap = Bitmap::FromStream(pStream);
83 + ExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream.");
84 +
85 + gs = pBitmap->GetLastStatus();
86 + ExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream.");
87 +
88 + *ppBitmap = pBitmap;
89 + pBitmap = NULL;
90 +
91 +LExit:
92 + if (pBitmap)
93 + {
94 + delete pBitmap;
95 + }
96 +
97 + ReleaseObject(pStream);
98 +
99 + if (pv)
100 + {
101 + ::GlobalUnlock(pv);
102 + }
103 +
104 + if (hGlobal)
105 + {
106 + ::GlobalFree(hGlobal);
107 + }
108 +
109 + return hr;
110 +}
111 +
112 +
113 +/********************************************************************
114 + GdipBitmapFromFile - read a GDI+ image from a file.
115 +
116 +********************************************************************/
117 +extern "C" HRESULT DAPI GdipBitmapFromFile(
118 + __in_z LPCWSTR wzFileName,
119 + __out Bitmap **ppBitmap
120 + )
121 +{
122 + HRESULT hr = S_OK;
123 + Bitmap *pBitmap = NULL;
124 + Status gs = Ok;
125 +
126 + ExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName");
127 +
128 + pBitmap = Bitmap::FromFile(wzFileName);
129 + ExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file.");
130 +
131 + gs = pBitmap->GetLastStatus();
132 + ExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName);
133 +
134 + *ppBitmap = pBitmap;
135 + pBitmap = NULL;
136 +
137 +LExit:
138 + if (pBitmap)
139 + {
140 + delete pBitmap;
141 + }
142 +
143 + return hr;
144 +}
145 +
146 +
147 +HRESULT DAPI GdipHresultFromStatus(
148 + __in Gdiplus::Status gs
149 + )
150 +{
151 + switch (gs)
152 + {
153 + case Ok:
154 + return S_OK;
155 +
156 + case GenericError:
157 + return E_FAIL;
158 +
159 + case InvalidParameter:
160 + return E_INVALIDARG;
161 +
162 + case OutOfMemory:
163 + return E_OUTOFMEMORY;
164 +
165 + case ObjectBusy:
166 + return HRESULT_FROM_WIN32(ERROR_BUSY);
167 +
168 + case InsufficientBuffer:
169 + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
170 +
171 + case NotImplemented:
172 + return E_NOTIMPL;
173 +
174 + case Win32Error:
175 + return E_FAIL;
176 +
177 + case WrongState:
178 + return E_FAIL;
179 +
180 + case Aborted:
181 + return E_ABORT;
182 +
183 + case FileNotFound:
184 + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
185 +
186 + case ValueOverflow:
187 + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
188 +
189 + case AccessDenied:
190 + return E_ACCESSDENIED;
191 +
192 + case UnknownImageFormat:
193 + return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
194 +
195 + case FontFamilyNotFound: __fallthrough;
196 + case FontStyleNotFound: __fallthrough;
197 + case NotTrueTypeFont:
198 + return E_UNEXPECTED;
199 +
200 + case UnsupportedGdiplusVersion:
201 + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
202 +
203 + case GdiplusNotInitialized:
204 + return E_UNEXPECTED;
205 +
206 + case PropertyNotFound: __fallthrough;
207 + case PropertyNotSupported:
208 + return E_FAIL;
209 + }
210 +
211 + return E_UNEXPECTED;
212 +}
src/dutil/guidutil.cpp new
+39
@@ -0,0 +1,39 @@
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 +
5 +extern "C" HRESULT DAPI GuidFixedCreate(
6 + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid
7 + )
8 +{
9 + HRESULT hr = S_OK;
10 + UUID guid = { };
11 +
12 + hr = HRESULT_FROM_RPC(::UuidCreate(&guid));
13 + ExitOnFailure(hr, "UuidCreate failed.");
14 +
15 + if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH))
16 + {
17 + hr = E_OUTOFMEMORY;
18 + ExitOnRootFailure(hr, "Failed to convert guid into string.");
19 + }
20 +
21 +LExit:
22 + return hr;
23 +}
24 +
25 +extern "C" HRESULT DAPI GuidCreate(
26 + __deref_out_z LPWSTR* psczGuid
27 + )
28 +{
29 + HRESULT hr = S_OK;
30 +
31 + hr = StrAlloc(psczGuid, GUID_STRING_LENGTH);
32 + ExitOnFailure(hr, "Failed to allocate space for guid");
33 +
34 + hr = GuidFixedCreate(*psczGuid);
35 + ExitOnFailure(hr, "Failed to create new guid.");
36 +
37 +LExit:
38 + return hr;
39 +}
src/dutil/iis7util.cpp new
+518
@@ -0,0 +1,518 @@
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 +#include "iis7util.h"
5 +
6 +#define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt)
7 +
8 +extern "C" HRESULT DAPI Iis7PutPropertyVariant(
9 + __in IAppHostElement *pElement,
10 + __in LPCWSTR wzPropName,
11 + __in VARIANT vtPut
12 + )
13 +{
14 + HRESULT hr = S_OK;
15 + IAppHostProperty *pProperty = NULL;
16 + BSTR bstrPropName = NULL;
17 +
18 + bstrPropName = ::SysAllocString(wzPropName);
19 + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString");
20 +
21 + hr = pElement->GetPropertyByName(bstrPropName, &pProperty);
22 + ExitOnFailure(hr, "Failed to get property object for %ls", wzPropName);
23 +
24 + hr = pProperty->put_Value(vtPut);
25 + ExitOnFailure(hr, "Failed to set property value for %ls", wzPropName);
26 +
27 +LExit:
28 + ReleaseBSTR(bstrPropName);
29 + // caller responsible for cleaning up variant vtPut
30 + ReleaseObject(pProperty);
31 +
32 + return hr;
33 +}
34 +
35 +extern "C" HRESULT DAPI Iis7PutPropertyString(
36 + __in IAppHostElement *pElement,
37 + __in LPCWSTR wzPropName,
38 + __in LPCWSTR wzString
39 + )
40 +{
41 + HRESULT hr = S_OK;
42 + VARIANT vtPut;
43 +
44 + ::VariantInit(&vtPut);
45 + vtPut.vt = VT_BSTR;
46 + vtPut.bstrVal = ::SysAllocString(wzString);
47 + ExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
48 +
49 + hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut);
50 +
51 +LExit:
52 + ReleaseVariant(vtPut);
53 +
54 + return hr;
55 +}
56 +
57 +extern "C" HRESULT DAPI Iis7PutPropertyInteger(
58 + __in IAppHostElement *pElement,
59 + __in LPCWSTR wzPropName,
60 + __in DWORD dValue
61 + )
62 +{
63 + VARIANT vtPut;
64 +
65 + ::VariantInit(&vtPut);
66 + vtPut.vt = VT_I4;
67 + vtPut.lVal = dValue;
68 + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut);
69 +}
70 +
71 +extern "C" HRESULT DAPI Iis7PutPropertyBool(
72 + __in IAppHostElement *pElement,
73 + __in LPCWSTR wzPropName,
74 + __in BOOL fValue)
75 +{
76 + VARIANT vtPut;
77 +
78 + ::VariantInit(&vtPut);
79 + vtPut.vt = VT_BOOL;
80 + vtPut.boolVal = (fValue == FALSE) ? VARIANT_FALSE : VARIANT_TRUE;
81 + return Iis7PutPropertyVariant(pElement, wzPropName, vtPut);
82 +}
83 +
84 +extern "C" HRESULT DAPI Iis7GetPropertyVariant(
85 + __in IAppHostElement *pElement,
86 + __in LPCWSTR wzPropName,
87 + __in VARIANT* vtGet
88 + )
89 +{
90 + HRESULT hr = S_OK;
91 + IAppHostProperty *pProperty = NULL;
92 + BSTR bstrPropName = NULL;
93 +
94 + bstrPropName = ::SysAllocString(wzPropName);
95 + ExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString");
96 +
97 + hr = pElement->GetPropertyByName(bstrPropName, &pProperty);
98 + ExitOnFailure(hr, "Failed to get property object for %ls", wzPropName);
99 +
100 + hr = pProperty->get_Value(vtGet);
101 + ExitOnFailure(hr, "Failed to get property value for %ls", wzPropName);
102 +
103 +LExit:
104 + ReleaseBSTR(bstrPropName);
105 + // caller responsible for cleaning up variant vtGet
106 + ReleaseObject(pProperty);
107 +
108 + return hr;
109 +}
110 +
111 +extern "C" HRESULT DAPI Iis7GetPropertyString(
112 + __in IAppHostElement *pElement,
113 + __in LPCWSTR wzPropName,
114 + __in LPWSTR* psczGet
115 + )
116 +{
117 + HRESULT hr = S_OK;
118 + VARIANT vtGet;
119 +
120 + ::VariantInit(&vtGet);
121 + hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet);
122 + ExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName);
123 +
124 + if (!ISSTRINGVARIANT(vtGet.vt))
125 + {
126 + hr = E_UNEXPECTED;
127 + ExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt);
128 + }
129 +
130 + hr = StrAllocString(psczGet, vtGet.bstrVal, 0);
131 +
132 +LExit:
133 + ReleaseVariant(vtGet);
134 +
135 + return hr;
136 +}
137 +
138 +BOOL DAPI CompareVariantDefault(
139 + __in VARIANT* pVariant1,
140 + __in VARIANT* pVariant2
141 + )
142 +{
143 + BOOL fEqual = FALSE;
144 +
145 + switch (pVariant1->vt)
146 + {
147 + // VarCmp doesn't work for unsigned ints
148 + // We'd like to allow signed/unsigned comparison as well since
149 + // IIS doesn't document variant type for integer fields
150 + case VT_I1:
151 + case VT_UI1:
152 + if (VT_I1 == pVariant2->vt || VT_UI1 == pVariant2->vt)
153 + {
154 + fEqual = pVariant1->bVal == pVariant2->bVal;
155 + }
156 + break;
157 + case VT_I2:
158 + case VT_UI2:
159 + if (VT_I2 == pVariant2->vt || VT_UI2 == pVariant2->vt)
160 + {
161 + fEqual = pVariant1->uiVal == pVariant2->uiVal;
162 + }
163 + break;
164 + case VT_UI4:
165 + case VT_I4:
166 + if (VT_I4 == pVariant2->vt || VT_UI4 == pVariant2->vt)
167 + {
168 + fEqual = pVariant1->ulVal == pVariant2->ulVal;
169 + }
170 + break;
171 + case VT_UI8:
172 + case VT_I8:
173 + if (VT_I8 == pVariant2->vt || VT_UI8 == pVariant2->vt)
174 + {
175 + fEqual = pVariant1->ullVal == pVariant2->ullVal;
176 + }
177 + break;
178 + default:
179 + fEqual = VARCMP_EQ == ::VarCmp(pVariant1,
180 + pVariant2,
181 + LOCALE_INVARIANT,
182 + NORM_IGNORECASE);
183 + }
184 +
185 + return fEqual;
186 +}
187 +
188 +BOOL DAPI CompareVariantPath(
189 + __in VARIANT* pVariant1,
190 + __in VARIANT* pVariant2
191 + )
192 +{
193 + HRESULT hr = S_OK;
194 + BOOL fEqual = FALSE;
195 + LPWSTR wzValue1 = NULL;
196 + LPWSTR wzValue2 = NULL;
197 +
198 + if (ISSTRINGVARIANT(pVariant1->vt))
199 + {
200 + hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
201 + ExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal);
202 + }
203 +
204 + if (ISSTRINGVARIANT(pVariant2->vt))
205 + {
206 + hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
207 + ExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal);
208 + }
209 +
210 + fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1);
211 +
212 +LExit:
213 + ReleaseNullStr(wzValue1);
214 + ReleaseNullStr(wzValue2);
215 + return fEqual;
216 +}
217 +
218 +BOOL DAPI IsMatchingAppHostElementCallback(
219 + __in IAppHostElement *pElement,
220 + __in_bcount(sizeof(IIS7_APPHOSTELEMENTCOMPARISON)) LPVOID pContext
221 + )
222 +{
223 + IIS7_APPHOSTELEMENTCOMPARISON* pComparison = (IIS7_APPHOSTELEMENTCOMPARISON*) pContext;
224 +
225 + return Iis7IsMatchingAppHostElement(pElement, pComparison);
226 +}
227 +
228 +extern "C" BOOL DAPI Iis7IsMatchingAppHostElement(
229 + __in IAppHostElement *pElement,
230 + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison
231 + )
232 +{
233 + HRESULT hr = S_OK;
234 + BOOL fResult = FALSE;
235 + IAppHostProperty *pProperty = NULL;
236 + BSTR bstrElementName = NULL;
237 +
238 + VARIANT vPropValue;
239 + ::VariantInit(&vPropValue);
240 +
241 + // Use the default comparator if a comparator is not specified
242 + VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault;
243 +
244 + hr = pElement->get_Name(&bstrElementName);
245 + ExitOnFailure(hr, "Failed to get name of element");
246 + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1))
247 + {
248 + ExitFunction();
249 + }
250 +
251 + hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue);
252 + ExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName);
253 +
254 + if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue))
255 + {
256 + fResult = TRUE;
257 + }
258 +
259 +LExit:
260 + ReleaseBSTR(bstrElementName);
261 + ReleaseVariant(vPropValue);
262 + ReleaseObject(pProperty);
263 +
264 + return fResult;
265 +}
266 +
267 +BOOL DAPI IsMatchingAppHostMethod(
268 + __in IAppHostMethod *pMethod,
269 + __in LPCWSTR wzMethodName
270 + )
271 +{
272 + HRESULT hr = S_OK;
273 + BOOL fResult = FALSE;
274 + BSTR bstrName = NULL;
275 +
276 + hr = pMethod->get_Name(&bstrName);
277 + ExitOnFailure(hr, "Failed to get name of element");
278 +
279 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1))
280 + {
281 + fResult = TRUE;
282 + }
283 +
284 +LExit:
285 + ReleaseBSTR(bstrName);
286 +
287 + return fResult;
288 +}
289 +
290 +extern "C" HRESULT DAPI Iis7FindAppHostElementPath(
291 + __in IAppHostElementCollection *pCollection,
292 + __in LPCWSTR wzElementName,
293 + __in LPCWSTR wzAttributeName,
294 + __in LPCWSTR wzAttributeValue,
295 + __out IAppHostElement** ppElement,
296 + __out DWORD* pdwIndex
297 + )
298 +{
299 + HRESULT hr = S_OK;
300 + IIS7_APPHOSTELEMENTCOMPARISON comparison = { };
301 + VARIANT vtValue = { };
302 + ::VariantInit(&vtValue);
303 +
304 + vtValue.vt = VT_BSTR;
305 + vtValue.bstrVal = ::SysAllocString(wzAttributeValue);
306 + ExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
307 +
308 + comparison.sczElementName = wzElementName;
309 + comparison.sczAttributeName = wzAttributeName;
310 + comparison.pvAttributeValue = &vtValue;
311 + comparison.pComparator = CompareVariantPath;
312 +
313 + hr = Iis7EnumAppHostElements(pCollection,
314 + IsMatchingAppHostElementCallback,
315 + &comparison,
316 + ppElement,
317 + pdwIndex);
318 +
319 +LExit:
320 + ReleaseVariant(vtValue);
321 +
322 + return hr;
323 +}
324 +
325 +extern "C" HRESULT DAPI Iis7FindAppHostElementString(
326 + __in IAppHostElementCollection *pCollection,
327 + __in LPCWSTR wzElementName,
328 + __in LPCWSTR wzAttributeName,
329 + __in LPCWSTR wzAttributeValue,
330 + __out IAppHostElement** ppElement,
331 + __out DWORD* pdwIndex
332 + )
333 +{
334 + HRESULT hr = S_OK;
335 + VARIANT vtValue;
336 + ::VariantInit(&vtValue);
337 +
338 + vtValue.vt = VT_BSTR;
339 + vtValue.bstrVal = ::SysAllocString(wzAttributeValue);
340 + ExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
341 +
342 + hr = Iis7FindAppHostElementVariant(pCollection,
343 + wzElementName,
344 + wzAttributeName,
345 + &vtValue,
346 + ppElement,
347 + pdwIndex);
348 +
349 +LExit:
350 + ReleaseVariant(vtValue);
351 +
352 + return hr;
353 +}
354 +
355 +extern "C" HRESULT DAPI Iis7FindAppHostElementInteger(
356 + __in IAppHostElementCollection *pCollection,
357 + __in LPCWSTR wzElementName,
358 + __in LPCWSTR wzAttributeName,
359 + __in DWORD dwAttributeValue,
360 + __out IAppHostElement** ppElement,
361 + __out DWORD* pdwIndex
362 + )
363 +{
364 + HRESULT hr = S_OK;
365 + VARIANT vtValue;
366 + ::VariantInit(&vtValue);
367 +
368 + vtValue.vt = VT_UI4;
369 + vtValue.ulVal = dwAttributeValue;
370 +
371 + hr = Iis7FindAppHostElementVariant(pCollection,
372 + wzElementName,
373 + wzAttributeName,
374 + &vtValue,
375 + ppElement,
376 + pdwIndex);
377 +
378 + ReleaseVariant(vtValue);
379 +
380 + return hr;
381 +}
382 +
383 +extern "C" HRESULT DAPI Iis7FindAppHostElementVariant(
384 + __in IAppHostElementCollection *pCollection,
385 + __in LPCWSTR wzElementName,
386 + __in LPCWSTR wzAttributeName,
387 + __in VARIANT* pvAttributeValue,
388 + __out IAppHostElement** ppElement,
389 + __out DWORD* pdwIndex
390 + )
391 +{
392 + IIS7_APPHOSTELEMENTCOMPARISON comparison = { };
393 + comparison.sczElementName = wzElementName;
394 + comparison.sczAttributeName = wzAttributeName;
395 + comparison.pvAttributeValue = pvAttributeValue;
396 + comparison.pComparator = CompareVariantDefault;
397 +
398 + return Iis7EnumAppHostElements(pCollection,
399 + IsMatchingAppHostElementCallback,
400 + &comparison,
401 + ppElement,
402 + pdwIndex);
403 +}
404 +
405 +extern "C" HRESULT DAPI Iis7EnumAppHostElements(
406 + __in IAppHostElementCollection *pCollection,
407 + __in ENUMAPHOSTELEMENTPROC pCallback,
408 + __in LPVOID pContext,
409 + __out IAppHostElement** ppElement,
410 + __out DWORD* pdwIndex
411 + )
412 +{
413 + HRESULT hr = S_OK;
414 + IAppHostElement *pElement = NULL;
415 + DWORD dwElements = 0;
416 +
417 + VARIANT vtIndex;
418 + ::VariantInit(&vtIndex);
419 +
420 + if (NULL != ppElement)
421 + {
422 + *ppElement = NULL;
423 + }
424 + if (NULL != pdwIndex)
425 + {
426 + *pdwIndex = MAXDWORD;
427 + }
428 +
429 + hr = pCollection->get_Count(&dwElements);
430 + ExitOnFailure(hr, "Failed get application IAppHostElementCollection count");
431 +
432 + vtIndex.vt = VT_UI4;
433 + for (DWORD i = 0; i < dwElements; ++i)
434 + {
435 + vtIndex.ulVal = i;
436 + hr = pCollection->get_Item(vtIndex , &pElement);
437 + ExitOnFailure(hr, "Failed get IAppHostElement element");
438 +
439 + if (pCallback(pElement, pContext))
440 + {
441 + if (NULL != ppElement)
442 + {
443 + *ppElement = pElement;
444 + pElement = NULL;
445 + }
446 + if (NULL != pdwIndex)
447 + {
448 + *pdwIndex = i;
449 + }
450 + break;
451 + }
452 +
453 + ReleaseNullObject(pElement);
454 + }
455 +
456 +LExit:
457 + ReleaseObject(pElement);
458 + ReleaseVariant(vtIndex);
459 +
460 + return hr;
461 +}
462 +
463 +extern "C" HRESULT DAPI Iis7FindAppHostMethod(
464 + __in IAppHostMethodCollection *pCollection,
465 + __in LPCWSTR wzMethodName,
466 + __out IAppHostMethod** ppMethod,
467 + __out DWORD* pdwIndex
468 + )
469 +{
470 + HRESULT hr = S_OK;
471 + IAppHostMethod *pMethod = NULL;
472 + DWORD dwMethods = 0;
473 +
474 + VARIANT vtIndex;
475 + ::VariantInit(&vtIndex);
476 +
477 + if (NULL != ppMethod)
478 + {
479 + *ppMethod = NULL;
480 + }
481 + if (NULL != pdwIndex)
482 + {
483 + *pdwIndex = MAXDWORD;
484 + }
485 +
486 + hr = pCollection->get_Count(&dwMethods);
487 + ExitOnFailure(hr, "Failed get application IAppHostMethodCollection count");
488 +
489 + vtIndex.vt = VT_UI4;
490 + for (DWORD i = 0; i < dwMethods; ++i)
491 + {
492 + vtIndex.ulVal = i;
493 + hr = pCollection->get_Item(vtIndex , &pMethod);
494 + ExitOnFailure(hr, "Failed get IAppHostMethod element");
495 +
496 + if (IsMatchingAppHostMethod(pMethod, wzMethodName))
497 + {
498 + if (NULL != ppMethod)
499 + {
500 + *ppMethod = pMethod;
501 + pMethod = NULL;
502 + }
503 + if (NULL != pdwIndex)
504 + {
505 + *pdwIndex = i;
506 + }
507 + break;
508 + }
509 +
510 + ReleaseNullObject(pMethod);
511 + }
512 +
513 +LExit:
514 + ReleaseObject(pMethod);
515 + ReleaseVariant(vtIndex);
516 +
517 + return hr;
518 +}
src/dutil/inc/aclutil.h new
+154
@@ -0,0 +1,154 @@
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 <aclapi.h>
6 +#include <sddl.h>
7 +
8 +#define ReleaseSid(x) if (x) { AclFreeSid(x); }
9 +#define ReleaseNullSid(x) if (x) { AclFreeSid(x); x = NULL; }
10 +
11 +#ifdef __cplusplus
12 +extern "C" {
13 +#endif
14 +
15 +// structs
16 +struct ACL_ACCESS
17 +{
18 + BOOL fDenyAccess;
19 + DWORD dwAccessMask;
20 +
21 + // TODO: consider using a union
22 + LPCWSTR pwzAccountName; // NOTE: the last three items in this structure are ignored if this is not NULL
23 +
24 + SID_IDENTIFIER_AUTHORITY sia; // used if pwzAccountName is NULL
25 + BYTE nSubAuthorityCount;
26 + DWORD nSubAuthority[8];
27 +};
28 +
29 +struct ACL_ACE
30 +{
31 + DWORD dwFlags;
32 + DWORD dwMask;
33 + PSID psid;
34 +};
35 +
36 +
37 +// functions
38 +HRESULT DAPI AclCheckAccess(
39 + __in HANDLE hToken,
40 + __in ACL_ACCESS* paa
41 + );
42 +HRESULT DAPI AclCheckAdministratorAccess(
43 + __in HANDLE hToken
44 + );
45 +HRESULT DAPI AclCheckLocalSystemAccess(
46 + __in HANDLE hToken
47 + );
48 +
49 +HRESULT DAPI AclGetWellKnownSid(
50 + __in WELL_KNOWN_SID_TYPE wkst,
51 + __deref_out PSID* ppsid
52 + );
53 +HRESULT DAPI AclGetAccountSid(
54 + __in_opt LPCWSTR wzSystem,
55 + __in_z LPCWSTR wzAccount,
56 + __deref_out PSID* ppsid
57 + );
58 +HRESULT DAPI AclGetAccountSidString(
59 + __in_z LPCWSTR wzSystem,
60 + __in_z LPCWSTR wzAccount,
61 + __deref_out_z LPWSTR* ppwzSid
62 + );
63 +
64 +HRESULT DAPI AclCreateDacl(
65 + __in_ecount(cDeny) ACL_ACE rgaaDeny[],
66 + __in DWORD cDeny,
67 + __in_ecount(cAllow) ACL_ACE rgaaAllow[],
68 + __in DWORD cAllow,
69 + __deref_out ACL** ppAcl
70 + );
71 +HRESULT DAPI AclAddToDacl(
72 + __in ACL* pAcl,
73 + __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[],
74 + __in DWORD cDeny,
75 + __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[],
76 + __in DWORD cAllow,
77 + __deref_out ACL** ppAclNew
78 + );
79 +HRESULT DAPI AclMergeDacls(
80 + __in const ACL* pAcl1,
81 + __in const ACL* pAcl2,
82 + __deref_out ACL** ppAclNew
83 + );
84 +HRESULT DAPI AclCreateDaclOld(
85 + __in_ecount(cAclAccesses) ACL_ACCESS* paa,
86 + __in DWORD cAclAccesses,
87 + __deref_out ACL** ppAcl
88 + );
89 +HRESULT DAPI AclCreateSecurityDescriptor(
90 + __in_ecount(cAclAccesses) ACL_ACCESS* paa,
91 + __in DWORD cAclAccesses,
92 + __deref_out SECURITY_DESCRIPTOR** ppsd
93 + );
94 +HRESULT DAPI AclCreateSecurityDescriptorFromDacl(
95 + __in ACL* pACL,
96 + __deref_out SECURITY_DESCRIPTOR** ppsd
97 + );
98 +HRESULT __cdecl AclCreateSecurityDescriptorFromString(
99 + __deref_out SECURITY_DESCRIPTOR** ppsd,
100 + __in_z __format_string LPCWSTR wzSddlFormat,
101 + ...
102 + );
103 +HRESULT DAPI AclDuplicateSecurityDescriptor(
104 + __in SECURITY_DESCRIPTOR* psd,
105 + __deref_out SECURITY_DESCRIPTOR** ppsd
106 + );
107 +HRESULT DAPI AclGetSecurityDescriptor(
108 + __in_z LPCWSTR wzObject,
109 + __in SE_OBJECT_TYPE sot,
110 + __in SECURITY_INFORMATION securityInformation,
111 + __deref_out SECURITY_DESCRIPTOR** ppsd
112 + );
113 +HRESULT DAPI AclSetSecurityWithRetry(
114 + __in_z LPCWSTR wzObject,
115 + __in SE_OBJECT_TYPE sot,
116 + __in SECURITY_INFORMATION securityInformation,
117 + __in_opt PSID psidOwner,
118 + __in_opt PSID psidGroup,
119 + __in_opt PACL pDacl,
120 + __in_opt PACL pSacl,
121 + __in DWORD cRetry,
122 + __in DWORD dwWaitMilliseconds
123 + );
124 +
125 +HRESULT DAPI AclFreeSid(
126 + __in PSID psid
127 + );
128 +HRESULT DAPI AclFreeDacl(
129 + __in ACL* pACL
130 + );
131 +HRESULT DAPI AclFreeSecurityDescriptor(
132 + __in SECURITY_DESCRIPTOR* psd
133 + );
134 +
135 +HRESULT DAPI AclAddAdminToSecurityDescriptor(
136 + __in SECURITY_DESCRIPTOR* pSecurity,
137 + __deref_out SECURITY_DESCRIPTOR** ppSecurityNew
138 + );
139 +
140 +// Following code in acl2util.cpp due to dependency on crypt32.dll.
141 +HRESULT DAPI AclCalculateServiceSidString(
142 + __in LPCWSTR wzServiceName,
143 + __in int cchServiceName,
144 + __deref_out_z LPWSTR* psczSid
145 + );
146 +HRESULT DAPI AclGetAccountSidStringEx(
147 + __in_z LPCWSTR wzSystem,
148 + __in_z LPCWSTR wzAccount,
149 + __deref_out_z LPWSTR* psczSid
150 + );
151 +
152 +#ifdef __cplusplus
153 +}
154 +#endif
src/dutil/inc/apputil.h new
+45
@@ -0,0 +1,45 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +// functions
10 +
11 +/********************************************************************
12 +AppFreeCommandLineArgs - frees argv from AppParseCommandLine.
13 +
14 +********************************************************************/
15 +void DAPI AppFreeCommandLineArgs(
16 + __in LPWSTR* argv
17 + );
18 +
19 +void DAPI AppInitialize(
20 + __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[],
21 + __in DWORD cSafelyLoadSystemDlls
22 + );
23 +
24 +/********************************************************************
25 +AppInitializeUnsafe - initializes without the full standard safety
26 + precautions for an application.
27 +
28 +********************************************************************/
29 +void DAPI AppInitializeUnsafe();
30 +
31 +/********************************************************************
32 +AppParseCommandLine - parses the command line using CommandLineToArgvW.
33 + The caller must free the value of pArgv on success
34 + by calling AppFreeCommandLineArgs.
35 +
36 +********************************************************************/
37 +DAPI_(HRESULT) AppParseCommandLine(
38 + __in LPCWSTR wzCommandLine,
39 + __in int* argc,
40 + __in LPWSTR** pArgv
41 + );
42 +
43 +#ifdef __cplusplus
44 +}
45 +#endif
src/dutil/inc/apuputil.h new
+86
@@ -0,0 +1,86 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseApupChain(p) if (p) { ApupFreeChain(p); p = NULL; }
10 +#define ReleaseNullApupChain(p) if (p) { ApupFreeChain(p); p = NULL; }
11 +
12 +
13 +const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn";
14 +
15 +typedef enum APUP_HASH_ALGORITHM
16 +{
17 + APUP_HASH_ALGORITHM_UNKNOWN,
18 + APUP_HASH_ALGORITHM_MD5,
19 + APUP_HASH_ALGORITHM_SHA1,
20 + APUP_HASH_ALGORITHM_SHA256,
21 +} APUP_HASH_ALGORITHM;
22 +
23 +
24 +struct APPLICATION_UPDATE_ENCLOSURE
25 +{
26 + LPWSTR wzUrl;
27 + LPWSTR wzLocalName;
28 + DWORD64 dw64Size;
29 +
30 + BYTE* rgbDigest;
31 + DWORD cbDigest;
32 + APUP_HASH_ALGORITHM digestAlgorithm;
33 +
34 + BOOL fInstaller;
35 +};
36 +
37 +
38 +struct APPLICATION_UPDATE_ENTRY
39 +{
40 + LPWSTR wzApplicationId;
41 + LPWSTR wzApplicationType;
42 + LPWSTR wzTitle;
43 + LPWSTR wzSummary;
44 + LPWSTR wzContentType;
45 + LPWSTR wzContent;
46 +
47 + LPWSTR wzUpgradeId;
48 + BOOL fUpgradeExclusive;
49 + DWORD64 dw64Version;
50 + DWORD64 dw64UpgradeVersion;
51 +
52 + DWORD64 dw64TotalSize;
53 +
54 + DWORD cEnclosures;
55 + APPLICATION_UPDATE_ENCLOSURE* rgEnclosures;
56 +};
57 +
58 +
59 +struct APPLICATION_UPDATE_CHAIN
60 +{
61 + LPWSTR wzDefaultApplicationId;
62 + LPWSTR wzDefaultApplicationType;
63 +
64 + DWORD cEntries;
65 + APPLICATION_UPDATE_ENTRY* rgEntries;
66 +};
67 +
68 +
69 +HRESULT DAPI ApupAllocChainFromAtom(
70 + __in ATOM_FEED* pFeed,
71 + __out APPLICATION_UPDATE_CHAIN** ppChain
72 + );
73 +
74 +HRESULT DAPI ApupFilterChain(
75 + __in APPLICATION_UPDATE_CHAIN* pChain,
76 + __in DWORD64 dw64Version,
77 + __out APPLICATION_UPDATE_CHAIN** ppFilteredChain
78 + );
79 +
80 +void DAPI ApupFreeChain(
81 + __in APPLICATION_UPDATE_CHAIN* pChain
82 + );
83 +
84 +#ifdef __cplusplus
85 +}
86 +#endif
src/dutil/inc/atomutil.h new
+146
@@ -0,0 +1,146 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); }
10 +#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; }
11 +
12 +
13 +struct ATOM_UNKNOWN_ATTRIBUTE
14 +{
15 + LPWSTR wzNamespace;
16 + LPWSTR wzAttribute;
17 + LPWSTR wzValue;
18 +
19 + ATOM_UNKNOWN_ATTRIBUTE* pNext;
20 +};
21 +
22 +struct ATOM_UNKNOWN_ELEMENT
23 +{
24 + LPWSTR wzNamespace;
25 + LPWSTR wzElement;
26 + LPWSTR wzValue;
27 +
28 + ATOM_UNKNOWN_ATTRIBUTE* pAttributes;
29 + ATOM_UNKNOWN_ELEMENT* pNext;
30 +};
31 +
32 +struct ATOM_LINK
33 +{
34 + LPWSTR wzRel;
35 + LPWSTR wzTitle;
36 + LPWSTR wzType;
37 + LPWSTR wzUrl;
38 + LPWSTR wzValue;
39 + DWORD64 dw64Length;
40 +
41 + ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttributes;
42 + ATOM_UNKNOWN_ELEMENT* pUnknownElements;
43 +};
44 +
45 +struct ATOM_CONTENT
46 +{
47 + LPWSTR wzType;
48 + LPWSTR wzUrl;
49 + LPWSTR wzValue;
50 +
51 + ATOM_UNKNOWN_ELEMENT* pUnknownElements;
52 +};
53 +
54 +struct ATOM_AUTHOR
55 +{
56 + LPWSTR wzName;
57 + LPWSTR wzEmail;
58 + LPWSTR wzUrl;
59 +};
60 +
61 +struct ATOM_CATEGORY
62 +{
63 + LPWSTR wzLabel;
64 + LPWSTR wzScheme;
65 + LPWSTR wzTerm;
66 +
67 + ATOM_UNKNOWN_ELEMENT* pUnknownElements;
68 +};
69 +
70 +struct ATOM_ENTRY
71 +{
72 + LPWSTR wzId;
73 + LPWSTR wzSummary;
74 + LPWSTR wzTitle;
75 + FILETIME ftPublished;
76 + FILETIME ftUpdated;
77 +
78 + ATOM_CONTENT* pContent;
79 +
80 + DWORD cAuthors;
81 + ATOM_AUTHOR* rgAuthors;
82 +
83 + DWORD cCategories;
84 + ATOM_CATEGORY* rgCategories;
85 +
86 + DWORD cLinks;
87 + ATOM_LINK* rgLinks;
88 +
89 + IXMLDOMNode* pixn;
90 + ATOM_UNKNOWN_ELEMENT* pUnknownElements;
91 +};
92 +
93 +struct ATOM_FEED
94 +{
95 + LPWSTR wzGenerator;
96 + LPWSTR wzIcon;
97 + LPWSTR wzId;
98 + LPWSTR wzLogo;
99 + LPWSTR wzSubtitle;
100 + LPWSTR wzTitle;
101 + FILETIME ftUpdated;
102 +
103 + DWORD cAuthors;
104 + ATOM_AUTHOR* rgAuthors;
105 +
106 + DWORD cCategories;
107 + ATOM_CATEGORY* rgCategories;
108 +
109 + DWORD cEntries;
110 + ATOM_ENTRY* rgEntries;
111 +
112 + DWORD cLinks;
113 + ATOM_LINK* rgLinks;
114 +
115 + IXMLDOMNode* pixn;
116 + ATOM_UNKNOWN_ELEMENT* pUnknownElements;
117 +};
118 +
119 +HRESULT DAPI AtomInitialize(
120 + );
121 +
122 +void DAPI AtomUninitialize(
123 + );
124 +
125 +HRESULT DAPI AtomParseFromString(
126 + __in_z LPCWSTR wzAtomString,
127 + __out ATOM_FEED **ppFeed
128 + );
129 +
130 +HRESULT DAPI AtomParseFromFile(
131 + __in_z LPCWSTR wzAtomFile,
132 + __out ATOM_FEED **ppFeed
133 + );
134 +
135 +HRESULT DAPI AtomParseFromDocument(
136 + __in IXMLDOMDocument* pixdDocument,
137 + __out ATOM_FEED **ppFeed
138 + );
139 +
140 +void DAPI AtomFreeFeed(
141 + __in_xcount(pFeed->cItems) ATOM_FEED *pFEED
142 + );
143 +
144 +#ifdef __cplusplus
145 +}
146 +#endif
src/dutil/inc/buffutil.h new
+80
@@ -0,0 +1,80 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +
10 +// macro definitions
11 +
12 +#define ReleaseBuffer ReleaseMem
13 +#define ReleaseNullBuffer ReleaseNullMem
14 +#define BuffFree MemFree
15 +
16 +
17 +// function declarations
18 +
19 +HRESULT BuffReadNumber(
20 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
21 + __in SIZE_T cbBuffer,
22 + __inout SIZE_T* piBuffer,
23 + __out DWORD* pdw
24 + );
25 +HRESULT BuffReadNumber64(
26 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
27 + __in SIZE_T cbBuffer,
28 + __inout SIZE_T* piBuffer,
29 + __out DWORD64* pdw64
30 + );
31 +HRESULT BuffReadString(
32 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
33 + __in SIZE_T cbBuffer,
34 + __inout SIZE_T* piBuffer,
35 + __deref_out_z LPWSTR* pscz
36 + );
37 +HRESULT BuffReadStringAnsi(
38 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
39 + __in SIZE_T cbBuffer,
40 + __inout SIZE_T* piBuffer,
41 + __deref_out_z LPSTR* pscz
42 + );
43 +HRESULT BuffReadStream(
44 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
45 + __in SIZE_T cbBuffer,
46 + __inout SIZE_T* piBuffer,
47 + __deref_out_bcount(*pcbStream) BYTE** ppbStream,
48 + __out SIZE_T* pcbStream
49 + );
50 +
51 +HRESULT BuffWriteNumber(
52 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
53 + __inout SIZE_T* piBuffer,
54 + __in DWORD dw
55 + );
56 +HRESULT BuffWriteNumber64(
57 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
58 + __inout SIZE_T* piBuffer,
59 + __in DWORD64 dw64
60 + );
61 +HRESULT BuffWriteString(
62 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
63 + __inout SIZE_T* piBuffer,
64 + __in_z_opt LPCWSTR scz
65 + );
66 +HRESULT BuffWriteStringAnsi(
67 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
68 + __inout SIZE_T* piBuffer,
69 + __in_z_opt LPCSTR scz
70 + );
71 +HRESULT BuffWriteStream(
72 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
73 + __inout SIZE_T* piBuffer,
74 + __in_bcount(cbStream) const BYTE* pbStream,
75 + __in SIZE_T cbStream
76 + );
77 +
78 +#ifdef __cplusplus
79 +}
80 +#endif
src/dutil/inc/butil.h new
+31
@@ -0,0 +1,31 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +enum BUNDLE_INSTALL_CONTEXT
10 +{
11 + BUNDLE_INSTALL_CONTEXT_MACHINE,
12 + BUNDLE_INSTALL_CONTEXT_USER,
13 +};
14 +
15 +HRESULT DAPI BundleGetBundleInfo(
16 + __in LPCWSTR szBundleId, // Bundle code
17 + __in LPCWSTR szAttribute, // attribute name
18 + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired
19 + __inout_opt LPDWORD pcchValueBuf // in/out buffer character count
20 + );
21 +
22 +HRESULT DAPI BundleEnumRelatedBundle(
23 + __in LPCWSTR lpUpgradeCode,
24 + __in BUNDLE_INSTALL_CONTEXT context,
25 + __inout PDWORD pdwStartIndex,
26 + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf
27 + );
28 +
29 +#ifdef __cplusplus
30 +}
31 +#endif
src/dutil/inc/cabcutil.h new
+62
@@ -0,0 +1,62 @@
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 <fci.h>
6 +#include <fcntl.h>
7 +#include <msi.h>
8 +
9 +// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method
10 +// First argument is the name of splitting cabinet without extension e.g. "cab1"
11 +// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"
12 +// Third argument is the file token of the first file present in the splitting cabinet
13 +typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR);
14 +
15 +#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866)
16 +
17 +#ifdef __cplusplus
18 +extern "C" {
19 +#endif
20 +
21 +extern const int CABC_HANDLE_BYTES;
22 +
23 +// time vs. space trade-off
24 +typedef enum COMPRESSION_TYPE
25 +{
26 + COMPRESSION_TYPE_NONE, // fastest
27 + COMPRESSION_TYPE_LOW,
28 + COMPRESSION_TYPE_MEDIUM,
29 + COMPRESSION_TYPE_HIGH, // smallest
30 + COMPRESSION_TYPE_MSZIP
31 +} COMPRESSION_TYPE;
32 +
33 +// functions
34 +HRESULT DAPI CabCBegin(
35 + __in_z LPCWSTR wzCab,
36 + __in_z LPCWSTR wzCabDir,
37 + __in DWORD dwMaxFiles,
38 + __in DWORD dwMaxSize,
39 + __in DWORD dwMaxThresh,
40 + __in COMPRESSION_TYPE ct,
41 + __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext
42 + );
43 +HRESULT DAPI CabCNextCab(
44 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
45 + );
46 +HRESULT DAPI CabCAddFile(
47 + __in_z LPCWSTR wzFile,
48 + __in_z_opt LPCWSTR wzToken,
49 + __in_opt PMSIFILEHASHINFO pmfHash,
50 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
51 + );
52 +HRESULT DAPI CabCFinish(
53 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext,
54 + __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback
55 + );
56 +void DAPI CabCCancel(
57 + __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
58 + );
59 +
60 +#ifdef __cplusplus
61 +}
62 +#endif
src/dutil/inc/cabutil.h new
+56
@@ -0,0 +1,56 @@
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 <fdi.h>
6 +#include <sys\stat.h>
7 +
8 +#ifdef __cplusplus
9 +extern "C" {
10 +#endif
11 +
12 +// structs
13 +
14 +
15 +// callback function prototypes
16 +typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile);
17 +typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead);
18 +typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead);
19 +typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod);
20 +typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile);
21 +
22 +typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile);
23 +typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile);
24 +typedef HRESULT (*CAB_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
25 +
26 +// function type with calling convention of __stdcall that .NET 1.1 understands only
27 +// .NET 2.0 will not need this
28 +typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin);
29 +
30 +
31 +// functions
32 +HRESULT DAPI CabInitialize(
33 + __in BOOL fDelayLoad
34 + );
35 +void DAPI CabUninitialize(
36 + );
37 +
38 +HRESULT DAPI CabExtract(
39 + __in_z LPCWSTR wzCabinet,
40 + __in_z LPCWSTR wzExtractFile,
41 + __in_z LPCWSTR wzExtractDir,
42 + __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
43 + __in_opt LPVOID pvContext,
44 + __in DWORD64 dw64EmbeddedOffset
45 + );
46 +
47 +HRESULT DAPI CabEnumerate(
48 + __in_z LPCWSTR wzCabinet,
49 + __in_z LPCWSTR wzEnumerateFile,
50 + __in STDCALL_PFNFDINOTIFY pfnNotify,
51 + __in DWORD64 dw64EmbeddedOffset
52 + );
53 +
54 +#ifdef __cplusplus
55 +}
56 +#endif
src/dutil/inc/certutil.h new
+66
@@ -0,0 +1,66 @@
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 +#define ReleaseCertStore(p) if (p) { ::CertCloseStore(p, 0); p = NULL; }
6 +#define ReleaseCertContext(p) if (p) { ::CertFreeCertificateContext(p); p = NULL; }
7 +#define ReleaseCertChain(p) if (p) { ::CertFreeCertificateChain(p); p = NULL; }
8 +
9 +#ifdef __cplusplus
10 +extern "C" {
11 +#endif
12 +
13 +HRESULT DAPI CertReadProperty(
14 + __in PCCERT_CONTEXT pCertContext,
15 + __in DWORD dwProperty,
16 + __out_bcount(*pcbValue) LPVOID pvValue,
17 + __out_opt DWORD* pcbValue
18 + );
19 +
20 +HRESULT DAPI CertGetAuthenticodeSigningTimestamp(
21 + __in CMSG_SIGNER_INFO* pSignerInfo,
22 + __out FILETIME* pft
23 + );
24 +
25 +HRESULT DAPI GetCryptProvFromCert(
26 + __in_opt HWND hwnd,
27 + __in PCCERT_CONTEXT pCert,
28 + __out HCRYPTPROV *phCryptProv,
29 + __out DWORD *pdwKeySpec,
30 + __in BOOL *pfDidCryptAcquire,
31 + __deref_opt_out LPWSTR *ppwszTmpContainer,
32 + __deref_opt_out LPWSTR *ppwszProviderName,
33 + __out DWORD *pdwProviderType
34 + );
35 +
36 +HRESULT DAPI FreeCryptProvFromCert(
37 + __in BOOL fAcquired,
38 + __in HCRYPTPROV hProv,
39 + __in_opt LPWSTR pwszCapiProvider,
40 + __in DWORD dwProviderType,
41 + __in_opt LPWSTR pwszTmpContainer
42 + );
43 +
44 +HRESULT DAPI GetProvSecurityDesc(
45 + __in HCRYPTPROV hProv,
46 + __deref_out SECURITY_DESCRIPTOR** pSecurity
47 + );
48 +
49 +HRESULT DAPI SetProvSecurityDesc(
50 + __in HCRYPTPROV hProv,
51 + __in SECURITY_DESCRIPTOR* pSecurity
52 + );
53 +
54 +BOOL DAPI CertHasPrivateKey(
55 + __in PCCERT_CONTEXT pCertContext,
56 + __out_opt DWORD* pdwKeySpec
57 + );
58 +
59 +HRESULT DAPI CertInstallSingleCertificate(
60 + __in HCERTSTORE hStore,
61 + __in PCCERT_CONTEXT pCertContext,
62 + __in LPCWSTR wzName
63 + );
64 +#ifdef __cplusplus
65 +}
66 +#endif
src/dutil/inc/condutil.h new
+19
@@ -0,0 +1,19 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +// function declarations
10 +
11 +HRESULT DAPI CondEvaluate(
12 + __in VARIABLES_HANDLE pVariables,
13 + __in_z LPCWSTR wzCondition,
14 + __out BOOL* pf
15 + );
16 +
17 +#if defined(__cplusplus)
18 +}
19 +#endif
src/dutil/inc/conutil.h new
+78
@@ -0,0 +1,78 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ConsoleExitOnFailure(x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; }
10 +#define ConsoleExitOnLastError(x, c, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; } }
11 +#define ConsoleExitOnNull(p, x, e, c, f, ...) if (NULL == p) { x = e; ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; }
12 +
13 +
14 +// the following macros need to go away
15 +#define ConsoleTrace(l, f, ...) { ConsoleWriteLine(CONSOLE_COLOR_NORMAL, f, __VA_ARGS__); Trace(l, f, __VA_ARGS__); }
16 +#define ConsoleWarning(f, ...) { ConsoleWriteLine(CONSOLE_COLOR_YELLOW, f, __VA_ARGS__); Trace(REPORT_STANDARD, f, __VA_ARGS__); }
17 +#define ConsoleError(x, f, ...) { ConsoleWriteError(x, CONSOLE_COLOR_RED, f, __VA_ARGS__); TraceError(x, f, __VA_ARGS__); }
18 +
19 +
20 +// enums
21 +typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR;
22 +
23 +// structs
24 +
25 +// functions
26 +HRESULT DAPI ConsoleInitialize();
27 +void DAPI ConsoleUninitialize();
28 +
29 +void DAPI ConsoleGreen();
30 +void DAPI ConsoleRed();
31 +void DAPI ConsoleYellow();
32 +void DAPI ConsoleNormal();
33 +
34 +HRESULT DAPI ConsoleWrite(
35 + CONSOLE_COLOR cc,
36 + __in_z __format_string LPCSTR szFormat,
37 + ...
38 + );
39 +HRESULT DAPI ConsoleWriteLine(
40 + CONSOLE_COLOR cc,
41 + __in_z __format_string LPCSTR szFormat,
42 + ...
43 + );
44 +HRESULT DAPI ConsoleWriteError(
45 + HRESULT hrError,
46 + CONSOLE_COLOR cc,
47 + __in_z __format_string LPCSTR szFormat,
48 + ...
49 + );
50 +
51 +HRESULT DAPI ConsoleReadW(
52 + __deref_out_z LPWSTR* ppwzBuffer
53 + );
54 +
55 +HRESULT DAPI ConsoleReadStringA(
56 + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer,
57 + CONST DWORD cchCharBuffer,
58 + __out DWORD* pcchNumCharReturn
59 + );
60 +HRESULT DAPI ConsoleReadStringW(
61 + __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer,
62 + CONST DWORD cchCharBuffer,
63 + __out DWORD* pcchNumCharReturn
64 + );
65 +
66 +HRESULT DAPI ConsoleReadNonBlockingW(
67 + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
68 + __out DWORD* pcchSize,
69 + BOOL fReadLine
70 + );
71 +
72 +HRESULT DAPI ConsoleSetReadHidden(void);
73 +HRESULT DAPI ConsoleSetReadNormal(void);
74 +
75 +#ifdef __cplusplus
76 +}
77 +#endif
78 +
src/dutil/inc/cryputil.h new
+103
@@ -0,0 +1,103 @@
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 +#define ReleaseCryptMsg(p) if (p) { ::CryptMsgClose(p); p = NULL; }
6 +
7 +#ifdef __cplusplus
8 +extern "C" {
9 +#endif
10 +
11 +
12 +// Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE.
13 +#define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE
14 +#define SHA1_HASH_LEN 20
15 +
16 +typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)(
17 + __inout PVOID Memory,
18 + __in ULONG MemoryLength,
19 + __in ULONG OptionFlags
20 + );
21 +
22 +typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)(
23 + __inout PVOID Memory,
24 + __in ULONG MemoryLength,
25 + __in ULONG OptionFlags
26 + );
27 +
28 +typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)(
29 + __inout LPVOID pData,
30 + __in DWORD cbData,
31 + __in DWORD dwFlags
32 + );
33 +
34 +typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)(
35 + __inout LPVOID pData,
36 + __in DWORD cbData,
37 + __in DWORD dwFlags
38 + );
39 +
40 +// function declarations
41 +
42 +HRESULT DAPI CrypInitialize();
43 +void DAPI CrypUninitialize();
44 +
45 +HRESULT DAPI CrypDecodeObject(
46 + __in_z LPCSTR szStructType,
47 + __in_ecount(cbData) const BYTE* pbData,
48 + __in DWORD cbData,
49 + __in DWORD dwFlags,
50 + __out LPVOID* ppvObject,
51 + __out_opt DWORD* pcbObject
52 + );
53 +
54 +HRESULT DAPI CrypMsgGetParam(
55 + __in HCRYPTMSG hCryptMsg,
56 + __in DWORD dwType,
57 + __in DWORD dwIndex,
58 + __out LPVOID* ppvData,
59 + __out_opt DWORD* pcbData
60 + );
61 +
62 +HRESULT DAPI CrypHashFile(
63 + __in_z LPCWSTR wzFilePath,
64 + __in DWORD dwProvType,
65 + __in ALG_ID algid,
66 + __out_bcount(cbHash) BYTE* pbHash,
67 + __in DWORD cbHash,
68 + __out_opt DWORD64* pqwBytesHashed
69 + );
70 +
71 +HRESULT DAPI CrypHashFileHandle(
72 + __in HANDLE hFile,
73 + __in DWORD dwProvType,
74 + __in ALG_ID algid,
75 + __out_bcount(cbHash) BYTE* pbHash,
76 + __in DWORD cbHash,
77 + __out_opt DWORD64* pqwBytesHashed
78 + );
79 +
80 +HRESULT DAPI CrypHashBuffer(
81 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
82 + __in SIZE_T cbBuffer,
83 + __in DWORD dwProvType,
84 + __in ALG_ID algid,
85 + __out_bcount(cbHash) BYTE* pbHash,
86 + __in DWORD cbHash
87 + );
88 +
89 +HRESULT DAPI CrypEncryptMemory(
90 + __inout LPVOID pData,
91 + __in DWORD cbData,
92 + __in DWORD dwFlags
93 + );
94 +
95 +HRESULT DAPI CrypDecryptMemory(
96 + __inout LPVOID pData,
97 + __in DWORD cbData,
98 + __in DWORD dwFlags
99 + );
100 +
101 +#ifdef __cplusplus
102 +}
103 +#endif
src/dutil/inc/dictutil.h new
+69
@@ -0,0 +1,69 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); }
10 +#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; }
11 +
12 +typedef void* STRINGDICT_HANDLE;
13 +typedef const void* C_STRINGDICT_HANDLE;
14 +
15 +extern const int STRINGDICT_HANDLE_BYTES;
16 +
17 +typedef enum DICT_FLAG
18 +{
19 + DICT_FLAG_NONE = 0,
20 + DICT_FLAG_CASEINSENSITIVE = 1
21 +} DICT_FLAG;
22 +
23 +HRESULT DAPI DictCreateWithEmbeddedKey(
24 + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
25 + __in DWORD dwNumExpectedItems,
26 + __in_opt void **ppvArray,
27 + __in size_t cByteOffset,
28 + __in DICT_FLAG dfFlags
29 + );
30 +HRESULT DAPI DictCreateStringList(
31 + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
32 + __in DWORD dwNumExpectedItems,
33 + __in DICT_FLAG dfFlags
34 + );
35 +HRESULT DAPI DictCreateStringListFromArray(
36 + __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
37 + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
38 + __in const DWORD cStringArray,
39 + __in DICT_FLAG dfFlags
40 + );
41 +HRESULT DAPI DictCompareStringListToArray(
42 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList,
43 + __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
44 + __in const DWORD cStringArray
45 + );
46 +HRESULT DAPI DictAddKey(
47 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
48 + __in_z LPCWSTR szString
49 + );
50 +HRESULT DAPI DictAddValue(
51 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
52 + __in void *pvValue
53 + );
54 +HRESULT DAPI DictKeyExists(
55 + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
56 + __in_z LPCWSTR szString
57 + );
58 +HRESULT DAPI DictGetValue(
59 + __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
60 + __in_z LPCWSTR szString,
61 + __out void **ppvValue
62 + );
63 +void DAPI DictDestroy(
64 + __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle
65 + );
66 +
67 +#ifdef __cplusplus
68 +}
69 +#endif
src/dutil/inc/dirutil.h new
+54
@@ -0,0 +1,54 @@
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 +typedef enum DIR_DELETE
6 +{
7 + DIR_DELETE_FILES = 1,
8 + DIR_DELETE_RECURSE = 2,
9 + DIR_DELETE_SCHEDULE = 4,
10 +} DIR_DELETE;
11 +
12 +#ifdef __cplusplus
13 +extern "C" {
14 +#endif
15 +
16 +BOOL DAPI DirExists(
17 + __in_z LPCWSTR wzPath,
18 + __out_opt DWORD *pdwAttributes
19 + );
20 +
21 +HRESULT DAPI DirCreateTempPath(
22 + __in_z LPCWSTR wzPrefix,
23 + __out_ecount_z(cchPath) LPWSTR wzPath,
24 + __in DWORD cchPath
25 + );
26 +
27 +HRESULT DAPI DirEnsureExists(
28 + __in_z LPCWSTR wzPath,
29 + __in_opt LPSECURITY_ATTRIBUTES psa
30 + );
31 +
32 +HRESULT DAPI DirEnsureDelete(
33 + __in_z LPCWSTR wzPath,
34 + __in BOOL fDeleteFiles,
35 + __in BOOL fRecurse
36 + );
37 +
38 +HRESULT DAPI DirEnsureDeleteEx(
39 + __in_z LPCWSTR wzPath,
40 + __in DWORD dwFlags
41 + );
42 +
43 +HRESULT DAPI DirGetCurrent(
44 + __deref_out_z LPWSTR* psczCurrentDirectory
45 + );
46 +
47 +HRESULT DAPI DirSetCurrent(
48 + __in_z LPCWSTR wzDirectory
49 + );
50 +
51 +#ifdef __cplusplus
52 +}
53 +#endif
54 +
src/dutil/inc/dlutil.h new
+59
@@ -0,0 +1,59 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +typedef HRESULT (WINAPI *LPAUTHENTICATION_ROUTINE)(
10 + __in LPVOID pVoid,
11 + __in HINTERNET hUrl,
12 + __in long lHttpCode,
13 + __out BOOL* pfRetrySend,
14 + __out BOOL* pfRetry
15 + );
16 +
17 +typedef int (WINAPI *LPCANCEL_ROUTINE)(
18 + __in HRESULT hrError,
19 + __in_z_opt LPCWSTR wzError,
20 + __in BOOL fAllowRetry,
21 + __in_opt LPVOID pvContext
22 + );
23 +
24 +// structs
25 +typedef struct _DOWNLOAD_SOURCE
26 +{
27 + LPWSTR sczUrl;
28 + LPWSTR sczUser;
29 + LPWSTR sczPassword;
30 +} DOWNLOAD_SOURCE;
31 +
32 +typedef struct _DOWNLOAD_CACHE_CALLBACK
33 +{
34 + LPPROGRESS_ROUTINE pfnProgress;
35 + LPCANCEL_ROUTINE pfnCancel;
36 + LPVOID pv;
37 +} DOWNLOAD_CACHE_CALLBACK;
38 +
39 +typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK
40 +{
41 + LPAUTHENTICATION_ROUTINE pfnAuthenticate;
42 + LPVOID pv;
43 +} DOWNLOAD_AUTHENTICATION_CALLBACK;
44 +
45 +
46 +// functions
47 +
48 +HRESULT DAPI DownloadUrl(
49 + __in DOWNLOAD_SOURCE* pDownloadSource,
50 + __in DWORD64 dw64AuthoredDownloadSize,
51 + __in LPCWSTR wzDestinationPath,
52 + __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
53 + __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
54 + );
55 +
56 +
57 +#ifdef __cplusplus
58 +}
59 +#endif
src/dutil/inc/dutil.h new
+160
@@ -0,0 +1,160 @@
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 +#define DAPI __stdcall
6 +#define DAPIV __cdecl // used only for functions taking variable length arguments
7 +
8 +#define DAPI_(type) EXTERN_C type DAPI
9 +#define DAPIV_(type) EXTERN_C type DAPIV
10 +
11 +
12 +// enums
13 +typedef enum REPORT_LEVEL
14 +{
15 + REPORT_NONE, // turns off report (only valid for XXXSetLevel())
16 + REPORT_WARNING, // written if want only warnings or reporting is on in general
17 + REPORT_STANDARD, // written if reporting is on
18 + REPORT_VERBOSE, // written only if verbose reporting is on
19 + REPORT_DEBUG, // reporting useful when debugging code
20 + REPORT_ERROR, // always gets reported, but can never be specified
21 +} REPORT_LEVEL;
22 +
23 +// asserts and traces
24 +typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz);
25 +
26 +#ifdef __cplusplus
27 +extern "C" {
28 +#endif
29 +
30 +void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule);
31 +void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn);
32 +void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine);
33 +void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z LPCSTR szMessage);
34 +
35 +void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames);
36 +REPORT_LEVEL DAPI Dutil_TraceGetLevel();
37 +void __cdecl Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...);
38 +void __cdecl Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...);
39 +void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError);
40 +
41 +#ifdef __cplusplus
42 +}
43 +#endif
44 +
45 +
46 +#ifdef DEBUG
47 +
48 +#define AssertSetModule(m) (void)Dutil_SetAssertModule(m)
49 +#define AssertSetDisplayFunction(pfn) (void)Dutil_SetAssertDisplayFunction(pfn)
50 +#define Assert(f) ((f) ? (void)0 : (void)Dutil_Assert(__FILE__, __LINE__))
51 +#define AssertSz(f, sz) ((f) ? (void)0 : (void)Dutil_AssertSz(__FILE__, __LINE__, sz))
52 +
53 +#define TraceSetLevel(l, f) (void)Dutil_TraceSetLevel(l, f)
54 +#define TraceGetLevel() (REPORT_LEVEL)Dutil_TraceGetLevel()
55 +#define Trace(l, f, ...) (void)Dutil_Trace(__FILE__, __LINE__, l, f, __VA_ARGS__)
56 +#define TraceError(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_ERROR, x, f, __VA_ARGS__)
57 +#define TraceErrorDebug(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_DEBUG, x, f, __VA_ARGS__)
58 +
59 +#else // !DEBUG
60 +
61 +#define AssertSetModule(m)
62 +#define AssertSetDisplayFunction(pfn)
63 +#define Assert(f)
64 +#define AssertSz(f, sz)
65 +
66 +#define TraceSetLevel(l, f)
67 +#define Trace(l, f, ...)
68 +#define TraceError(x, f, ...)
69 +#define TraceErrorDebug(x, f, ...)
70 +
71 +#endif // DEBUG
72 +
73 +// ExitTrace can be overriden
74 +#ifndef ExitTrace
75 +#define ExitTrace TraceError
76 +#endif
77 +
78 +// Exit macros
79 +#define ExitFunction() { goto LExit; }
80 +#define ExitFunction1(x) { x; goto LExit; }
81 +
82 +#define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; }
83 +
84 +#define ExitOnLastError(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } }
85 +#define ExitOnLastErrorDebugTrace(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; } }
86 +#define ExitWithLastError(x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }
87 +#define ExitOnFailure(x, s, ...) if (FAILED(x)) { ExitTrace(x, s, __VA_ARGS__); goto LExit; }
88 +#define ExitOnRootFailure(x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }
89 +#define ExitOnFailureDebugTrace(x, s, ...) if (FAILED(x)) { TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; }
90 +#define ExitOnNull(p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }
91 +#define ExitOnNullWithLastError(p, x, s, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }
92 +#define ExitOnNullDebugTrace(p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); TraceErrorDebug(x, s, __VA_ARGS__); goto LExit; }
93 +#define ExitOnInvalidHandleWithLastError(p, x, s, ...) if (INVALID_HANDLE_VALUE == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }
94 +#define ExitOnWin32Error(e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; }
95 +
96 +// release macros
97 +#define ReleaseObject(x) if (x) { x->Release(); }
98 +#define ReleaseObjectArray(prg, cel) if (prg) { for (DWORD Dutil_ReleaseObjectArrayIndex = 0; Dutil_ReleaseObjectArrayIndex < cel; ++Dutil_ReleaseObjectArrayIndex) { ReleaseObject(prg[Dutil_ReleaseObjectArrayIndex]); } ReleaseMem(prg); }
99 +#define ReleaseVariant(x) { ::VariantClear(&x); }
100 +#define ReleaseNullObject(x) if (x) { (x)->Release(); x = NULL; }
101 +#define ReleaseCertificate(x) if (x) { ::CertFreeCertificateContext(x); x=NULL; }
102 +#define ReleaseHandle(x) if (x) { ::CloseHandle(x); x = NULL; }
103 +
104 +
105 +// useful defines and macros
106 +#define Unused(x) ((void)x)
107 +
108 +#ifndef countof
109 +#if 1
110 +#define countof(ary) (sizeof(ary) / sizeof(ary[0]))
111 +#else
112 +#ifndef __cplusplus
113 +#define countof(ary) (sizeof(ary) / sizeof(ary[0]))
114 +#else
115 +template<typename T> static char countofVerify(void const *, T) throw() { return 0; }
116 +template<typename T> static void countofVerify(T *const, T *const *) throw() {};
117 +#define countof(arr) (sizeof(countofVerify(arr,&(arr))) * sizeof(arr)/sizeof(*(arr)))
118 +#endif
119 +#endif
120 +#endif
121 +
122 +#define roundup(x, n) roundup_typed(x, n, DWORD)
123 +#define roundup_typed(x, n, t) (((t)(x) + ((t)(n) - 1)) & ~((t)(n) - 1))
124 +
125 +#define HRESULT_FROM_RPC(x) ((HRESULT) ((x) | FACILITY_RPC))
126 +
127 +#ifndef MAXSIZE_T
128 +#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0))
129 +#endif
130 +
131 +typedef const BYTE* LPCBYTE;
132 +
133 +#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
134 +#define E_PATHNOTFOUND HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)
135 +#define E_INVALIDDATA HRESULT_FROM_WIN32(ERROR_INVALID_DATA)
136 +#define E_INVALIDSTATE HRESULT_FROM_WIN32(ERROR_INVALID_STATE)
137 +#define E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
138 +#define E_MOREDATA HRESULT_FROM_WIN32(ERROR_MORE_DATA)
139 +#define E_NOMOREITEMS HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)
140 +#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
141 +#define E_MODNOTFOUND HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND)
142 +#define E_BADCONFIGURATION HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION)
143 +
144 +#define AddRefAndRelease(x) { x->AddRef(); x->Release(); }
145 +
146 +#define MAKEDWORD(lo, hi) ((DWORD)MAKELONG(lo, hi))
147 +#define MAKEQWORDVERSION(mj, mi, b, r) (((DWORD64)MAKELONG(r, b)) | (((DWORD64)MAKELONG(mi, mj)) << 32))
148 +
149 +
150 +#ifdef __cplusplus
151 +extern "C" {
152 +#endif
153 +
154 +// other functions
155 +HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule);
156 +HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath);
157 +
158 +#ifdef __cplusplus
159 +}
160 +#endif
src/dutil/inc/eseutil.h new
+223
@@ -0,0 +1,223 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); }
10 +#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; }
11 +
12 +struct ESE_COLUMN_SCHEMA
13 +{
14 + JET_COLUMNID jcColumn;
15 + LPCWSTR pszName;
16 + JET_COLTYP jcColumnType;
17 + BOOL fKey; // If this column is part of the key of the table
18 + BOOL fFixed;
19 + BOOL fNullable;
20 + BOOL fAutoIncrement;
21 +};
22 +
23 +struct ESE_TABLE_SCHEMA
24 +{
25 + JET_TABLEID jtTable;
26 + LPCWSTR pszName;
27 + DWORD dwColumns;
28 + ESE_COLUMN_SCHEMA *pcsColumns;
29 +};
30 +
31 +struct ESE_DATABASE_SCHEMA
32 +{
33 + DWORD dwTables;
34 + ESE_TABLE_SCHEMA *ptsTables;
35 +};
36 +
37 +typedef enum ESE_QUERY_TYPE
38 +{
39 + ESE_QUERY_EXACT,
40 + ESE_QUERY_FROM_TOP,
41 + ESE_QUERY_FROM_BOTTOM
42 +} ESE_QUERY_TYPE;
43 +
44 +typedef void* ESE_QUERY_HANDLE;
45 +
46 +HRESULT DAPI EseBeginSession(
47 + __out JET_INSTANCE *pjiInstance,
48 + __out JET_SESID *pjsSession,
49 + __in_z LPCWSTR pszInstance,
50 + __in_z LPCWSTR pszPath
51 + );
52 +HRESULT DAPI EseEndSession(
53 + __in JET_INSTANCE jiInstance,
54 + __in JET_SESID jsSession
55 + );
56 +HRESULT DAPI EseEnsureDatabase(
57 + __in JET_SESID jsSession,
58 + __in_z LPCWSTR pszFile,
59 + __in ESE_DATABASE_SCHEMA *pdsSchema,
60 + __out JET_DBID* pjdbDb,
61 + __in BOOL fExclusive,
62 + __in BOOL fReadonly
63 + );
64 +HRESULT DAPI EseCloseDatabase(
65 + __in JET_SESID jsSession,
66 + __in JET_DBID jdbDb
67 + );
68 +HRESULT DAPI EseCreateTable(
69 + __in JET_SESID jsSession,
70 + __in JET_DBID jdbDb,
71 + __in_z LPCWSTR pszTable,
72 + __out JET_TABLEID *pjtTable
73 + );
74 +HRESULT DAPI EseOpenTable(
75 + __in JET_SESID jsSession,
76 + __in JET_DBID jdbDb,
77 + __in_z LPCWSTR pszTable,
78 + __out JET_TABLEID *pjtTable
79 + );
80 +HRESULT DAPI EseCloseTable(
81 + __in JET_SESID jsSession,
82 + __in JET_TABLEID jtTable
83 + );
84 +HRESULT DAPI EseEnsureColumn(
85 + __in JET_SESID jsSession,
86 + __in JET_TABLEID jtTable,
87 + __in_z LPCWSTR pszColumnName,
88 + __in JET_COLTYP jcColumnType,
89 + __in ULONG ulColumnSize,
90 + __in BOOL fFixed,
91 + __in BOOL fNullable,
92 + __out_opt JET_COLUMNID *pjcColumn
93 + );
94 +HRESULT DAPI EseGetColumn(
95 + __in JET_SESID jsSession,
96 + __in JET_TABLEID jtTable,
97 + __in_z LPCWSTR pszColumnName,
98 + __out JET_COLUMNID *pjcColumn
99 + );
100 +HRESULT DAPI EseMoveCursor(
101 + __in JET_SESID jsSession,
102 + __in JET_TABLEID jtTable,
103 + __in LONG lRow
104 + );
105 +HRESULT DAPI EseDeleteRow(
106 + __in JET_SESID jsSession,
107 + __in JET_TABLEID jtTable
108 + );
109 +HRESULT DAPI EseBeginTransaction(
110 + __in JET_SESID jsSession
111 + );
112 +HRESULT DAPI EseRollbackTransaction(
113 + __in JET_SESID jsSession,
114 + __in BOOL fAll
115 + );
116 +HRESULT DAPI EseCommitTransaction(
117 + __in JET_SESID jsSession
118 + );
119 +HRESULT DAPI EsePrepareUpdate(
120 + __in JET_SESID jsSession,
121 + __in JET_TABLEID jtTable,
122 + __in ULONG ulPrep
123 + );
124 +HRESULT DAPI EseFinishUpdate(
125 + __in JET_SESID jsSession,
126 + __in JET_TABLEID jtTable,
127 + __in BOOL fSeekToInsertedRecord
128 + );
129 +HRESULT DAPI EseSetColumnBinary(
130 + __in JET_SESID jsSession,
131 + __in ESE_TABLE_SCHEMA tsTable,
132 + __in DWORD dwColumn,
133 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
134 + __in SIZE_T cbBuffer
135 + );
136 +HRESULT DAPI EseSetColumnDword(
137 + __in JET_SESID jsSession,
138 + __in ESE_TABLE_SCHEMA tsTable,
139 + __in DWORD dwColumn,
140 + __in DWORD dwValue
141 + );
142 +HRESULT DAPI EseSetColumnBool(
143 + __in JET_SESID jsSession,
144 + __in ESE_TABLE_SCHEMA tsTable,
145 + __in DWORD dwColumn,
146 + __in BOOL fValue
147 + );
148 +HRESULT DAPI EseSetColumnString(
149 + __in JET_SESID jsSession,
150 + __in ESE_TABLE_SCHEMA tsTable,
151 + __in DWORD dwColumn,
152 + __in_z LPCWSTR pszValue
153 + );
154 +HRESULT DAPI EseSetColumnEmpty(
155 + __in JET_SESID jsSession,
156 + __in ESE_TABLE_SCHEMA tsTable,
157 + __in DWORD dwColumn
158 + );
159 +HRESULT DAPI EseGetColumnBinary(
160 + __in JET_SESID jsSession,
161 + __in ESE_TABLE_SCHEMA tsTable,
162 + __in DWORD dwColumn,
163 + __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
164 + __inout SIZE_T* piBuffer
165 + );
166 +HRESULT DAPI EseGetColumnDword(
167 + __in JET_SESID jsSession,
168 + __in ESE_TABLE_SCHEMA tsTable,
169 + __in DWORD dwColumn,
170 + __out DWORD *pdwValue
171 + );
172 +HRESULT DAPI EseGetColumnBool(
173 + __in JET_SESID jsSession,
174 + __in ESE_TABLE_SCHEMA tsTable,
175 + __in DWORD dwColumn,
176 + __out BOOL *pfValue
177 + );
178 +HRESULT DAPI EseGetColumnString(
179 + __in JET_SESID jsSession,
180 + __in ESE_TABLE_SCHEMA tsTable,
181 + __in DWORD dwColumn,
182 + __out LPWSTR *ppszValue
183 + );
184 +
185 +// Call this once for each key column in the table
186 +HRESULT DAPI EseBeginQuery(
187 + __in JET_SESID jsSession,
188 + __in JET_TABLEID jtTable,
189 + __in ESE_QUERY_TYPE qtQueryType,
190 + __out ESE_QUERY_HANDLE *peqhHandle
191 + );
192 +HRESULT DAPI EseSetQueryColumnBinary(
193 + __in ESE_QUERY_HANDLE eqhHandle,
194 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
195 + __in SIZE_T cbBuffer,
196 + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
197 + );
198 +HRESULT DAPI EseSetQueryColumnDword(
199 + __in ESE_QUERY_HANDLE eqhHandle,
200 + __in DWORD dwData,
201 + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
202 + );
203 +HRESULT DAPI EseSetQueryColumnBool(
204 + __in ESE_QUERY_HANDLE eqhHandle,
205 + __in BOOL fValue,
206 + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
207 + );
208 +HRESULT DAPI EseSetQueryColumnString(
209 + __in ESE_QUERY_HANDLE eqhHandle,
210 + __in_z LPCWSTR pszString,
211 + __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
212 + );
213 +HRESULT DAPI EseFinishQuery(
214 + __in ESE_QUERY_HANDLE eqhHandle
215 + );
216 +// Once all columns have been set up, call this and read the result
217 +HRESULT DAPI EseRunQuery(
218 + __in ESE_QUERY_HANDLE eqhHandle
219 + );
220 +
221 +#ifdef __cplusplus
222 +}
223 +#endif
src/dutil/inc/fileutil.h new
+235
@@ -0,0 +1,235 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseFile(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; }
10 +#define ReleaseFileHandle(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; }
11 +#define ReleaseFileFindHandle(h) if (INVALID_HANDLE_VALUE != h) { ::FindClose(h); h = INVALID_HANDLE_VALUE; }
12 +
13 +#define FILEMAKEVERSION(major, minor, build, revision) static_cast<DWORD64>((static_cast<DWORD64>(major & 0xFFFF) << 48) \
14 + | (static_cast<DWORD64>(minor & 0xFFFF) << 32) \
15 + | (static_cast<DWORD64>(build & 0xFFFF) << 16) \
16 + | (static_cast<DWORD64>(revision & 0xFFFF)))
17 +
18 +typedef enum FILE_ARCHITECTURE
19 +{
20 + FILE_ARCHITECTURE_UNKNOWN,
21 + FILE_ARCHITECTURE_X86,
22 + FILE_ARCHITECTURE_X64,
23 + FILE_ARCHITECTURE_IA64,
24 +} FILE_ARCHITECTURE;
25 +
26 +typedef enum FILE_ENCODING
27 +{
28 + FILE_ENCODING_UNSPECIFIED = 0,
29 + // TODO: distinguish between non-BOM utf-8 and ANSI in the future?
30 + FILE_ENCODING_UTF8,
31 + FILE_ENCODING_UTF8_WITH_BOM,
32 + FILE_ENCODING_UTF16,
33 + FILE_ENCODING_UTF16_WITH_BOM,
34 +} FILE_ENCODING;
35 +
36 +
37 +LPWSTR DAPI FileFromPath(
38 + __in_z LPCWSTR wzPath
39 + );
40 +HRESULT DAPI FileResolvePath(
41 + __in_z LPCWSTR wzRelativePath,
42 + __out LPWSTR *ppwzFullPath
43 + );
44 +HRESULT DAPI FileStripExtension(
45 + __in_z LPCWSTR wzFileName,
46 + __out LPWSTR *ppwzFileNameNoExtension
47 + );
48 +HRESULT DAPI FileChangeExtension(
49 + __in_z LPCWSTR wzFileName,
50 + __in_z LPCWSTR wzNewExtension,
51 + __out LPWSTR *ppwzFileNameNewExtension
52 + );
53 +HRESULT DAPI FileAddSuffixToBaseName(
54 + __in_z LPCWSTR wzFileName,
55 + __in_z LPCWSTR wzSuffix,
56 + __out_z LPWSTR* psczNewFileName
57 + );
58 +HRESULT DAPI FileVersionFromString(
59 + __in_z LPCWSTR wzVersion,
60 + __out DWORD *pdwVerMajor,
61 + __out DWORD* pdwVerMinor
62 + );
63 +HRESULT DAPI FileVersionFromStringEx(
64 + __in_z LPCWSTR wzVersion,
65 + __in DWORD cchVersion,
66 + __out DWORD64* pqwVersion
67 + );
68 +HRESULT DAPI FileVersionToStringEx(
69 + __in DWORD64 qwVersion,
70 + __out LPWSTR* psczVersion
71 + );
72 +HRESULT DAPI FileSetPointer(
73 + __in HANDLE hFile,
74 + __in DWORD64 dw64Move,
75 + __out_opt DWORD64* pdw64NewPosition,
76 + __in DWORD dwMoveMethod
77 + );
78 +HRESULT DAPI FileSize(
79 + __in_z LPCWSTR pwzFileName,
80 + __out LONGLONG* pllSize
81 + );
82 +HRESULT DAPI FileSizeByHandle(
83 + __in HANDLE hFile,
84 + __out LONGLONG* pllSize
85 + );
86 +BOOL DAPI FileExistsEx(
87 + __in_z LPCWSTR wzPath,
88 + __out_opt DWORD *pdwAttributes
89 + );
90 +BOOL DAPI FileExistsAfterRestart(
91 + __in_z LPCWSTR wzPath,
92 + __out_opt DWORD *pdwAttributes
93 + );
94 +HRESULT DAPI FileRemoveFromPendingRename(
95 + __in_z LPCWSTR wzPath
96 + );
97 +HRESULT DAPI FileRead(
98 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
99 + __out DWORD* pcbDest,
100 + __in_z LPCWSTR wzSrcPath
101 + );
102 +HRESULT DAPI FileReadEx(
103 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
104 + __out DWORD* pcbDest,
105 + __in_z LPCWSTR wzSrcPath,
106 + __in DWORD dwShareMode
107 + );
108 +HRESULT DAPI FileReadUntil(
109 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
110 + __out_range(<=, cbMaxRead) DWORD* pcbDest,
111 + __in_z LPCWSTR wzSrcPath,
112 + __in DWORD cbMaxRead
113 + );
114 +HRESULT DAPI FileReadPartial(
115 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
116 + __out_range(<=, cbMaxRead) DWORD* pcbDest,
117 + __in_z LPCWSTR wzSrcPath,
118 + __in BOOL fSeek,
119 + __in DWORD cbStartPosition,
120 + __in DWORD cbMaxRead,
121 + __in BOOL fPartialOK
122 + );
123 +HRESULT DAPI FileReadPartialEx(
124 + __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
125 + __out_range(<=, cbMaxRead) DWORD* pcbDest,
126 + __in_z LPCWSTR wzSrcPath,
127 + __in BOOL fSeek,
128 + __in DWORD cbStartPosition,
129 + __in DWORD cbMaxRead,
130 + __in BOOL fPartialOK,
131 + __in DWORD dwShareMode
132 + );
133 +HRESULT DAPI FileWrite(
134 + __in_z LPCWSTR pwzFileName,
135 + __in DWORD dwFlagsAndAttributes,
136 + __in_bcount_opt(cbData) LPCBYTE pbData,
137 + __in DWORD cbData,
138 + __out_opt HANDLE* pHandle
139 + );
140 +HRESULT DAPI FileWriteHandle(
141 + __in HANDLE hFile,
142 + __in_bcount_opt(cbData) LPCBYTE pbData,
143 + __in DWORD cbData
144 + );
145 +HRESULT DAPI FileCopyUsingHandles(
146 + __in HANDLE hSource,
147 + __in HANDLE hTarget,
148 + __in DWORD64 cbCopy,
149 + __out_opt DWORD64* pcbCopied
150 + );
151 +HRESULT DAPI FileEnsureCopy(
152 + __in_z LPCWSTR wzSource,
153 + __in_z LPCWSTR wzTarget,
154 + __in BOOL fOverwrite
155 + );
156 +HRESULT DAPI FileEnsureCopyWithRetry(
157 + __in LPCWSTR wzSource,
158 + __in LPCWSTR wzTarget,
159 + __in BOOL fOverwrite,
160 + __in DWORD cRetry,
161 + __in DWORD dwWaitMilliseconds
162 + );
163 +HRESULT DAPI FileEnsureMove(
164 + __in_z LPCWSTR wzSource,
165 + __in_z LPCWSTR wzTarget,
166 + __in BOOL fOverwrite,
167 + __in BOOL fAllowCopy
168 + );
169 +HRESULT DAPI FileEnsureMoveWithRetry(
170 + __in LPCWSTR wzSource,
171 + __in LPCWSTR wzTarget,
172 + __in BOOL fOverwrite,
173 + __in BOOL fAllowCopy,
174 + __in DWORD cRetry,
175 + __in DWORD dwWaitMilliseconds
176 + );
177 +HRESULT DAPI FileCreateTemp(
178 + __in_z LPCWSTR wzPrefix,
179 + __in_z LPCWSTR wzExtension,
180 + __deref_opt_out_z LPWSTR* ppwzTempFile,
181 + __out_opt HANDLE* phTempFile
182 + );
183 +HRESULT DAPI FileCreateTempW(
184 + __in_z LPCWSTR wzPrefix,
185 + __in_z LPCWSTR wzExtension,
186 + __deref_opt_out_z LPWSTR* ppwzTempFile,
187 + __out_opt HANDLE* phTempFile
188 + );
189 +HRESULT DAPI FileVersion(
190 + __in_z LPCWSTR wzFilename,
191 + __out DWORD *pdwVerMajor,
192 + __out DWORD* pdwVerMinor
193 + );
194 +HRESULT DAPI FileIsSame(
195 + __in_z LPCWSTR wzFile1,
196 + __in_z LPCWSTR wzFile2,
197 + __out LPBOOL lpfSameFile
198 + );
199 +HRESULT DAPI FileEnsureDelete(
200 + __in_z LPCWSTR wzFile
201 + );
202 +HRESULT DAPI FileGetTime(
203 + __in_z LPCWSTR wzFile,
204 + __out_opt LPFILETIME lpCreationTime,
205 + __out_opt LPFILETIME lpLastAccessTime,
206 + __out_opt LPFILETIME lpLastWriteTime
207 + );
208 +HRESULT DAPI FileSetTime(
209 + __in_z LPCWSTR wzFile,
210 + __in_opt const FILETIME *lpCreationTime,
211 + __in_opt const FILETIME *lpLastAccessTime,
212 + __in_opt const FILETIME *lpLastWriteTime
213 + );
214 +HRESULT DAPI FileResetTime(
215 + __in_z LPCWSTR wzFile
216 + );
217 +HRESULT DAPI FileExecutableArchitecture(
218 + __in_z LPCWSTR wzFile,
219 + __out FILE_ARCHITECTURE *pArchitecture
220 + );
221 +HRESULT DAPI FileToString(
222 + __in_z LPCWSTR wzFile,
223 + __out LPWSTR *psczString,
224 + __out_opt FILE_ENCODING *pfeEncoding
225 + );
226 +HRESULT DAPI FileFromString(
227 + __in_z LPCWSTR wzFile,
228 + __in DWORD dwFlagsAndAttributes,
229 + __in_z LPCWSTR sczString,
230 + __in FILE_ENCODING feEncoding
231 + );
232 +
233 +#ifdef __cplusplus
234 +}
235 +#endif
src/dutil/inc/gdiputil.h new
+38
@@ -0,0 +1,38 @@
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 +#define ExitOnGdipFailure(g, x, s, ...) { x = GdipHresultFromStatus(g); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, s, __VA_ARGS__); goto LExit; } }
6 +
7 +#ifdef __cplusplus
8 +extern "C" {
9 +#endif
10 +
11 +HRESULT DAPI GdipInitialize(
12 + __in const Gdiplus::GdiplusStartupInput* pInput,
13 + __out ULONG_PTR* pToken,
14 + __out_opt Gdiplus::GdiplusStartupOutput *pOutput
15 + );
16 +
17 +void DAPI GdipUninitialize(
18 + __in ULONG_PTR token
19 + );
20 +
21 +HRESULT DAPI GdipBitmapFromResource(
22 + __in_opt HINSTANCE hinst,
23 + __in_z LPCSTR szId,
24 + __out Gdiplus::Bitmap **ppBitmap
25 + );
26 +
27 +HRESULT DAPI GdipBitmapFromFile(
28 + __in_z LPCWSTR wzFileName,
29 + __out Gdiplus::Bitmap **ppBitmap
30 + );
31 +
32 +HRESULT DAPI GdipHresultFromStatus(
33 + __in Gdiplus::Status gs
34 + );
35 +
36 +#ifdef __cplusplus
37 +}
38 +#endif
src/dutil/inc/guidutil.h new
+21
@@ -0,0 +1,21 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define GUID_STRING_LENGTH 39
10 +
11 +HRESULT DAPI GuidFixedCreate(
12 + _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid
13 + );
14 +
15 +HRESULT DAPI GuidCreate(
16 + __deref_out_z LPWSTR* psczGuid
17 + );
18 +
19 +#ifdef __cplusplus
20 +}
21 +#endif
src/dutil/inc/iis7util.h new
+222
@@ -0,0 +1,222 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +// IIS Config schema names
10 +#define IIS_CONFIG_ADD L"add"
11 +#define IIS_CONFIG_ALLOWED L"allowed"
12 +#define IIS_CONFIG_APPHOST_ROOT L"MACHINE/WEBROOT/APPHOST"
13 +#define IIS_CONFIG_APPLICATION L"application"
14 +#define IIS_CONFIG_APPPOOL L"applicationPool"
15 +#define IIS_CONFIG_APPPOOL_AUTO L"autoStart"
16 +#define IIS_CONFIG_APPPOOL_SECTION L"system.applicationHost/applicationPools"
17 +#define IIS_CONFIG_AUTOSTART L"serverAutoStart"
18 +#define IIS_CONFIG_BINDING L"binding"
19 +#define IIS_CONFIG_BINDINGINFO L"bindingInformation"
20 +#define IIS_CONFIG_BINDINGS L"bindings"
21 +#define IIS_CONFIG_DESC L"description"
22 +#define IIS_CONFIG_EXECUTABLE L"scriptProcessor"
23 +#define IIS_CONFIG_ENABLED L"enabled"
24 +#define IIS_CONFIG_ENABLE32 L"enable32BitAppOnWin64"
25 +#define IIS_CONFIG_FILEEXT L"fileExtension"
26 +#define IIS_CONFIG_FILTER L"filter"
27 +#define IIS_CONFIG_GROUPID L"groupId"
28 +#define IIS_CONFIG_HEADERS L"customHeaders"
29 +#define IIS_CONFIG_HTTPERRORS_SECTION L"system.webServer/httpErrors"
30 +#define IIS_CONFIG_ID L"id"
31 +#define IIS_CONFIG_ISAPI_SECTION L"system.webServer/isapiFilters"
32 +#define IIS_CONFIG_HTTPPROTO_SECTION L"system.webServer/httpProtocol"
33 +#define IIS_CONFIG_LOG_SECTION L"system.applicationHost/log"
34 +#define IIS_CONFIG_LOG_UTF8 L"logInUTF8"
35 +#define IIS_CONFIG_LIMITS L"limits"
36 +#define IIS_CONFIG_PIPELINEMODE L"managedPipelineMode"
37 +#define IIS_CONFIG_MANAGEDRUNTIMEVERSION L"managedRuntimeVersion"
38 +#define IIS_CONFIG_WEBLOG L"logFile"
39 +#define IIS_CONFIG_LOGFORMAT L"logFormat"
40 +#define IIS_CONFIG_MIMEMAP L"mimeMap"
41 +#define IIS_CONFIG_MIMETYPE L"mimeType"
42 +#define IIS_CONFIG_MODULES L"modules"
43 +#define IIS_CONFIG_NAME L"name"
44 +#define IIS_CONFIG_PATH L"path"
45 +#define IIS_CONFIG_PHYSPATH L"physicalPath"
46 +#define IIS_CONFIG_PROTOCOL L"protocol"
47 +#define IIS_CONFIG_RESTRICTION_SECTION L"system.webServer/security/isapiCgiRestriction"
48 +#define IIS_CONFIG_SITE L"site"
49 +#define IIS_CONFIG_SITE_ID L"id"
50 +#define IIS_CONFIG_SITES_SECTION L"system.applicationHost/sites"
51 +#define IIS_CONFIG_CONNECTTIMEOUT L"connectionTimeout"
52 +#define IIS_CONFIG_VDIR L"virtualDirectory"
53 +#define IIS_CONFIG_VALUE L"value"
54 +#define IIS_CONFIG_VERBS L"verb"
55 +#define IIS_CONFIG_WEBLIMITS_SECTION L"system.applicationHost/webLimits"
56 +#define IIS_CONFIG_WEBLIMITS_MAXBAND L"maxGlobalBandwidth"
57 +#define IIS_CONFIG_TRUE L"true"
58 +#define IIS_CONFIG_FALSE L"false"
59 +#define IIS_CONFIG_ERROR L"error"
60 +#define IIS_CONFIG_STATUSCODE L"statusCode"
61 +#define IIS_CONFIG_SUBSTATUS L"subStatusCode"
62 +#define IIS_CONFIG_LANGPATH L"prefixLanguageFilePath"
63 +#define IIS_CONFIG_RESPMODE L"responseMode"
64 +#define IIS_CONFIG_CLEAR L"clear"
65 +#define IIS_CONFIG_RECYCLING L"recycling"
66 +#define IIS_CONFIG_PEROIDRESTART L"periodicRestart"
67 +#define IIS_CONFIG_TIME L"time"
68 +#define IIS_CONFIG_REQUESTS L"requests"
69 +#define IIS_CONFIG_SCHEDULE L"schedule"
70 +#define IIS_CONFIG_MEMORY L"memory"
71 +#define IIS_CONFIG_PRIVMEMORY L"privateMemory"
72 +#define IIS_CONFIG_PROCESSMODEL L"processModel"
73 +#define IIS_CONFIG_IDLETIMEOUT L"idleTimeout"
74 +#define IIS_CONFIG_QUEUELENGTH L"queueLength"
75 +#define IIS_CONFIG_IDENITITYTYPE L"identityType"
76 +#define IIS_CONFIG_LOCALSYSTEM L"LocalSystem"
77 +#define IIS_CONFIG_LOCALSERVICE L"LocalService"
78 +#define IIS_CONFIG_NETWORKSERVICE L"NetworkService"
79 +#define IIS_CONFIG_SPECIFICUSER L"SpecificUser"
80 +#define IIS_CONFIG_APPLICATIONPOOLIDENTITY L"ApplicationPoolIdentity"
81 +#define IIS_CONFIG_USERNAME L"userName"
82 +#define IIS_CONFIG_PASSWORD L"password"
83 +#define IIS_CONFIG_CPU L"cpu"
84 +#define IIS_CONFIG_LIMIT L"limit"
85 +#define IIS_CONFIG_CPU_ACTION L"action"
86 +#define IIS_CONFIG_KILLW3WP L"KillW3wp"
87 +#define IIS_CONFIG_NOACTION L"NoAction"
88 +#define IIS_CONFIG_RESETINTERVAL L"resetInterval"
89 +#define IIS_CONFIG_MAXWRKPROCESSES L"maxProcesses"
90 +#define IIS_CONFIG_HANDLERS_SECTION L"system.webServer/handlers"
91 +#define IIS_CONFIG_DEFAULTDOC_SECTION L"system.webServer/defaultDocument"
92 +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp"
93 +#define IIS_CONFIG_SCRIPTERROR L"scriptErrorSentToBrowser"
94 +#define IIS_CONFIG_STATICCONTENT_SECTION L"system.webServer/staticContent"
95 +#define IIS_CONFIG_HTTPEXPIRES L"httpExpires"
96 +#define IIS_CONFIG_MAXAGE L"cacheControlMaxAge"
97 +#define IIS_CONFIG_CLIENTCACHE L"clientCache"
98 +#define IIS_CONFIG_CACHECONTROLMODE L"cacheControlMode"
99 +#define IIS_CONFIG_USEMAXAGE L"UseMaxAge"
100 +#define IIS_CONFIG_USEEXPIRES L"UseExpires"
101 +#define IIS_CONFIG_CACHECUST L"cacheControlCustom"
102 +#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp"
103 +#define IIS_CONFIG_SESSION L"session"
104 +#define IIS_CONFIG_ALLOWSTATE L"allowSessionState"
105 +#define IIS_CONFIG_TIMEOUT L"timeout"
106 +#define IIS_CONFIG_BUFFERING L"bufferingOn"
107 +#define IIS_CONFIG_PARENTPATHS L"enableParentPaths"
108 +#define IIS_CONFIG_SCRIPTLANG L"scriptLanguage"
109 +#define IIS_CONFIG_SCRIPTTIMEOUT L"scriptTimeout"
110 +#define IIS_CONFIG_LIMITS L"limits"
111 +#define IIS_CONFIG_ALLOWDEBUG L"appAllowDebugging"
112 +#define IIS_CONFIG_ALLOWCLIENTDEBUG L"appAllowClientDebug"
113 +#define IIS_CONFIG_CERTIFICATEHASH L"certificateHash"
114 +#define IIS_CONFIG_CERTIFICATESTORENAME L"certificateStoreName"
115 +#define IIS_CONFIG_HTTPLOGGING_SECTION L"system.webServer/httpLogging"
116 +#define IIS_CONFIG_DONTLOG L"dontLog"
117 +
118 +typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID);
119 +typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*);
120 +
121 +HRESULT DAPI Iis7PutPropertyVariant(
122 + __in IAppHostElement *pElement,
123 + __in LPCWSTR wzPropName,
124 + __in VARIANT vtPut
125 + );
126 +
127 +HRESULT DAPI Iis7PutPropertyInteger(
128 + __in IAppHostElement *pElement,
129 + __in LPCWSTR wzPropName,
130 + __in DWORD dValue
131 + );
132 +
133 +HRESULT DAPI Iis7PutPropertyString(
134 + __in IAppHostElement *pElement,
135 + __in LPCWSTR wzPropName,
136 + __in LPCWSTR wzString
137 + );
138 +
139 +HRESULT DAPI Iis7PutPropertyBool(
140 + __in IAppHostElement *pElement,
141 + __in LPCWSTR wzPropName,
142 + __in BOOL fValue);
143 +
144 +HRESULT DAPI Iis7GetPropertyVariant(
145 + __in IAppHostElement *pElement,
146 + __in LPCWSTR wzPropName,
147 + __in VARIANT* vtGet
148 + );
149 +
150 +HRESULT DAPI Iis7GetPropertyString(
151 + __in IAppHostElement *pElement,
152 + __in LPCWSTR wzPropName,
153 + __in LPWSTR* psczGet
154 + );
155 +
156 +struct IIS7_APPHOSTELEMENTCOMPARISON
157 +{
158 + LPCWSTR sczElementName;
159 + LPCWSTR sczAttributeName;
160 + VARIANT* pvAttributeValue;
161 + VARIANTCOMPARATORPROC pComparator;
162 +};
163 +
164 +BOOL DAPI Iis7IsMatchingAppHostElement(
165 + __in IAppHostElement *pElement,
166 + __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison
167 + );
168 +
169 +HRESULT DAPI Iis7FindAppHostElementString(
170 + __in IAppHostElementCollection *pCollection,
171 + __in LPCWSTR wzElementName,
172 + __in LPCWSTR wzAttributeName,
173 + __in LPCWSTR wzAttributeValue,
174 + __out IAppHostElement** ppElement,
175 + __out DWORD* pdwIndex
176 + );
177 +
178 +HRESULT DAPI Iis7FindAppHostElementPath(
179 + __in IAppHostElementCollection *pCollection,
180 + __in LPCWSTR wzElementName,
181 + __in LPCWSTR wzAttributeName,
182 + __in LPCWSTR wzAttributeValue,
183 + __out IAppHostElement** ppElement,
184 + __out DWORD* pdwIndex
185 + );
186 +
187 +HRESULT DAPI Iis7FindAppHostElementInteger(
188 + __in IAppHostElementCollection *pCollection,
189 + __in LPCWSTR wzElementName,
190 + __in LPCWSTR wzAttributeName,
191 + __in DWORD dwAttributeValue,
192 + __out IAppHostElement** ppElement,
193 + __out DWORD* pdwIndex
194 + );
195 +
196 +HRESULT DAPI Iis7FindAppHostElementVariant(
197 + __in IAppHostElementCollection *pCollection,
198 + __in LPCWSTR wzElementName,
199 + __in LPCWSTR wzAttributeName,
200 + __in VARIANT* pvAttributeValue,
201 + __out IAppHostElement** ppElement,
202 + __out DWORD* pdwIndex
203 + );
204 +
205 +HRESULT DAPI Iis7EnumAppHostElements(
206 + __in IAppHostElementCollection *pCollection,
207 + __in ENUMAPHOSTELEMENTPROC pCallback,
208 + __in LPVOID pContext,
209 + __out IAppHostElement** ppElement,
210 + __out DWORD* pdwIndex
211 + );
212 +
213 +HRESULT DAPI Iis7FindAppHostMethod(
214 + __in IAppHostMethodCollection *pCollection,
215 + __in LPCWSTR wzMethodName,
216 + __out IAppHostMethod** ppMethod,
217 + __out DWORD* pdwIndex
218 + );
219 +
220 +#ifdef __cplusplus
221 +}
222 +#endif
src/dutil/inc/inetutil.h new
+39
@@ -0,0 +1,39 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; }
10 +#define ReleaseNullInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; }
11 +
12 +
13 +// functions
14 +HRESULT DAPI InternetGetSizeByHandle(
15 + __in HINTERNET hiFile,
16 + __out LONGLONG* pllSize
17 + );
18 +
19 +HRESULT DAPI InternetGetCreateTimeByHandle(
20 + __in HINTERNET hiFile,
21 + __out LPFILETIME pft
22 + );
23 +
24 +HRESULT DAPI InternetQueryInfoString(
25 + __in HINTERNET h,
26 + __in DWORD dwInfo,
27 + __deref_out_z LPWSTR* psczValue
28 + );
29 +
30 +HRESULT DAPI InternetQueryInfoNumber(
31 + __in HINTERNET h,
32 + __in DWORD dwInfo,
33 + __out LONG* plInfo
34 + );
35 +
36 +#ifdef __cplusplus
37 +}
38 +#endif
39 +
src/dutil/inc/iniutil.h new
+79
@@ -0,0 +1,79 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); }
10 +#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; }
11 +
12 +typedef void* INI_HANDLE;
13 +typedef const void* C_INI_HANDLE;
14 +
15 +extern const int INI_HANDLE_BYTES;
16 +
17 +struct INI_VALUE
18 +{
19 + LPCWSTR wzName;
20 + LPCWSTR wzValue;
21 +
22 + DWORD dwLineNumber;
23 +};
24 +
25 +HRESULT DAPI IniInitialize(
26 + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle
27 + );
28 +void DAPI IniUninitialize(
29 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle
30 + );
31 +HRESULT DAPI IniSetOpenTag(
32 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
33 + __in_z_opt LPCWSTR wzOpenTagPrefix,
34 + __in_z_opt LPCWSTR wzOpenTagPostfix
35 + );
36 +HRESULT DAPI IniSetValueStyle(
37 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
38 + __in_z_opt LPCWSTR wzValuePrefix,
39 + __in_z_opt LPCWSTR wzValueSeparator
40 + );
41 +HRESULT DAPI IniSetValueSeparatorException(
42 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
43 + __in_z LPCWSTR wzValueNamePrefix
44 + );
45 +HRESULT DAPI IniSetCommentStyle(
46 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
47 + __in_z_opt LPCWSTR wzLinePrefix
48 + );
49 +HRESULT DAPI IniParse(
50 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
51 + __in LPCWSTR wzPath,
52 + __out_opt FILE_ENCODING *pfeEncodingFound
53 + );
54 +// Gets the full value array, this includes values that may have been deleted
55 +// (their value will be NULL)
56 +HRESULT DAPI IniGetValueList(
57 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
58 + __deref_out_ecount_opt(pcValues) INI_VALUE** prgivValues,
59 + __out DWORD *pcValues
60 + );
61 +HRESULT DAPI IniGetValue(
62 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
63 + __in LPCWSTR wzValueName,
64 + __deref_out_z LPWSTR* psczValue
65 + );
66 +HRESULT DAPI IniSetValue(
67 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
68 + __in LPCWSTR wzValueName,
69 + __in_z_opt LPCWSTR wzValue
70 + );
71 +HRESULT DAPI IniWriteFile(
72 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
73 + __in_z_opt LPCWSTR wzPath,
74 + __in FILE_ENCODING feOverrideEncoding
75 + );
76 +
77 +#ifdef __cplusplus
78 +}
79 +#endif
src/dutil/inc/jsonutil.h new
+112
@@ -0,0 +1,112 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +typedef enum JSON_TOKEN
10 +{
11 + JSON_TOKEN_NONE,
12 + JSON_TOKEN_ARRAY_START,
13 + JSON_TOKEN_ARRAY_VALUE,
14 + JSON_TOKEN_ARRAY_END,
15 + JSON_TOKEN_OBJECT_START,
16 + JSON_TOKEN_OBJECT_KEY,
17 + JSON_TOKEN_OBJECT_VALUE,
18 + JSON_TOKEN_OBJECT_END,
19 + JSON_TOKEN_VALUE,
20 +} JSON_TOKEN;
21 +
22 +typedef struct _JSON_VALUE
23 +{
24 +} JSON_VALUE;
25 +
26 +typedef struct _JSON_READER
27 +{
28 + CRITICAL_SECTION cs;
29 + LPWSTR sczJson;
30 +
31 + LPWSTR pwz;
32 + JSON_TOKEN token;
33 +} JSON_READER;
34 +
35 +typedef struct _JSON_WRITER
36 +{
37 + CRITICAL_SECTION cs;
38 + LPWSTR sczJson;
39 +
40 + JSON_TOKEN* rgTokenStack;
41 + DWORD cTokens;
42 + DWORD cMaxTokens;
43 +} JSON_WRITER;
44 +
45 +
46 +DAPI_(HRESULT) JsonInitializeReader(
47 + __in_z LPCWSTR wzJson,
48 + __in JSON_READER* pReader
49 + );
50 +
51 +DAPI_(void) JsonUninitializeReader(
52 + __in JSON_READER* pReader
53 + );
54 +
55 +DAPI_(HRESULT) JsonReadNext(
56 + __in JSON_READER* pReader,
57 + __out JSON_TOKEN* pToken,
58 + __out JSON_VALUE* pValue
59 + );
60 +
61 +DAPI_(HRESULT) JsonReadValue(
62 + __in JSON_READER* pReader,
63 + __in JSON_VALUE* pValue
64 + );
65 +
66 +DAPI_(HRESULT) JsonInitializeWriter(
67 + __in JSON_WRITER* pWriter
68 + );
69 +
70 +DAPI_(void) JsonUninitializeWriter(
71 + __in JSON_WRITER* pWriter
72 + );
73 +
74 +DAPI_(HRESULT) JsonWriteBool(
75 + __in JSON_WRITER* pWriter,
76 + __in BOOL fValue
77 + );
78 +
79 +DAPI_(HRESULT) JsonWriteNumber(
80 + __in JSON_WRITER* pWriter,
81 + __in DWORD dwValue
82 + );
83 +
84 +DAPI_(HRESULT) JsonWriteString(
85 + __in JSON_WRITER* pWriter,
86 + __in_z LPCWSTR wzValue
87 + );
88 +
89 +DAPI_(HRESULT) JsonWriteArrayStart(
90 + __in JSON_WRITER* pWriter
91 + );
92 +
93 +DAPI_(HRESULT) JsonWriteArrayEnd(
94 + __in JSON_WRITER* pWriter
95 + );
96 +
97 +DAPI_(HRESULT) JsonWriteObjectStart(
98 + __in JSON_WRITER* pWriter
99 + );
100 +
101 +DAPI_(HRESULT) JsonWriteObjectKey(
102 + __in JSON_WRITER* pWriter,
103 + __in_z LPCWSTR wzKey
104 + );
105 +
106 +DAPI_(HRESULT) JsonWriteObjectEnd(
107 + __in JSON_WRITER* pWriter
108 + );
109 +
110 +#ifdef __cplusplus
111 +}
112 +#endif
src/dutil/inc/locutil.h new
+120
@@ -0,0 +1,120 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +struct LOC_STRING
10 +{
11 + LPWSTR wzId;
12 + LPWSTR wzText;
13 + BOOL bOverridable;
14 +};
15 +
16 +const int LOC_CONTROL_NOT_SET = INT_MAX;
17 +
18 +struct LOC_CONTROL
19 +{
20 + LPWSTR wzControl;
21 + int nX;
22 + int nY;
23 + int nWidth;
24 + int nHeight;
25 + LPWSTR wzText;
26 +};
27 +
28 +const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX;
29 +
30 +struct WIX_LOCALIZATION
31 +{
32 + DWORD dwLangId;
33 +
34 + DWORD cLocStrings;
35 + LOC_STRING* rgLocStrings;
36 +
37 + DWORD cLocControls;
38 + LOC_CONTROL* rgLocControls;
39 +};
40 +
41 +/********************************************************************
42 + LocProbeForFile - Searches for a localization file on disk.
43 +
44 +*******************************************************************/
45 +HRESULT DAPI LocProbeForFile(
46 + __in_z LPCWSTR wzBasePath,
47 + __in_z LPCWSTR wzLocFileName,
48 + __in_z_opt LPCWSTR wzLanguage,
49 + __inout LPWSTR* psczPath
50 + );
51 +
52 +/********************************************************************
53 + LocLoadFromFile - Loads a localization file
54 +
55 +*******************************************************************/
56 +HRESULT DAPI LocLoadFromFile(
57 + __in_z LPCWSTR wzWxlFile,
58 + __out WIX_LOCALIZATION** ppWixLoc
59 + );
60 +
61 +/********************************************************************
62 + LocLoadFromResource - loads a localization file from a module's data
63 + resource.
64 +
65 + NOTE: The resource data must be UTF-8 encoded.
66 +*******************************************************************/
67 +HRESULT DAPI LocLoadFromResource(
68 + __in HMODULE hModule,
69 + __in_z LPCSTR szResource,
70 + __out WIX_LOCALIZATION** ppWixLoc
71 + );
72 +
73 +/********************************************************************
74 + LocFree - free memory allocated when loading a localization file
75 +
76 +*******************************************************************/
77 +void DAPI LocFree(
78 + __in_opt WIX_LOCALIZATION* pWixLoc
79 + );
80 +
81 +/********************************************************************
82 + LocLocalizeString - replace any #(loc.id) in a string with the
83 + correct sub string
84 +*******************************************************************/
85 +HRESULT DAPI LocLocalizeString(
86 + __in const WIX_LOCALIZATION* pWixLoc,
87 + __inout LPWSTR* psczInput
88 + );
89 +
90 +/********************************************************************
91 + LocGetControl - returns a control's localization information
92 +*******************************************************************/
93 +HRESULT DAPI LocGetControl(
94 + __in const WIX_LOCALIZATION* pWixLoc,
95 + __in_z LPCWSTR wzId,
96 + __out LOC_CONTROL** ppLocControl
97 + );
98 +
99 +/********************************************************************
100 +LocGetString - returns a string's localization information
101 +*******************************************************************/
102 +extern "C" HRESULT DAPI LocGetString(
103 + __in const WIX_LOCALIZATION* pWixLoc,
104 + __in_z LPCWSTR wzId,
105 + __out LOC_STRING** ppLocString
106 + );
107 +
108 +/********************************************************************
109 +LocAddString - adds a localization string
110 +*******************************************************************/
111 +extern "C" HRESULT DAPI LocAddString(
112 + __in WIX_LOCALIZATION* pWixLoc,
113 + __in_z LPCWSTR wzId,
114 + __in_z LPCWSTR wzLocString,
115 + __in BOOL bOverridable
116 + );
117 +
118 +#ifdef __cplusplus
119 +}
120 +#endif
src/dutil/inc/logutil.h new
+190
@@ -0,0 +1,190 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define LogExitOnFailure(x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTrace(x, f, __VA_ARGS__); goto LExit; }
10 +
11 +#define LogExitOnRootFailure(x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTrace(x, f, __VA_ARGS__); goto LExit; }
12 +
13 +typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)(
14 + __in_z LPCSTR szString,
15 + __in_opt LPVOID pvContext
16 + );
17 +
18 +// enums
19 +
20 +// structs
21 +
22 +// functions
23 +BOOL DAPI IsLogInitialized();
24 +
25 +BOOL DAPI IsLogOpen();
26 +
27 +void DAPI LogInitialize(
28 + __in HMODULE hModule
29 + );
30 +
31 +HRESULT DAPI LogOpen(
32 + __in_z_opt LPCWSTR wzDirectory,
33 + __in_z LPCWSTR wzLog,
34 + __in_z_opt LPCWSTR wzPostfix,
35 + __in_z_opt LPCWSTR wzExt,
36 + __in BOOL fAppend,
37 + __in BOOL fHeader,
38 + __out_z_opt LPWSTR* psczLogPath
39 + );
40 +
41 +void DAPI LogDisable();
42 +
43 +void DAPI LogRedirect(
44 + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw,
45 + __in_opt LPVOID pvContext
46 + );
47 +
48 +HRESULT DAPI LogRename(
49 + __in_z LPCWSTR wzNewPath
50 + );
51 +
52 +void DAPI LogClose(
53 + __in BOOL fFooter
54 + );
55 +
56 +void DAPI LogUninitialize(
57 + __in BOOL fFooter
58 + );
59 +
60 +BOOL DAPI LogIsOpen();
61 +
62 +HRESULT DAPI LogSetSpecialParams(
63 + __in_z_opt LPCWSTR wzSpecialBeginLine,
64 + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp,
65 + __in_z_opt LPCWSTR wzSpecialEndLine
66 + );
67 +
68 +REPORT_LEVEL DAPI LogSetLevel(
69 + __in REPORT_LEVEL rl,
70 + __in BOOL fLogChange
71 + );
72 +
73 +REPORT_LEVEL DAPI LogGetLevel();
74 +
75 +HRESULT DAPI LogGetPath(
76 + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath,
77 + __in DWORD cchLogPath
78 + );
79 +
80 +HANDLE DAPI LogGetHandle();
81 +
82 +HRESULT DAPIV LogString(
83 + __in REPORT_LEVEL rl,
84 + __in_z __format_string LPCSTR szFormat,
85 + ...
86 + );
87 +
88 +HRESULT DAPI LogStringArgs(
89 + __in REPORT_LEVEL rl,
90 + __in_z __format_string LPCSTR szFormat,
91 + __in va_list args
92 + );
93 +
94 +HRESULT DAPIV LogStringLine(
95 + __in REPORT_LEVEL rl,
96 + __in_z __format_string LPCSTR szFormat,
97 + ...
98 + );
99 +
100 +HRESULT DAPI LogStringLineArgs(
101 + __in REPORT_LEVEL rl,
102 + __in_z __format_string LPCSTR szFormat,
103 + __in va_list args
104 + );
105 +
106 +HRESULT DAPI LogIdModuleArgs(
107 + __in REPORT_LEVEL rl,
108 + __in DWORD dwLogId,
109 + __in_opt HMODULE hModule,
110 + __in va_list args
111 + );
112 +
113 +/*
114 + * Wraps LogIdModuleArgs, so inline to save the function call
115 + */
116 +
117 +inline HRESULT LogId(
118 + __in REPORT_LEVEL rl,
119 + __in DWORD dwLogId,
120 + ...
121 + )
122 +{
123 + HRESULT hr = S_OK;
124 + va_list args;
125 +
126 + va_start(args, dwLogId);
127 + hr = LogIdModuleArgs(rl, dwLogId, NULL, args);
128 + va_end(args);
129 +
130 + return hr;
131 +}
132 +
133 +
134 +/*
135 + * Wraps LogIdModuleArgs, so inline to save the function call
136 + */
137 +
138 +inline HRESULT LogIdArgs(
139 + __in REPORT_LEVEL rl,
140 + __in DWORD dwLogId,
141 + __in va_list args
142 + )
143 +{
144 + return LogIdModuleArgs(rl, dwLogId, NULL, args);
145 +}
146 +
147 +HRESULT DAPIV LogErrorString(
148 + __in HRESULT hrError,
149 + __in_z __format_string LPCSTR szFormat,
150 + ...
151 + );
152 +
153 +HRESULT DAPI LogErrorStringArgs(
154 + __in HRESULT hrError,
155 + __in_z __format_string LPCSTR szFormat,
156 + __in va_list args
157 + );
158 +
159 +HRESULT DAPI LogErrorIdModule(
160 + __in HRESULT hrError,
161 + __in DWORD dwLogId,
162 + __in_opt HMODULE hModule,
163 + __in_z_opt LPCWSTR wzString1,
164 + __in_z_opt LPCWSTR wzString2,
165 + __in_z_opt LPCWSTR wzString3
166 + );
167 +
168 +inline HRESULT LogErrorId(
169 + __in HRESULT hrError,
170 + __in DWORD dwLogId,
171 + __in_z_opt LPCWSTR wzString1 = NULL,
172 + __in_z_opt LPCWSTR wzString2 = NULL,
173 + __in_z_opt LPCWSTR wzString3 = NULL
174 + )
175 +{
176 + return LogErrorIdModule(hrError, dwLogId, NULL, wzString1, wzString2, wzString3);
177 +}
178 +
179 +HRESULT DAPI LogHeader();
180 +
181 +HRESULT DAPI LogFooter();
182 +
183 +HRESULT LogStringWorkRaw(
184 + __in_z LPCSTR szLogData
185 + );
186 +
187 +#ifdef __cplusplus
188 +}
189 +#endif
190 +
src/dutil/inc/memutil.h new
+80
@@ -0,0 +1,80 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseMem(p) if (p) { MemFree(p); }
10 +#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; }
11 +
12 +HRESULT DAPI MemInitialize();
13 +void DAPI MemUninitialize();
14 +
15 +LPVOID DAPI MemAlloc(
16 + __in SIZE_T cbSize,
17 + __in BOOL fZero
18 + );
19 +LPVOID DAPI MemReAlloc(
20 + __in LPVOID pv,
21 + __in SIZE_T cbSize,
22 + __in BOOL fZero
23 + );
24 +HRESULT DAPI MemReAllocSecure(
25 + __in LPVOID pv,
26 + __in SIZE_T cbSize,
27 + __in BOOL fZero,
28 + __deref_out LPVOID* ppvNew
29 + );
30 +HRESULT DAPI MemAllocArray(
31 + __inout LPVOID* ppvArray,
32 + __in SIZE_T cbArrayType,
33 + __in DWORD dwItemCount
34 + );
35 +HRESULT DAPI MemReAllocArray(
36 + __inout LPVOID* ppvArray,
37 + __in DWORD cArray,
38 + __in SIZE_T cbArrayType,
39 + __in DWORD dwNewItemCount
40 + );
41 +HRESULT DAPI MemEnsureArraySize(
42 + __deref_out_bcount(cArray * cbArrayType) LPVOID* ppvArray,
43 + __in DWORD cArray,
44 + __in SIZE_T cbArrayType,
45 + __in DWORD dwGrowthCount
46 + );
47 +HRESULT DAPI MemInsertIntoArray(
48 + __deref_out_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray,
49 + __in DWORD dwInsertIndex,
50 + __in DWORD cInsertItems,
51 + __in DWORD cExistingArray,
52 + __in SIZE_T cbArrayType,
53 + __in DWORD dwGrowthCount
54 + );
55 +void DAPI MemRemoveFromArray(
56 + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray,
57 + __in DWORD dwRemoveIndex,
58 + __in DWORD cRemoveItems,
59 + __in DWORD cExistingArray,
60 + __in SIZE_T cbArrayType,
61 + __in BOOL fPreserveOrder
62 + );
63 +void DAPI MemArraySwapItems(
64 + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray,
65 + __in DWORD dwIndex1,
66 + __in DWORD dwIndex2,
67 + __in SIZE_T cbArrayType
68 + );
69 +
70 +HRESULT DAPI MemFree(
71 + __in LPVOID pv
72 + );
73 +SIZE_T DAPI MemSize(
74 + __in LPCVOID pv
75 + );
76 +
77 +#ifdef __cplusplus
78 +}
79 +#endif
80 +
src/dutil/inc/metautil.h new
+52
@@ -0,0 +1,52 @@
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 <iadmw.h>
6 +#include <iiscnfg.h>
7 +#include <iwamreg.h>
8 +#include <mddefw.h>
9 +
10 +#ifdef __cplusplus
11 +extern "C" {
12 +#endif
13 +
14 +// structs
15 +
16 +// prototypes
17 +HRESULT DAPI MetaFindWebBase(
18 + __in IMSAdminBaseW* piMetabase,
19 + __in_z LPCWSTR wzIP,
20 + __in int iPort,
21 + __in_z LPCWSTR wzHeader,
22 + __in BOOL fSecure,
23 + __out_ecount(cchWebBase) LPWSTR wzWebBase,
24 + __in DWORD cchWebBase
25 + );
26 +HRESULT DAPI MetaFindFreeWebBase(
27 + __in IMSAdminBaseW* piMetabase,
28 + __out_ecount(cchWebBase) LPWSTR wzWebBase,
29 + __in DWORD cchWebBase
30 + );
31 +
32 +HRESULT DAPI MetaOpenKey(
33 + __in IMSAdminBaseW* piMetabase,
34 + __in METADATA_HANDLE mhKey,
35 + __in_z LPCWSTR wzKey,
36 + __in DWORD dwAccess,
37 + __in DWORD cRetries,
38 + __out METADATA_HANDLE* pmh
39 + );
40 +HRESULT DAPI MetaGetValue(
41 + __in IMSAdminBaseW* piMetabase,
42 + __in METADATA_HANDLE mhKey,
43 + __in_z LPCWSTR wzKey,
44 + __inout METADATA_RECORD* pmr
45 + );
46 +void DAPI MetaFreeValue(
47 + __in METADATA_RECORD* pmr
48 + );
49 +
50 +#ifdef __cplusplus
51 +}
52 +#endif
src/dutil/inc/monutil.h new
+108
@@ -0,0 +1,108 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseMon(mh) if (mh) { MonDestroy(mh); }
10 +#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; }
11 +
12 +typedef void* MON_HANDLE;
13 +typedef const void* C_MON_HANDLE;
14 +
15 +// Defined in regutil.h
16 +enum REG_KEY_BITNESS;
17 +
18 +extern const int MON_HANDLE_BYTES;
19 +
20 +// Note: callbacks must be implemented in a thread-safe manner. They will be called asynchronously by a MonUtil-spawned thread.
21 +// They must also be written to return as soon as possible - they are called from the waiter thread
22 +typedef void (*PFN_MONGENERAL)(
23 + __in HRESULT hr,
24 + __in_opt LPVOID pvContext
25 + );
26 +// This callback is not specific to any wait - it will notify client of any drive status changes, such as removable drive insertion / removal
27 +typedef void (*PFN_MONDRIVESTATUS)(
28 + __in WCHAR chDrive,
29 + __in BOOL fArriving,
30 + __in_opt LPVOID pvContext
31 + );
32 +// Note if these fire with a failed result it means an error has occurred with the wait, and so the wait will stay in the list and be retried. When waits start succeeding again,
33 +// MonUtil will notify of changes, because it may have not noticed changes during the interval for which the wait had failed. This behavior can result in false positive notifications,
34 +// so all consumers of MonUtil should be designed with this in mind.
35 +typedef void (*PFN_MONDIRECTORY)(
36 + __in HRESULT hr,
37 + __in_z LPCWSTR wzPath,
38 + __in BOOL fRecursive,
39 + __in_opt LPVOID pvContext,
40 + __in_opt LPVOID pvDirectoryContext
41 + );
42 +typedef void (*PFN_MONREGKEY)(
43 + __in HRESULT hr,
44 + __in HKEY hkRoot,
45 + __in_z LPCWSTR wzSubKey,
46 + __in REG_KEY_BITNESS kbKeyBitness,
47 + __in BOOL fRecursive,
48 + __in_opt LPVOID pvContext,
49 + __in_opt LPVOID pvRegKeyContext
50 + );
51 +
52 +// Silence period allows you to avoid lots of notifications when a lot of writes are going on in a directory
53 +// MonUtil will wait until the directory has been "silent" for at least dwSilencePeriodInMs milliseconds
54 +// The drawback to setting this to a value higher than zero is that even single write notifications
55 +// are delayed by this amount
56 +HRESULT DAPI MonCreate(
57 + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle,
58 + __in PFN_MONGENERAL vpfMonGeneral,
59 + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus,
60 + __in_opt PFN_MONDIRECTORY vpfMonDirectory,
61 + __in_opt PFN_MONREGKEY vpfMonRegKey,
62 + __in_opt LPVOID pvContext
63 + );
64 +// Don't add multiple identical waits! Not only is it wasteful and will cause multiple fires for the exact same change, it will also
65 +// result in slightly odd behavior when you remove a duplicated wait (removing a wait may or may not remove multiple waits)
66 +// This is due to the way coordinator thread and waiter threads handle removing, and while it is possible to solve, doing so would complicate the code.
67 +// So instead, de-dupe your wait requests before sending them to MonUtil.
68 +// Special notes for network waits: MonUtil can send false positive notifications (i.e. notifications when nothing had changed) if connection
69 +// to the share is lost and reconnected, because MonUtil can't know for sure whether changes occurred while the connection was lost.
70 +// Also, MonUtil will very every 20 minutes retry even successful network waits, because the underlying Win32 API cannot notify us if a remote server
71 +// had its network cable unplugged or similar sudden failure. When we retry the successful network waits, we will also send a false positive notification,
72 +// because it's impossible for MonUtil to detect if we're reconnecting to a server that had died and come back to life, or if we're reconnecting to a server that had
73 +// been up all along. For both of the above reasons, clients of MonUtil must be written to do very, very little work in the case of false positive network waits.
74 +HRESULT DAPI MonAddDirectory(
75 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
76 + __in_z LPCWSTR wzPath,
77 + __in BOOL fRecursive,
78 + __in DWORD dwSilencePeriodInMs,
79 + __in_opt LPVOID pvDirectoryContext
80 + );
81 +HRESULT DAPI MonAddRegKey(
82 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
83 + __in HKEY hkRoot,
84 + __in_z LPCWSTR wzSubKey,
85 + __in REG_KEY_BITNESS kbKeyBitness,
86 + __in BOOL fRecursive,
87 + __in DWORD dwSilencePeriodInMs,
88 + __in_opt LPVOID pvRegKeyContext
89 + );
90 +HRESULT DAPI MonRemoveDirectory(
91 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
92 + __in_z LPCWSTR wzPath,
93 + __in BOOL fRecursive
94 + );
95 +HRESULT DAPI MonRemoveRegKey(
96 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
97 + __in HKEY hkRoot,
98 + __in_z LPCWSTR wzSubKey,
99 + __in REG_KEY_BITNESS kbKeyBitness,
100 + __in BOOL fRecursive
101 + );
102 +void DAPI MonDestroy(
103 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle
104 + );
105 +
106 +#ifdef __cplusplus
107 +}
108 +#endif
src/dutil/inc/osutil.h new
+39
@@ -0,0 +1,39 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +typedef enum OS_VERSION
10 +{
11 + OS_VERSION_UNKNOWN,
12 + OS_VERSION_WINNT,
13 + OS_VERSION_WIN2000,
14 + OS_VERSION_WINXP,
15 + OS_VERSION_WIN2003,
16 + OS_VERSION_VISTA,
17 + OS_VERSION_WIN2008,
18 + OS_VERSION_WIN7,
19 + OS_VERSION_WIN2008_R2,
20 + OS_VERSION_FUTURE
21 +} OS_VERSION;
22 +
23 +void DAPI OsGetVersion(
24 + __out OS_VERSION* pVersion,
25 + __out DWORD* pdwServicePack
26 + );
27 +HRESULT DAPI OsCouldRunPrivileged(
28 + __out BOOL* pfPrivileged
29 + );
30 +HRESULT DAPI OsIsRunningPrivileged(
31 + __out BOOL* pfPrivileged
32 + );
33 +HRESULT DAPI OsIsUacEnabled(
34 + __out BOOL* pfUacEnabled
35 + );
36 +
37 +#ifdef __cplusplus
38 +}
39 +#endif
src/dutil/inc/pathutil.h new
+232
@@ -0,0 +1,232 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +typedef enum PATH_EXPAND
10 +{
11 + PATH_EXPAND_ENVIRONMENT = 0x0001,
12 + PATH_EXPAND_FULLPATH = 0x0002,
13 +} PATH_EXPAND;
14 +
15 +
16 +/*******************************************************************
17 + PathCommandLineAppend - appends a command line argument on to a
18 + string such that ::CommandLineToArgv() will shred them correctly
19 + (i.e. quote arguments with spaces in them).
20 +********************************************************************/
21 +DAPI_(HRESULT) PathCommandLineAppend(
22 + __deref_out_z LPWSTR* psczCommandLine,
23 + __in_z LPCWSTR wzArgument
24 + );
25 +
26 +/*******************************************************************
27 + PathFile - returns a pointer to the file part of the path.
28 +********************************************************************/
29 +DAPI_(LPWSTR) PathFile(
30 + __in_z LPCWSTR wzPath
31 + );
32 +
33 +/*******************************************************************
34 + PathExtension - returns a pointer to the extension part of the path
35 + (including the dot).
36 +********************************************************************/
37 +DAPI_(LPCWSTR) PathExtension(
38 + __in_z LPCWSTR wzPath
39 + );
40 +
41 +/*******************************************************************
42 + PathGetDirectory - extracts the directory from a path.
43 +********************************************************************/
44 +DAPI_(HRESULT) PathGetDirectory(
45 + __in_z LPCWSTR wzPath,
46 + __out LPWSTR *psczDirectory
47 + );
48 +
49 +/*******************************************************************
50 + PathExpand - gets the full path to a file resolving environment
51 + variables along the way.
52 +********************************************************************/
53 +DAPI_(HRESULT) PathExpand(
54 + __out LPWSTR *psczFullPath,
55 + __in_z LPCWSTR wzRelativePath,
56 + __in DWORD dwResolveFlags
57 + );
58 +
59 +/*******************************************************************
60 + PathPrefix - prefixes a full path with \\?\ or \\?\UNC as
61 + appropriate.
62 +********************************************************************/
63 +DAPI_(HRESULT) PathPrefix(
64 + __inout LPWSTR *psczFullPath
65 + );
66 +
67 +/*******************************************************************
68 + PathFixedBackslashTerminate - appends a \ if path does not have it
69 + already, but fails if the buffer is
70 + insufficient.
71 +********************************************************************/
72 +DAPI_(HRESULT) PathFixedBackslashTerminate(
73 + __inout_ecount_z(cchPath) LPWSTR wzPath,
74 + __in DWORD_PTR cchPath
75 + );
76 +
77 +/*******************************************************************
78 + PathBackslashTerminate - appends a \ if path does not have it
79 + already.
80 +********************************************************************/
81 +DAPI_(HRESULT) PathBackslashTerminate(
82 + __inout LPWSTR* psczPath
83 + );
84 +
85 +/*******************************************************************
86 + PathForCurrentProcess - gets the full path to the currently executing
87 + process or (optionally) a module inside the process.
88 +********************************************************************/
89 +DAPI_(HRESULT) PathForCurrentProcess(
90 + __inout LPWSTR *psczFullPath,
91 + __in_opt HMODULE hModule
92 + );
93 +
94 +/*******************************************************************
95 + PathRelativeToModule - gets the name of a file in the same
96 + directory as the current process or (optionally) a module inside
97 + the process
98 +********************************************************************/
99 +DAPI_(HRESULT) PathRelativeToModule(
100 + __inout LPWSTR *psczFullPath,
101 + __in_opt LPCWSTR wzFileName,
102 + __in_opt HMODULE hModule
103 + );
104 +
105 +/*******************************************************************
106 + PathCreateTempFile
107 +
108 + Note: if wzDirectory is null, ::GetTempPath() will be used instead.
109 + if wzFileNameTemplate is null, GetTempFileName() will be used instead.
110 +*******************************************************************/
111 +DAPI_(HRESULT) PathCreateTempFile(
112 + __in_opt LPCWSTR wzDirectory,
113 + __in_opt __format_string LPCWSTR wzFileNameTemplate,
114 + __in DWORD dwUniqueCount,
115 + __in DWORD dwFileAttributes,
116 + __out_opt LPWSTR* psczTempFile,
117 + __out_opt HANDLE* phTempFile
118 + );
119 +
120 +/*******************************************************************
121 + PathCreateTimeBasedTempFile - creates an empty temp file based on current
122 + system time
123 +********************************************************************/
124 +DAPI_(HRESULT) PathCreateTimeBasedTempFile(
125 + __in_z_opt LPCWSTR wzDirectory,
126 + __in_z LPCWSTR wzPrefix,
127 + __in_z_opt LPCWSTR wzPostfix,
128 + __in_z LPCWSTR wzExtension,
129 + __deref_opt_out_z LPWSTR* psczTempFile,
130 + __out_opt HANDLE* phTempFile
131 + );
132 +
133 +/*******************************************************************
134 + PathCreateTempDirectory
135 +
136 + Note: if wzDirectory is null, ::GetTempPath() will be used instead.
137 +*******************************************************************/
138 +DAPI_(HRESULT) PathCreateTempDirectory(
139 + __in_opt LPCWSTR wzDirectory,
140 + __in __format_string LPCWSTR wzDirectoryNameTemplate,
141 + __in DWORD dwUniqueCount,
142 + __out LPWSTR* psczTempDirectory
143 + );
144 +
145 +/*******************************************************************
146 + PathGetKnownFolder - returns the path to a well-known shell folder
147 +
148 +*******************************************************************/
149 +DAPI_(HRESULT) PathGetKnownFolder(
150 + __in int csidl,
151 + __out LPWSTR* psczKnownFolder
152 + );
153 +
154 +/*******************************************************************
155 + PathIsAbsolute - returns true if the path is absolute; false
156 + otherwise.
157 +*******************************************************************/
158 +DAPI_(BOOL) PathIsAbsolute(
159 + __in_z LPCWSTR wzPath
160 + );
161 +
162 +/*******************************************************************
163 + PathConcat - like .NET's Path.Combine, lets you build up a path
164 + one piece -- file or directory -- at a time.
165 +*******************************************************************/
166 +DAPI_(HRESULT) PathConcat(
167 + __in_opt LPCWSTR wzPath1,
168 + __in_opt LPCWSTR wzPath2,
169 + __deref_out_z LPWSTR* psczCombined
170 + );
171 +
172 +/*******************************************************************
173 + PathEnsureQuoted - ensures that a path is quoted; optionally,
174 + this function also terminates a directory with a backslash
175 + if it is not already.
176 +*******************************************************************/
177 +DAPI_(HRESULT) PathEnsureQuoted(
178 + __inout LPWSTR* ppszPath,
179 + __in BOOL fDirectory
180 + );
181 +
182 +/*******************************************************************
183 + PathCompare - compares the fully expanded path of the two paths using
184 + ::CompareStringW().
185 +*******************************************************************/
186 +DAPI_(HRESULT) PathCompare(
187 + __in_z LPCWSTR wzPath1,
188 + __in_z LPCWSTR wzPath2,
189 + __out int* pnResult
190 + );
191 +
192 +/*******************************************************************
193 + PathCompress - sets the compression state on an existing file or
194 + directory. A no-op on file systems that don't
195 + support compression.
196 +*******************************************************************/
197 +DAPI_(HRESULT) PathCompress(
198 + __in_z LPCWSTR wzPath
199 + );
200 +
201 +/*******************************************************************
202 + PathGetHierarchyArray - allocates an array containing,
203 + in order, every parent directory of the specified path,
204 + ending with the actual input path
205 + This function also works with registry subkeys
206 +*******************************************************************/
207 +DAPI_(HRESULT) PathGetHierarchyArray(
208 + __in_z LPCWSTR wzPath,
209 + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczPathArray,
210 + __inout LPUINT pcPathArray
211 + );
212 +
213 +/*******************************************************************
214 + PathCanonicalizePath - wrapper around PathCanonicalizeW.
215 +*******************************************************************/
216 +DAPI_(HRESULT) PathCanonicalizePath(
217 + __in_z LPCWSTR wzPath,
218 + __deref_out_z LPWSTR* psczCanonicalized
219 + );
220 +
221 +/*******************************************************************
222 +PathDirectoryContainsPath - checks if wzPath is located inside
223 + wzDirectory.
224 +*******************************************************************/
225 +DAPI_(HRESULT) PathDirectoryContainsPath(
226 + __in_z LPCWSTR wzDirectory,
227 + __in_z LPCWSTR wzPath
228 + );
229 +
230 +#ifdef __cplusplus
231 +}
232 +#endif
src/dutil/inc/perfutil.h new
+24
@@ -0,0 +1,24 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +// structs
10 +
11 +
12 +// functions
13 +void DAPI PerfInitialize(
14 + );
15 +void DAPI PerfClickTime(
16 + __out_opt LARGE_INTEGER* pliElapsed
17 + );
18 +double DAPI PerfConvertToSeconds(
19 + __in const LARGE_INTEGER* pli
20 + );
21 +
22 +#ifdef __cplusplus
23 +}
24 +#endif
src/dutil/inc/polcutil.h new
+39
@@ -0,0 +1,39 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn";
10 +
11 +/********************************************************************
12 +PolcReadNumber - reads a number from policy.
13 +
14 +NOTE: S_FALSE returned if policy not set.
15 +NOTE: out is set to default on S_FALSE or any error.
16 +********************************************************************/
17 +HRESULT DAPI PolcReadNumber(
18 + __in_z LPCWSTR wzPolicyPath,
19 + __in_z LPCWSTR wzPolicyName,
20 + __in DWORD dwDefault,
21 + __out DWORD* pdw
22 + );
23 +
24 +/********************************************************************
25 +PolcReadString - reads a string from policy.
26 +
27 +NOTE: S_FALSE returned if policy not set.
28 +NOTE: out is set to default on S_FALSE or any error.
29 +********************************************************************/
30 +HRESULT DAPI PolcReadString(
31 + __in_z LPCWSTR wzPolicyPath,
32 + __in_z LPCWSTR wzPolicyName,
33 + __in_z_opt LPCWSTR wzDefault,
34 + __deref_out_z LPWSTR* pscz
35 + );
36 +
37 +#ifdef __cplusplus
38 +}
39 +#endif
src/dutil/inc/procutil.h new
+75
@@ -0,0 +1,75 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +// structs
10 +typedef struct _PROC_FILESYSTEMREDIRECTION
11 +{
12 + BOOL fDisabled;
13 + LPVOID pvRevertState;
14 +} PROC_FILESYSTEMREDIRECTION;
15 +
16 +HRESULT DAPI ProcElevated(
17 + __in HANDLE hProcess,
18 + __out BOOL* pfElevated
19 + );
20 +
21 +HRESULT DAPI ProcWow64(
22 + __in HANDLE hProcess,
23 + __out BOOL* pfWow64
24 + );
25 +HRESULT DAPI ProcDisableWowFileSystemRedirection(
26 + __in PROC_FILESYSTEMREDIRECTION* pfsr
27 + );
28 +HRESULT DAPI ProcRevertWowFileSystemRedirection(
29 + __in PROC_FILESYSTEMREDIRECTION* pfsr
30 + );
31 +
32 +HRESULT DAPI ProcExec(
33 + __in_z LPCWSTR wzExecutablePath,
34 + __in_z_opt LPCWSTR wzCommandLine,
35 + __in int nCmdShow,
36 + __out HANDLE *phProcess
37 + );
38 +HRESULT DAPI ProcExecute(
39 + __in_z LPWSTR wzCommand,
40 + __out HANDLE *phProcess,
41 + __out_opt HANDLE *phChildStdIn,
42 + __out_opt HANDLE *phChildStdOutErr
43 + );
44 +HRESULT DAPI ProcWaitForCompletion(
45 + __in HANDLE hProcess,
46 + __in DWORD dwTimeout,
47 + __out DWORD *pReturnCode
48 + );
49 +HRESULT DAPI ProcWaitForIds(
50 + __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
51 + __in DWORD cProcessIds,
52 + __in DWORD dwMilliseconds
53 + );
54 +HRESULT DAPI ProcCloseIds(
55 + __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
56 + __in DWORD cProcessIds
57 + );
58 +
59 +// following code in proc2utl.cpp due to dependency on PSAPI.DLL.
60 +HRESULT DAPI ProcFindAllIdsFromExeName(
61 + __in_z LPCWSTR wzExeName,
62 + __out DWORD** ppdwProcessIds,
63 + __out DWORD* pcProcessIds
64 + );
65 +
66 +// following code in proc3utl.cpp due to dependency on Wtsapi32.DLL.
67 +HRESULT DAPI ProcExecuteAsInteractiveUser(
68 + __in_z LPCWSTR wzExecutablePath,
69 + __in_z LPCWSTR wzCommand,
70 + __out HANDLE *phProcess
71 + );
72 +
73 +#ifdef __cplusplus
74 +}
75 +#endif
src/dutil/inc/regutil.h new
+233
@@ -0,0 +1,233 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +
10 +#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; }
11 +
12 +typedef enum REG_KEY_BITNESS
13 +{
14 + REG_KEY_DEFAULT = 0,
15 + REG_KEY_32BIT = 1,
16 + REG_KEY_64BIT = 2
17 +} REG_KEY_BITNESS;
18 +
19 +typedef LSTATUS (APIENTRY *PFN_REGCREATEKEYEXW)(
20 + __in HKEY hKey,
21 + __in LPCWSTR lpSubKey,
22 + __reserved DWORD Reserved,
23 + __in_opt LPWSTR lpClass,
24 + __in DWORD dwOptions,
25 + __in REGSAM samDesired,
26 + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
27 + __out PHKEY phkResult,
28 + __out_opt LPDWORD lpdwDisposition
29 + );
30 +typedef LSTATUS (APIENTRY *PFN_REGOPENKEYEXW)(
31 + __in HKEY hKey,
32 + __in_opt LPCWSTR lpSubKey,
33 + __reserved DWORD ulOptions,
34 + __in REGSAM samDesired,
35 + __out PHKEY phkResult
36 + );
37 +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)(
38 + __in HKEY hKey,
39 + __in LPCWSTR lpSubKey,
40 + __in REGSAM samDesired,
41 + __reserved DWORD Reserved
42 + );
43 +typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)(
44 + __in HKEY hKey,
45 + __in LPCWSTR lpSubKey
46 + );
47 +typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)(
48 + __in HKEY hKey,
49 + __in DWORD dwIndex,
50 + __out LPWSTR lpName,
51 + __inout LPDWORD lpcName,
52 + __reserved LPDWORD lpReserved,
53 + __inout LPWSTR lpClass,
54 + __inout_opt LPDWORD lpcClass,
55 + __out_opt PFILETIME lpftLastWriteTime
56 + );
57 +typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)(
58 + __in HKEY hKey,
59 + __in DWORD dwIndex,
60 + __out LPWSTR lpValueName,
61 + __inout LPDWORD lpcchValueName,
62 + __reserved LPDWORD lpReserved,
63 + __out_opt LPDWORD lpType,
64 + __out_opt LPBYTE lpData,
65 + __out_opt LPDWORD lpcbData
66 + );
67 +typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)(
68 + __in HKEY hKey,
69 + __out LPWSTR lpClass,
70 + __inout_opt LPDWORD lpcClass,
71 + __reserved LPDWORD lpReserved,
72 + __out_opt LPDWORD lpcSubKeys,
73 + __out_opt LPDWORD lpcMaxSubKeyLen,
74 + __out_opt LPDWORD lpcMaxClassLen,
75 + __out_opt LPDWORD lpcValues,
76 + __out_opt LPDWORD lpcMaxValueNameLen,
77 + __out_opt LPDWORD lpcMaxValueLen,
78 + __out_opt LPDWORD lpcbSecurityDescriptor,
79 + __out_opt PFILETIME lpftLastWriteTime
80 + );
81 +typedef LSTATUS (APIENTRY *PFN_REGQUERYVALUEEXW)(
82 + __in HKEY hKey,
83 + __in_opt LPCWSTR lpValueName,
84 + __reserved LPDWORD lpReserved,
85 + __out_opt LPDWORD lpType,
86 + __out_bcount_part_opt(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData,
87 + __inout_opt LPDWORD lpcbData
88 + );
89 +typedef LSTATUS (APIENTRY *PFN_REGSETVALUEEXW)(
90 + __in HKEY hKey,
91 + __in_opt LPCWSTR lpValueName,
92 + __reserved DWORD Reserved,
93 + __in DWORD dwType,
94 + __in_bcount_opt(cbData) CONST BYTE* lpData,
95 + __in DWORD cbData
96 + );
97 +typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)(
98 + __in HKEY hKey,
99 + __in_opt LPCWSTR lpValueName
100 + );
101 +
102 +HRESULT DAPI RegInitialize();
103 +void DAPI RegUninitialize();
104 +
105 +void DAPI RegFunctionOverride(
106 + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW,
107 + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW,
108 + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW,
109 + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW,
110 + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW,
111 + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
112 + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
113 + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
114 + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW
115 + );
116 +HRESULT DAPI RegCreate(
117 + __in HKEY hkRoot,
118 + __in_z LPCWSTR wzSubKey,
119 + __in DWORD dwAccess,
120 + __out HKEY* phk
121 + );
122 +HRESULT DAPI RegCreateEx(
123 + __in HKEY hkRoot,
124 + __in_z LPCWSTR wzSubKey,
125 + __in DWORD dwAccess,
126 + __in BOOL fVolatile,
127 + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes,
128 + __out HKEY* phk,
129 + __out_opt BOOL* pfCreated
130 + );
131 +HRESULT DAPI RegOpen(
132 + __in HKEY hkRoot,
133 + __in_z LPCWSTR wzSubKey,
134 + __in DWORD dwAccess,
135 + __out HKEY* phk
136 + );
137 +HRESULT DAPI RegDelete(
138 + __in HKEY hkRoot,
139 + __in_z LPCWSTR wzSubKey,
140 + __in REG_KEY_BITNESS kbKeyBitness,
141 + __in BOOL fDeleteTree
142 + );
143 +HRESULT DAPI RegKeyEnum(
144 + __in HKEY hk,
145 + __in DWORD dwIndex,
146 + __deref_out_z LPWSTR* psczKey
147 + );
148 +HRESULT DAPI RegValueEnum(
149 + __in HKEY hk,
150 + __in DWORD dwIndex,
151 + __deref_out_z LPWSTR* psczName,
152 + __out_opt DWORD *pdwType
153 + );
154 +HRESULT DAPI RegGetType(
155 + __in HKEY hk,
156 + __in_z_opt LPCWSTR wzName,
157 + __out DWORD *pdwType
158 + );
159 +HRESULT DAPI RegReadBinary(
160 + __in HKEY hk,
161 + __in_z_opt LPCWSTR wzName,
162 + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
163 + __out SIZE_T *pcbBuffer
164 + );
165 +HRESULT DAPI RegReadString(
166 + __in HKEY hk,
167 + __in_z_opt LPCWSTR wzName,
168 + __deref_out_z LPWSTR* psczValue
169 + );
170 +HRESULT DAPI RegReadStringArray(
171 + __in HKEY hk,
172 + __in_z_opt LPCWSTR wzName,
173 + __deref_out_ecount_opt(pcStrings) LPWSTR** prgsczStrings,
174 + __out DWORD *pcStrings
175 + );
176 +HRESULT DAPI RegReadVersion(
177 + __in HKEY hk,
178 + __in_z_opt LPCWSTR wzName,
179 + __out DWORD64* pdw64Version
180 + );
181 +HRESULT DAPI RegReadNumber(
182 + __in HKEY hk,
183 + __in_z_opt LPCWSTR wzName,
184 + __out DWORD* pdwValue
185 + );
186 +HRESULT DAPI RegReadQword(
187 + __in HKEY hk,
188 + __in_z_opt LPCWSTR wzName,
189 + __out DWORD64* pqwValue
190 + );
191 +HRESULT DAPI RegWriteBinary(
192 + __in HKEY hk,
193 + __in_z_opt LPCWSTR wzName,
194 + __in_bcount(cbBuffer) const BYTE *pbBuffer,
195 + __in DWORD cbBuffer
196 + );
197 +HRESULT DAPI RegWriteString(
198 + __in HKEY hk,
199 + __in_z_opt LPCWSTR wzName,
200 + __in_z_opt LPCWSTR wzValue
201 + );
202 +HRESULT DAPI RegWriteStringArray(
203 + __in HKEY hk,
204 + __in_z_opt LPCWSTR wzName,
205 + __in_ecount(cValues) LPWSTR *rgwzStrings,
206 + __in DWORD cStrings
207 + );
208 +HRESULT DAPI RegWriteStringFormatted(
209 + __in HKEY hk,
210 + __in_z_opt LPCWSTR wzName,
211 + __in __format_string LPCWSTR szFormat,
212 + ...
213 + );
214 +HRESULT DAPI RegWriteNumber(
215 + __in HKEY hk,
216 + __in_z_opt LPCWSTR wzName,
217 + __in DWORD dwValue
218 + );
219 +HRESULT DAPI RegWriteQword(
220 + __in HKEY hk,
221 + __in_z_opt LPCWSTR wzName,
222 + __in DWORD64 qwValue
223 + );
224 +HRESULT DAPI RegQueryKey(
225 + __in HKEY hk,
226 + __out_opt DWORD* pcSubKeys,
227 + __out_opt DWORD* pcValues
228 + );
229 +
230 +#ifdef __cplusplus
231 +}
232 +#endif
233 +
src/dutil/inc/resrutil.h new
+43
@@ -0,0 +1,43 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +HRESULT DAPI ResGetStringLangId(
10 + __in_opt LPCWSTR wzPath,
11 + __in UINT uID,
12 + __out WORD *pwLangId
13 + );
14 +
15 +HRESULT DAPI ResReadString(
16 + __in HINSTANCE hinst,
17 + __in UINT uID,
18 + __deref_out_z LPWSTR* ppwzString
19 + );
20 +
21 +HRESULT DAPI ResReadStringAnsi(
22 + __in HINSTANCE hinst,
23 + __in UINT uID,
24 + __deref_out_z LPSTR* ppszString
25 + );
26 +
27 +HRESULT DAPI ResReadData(
28 + __in_opt HINSTANCE hinst,
29 + __in_z LPCSTR szDataName,
30 + __deref_out_bcount(*pcb) PVOID *ppv,
31 + __out DWORD *pcb
32 + );
33 +
34 +HRESULT DAPI ResExportDataToFile(
35 + __in_z LPCSTR szDataName,
36 + __in_z LPCWSTR wzTargetFile,
37 + __in DWORD dwCreationDisposition
38 + );
39 +
40 +#ifdef __cplusplus
41 +}
42 +#endif
43 +
src/dutil/inc/reswutil.h new
+31
@@ -0,0 +1,31 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +HRESULT DAPI ResWriteString(
10 + __in_z LPCWSTR wzResourceFile,
11 + __in DWORD dwDataId,
12 + __in_z LPCWSTR wzData,
13 + __in WORD wLangId
14 + );
15 +
16 +HRESULT DAPI ResWriteData(
17 + __in_z LPCWSTR wzResourceFile,
18 + __in_z LPCSTR szDataName,
19 + __in PVOID pData,
20 + __in DWORD cbData
21 + );
22 +
23 +HRESULT DAPI ResImportDataFromFile(
24 + __in_z LPCWSTR wzTargetFile,
25 + __in_z LPCWSTR wzSourceFile,
26 + __in_z LPCSTR szDataName
27 + );
28 +
29 +#ifdef __cplusplus
30 +}
31 +#endif
src/dutil/inc/rexutil.h new
+54
@@ -0,0 +1,54 @@
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 <sys\stat.h>
6 +#include <fdi.h>
7 +
8 +#ifdef __cplusplus
9 +extern "C" {
10 +#endif
11 +
12 +// defines
13 +#define FILETABLESIZE 40
14 +
15 +// structs
16 +struct MEM_FILE
17 +{
18 + LPCBYTE vpStart;
19 + UINT uiCurrent;
20 + UINT uiLength;
21 +};
22 +
23 +typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE;
24 +
25 +typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
26 +typedef VOID (*REX_CALLBACK_WRITE)(UINT cb);
27 +
28 +
29 +struct FAKE_FILE // used __in internal file table
30 +{
31 + BOOL fUsed;
32 + FAKE_FILE_TYPE fftType;
33 + MEM_FILE mfFile; // State for memory file
34 + HANDLE hFile; // Handle for disk file
35 +};
36 +
37 +// functions
38 +HRESULT RexInitialize();
39 +void RexUninitialize();
40 +
41 +HRESULT RexExtract(
42 + __in_z LPCSTR szResource,
43 + __in_z LPCWSTR wzExtractId,
44 + __in_z LPCWSTR wzExtractDir,
45 + __in_z LPCWSTR wzExtractName,
46 + __in REX_CALLBACK_PROGRESS pfnProgress,
47 + __in REX_CALLBACK_WRITE pfnWrite,
48 + __in LPVOID pvContext
49 + );
50 +
51 +#ifdef __cplusplus
52 +}
53 +#endif
54 +
src/dutil/inc/rmutil.h new
+46
@@ -0,0 +1,46 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +typedef struct _RMU_SESSION *PRMU_SESSION;
10 +
11 +HRESULT DAPI RmuJoinSession(
12 + __out PRMU_SESSION *ppSession,
13 + __in_z LPCWSTR wzSessionKey
14 + );
15 +
16 +HRESULT DAPI RmuAddFile(
17 + __in PRMU_SESSION pSession,
18 + __in_z LPCWSTR wzPath
19 + );
20 +
21 +HRESULT DAPI RmuAddProcessById(
22 + __in PRMU_SESSION pSession,
23 + __in DWORD dwProcessId
24 + );
25 +
26 +HRESULT DAPI RmuAddProcessesByName(
27 + __in PRMU_SESSION pSession,
28 + __in_z LPCWSTR wzProcessName
29 + );
30 +
31 +HRESULT DAPI RmuAddService(
32 + __in PRMU_SESSION pSession,
33 + __in_z LPCWSTR wzServiceName
34 + );
35 +
36 +HRESULT DAPI RmuRegisterResources(
37 + __in PRMU_SESSION pSession
38 + );
39 +
40 +HRESULT DAPI RmuEndSession(
41 + __in PRMU_SESSION pSession
42 + );
43 +
44 +#ifdef __cplusplus
45 +}
46 +#endif
src/dutil/inc/rssutil.h new
+89
@@ -0,0 +1,89 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); }
10 +#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; }
11 +
12 +
13 +struct RSS_UNKNOWN_ATTRIBUTE
14 +{
15 + LPWSTR wzNamespace;
16 + LPWSTR wzAttribute;
17 + LPWSTR wzValue;
18 +
19 + RSS_UNKNOWN_ATTRIBUTE* pNext;
20 +};
21 +
22 +struct RSS_UNKNOWN_ELEMENT
23 +{
24 + LPWSTR wzNamespace;
25 + LPWSTR wzElement;
26 + LPWSTR wzValue;
27 +
28 + RSS_UNKNOWN_ATTRIBUTE* pAttributes;
29 + RSS_UNKNOWN_ELEMENT* pNext;
30 +};
31 +
32 +struct RSS_ITEM
33 +{
34 + LPWSTR wzTitle;
35 + LPWSTR wzLink;
36 + LPWSTR wzDescription;
37 +
38 + LPWSTR wzGuid;
39 + FILETIME ftPublished;
40 +
41 + LPWSTR wzEnclosureUrl;
42 + DWORD dwEnclosureSize;
43 + LPWSTR wzEnclosureType;
44 +
45 + RSS_UNKNOWN_ELEMENT* pUnknownElements;
46 +};
47 +
48 +struct RSS_CHANNEL
49 +{
50 + LPWSTR wzTitle;
51 + LPWSTR wzLink;
52 + LPWSTR wzDescription;
53 + DWORD dwTimeToLive;
54 +
55 + RSS_UNKNOWN_ELEMENT* pUnknownElements;
56 +
57 + DWORD cItems;
58 + RSS_ITEM rgItems[1];
59 +};
60 +
61 +HRESULT DAPI RssInitialize(
62 + );
63 +
64 +void DAPI RssUninitialize(
65 + );
66 +
67 +HRESULT DAPI RssParseFromString(
68 + __in_z LPCWSTR wzRssString,
69 + __out RSS_CHANNEL **ppChannel
70 + );
71 +
72 +HRESULT DAPI RssParseFromFile(
73 + __in_z LPCWSTR wzRssFile,
74 + __out RSS_CHANNEL **ppChannel
75 + );
76 +
77 +// Adding this until we have the updated specstrings.h
78 +#ifndef __in_xcount
79 +#define __in_xcount(size)
80 +#endif
81 +
82 +void DAPI RssFreeChannel(
83 + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
84 + );
85 +
86 +#ifdef __cplusplus
87 +}
88 +#endif
89 +
src/dutil/inc/sceutil.h new
+273
@@ -0,0 +1,273 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#include <sqlce_oledb.h>
10 +#include <sqlce_sync.h>
11 +#include <sqlce_err.h>
12 +
13 +typedef void* SCE_DATABASE_HANDLE;
14 +typedef void* SCE_ROW_HANDLE;
15 +typedef void* SCE_QUERY_HANDLE;
16 +typedef void* SCE_QUERY_RESULTS_HANDLE;
17 +
18 +extern const int SCE_ROW_HANDLE_BYTES;
19 +extern const int SCE_QUERY_HANDLE_BYTES;
20 +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES;
21 +
22 +#define ReleaseSceRow(prrh) if (prrh) { SceFreeRow(prrh); }
23 +#define ReleaseNullSceRow(prrh) if (prrh) { SceFreeRow(prrh); prrh = NULL; }
24 +#define ReleaseSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); }
25 +#define ReleaseNullSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); pqh = NULL; }
26 +#define ReleaseSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); }
27 +#define ReleaseNullSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); pqh = NULL; }
28 +
29 +struct SCE_COLUMN_SCHEMA
30 +{
31 + LPCWSTR wzName;
32 + DBTYPE dbtColumnType;
33 + DWORD dwLength;
34 + BOOL fPrimaryKey; // If this column is the primary key
35 + BOOL fNullable;
36 + BOOL fAutoIncrement;
37 + BOOL fDescending; // If this column should be descending when used in an index (default is ascending)
38 +
39 + LPWSTR wzRelationName;
40 + DWORD dwForeignKeyTable;
41 + DWORD dwForeignKeyColumn;
42 +};
43 +
44 +struct SCE_INDEX_SCHEMA
45 +{
46 + LPWSTR wzName;
47 +
48 + DWORD *rgColumns;
49 + DWORD cColumns;
50 +};
51 +
52 +struct SCE_TABLE_SCHEMA
53 +{
54 + LPCWSTR wzName;
55 + DWORD cColumns;
56 + SCE_COLUMN_SCHEMA *rgColumns;
57 +
58 + DWORD cIndexes;
59 + SCE_INDEX_SCHEMA *rgIndexes;
60 +
61 + // Internal to SCEUtil - consumers shouldn't access or modify
62 + // TODO: enforce / hide in a handle of some sort?
63 + IRowset *pIRowset;
64 + IRowsetChange *pIRowsetChange;
65 +};
66 +
67 +struct SCE_DATABASE_SCHEMA
68 +{
69 + DWORD cTables;
70 + SCE_TABLE_SCHEMA *rgTables;
71 +};
72 +
73 +struct SCE_DATABASE
74 +{
75 + SCE_DATABASE_HANDLE sdbHandle;
76 + SCE_DATABASE_SCHEMA *pdsSchema;
77 +};
78 +
79 +HRESULT DAPI SceCreateDatabase(
80 + __in_z LPCWSTR sczFile,
81 + __in_z_opt LPCWSTR wzSqlCeDllPath,
82 + __deref_out SCE_DATABASE **ppDatabase
83 + );
84 +HRESULT DAPI SceOpenDatabase(
85 + __in_z LPCWSTR sczFile,
86 + __in_z_opt LPCWSTR wzSqlCeDllPath,
87 + __in LPCWSTR wzSchemaType,
88 + __in DWORD dwExpectedVersion,
89 + __deref_out SCE_DATABASE **ppDatabase,
90 + __in BOOL fReadOnly
91 + );
92 +HRESULT DAPI SceEnsureDatabase(
93 + __in_z LPCWSTR sczFile,
94 + __in_z_opt LPCWSTR wzSqlCeDllPath,
95 + __in LPCWSTR wzSchemaType,
96 + __in DWORD dwExpectedVersion,
97 + __in SCE_DATABASE_SCHEMA *pdsSchema,
98 + __deref_out SCE_DATABASE **ppDatabase
99 + );
100 +HRESULT DAPI SceIsTableEmpty(
101 + __in SCE_DATABASE *pDatabase,
102 + __in DWORD dwTableIndex,
103 + __out BOOL *pfEmpty
104 + );
105 +HRESULT DAPI SceGetFirstRow(
106 + __in SCE_DATABASE *pDatabase,
107 + __in DWORD dwTableIndex,
108 + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
109 + );
110 +HRESULT DAPI SceGetNextRow(
111 + __in SCE_DATABASE *pDatabase,
112 + __in DWORD dwTableIndex,
113 + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
114 + );
115 +HRESULT DAPI SceBeginTransaction(
116 + __in SCE_DATABASE *pDatabase
117 + );
118 +HRESULT DAPI SceCommitTransaction(
119 + __in SCE_DATABASE *pDatabase
120 + );
121 +HRESULT DAPI SceRollbackTransaction(
122 + __in SCE_DATABASE *pDatabase
123 + );
124 +HRESULT DAPI SceDeleteRow(
125 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
126 + );
127 +HRESULT DAPI ScePrepareInsert(
128 + __in SCE_DATABASE *pDatabase,
129 + __in DWORD dwTableIndex,
130 + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
131 + );
132 +HRESULT DAPI SceFinishUpdate(
133 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
134 + );
135 +HRESULT DAPI SceSetColumnBinary(
136 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
137 + __in DWORD dwColumnIndex,
138 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
139 + __in SIZE_T cbBuffer
140 + );
141 +HRESULT DAPI SceSetColumnDword(
142 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
143 + __in DWORD dwColumnIndex,
144 + __in const DWORD dwValue
145 + );
146 +HRESULT DAPI SceSetColumnQword(
147 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
148 + __in DWORD dwColumnIndex,
149 + __in const DWORD64 qwValue
150 + );
151 +HRESULT DAPI SceSetColumnBool(
152 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
153 + __in DWORD dwColumnIndex,
154 + __in const BOOL fValue
155 + );
156 +HRESULT DAPI SceSetColumnString(
157 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
158 + __in DWORD dwColumnIndex,
159 + __in_z_opt LPCWSTR wzValue
160 + );
161 +HRESULT DAPI SceSetColumnSystemTime(
162 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
163 + __in DWORD dwColumnIndex,
164 + __in const SYSTEMTIME *pst
165 + );
166 +HRESULT DAPI SceSetColumnNull(
167 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
168 + __in DWORD dwColumnIndex
169 + );
170 +HRESULT DAPI SceGetColumnBinary(
171 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
172 + __in DWORD dwColumnIndex,
173 + __out_opt BYTE **ppbBuffer,
174 + __inout SIZE_T *pcbBuffer
175 + );
176 +HRESULT DAPI SceGetColumnDword(
177 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
178 + __in DWORD dwColumnIndex,
179 + __out DWORD *pdwValue
180 + );
181 +HRESULT DAPI SceGetColumnQword(
182 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
183 + __in DWORD dwColumnIndex,
184 + __out DWORD64 *pqwValue
185 + );
186 +HRESULT DAPI SceGetColumnBool(
187 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
188 + __in DWORD dwColumnIndex,
189 + __out BOOL *pfValue
190 + );
191 +HRESULT DAPI SceGetColumnString(
192 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
193 + __in DWORD dwColumnIndex,
194 + __out_z LPWSTR *psczValue
195 + );
196 +HRESULT DAPI SceGetColumnSystemTime(
197 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
198 + __in DWORD dwColumnIndex,
199 + __out SYSTEMTIME *pst
200 + );
201 +HRESULT DAPI SceBeginQuery(
202 + __in SCE_DATABASE *pDatabase,
203 + __in DWORD dwTableIndex,
204 + __in DWORD dwIndex,
205 + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle
206 + );
207 +HRESULT DAPI SceSetQueryColumnBinary(
208 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
209 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
210 + __in SIZE_T cbBuffer
211 + );
212 +HRESULT DAPI SceSetQueryColumnDword(
213 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
214 + __in const DWORD dwValue
215 + );
216 +HRESULT DAPI SceSetQueryColumnQword(
217 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
218 + __in const DWORD64 qwValue
219 + );
220 +HRESULT DAPI SceSetQueryColumnBool(
221 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
222 + __in const BOOL fValue
223 + );
224 +HRESULT DAPI SceSetQueryColumnString(
225 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
226 + __in_z_opt LPCWSTR wzString
227 + );
228 +HRESULT DAPI SceSetQueryColumnSystemTime(
229 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
230 + __in const SYSTEMTIME *pst
231 + );
232 +HRESULT DAPI SceSetQueryColumnEmpty(
233 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle
234 + );
235 +HRESULT DAPI SceRunQueryExact(
236 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle,
237 + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
238 + );
239 +HRESULT DAPI SceRunQueryRange(
240 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle,
241 + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle
242 + );
243 +HRESULT DAPI SceGetNextResultRow(
244 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle,
245 + __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
246 + );
247 +void DAPI SceCloseTable(
248 + __in SCE_TABLE_SCHEMA *pTable
249 + );
250 +// Returns whether the data in the database changed. Ignores schema changes.
251 +BOOL DAPI SceDatabaseChanged(
252 + __in SCE_DATABASE *pDatabase
253 + );
254 +// Resets the database changed flag
255 +void DAPI SceResetDatabaseChanged(
256 + __in SCE_DATABASE *pDatabase
257 + );
258 +HRESULT DAPI SceCloseDatabase(
259 + __in SCE_DATABASE *pDatabase
260 + );
261 +void DAPI SceFreeRow(
262 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle
263 + );
264 +void DAPI SceFreeQuery(
265 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle
266 + );
267 +void DAPI SceFreeQueryResults(
268 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle
269 + );
270 +
271 +#ifdef __cplusplus
272 +}
273 +#endif
src/dutil/inc/sczutil.h new
+30
@@ -0,0 +1,30 @@
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 +#ifdef __cplusplus
6 +class PSCZ
7 +{
8 +public:
9 + PSCZ() : m_scz(NULL) { }
10 +
11 + ~PSCZ() { ReleaseNullStr(m_scz); }
12 +
13 + operator LPWSTR() { return m_scz; }
14 +
15 + operator LPCWSTR() { return m_scz; }
16 +
17 + operator bool() { return NULL != m_scz; }
18 +
19 + LPWSTR* operator &() { return &m_scz; }
20 +
21 + bool operator !() { return !m_scz; }
22 +
23 + WCHAR operator *() { return *m_scz; }
24 +
25 + LPWSTR Detach() { LPWSTR scz = m_scz; m_scz = NULL; return scz; }
26 +
27 +private:
28 + LPWSTR m_scz;
29 +};
30 +#endif //__cplusplus
src/dutil/inc/shelutil.h new
+47
@@ -0,0 +1,47 @@
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 +#ifndef REFKNOWNFOLDERID
6 +#define REFKNOWNFOLDERID REFGUID
7 +#endif
8 +
9 +#ifdef __cplusplus
10 +extern "C" {
11 +#endif
12 +
13 +typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)(
14 + __inout LPSHELLEXECUTEINFOW lpExecInfo
15 + );
16 +
17 +void DAPI ShelFunctionOverride(
18 + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW
19 + );
20 +HRESULT DAPI ShelExec(
21 + __in_z LPCWSTR wzTargetPath,
22 + __in_opt LPCWSTR wzParameters,
23 + __in_opt LPCWSTR wzVerb,
24 + __in_opt LPCWSTR wzWorkingDirectory,
25 + __in int nShowCmd,
26 + __in_opt HWND hwndParent,
27 + __out_opt HANDLE* phProcess
28 + );
29 +HRESULT DAPI ShelExecUnelevated(
30 + __in_z LPCWSTR wzTargetPath,
31 + __in_z_opt LPCWSTR wzParameters,
32 + __in_z_opt LPCWSTR wzVerb,
33 + __in_z_opt LPCWSTR wzWorkingDirectory,
34 + __in int nShowCmd
35 + );
36 +HRESULT DAPI ShelGetFolder(
37 + __out_z LPWSTR* psczFolderPath,
38 + __in int csidlFolder
39 + );
40 +HRESULT DAPI ShelGetKnownFolder(
41 + __out_z LPWSTR* psczFolderPath,
42 + __in REFKNOWNFOLDERID rfidFolder
43 + );
44 +
45 +#ifdef __cplusplus
46 +}
47 +#endif
src/dutil/inc/sqlutil.h new
+136
@@ -0,0 +1,136 @@
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 <cguid.h>
6 +#include <oledberr.h>
7 +#include <sqloledb.h>
8 +
9 +
10 +#ifdef __cplusplus
11 +extern "C" {
12 +#endif
13 +
14 +// Adding this until the SQL annotations are published to specstrings.h
15 +#ifndef __sql_command
16 +#define __sql_command
17 +#endif
18 +
19 +// structs
20 +struct SQL_FILESPEC
21 +{
22 + WCHAR wzName[MAX_PATH];
23 + WCHAR wzFilename[MAX_PATH];
24 + WCHAR wzSize[MAX_PATH];
25 + WCHAR wzMaxSize[MAX_PATH];
26 + WCHAR wzGrow[MAX_PATH];
27 +};
28 +
29 +
30 +// functions
31 +HRESULT DAPI SqlConnectDatabase(
32 + __in_z LPCWSTR wzServer,
33 + __in_z LPCWSTR wzInstance,
34 + __in_z LPCWSTR wzDatabase,
35 + __in BOOL fIntegratedAuth,
36 + __in_z LPCWSTR wzUser,
37 + __in_z LPCWSTR wzPassword,
38 + __out IDBCreateSession** ppidbSession
39 + );
40 +HRESULT DAPI SqlStartTransaction(
41 + __in IDBCreateSession* pidbSession,
42 + __out IDBCreateCommand** ppidbCommand,
43 + __out ITransaction** ppit
44 + );
45 +HRESULT DAPI SqlEndTransaction(
46 + __in ITransaction* pit,
47 + __in BOOL fCommit
48 + );
49 +HRESULT DAPI SqlDatabaseExists(
50 + __in_z LPCWSTR wzServer,
51 + __in_z LPCWSTR wzInstance,
52 + __in_z LPCWSTR wzDatabase,
53 + __in BOOL fIntegratedAuth,
54 + __in_z LPCWSTR wzUser,
55 + __in_z LPCWSTR wzPassword,
56 + __out_opt BSTR* pbstrErrorDescription
57 + );
58 +HRESULT DAPI SqlSessionDatabaseExists(
59 + __in IDBCreateSession* pidbSession,
60 + __in_z LPCWSTR wzDatabase,
61 + __out_opt BSTR* pbstrErrorDescription
62 + );
63 +HRESULT DAPI SqlDatabaseEnsureExists(
64 + __in_z LPCWSTR wzServer,
65 + __in_z LPCWSTR wzInstance,
66 + __in_z LPCWSTR wzDatabase,
67 + __in BOOL fIntegratedAuth,
68 + __in_z LPCWSTR wzUser,
69 + __in_z LPCWSTR wzPassword,
70 + __in_opt const SQL_FILESPEC* psfDatabase,
71 + __in_opt const SQL_FILESPEC* psfLog,
72 + __out_opt BSTR* pbstrErrorDescription
73 + );
74 +HRESULT DAPI SqlSessionDatabaseEnsureExists(
75 + __in IDBCreateSession* pidbSession,
76 + __in_z LPCWSTR wzDatabase,
77 + __in_opt const SQL_FILESPEC* psfDatabase,
78 + __in_opt const SQL_FILESPEC* psfLog,
79 + __out_opt BSTR* pbstrErrorDescription
80 + );
81 +HRESULT DAPI SqlCreateDatabase(
82 + __in_z LPCWSTR wzServer,
83 + __in_z LPCWSTR wzInstance,
84 + __in_z LPCWSTR wzDatabase,
85 + __in BOOL fIntegratedAuth,
86 + __in_z LPCWSTR wzUser,
87 + __in_z LPCWSTR wzPassword,
88 + __in_opt const SQL_FILESPEC* psfDatabase,
89 + __in_opt const SQL_FILESPEC* psfLog,
90 + __out_opt BSTR* pbstrErrorDescription
91 + );
92 +HRESULT DAPI SqlSessionCreateDatabase(
93 + __in IDBCreateSession* pidbSession,
94 + __in_z LPCWSTR wzDatabase,
95 + __in_opt const SQL_FILESPEC* psfDatabase,
96 + __in_opt const SQL_FILESPEC* psfLog,
97 + __out_opt BSTR* pbstrErrorDescription
98 + );
99 +HRESULT DAPI SqlDropDatabase(
100 + __in_z LPCWSTR wzServer,
101 + __in_z LPCWSTR wzInstance,
102 + __in_z LPCWSTR wzDatabase,
103 + __in BOOL fIntegratedAuth,
104 + __in_z LPCWSTR wzUser,
105 + __in_z LPCWSTR wzPassword,
106 + __out_opt BSTR* pbstrErrorDescription
107 + );
108 +HRESULT DAPI SqlSessionDropDatabase(
109 + __in IDBCreateSession* pidbSession,
110 + __in_z LPCWSTR wzDatabase,
111 + __out_opt BSTR* pbstrErrorDescription
112 + );
113 +HRESULT DAPI SqlSessionExecuteQuery(
114 + __in IDBCreateSession* pidbSession,
115 + __in __sql_command LPCWSTR wzSql,
116 + __out_opt IRowset** ppirs,
117 + __out_opt DBROWCOUNT* pcRows,
118 + __out_opt BSTR* pbstrErrorDescription
119 + );
120 +HRESULT DAPI SqlCommandExecuteQuery(
121 + __in IDBCreateCommand* pidbCommand,
122 + __in __sql_command LPCWSTR wzSql,
123 + __out IRowset** ppirs,
124 + __out DBROWCOUNT* pcRows
125 + );
126 +HRESULT DAPI SqlGetErrorInfo(
127 + __in IUnknown* pObjectWithError,
128 + __in REFIID IID_InterfaceWithError,
129 + __in DWORD dwLocaleId,
130 + __out_opt BSTR* pbstrErrorSource,
131 + __out_opt BSTR* pbstrErrorDescription
132 + );
133 +
134 +#ifdef __cplusplus
135 +}
136 +#endif
src/dutil/inc/srputil.h new
+45
@@ -0,0 +1,45 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +
10 +typedef enum SRP_ACTION
11 +{
12 + SRP_ACTION_UNKNOWN,
13 + SRP_ACTION_UNINSTALL,
14 + SRP_ACTION_INSTALL,
15 + SRP_ACTION_MODIFY,
16 +} SRP_ACTION;
17 +
18 +
19 +/********************************************************************
20 + SrpInitialize - initializes system restore point functionality.
21 +
22 +*******************************************************************/
23 +DAPI_(HRESULT) SrpInitialize(
24 + __in BOOL fInitializeComSecurity
25 + );
26 +
27 +/********************************************************************
28 + SrpUninitialize - uninitializes system restore point functionality.
29 +
30 +*******************************************************************/
31 +DAPI_(void) SrpUninitialize();
32 +
33 +/********************************************************************
34 + SrpCreateRestorePoint - creates a system restore point.
35 +
36 +*******************************************************************/
37 +DAPI_(HRESULT) SrpCreateRestorePoint(
38 + __in_z LPCWSTR wzApplicationName,
39 + __in SRP_ACTION action
40 + );
41 +
42 +#ifdef __cplusplus
43 +}
44 +#endif
45 +
src/dutil/inc/strutil.h new
+311
@@ -0,0 +1,311 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseStr(pwz) if (pwz) { StrFree(pwz); }
10 +#define ReleaseNullStr(pwz) if (pwz) { StrFree(pwz); pwz = NULL; }
11 +#define ReleaseBSTR(bstr) if (bstr) { ::SysFreeString(bstr); }
12 +#define ReleaseNullBSTR(bstr) if (bstr) { ::SysFreeString(bstr); bstr = NULL; }
13 +#define ReleaseStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); } }
14 +#define ReleaseNullStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); c = 0; rg = NULL; } }
15 +#define ReleaseNullStrSecure(pwz) if (pwz) { StrSecureZeroFreeString(pwz); pwz = NULL; }
16 +
17 +#define DeclareConstBSTR(bstr_const, wz) const WCHAR bstr_const[] = { 0x00, 0x00, sizeof(wz)-sizeof(WCHAR), 0x00, wz }
18 +#define UseConstBSTR(bstr_const) const_cast<BSTR>(bstr_const + 4)
19 +
20 +HRESULT DAPI StrAlloc(
21 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
22 + __in DWORD_PTR cch
23 + );
24 +HRESULT DAPI StrAllocSecure(
25 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
26 + __in DWORD_PTR cch
27 + );
28 +HRESULT DAPI StrTrimCapacity(
29 + __deref_out_z LPWSTR* ppwz
30 + );
31 +HRESULT DAPI StrTrimWhitespace(
32 + __deref_out_z LPWSTR* ppwz,
33 + __in_z LPCWSTR wzSource
34 + );
35 +HRESULT DAPI StrAnsiAlloc(
36 + __deref_out_ecount_part(cch, 0) LPSTR* ppz,
37 + __in DWORD_PTR cch
38 + );
39 +HRESULT DAPI StrAnsiTrimCapacity(
40 + __deref_out_z LPSTR* ppz
41 + );
42 +HRESULT DAPI StrAnsiTrimWhitespace(
43 + __deref_out_z LPSTR* ppz,
44 + __in_z LPCSTR szSource
45 + );
46 +HRESULT DAPI StrAllocString(
47 + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
48 + __in_z LPCWSTR wzSource,
49 + __in DWORD_PTR cchSource
50 + );
51 +HRESULT DAPI StrAllocStringSecure(
52 + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
53 + __in_z LPCWSTR wzSource,
54 + __in DWORD_PTR cchSource
55 + );
56 +HRESULT DAPI StrAnsiAllocString(
57 + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
58 + __in_z LPCWSTR wzSource,
59 + __in DWORD_PTR cchSource,
60 + __in UINT uiCodepage
61 + );
62 +HRESULT DAPI StrAllocStringAnsi(
63 + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
64 + __in_z LPCSTR szSource,
65 + __in DWORD_PTR cchSource,
66 + __in UINT uiCodepage
67 + );
68 +HRESULT DAPI StrAnsiAllocStringAnsi(
69 + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
70 + __in_z LPCSTR szSource,
71 + __in DWORD_PTR cchSource
72 + );
73 +HRESULT DAPI StrAllocPrefix(
74 + __deref_out_z LPWSTR* ppwz,
75 + __in_z LPCWSTR wzPrefix,
76 + __in DWORD_PTR cchPrefix
77 + );
78 +HRESULT DAPI StrAllocConcat(
79 + __deref_out_z LPWSTR* ppwz,
80 + __in_z LPCWSTR wzSource,
81 + __in DWORD_PTR cchSource
82 + );
83 +HRESULT DAPI StrAllocConcatSecure(
84 + __deref_out_z LPWSTR* ppwz,
85 + __in_z LPCWSTR wzSource,
86 + __in DWORD_PTR cchSource
87 + );
88 +HRESULT DAPI StrAnsiAllocConcat(
89 + __deref_out_z LPSTR* ppz,
90 + __in_z LPCSTR pzSource,
91 + __in DWORD_PTR cchSource
92 + );
93 +HRESULT __cdecl StrAllocFormatted(
94 + __deref_out_z LPWSTR* ppwz,
95 + __in __format_string LPCWSTR wzFormat,
96 + ...
97 + );
98 +HRESULT __cdecl StrAllocConcatFormatted(
99 + __deref_out_z LPWSTR* ppwz,
100 + __in __format_string LPCWSTR wzFormat,
101 + ...
102 + );
103 +HRESULT __cdecl StrAllocFormattedSecure(
104 + __deref_out_z LPWSTR* ppwz,
105 + __in __format_string LPCWSTR wzFormat,
106 + ...
107 + );
108 +HRESULT __cdecl StrAnsiAllocFormatted(
109 + __deref_out_z LPSTR* ppsz,
110 + __in __format_string LPCSTR szFormat,
111 + ...
112 + );
113 +HRESULT DAPI StrAllocFormattedArgs(
114 + __deref_out_z LPWSTR* ppwz,
115 + __in __format_string LPCWSTR wzFormat,
116 + __in va_list args
117 + );
118 +HRESULT DAPI StrAllocFormattedArgsSecure(
119 + __deref_out_z LPWSTR* ppwz,
120 + __in __format_string LPCWSTR wzFormat,
121 + __in va_list args
122 + );
123 +HRESULT DAPI StrAnsiAllocFormattedArgs(
124 + __deref_out_z LPSTR* ppsz,
125 + __in __format_string LPCSTR szFormat,
126 + __in va_list args
127 + );
128 +HRESULT DAPI StrAllocFromError(
129 + __inout LPWSTR *ppwzMessage,
130 + __in HRESULT hrError,
131 + __in_opt HMODULE hModule,
132 + ...
133 + );
134 +
135 +HRESULT DAPI StrMaxLength(
136 + __in LPCVOID p,
137 + __out DWORD_PTR* pcch
138 + );
139 +HRESULT DAPI StrSize(
140 + __in LPCVOID p,
141 + __out DWORD_PTR* pcb
142 + );
143 +
144 +HRESULT DAPI StrFree(
145 + __in LPVOID p
146 + );
147 +
148 +
149 +HRESULT DAPI StrReplaceStringAll(
150 + __inout LPWSTR* ppwzOriginal,
151 + __in_z LPCWSTR wzOldSubString,
152 + __in_z LPCWSTR wzNewSubString
153 + );
154 +HRESULT DAPI StrReplaceString(
155 + __inout LPWSTR* ppwzOriginal,
156 + __inout DWORD* pdwStartIndex,
157 + __in_z LPCWSTR wzOldSubString,
158 + __in_z LPCWSTR wzNewSubString
159 + );
160 +
161 +HRESULT DAPI StrHexEncode(
162 + __in_ecount(cbSource) const BYTE* pbSource,
163 + __in DWORD_PTR cbSource,
164 + __out_ecount(cchDest) LPWSTR wzDest,
165 + __in DWORD_PTR cchDest
166 + );
167 +HRESULT DAPI StrAllocHexEncode(
168 + __in_ecount(cbSource) const BYTE* pbSource,
169 + __in DWORD_PTR cbSource,
170 + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest
171 + );
172 +HRESULT DAPI StrHexDecode(
173 + __in_z LPCWSTR wzSource,
174 + __out_bcount(cbDest) BYTE* pbDest,
175 + __in DWORD_PTR cbDest
176 + );
177 +HRESULT DAPI StrAllocHexDecode(
178 + __in_z LPCWSTR wzSource,
179 + __out_bcount(*pcbDest) BYTE** ppbDest,
180 + __out_opt DWORD* pcbDest
181 + );
182 +
183 +HRESULT DAPI StrAllocBase85Encode(
184 + __in_bcount_opt(cbSource) const BYTE* pbSource,
185 + __in DWORD_PTR cbSource,
186 + __deref_out_z LPWSTR* pwzDest
187 + );
188 +HRESULT DAPI StrAllocBase85Decode(
189 + __in_z LPCWSTR wzSource,
190 + __deref_out_bcount(*pcbDest) BYTE** hbDest,
191 + __out DWORD_PTR* pcbDest
192 + );
193 +
194 +HRESULT DAPI MultiSzLen(
195 + __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz,
196 + __out DWORD_PTR* pcch
197 + );
198 +HRESULT DAPI MultiSzPrepend(
199 + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz,
200 + __inout_opt DWORD_PTR *pcchMultiSz,
201 + __in __nullnullterminated LPCWSTR pwzInsert
202 + );
203 +HRESULT DAPI MultiSzFindSubstring(
204 + __in __nullnullterminated LPCWSTR pwzMultiSz,
205 + __in __nullnullterminated LPCWSTR pwzSubstring,
206 + __out_opt DWORD_PTR* pdwIndex,
207 + __deref_opt_out_z LPCWSTR* ppwzFoundIn
208 + );
209 +HRESULT DAPI MultiSzFindString(
210 + __in __nullnullterminated LPCWSTR pwzMultiSz,
211 + __in __nullnullterminated LPCWSTR pwzString,
212 + __out_opt DWORD_PTR* pdwIndex,
213 + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound
214 + );
215 +HRESULT DAPI MultiSzRemoveString(
216 + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
217 + __in DWORD_PTR dwIndex
218 + );
219 +HRESULT DAPI MultiSzInsertString(
220 + __deref_inout_z LPWSTR* ppwzMultiSz,
221 + __inout_opt DWORD_PTR *pcchMultiSz,
222 + __in DWORD_PTR dwIndex,
223 + __in_z LPCWSTR pwzInsert
224 + );
225 +HRESULT DAPI MultiSzReplaceString(
226 + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
227 + __in DWORD_PTR dwIndex,
228 + __in_z LPCWSTR pwzString
229 + );
230 +
231 +LPCWSTR DAPI wcsistr(
232 + __in_z LPCWSTR wzString,
233 + __in_z LPCWSTR wzCharSet
234 + );
235 +
236 +HRESULT DAPI StrStringToInt16(
237 + __in_z LPCWSTR wzIn,
238 + __in DWORD cchIn,
239 + __out SHORT* psOut
240 + );
241 +HRESULT DAPI StrStringToUInt16(
242 + __in_z LPCWSTR wzIn,
243 + __in DWORD cchIn,
244 + __out USHORT* pusOut
245 + );
246 +HRESULT DAPI StrStringToInt32(
247 + __in_z LPCWSTR wzIn,
248 + __in DWORD cchIn,
249 + __out INT* piOut
250 + );
251 +HRESULT DAPI StrStringToUInt32(
252 + __in_z LPCWSTR wzIn,
253 + __in DWORD cchIn,
254 + __out UINT* puiOut
255 + );
256 +HRESULT DAPI StrStringToInt64(
257 + __in_z LPCWSTR wzIn,
258 + __in DWORD cchIn,
259 + __out LONGLONG* pllOut
260 + );
261 +HRESULT DAPI StrStringToUInt64(
262 + __in_z LPCWSTR wzIn,
263 + __in DWORD cchIn,
264 + __out ULONGLONG* pullOut
265 + );
266 +void DAPI StrStringToUpper(
267 + __inout_z LPWSTR wzIn
268 + );
269 +void DAPI StrStringToLower(
270 + __inout_z LPWSTR wzIn
271 + );
272 +HRESULT DAPI StrAllocStringToUpperInvariant(
273 + __deref_out_z LPWSTR* pscz,
274 + __in_z LPCWSTR wzSource,
275 + __in int cchSource
276 + );
277 +HRESULT DAPI StrAllocStringToLowerInvariant(
278 + __deref_out_z LPWSTR* pscz,
279 + __in_z LPCWSTR wzSource,
280 + __in int cchSource
281 + );
282 +
283 +HRESULT DAPI StrArrayAllocString(
284 + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
285 + __inout LPUINT pcStrArray,
286 + __in_z LPCWSTR wzSource,
287 + __in DWORD_PTR cchSource
288 + );
289 +
290 +HRESULT DAPI StrArrayFree(
291 + __in_ecount(cStrArray) LPWSTR *rgsczStrArray,
292 + __in UINT cStrArray
293 + );
294 +
295 +HRESULT DAPI StrSplitAllocArray(
296 + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
297 + __inout LPUINT pcStrArray,
298 + __in_z LPCWSTR wzSource,
299 + __in_z LPCWSTR wzDelim
300 + );
301 +
302 +HRESULT DAPI StrSecureZeroString(
303 + __in LPWSTR pwz
304 + );
305 +HRESULT DAPI StrSecureZeroFreeString(
306 + __in LPWSTR pwz
307 + );
308 +
309 +#ifdef __cplusplus
310 +}
311 +#endif
src/dutil/inc/svcutil.h new
+21
@@ -0,0 +1,21 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +
10 +#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; }
11 +
12 +
13 +HRESULT DAPI SvcQueryConfig(
14 + __in SC_HANDLE sch,
15 + __out QUERY_SERVICE_CONFIGW** ppConfig
16 + );
17 +
18 +
19 +#ifdef __cplusplus
20 +}
21 +#endif
src/dutil/inc/thmutil.h new
+711
@@ -0,0 +1,711 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; }
10 +
11 +typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)(
12 + __in_z LPCWSTR wzCondition,
13 + __out BOOL* pf,
14 + __in_opt LPVOID pvContext
15 + );
16 +typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)(
17 + __in_z LPCWSTR wzFormat,
18 + __inout LPWSTR* psczOut,
19 + __in_opt LPVOID pvContext
20 + );
21 +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)(
22 + __in_z LPCWSTR wzVariable,
23 + __out LONGLONG* pllValue,
24 + __in_opt LPVOID pvContext
25 + );
26 +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)(
27 + __in_z LPCWSTR wzVariable,
28 + __in LONGLONG llValue,
29 + __in_opt LPVOID pvContext
30 + );
31 +typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)(
32 + __in_z LPCWSTR wzVariable,
33 + __inout LPWSTR* psczValue,
34 + __in_opt LPVOID pvContext
35 + );
36 +typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)(
37 + __in_z LPCWSTR wzVariable,
38 + __in_z_opt LPCWSTR wzValue,
39 + __in_opt LPVOID pvContext
40 + );
41 +
42 +typedef enum THEME_ACTION_TYPE
43 +{
44 + THEME_ACTION_TYPE_BROWSE_DIRECTORY,
45 + THEME_ACTION_TYPE_CHANGE_PAGE,
46 + THEME_ACTION_TYPE_CLOSE_WINDOW,
47 +} THEME_ACTION_TYPE;
48 +
49 +typedef enum THEME_CONTROL_DATA
50 +{
51 + THEME_CONTROL_DATA_HOVER = 1,
52 +} THEME_CONTROL_DATA;
53 +
54 +typedef enum THEME_CONTROL_TYPE
55 +{
56 + THEME_CONTROL_TYPE_UNKNOWN,
57 + THEME_CONTROL_TYPE_BILLBOARD,
58 + THEME_CONTROL_TYPE_BUTTON,
59 + THEME_CONTROL_TYPE_CHECKBOX,
60 + THEME_CONTROL_TYPE_COMBOBOX,
61 + THEME_CONTROL_TYPE_COMMANDLINK,
62 + THEME_CONTROL_TYPE_EDITBOX,
63 + THEME_CONTROL_TYPE_HYPERLINK,
64 + THEME_CONTROL_TYPE_HYPERTEXT,
65 + THEME_CONTROL_TYPE_IMAGE,
66 + THEME_CONTROL_TYPE_LABEL,
67 + THEME_CONTROL_TYPE_PANEL,
68 + THEME_CONTROL_TYPE_PROGRESSBAR,
69 + THEME_CONTROL_TYPE_RADIOBUTTON,
70 + THEME_CONTROL_TYPE_RICHEDIT,
71 + THEME_CONTROL_TYPE_STATIC,
72 + THEME_CONTROL_TYPE_LISTVIEW,
73 + THEME_CONTROL_TYPE_TREEVIEW,
74 + THEME_CONTROL_TYPE_TAB,
75 +} THEME_CONTROL_TYPE;
76 +
77 +typedef enum THEME_SHOW_PAGE_REASON
78 +{
79 + THEME_SHOW_PAGE_REASON_DEFAULT,
80 + THEME_SHOW_PAGE_REASON_CANCEL,
81 + THEME_SHOW_PAGE_REASON_REFRESH,
82 +} THEME_SHOW_PAGE_REASON;
83 +
84 +
85 +struct THEME_COLUMN
86 +{
87 + LPWSTR pszName;
88 + UINT uStringId;
89 + int nBaseWidth;
90 + int nWidth;
91 + BOOL fExpands;
92 +};
93 +
94 +
95 +struct THEME_TAB
96 +{
97 + LPWSTR pszName;
98 + UINT uStringId;
99 +};
100 +
101 +struct THEME_ACTION
102 +{
103 + LPWSTR sczCondition;
104 + THEME_ACTION_TYPE type;
105 + union
106 + {
107 + struct
108 + {
109 + LPWSTR sczVariableName;
110 + } BrowseDirectory;
111 + struct
112 + {
113 + LPWSTR sczPageName;
114 + BOOL fCancel;
115 + } ChangePage;
116 + };
117 +};
118 +
119 +struct THEME_CONDITIONAL_TEXT
120 +{
121 + LPWSTR sczCondition;
122 + LPWSTR sczText;
123 +};
124 +
125 +// THEME_ASSIGN_CONTROL_ID - Used to apply a specific id to a named control (usually
126 +// to set the WM_COMMAND).
127 +struct THEME_ASSIGN_CONTROL_ID
128 +{
129 + WORD wId; // id to apply to control
130 + LPCWSTR wzName; // name of control to match
131 +};
132 +
133 +const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned.
134 +
135 +struct THEME_CONTROL
136 +{
137 + THEME_CONTROL_TYPE type;
138 +
139 + WORD wId;
140 + WORD wPageId;
141 +
142 + LPWSTR sczName; // optional name for control, used to apply control id and link the control to a variable.
143 + LPWSTR sczText;
144 + LPWSTR sczTooltip;
145 + LPWSTR sczNote; // optional text for command link
146 + int nX;
147 + int nY;
148 + int nHeight;
149 + int nWidth;
150 + int nSourceX;
151 + int nSourceY;
152 + UINT uStringId;
153 +
154 + LPWSTR sczEnableCondition;
155 + LPWSTR sczVisibleCondition;
156 + BOOL fDisableVariableFunctionality;
157 +
158 + HBITMAP hImage;
159 + HICON hIcon;
160 +
161 + // Don't free these; it's just a handle to the central image lists stored in THEME. The handle is freed once, there.
162 + HIMAGELIST rghImageList[4];
163 +
164 + DWORD dwStyle;
165 + DWORD dwExtendedStyle;
166 + DWORD dwInternalStyle;
167 +
168 + DWORD dwFontId;
169 +
170 + // child controls
171 + DWORD cControls;
172 + THEME_CONTROL* rgControls;
173 +
174 + // Used by billboard controls
175 + WORD wBillboardInterval;
176 + BOOL fBillboardLoops;
177 +
178 + // Used by button and command link controls
179 + THEME_ACTION* rgActions;
180 + DWORD cActions;
181 + THEME_ACTION* pDefaultAction;
182 +
183 + // Used by hyperlink and owner-drawn button controls
184 + DWORD dwFontHoverId;
185 + DWORD dwFontSelectedId;
186 +
187 + // Used by listview controls
188 + THEME_COLUMN *ptcColumns;
189 + DWORD cColumns;
190 +
191 + // Used by radio button controls
192 + BOOL fLastRadioButton;
193 + LPWSTR sczValue;
194 + LPWSTR sczVariable;
195 +
196 + // Used by tab controls
197 + THEME_TAB *pttTabs;
198 + DWORD cTabs;
199 +
200 + // Used by controls that have text
201 + DWORD cConditionalText;
202 + THEME_CONDITIONAL_TEXT* rgConditionalText;
203 +
204 + // Used by command link controls
205 + DWORD cConditionalNotes;
206 + THEME_CONDITIONAL_TEXT* rgConditionalNotes;
207 +
208 + // state variables that should be ignored
209 + HWND hWnd;
210 + DWORD dwData; // type specific data
211 +};
212 +
213 +
214 +struct THEME_IMAGELIST
215 +{
216 + LPWSTR sczName;
217 +
218 + HIMAGELIST hImageList;
219 +};
220 +
221 +struct THEME_SAVEDVARIABLE
222 +{
223 + LPWSTR wzName;
224 + LPWSTR sczValue;
225 +};
226 +
227 +struct THEME_PAGE
228 +{
229 + WORD wId;
230 + LPWSTR sczName;
231 +
232 + DWORD cControlIndices;
233 +
234 + DWORD cSavedVariables;
235 + THEME_SAVEDVARIABLE* rgSavedVariables;
236 +};
237 +
238 +struct THEME_FONT
239 +{
240 + HFONT hFont;
241 + COLORREF crForeground;
242 + HBRUSH hForeground;
243 + COLORREF crBackground;
244 + HBRUSH hBackground;
245 +};
246 +
247 +
248 +struct THEME
249 +{
250 + WORD wId;
251 +
252 + BOOL fAutoResize;
253 +
254 + DWORD dwStyle;
255 + DWORD dwFontId;
256 + HANDLE hIcon;
257 + LPWSTR sczCaption;
258 + int nHeight;
259 + int nMinimumHeight;
260 + int nWidth;
261 + int nMinimumWidth;
262 + int nSourceX;
263 + int nSourceY;
264 + UINT uStringId;
265 +
266 + HBITMAP hImage;
267 +
268 + DWORD cFonts;
269 + THEME_FONT* rgFonts;
270 +
271 + DWORD cPages;
272 + THEME_PAGE* rgPages;
273 +
274 + DWORD cImageLists;
275 + THEME_IMAGELIST* rgImageLists;
276 +
277 + DWORD cControls;
278 + THEME_CONTROL* rgControls;
279 +
280 + // internal state variables -- do not use outside ThmUtil.cpp
281 + HWND hwndParent; // parent for loaded controls
282 + HWND hwndHover; // current hwnd hovered over
283 + DWORD dwCurrentPageId;
284 + HWND hwndTooltip;
285 +
286 + // callback functions
287 + PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition;
288 + PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString;
289 + PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable;
290 + PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable;
291 + PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable;
292 + PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable;
293 +
294 + LPVOID pvVariableContext;
295 +};
296 +
297 +
298 +/********************************************************************
299 + ThemeInitialize - initialized theme management.
300 +
301 +*******************************************************************/
302 +DAPI_(HRESULT) ThemeInitialize(
303 + __in_opt HMODULE hModule
304 + );
305 +
306 +/********************************************************************
307 + ThemeUninitialize - uninitialize theme management.
308 +
309 +*******************************************************************/
310 +DAPI_(void) ThemeUninitialize();
311 +
312 +/********************************************************************
313 + ThemeLoadFromFile - loads a theme from a loose file.
314 +
315 + *******************************************************************/
316 +DAPI_(HRESULT) ThemeLoadFromFile(
317 + __in_z LPCWSTR wzThemeFile,
318 + __out THEME** ppTheme
319 + );
320 +
321 +/********************************************************************
322 + ThemeLoadFromResource - loads a theme from a module's data resource.
323 +
324 + NOTE: The resource data must be UTF-8 encoded.
325 +*******************************************************************/
326 +DAPI_(HRESULT) ThemeLoadFromResource(
327 + __in_opt HMODULE hModule,
328 + __in_z LPCSTR szResource,
329 + __out THEME** ppTheme
330 + );
331 +
332 +/********************************************************************
333 + ThemeFree - frees any memory associated with a theme.
334 +
335 +*******************************************************************/
336 +DAPI_(void) ThemeFree(
337 + __in THEME* pTheme
338 + );
339 +
340 +/********************************************************************
341 +ThemeRegisterVariableCallbacks - registers a context and callbacks
342 + for working with variables.
343 +
344 +*******************************************************************/
345 +DAPI_(HRESULT) ThemeRegisterVariableCallbacks(
346 + __in THEME* pTheme,
347 + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition,
348 + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString,
349 + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable,
350 + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable,
351 + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable,
352 + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable,
353 + __in_opt LPVOID pvContext
354 + );
355 +
356 +/********************************************************************
357 + ThemeLoadControls - creates the windows for all the theme controls.
358 +
359 +*******************************************************************/
360 +DAPI_(HRESULT) ThemeLoadControls(
361 + __in THEME* pTheme,
362 + __in HWND hwndParent,
363 + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
364 + __in DWORD cAssignControlIds
365 + );
366 +
367 +/********************************************************************
368 + ThemeUnloadControls - resets all the theme control windows so the theme
369 + controls can be reloaded.
370 +
371 +*******************************************************************/
372 +DAPI_(void) ThemeUnloadControls(
373 + __in THEME* pTheme
374 + );
375 +
376 +/********************************************************************
377 + ThemeLocalize - Localizes all of the strings in the theme.
378 +
379 +*******************************************************************/
380 +DAPI_(HRESULT) ThemeLocalize(
381 + __in THEME *pTheme,
382 + __in const WIX_LOCALIZATION *pLocStringSet
383 + );
384 +
385 +DAPI_(HRESULT) ThemeLoadStrings(
386 + __in THEME* pTheme,
387 + __in HMODULE hResModule
388 + );
389 +
390 +/********************************************************************
391 + ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file.
392 +
393 + *******************************************************************/
394 +DAPI_(HRESULT) ThemeLoadRichEditFromFile(
395 + __in THEME* pTheme,
396 + __in DWORD dwControl,
397 + __in_z LPCWSTR wzFileName,
398 + __in HMODULE hModule
399 + );
400 +
401 +/********************************************************************
402 + ThemeLoadRichEditFromResource - Attach a richedit control to resource data.
403 +
404 + *******************************************************************/
405 +DAPI_(HRESULT) ThemeLoadRichEditFromResource(
406 + __in THEME* pTheme,
407 + __in DWORD dwControl,
408 + __in_z LPCSTR szResourceName,
409 + __in HMODULE hModule
410 + );
411 +
412 +/********************************************************************
413 + ThemeLoadRichEditFromResourceToHWnd - Attach a richedit control (by
414 + HWND) to resource data.
415 +
416 + *******************************************************************/
417 +DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd(
418 + __in HWND hWnd,
419 + __in_z LPCSTR szResourceName,
420 + __in HMODULE hModule
421 + );
422 +
423 +/********************************************************************
424 + ThemeHandleKeyboardMessage - will translate the message using the active
425 + accelerator table.
426 +
427 +*******************************************************************/
428 +DAPI_(BOOL) ThemeHandleKeyboardMessage(
429 + __in_opt THEME* pTheme,
430 + __in HWND hWnd,
431 + __in MSG* pMsg
432 + );
433 +
434 +/********************************************************************
435 + ThemeDefWindowProc - replacement for DefWindowProc() when using theme.
436 +
437 +*******************************************************************/
438 +LRESULT CALLBACK ThemeDefWindowProc(
439 + __in_opt THEME* pTheme,
440 + __in HWND hWnd,
441 + __in UINT uMsg,
442 + __in WPARAM wParam,
443 + __in LPARAM lParam
444 + );
445 +
446 +/********************************************************************
447 + ThemeGetPageIds - gets the page ids for the theme via page names.
448 +
449 +*******************************************************************/
450 +DAPI_(void) ThemeGetPageIds(
451 + __in const THEME* pTheme,
452 + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames,
453 + __inout_ecount(cGetPages) DWORD* rgdwPageIds,
454 + __in DWORD cGetPages
455 + );
456 +
457 +/********************************************************************
458 + ThemeGetPage - gets a theme page by id.
459 +
460 + *******************************************************************/
461 +DAPI_(THEME_PAGE*) ThemeGetPage(
462 + __in const THEME* pTheme,
463 + __in DWORD dwPage
464 + );
465 +
466 +/********************************************************************
467 + ThemeShowPage - shows or hides all of the controls in the page at one time.
468 +
469 + *******************************************************************/
470 +DAPI_(HRESULT) ThemeShowPage(
471 + __in THEME* pTheme,
472 + __in DWORD dwPage,
473 + __in int nCmdShow
474 + );
475 +
476 +/********************************************************************
477 +ThemeShowPageEx - shows or hides all of the controls in the page at one time.
478 + When using variables, TSPR_CANCEL reverts any changes made.
479 + TSPR_REFRESH forces reevaluation of conditions.
480 + It is expected that the current page is hidden before
481 + showing a new page.
482 +
483 +*******************************************************************/
484 +DAPI_(HRESULT) ThemeShowPageEx(
485 + __in THEME* pTheme,
486 + __in DWORD dwPage,
487 + __in int nCmdShow,
488 + __in THEME_SHOW_PAGE_REASON reason
489 + );
490 +
491 +
492 +/********************************************************************
493 +ThemeShowChild - shows a control's specified child control, hiding the rest.
494 +
495 +*******************************************************************/
496 +DAPI_(void) ThemeShowChild(
497 + __in THEME* pTheme,
498 + __in THEME_CONTROL* pParentControl,
499 + __in DWORD dwIndex
500 + );
501 +
502 +/********************************************************************
503 + ThemeControlExists - check if a control with the specified id exists.
504 +
505 + *******************************************************************/
506 +DAPI_(BOOL) ThemeControlExists(
507 + __in const THEME* pTheme,
508 + __in DWORD dwControl
509 + );
510 +
511 +/********************************************************************
512 + ThemeControlEnable - enables/disables a control.
513 +
514 + *******************************************************************/
515 +DAPI_(void) ThemeControlEnable(
516 + __in THEME* pTheme,
517 + __in DWORD dwControl,
518 + __in BOOL fEnable
519 + );
520 +
521 +/********************************************************************
522 + ThemeControlEnabled - returns whether a control is enabled/disabled.
523 +
524 + *******************************************************************/
525 +DAPI_(BOOL) ThemeControlEnabled(
526 + __in THEME* pTheme,
527 + __in DWORD dwControl
528 + );
529 +
530 +/********************************************************************
531 + ThemeControlElevates - sets/removes the shield icon on a control.
532 +
533 + *******************************************************************/
534 +DAPI_(void) ThemeControlElevates(
535 + __in THEME* pTheme,
536 + __in DWORD dwControl,
537 + __in BOOL fElevates
538 + );
539 +
540 +/********************************************************************
541 + ThemeShowControl - shows/hides a control.
542 +
543 + *******************************************************************/
544 +DAPI_(void) ThemeShowControl(
545 + __in THEME* pTheme,
546 + __in DWORD dwControl,
547 + __in int nCmdShow
548 + );
549 +
550 +/********************************************************************
551 +ThemeShowControlEx - shows/hides a control with support for
552 +conditional text and notes.
553 +
554 +*******************************************************************/
555 +DAPI_(void) ThemeShowControlEx(
556 + __in THEME* pTheme,
557 + __in DWORD dwControl,
558 + __in int nCmdShow
559 + );
560 +
561 +/********************************************************************
562 + ThemeControlVisible - returns whether a control is visible.
563 +
564 + *******************************************************************/
565 +DAPI_(BOOL) ThemeControlVisible(
566 + __in THEME* pTheme,
567 + __in DWORD dwControl
568 + );
569 +
570 +DAPI_(BOOL) ThemePostControlMessage(
571 + __in THEME* pTheme,
572 + __in DWORD dwControl,
573 + __in UINT Msg,
574 + __in WPARAM wParam,
575 + __in LPARAM lParam
576 + );
577 +
578 +DAPI_(LRESULT) ThemeSendControlMessage(
579 + __in const THEME* pTheme,
580 + __in DWORD dwControl,
581 + __in UINT Msg,
582 + __in WPARAM wParam,
583 + __in LPARAM lParam
584 + );
585 +
586 +/********************************************************************
587 + ThemeDrawBackground - draws the theme background.
588 +
589 +*******************************************************************/
590 +DAPI_(HRESULT) ThemeDrawBackground(
591 + __in THEME* pTheme,
592 + __in PAINTSTRUCT* pps
593 + );
594 +
595 +/********************************************************************
596 + ThemeDrawControl - draw an owner drawn control.
597 +
598 +*******************************************************************/
599 +DAPI_(HRESULT) ThemeDrawControl(
600 + __in THEME* pTheme,
601 + __in DRAWITEMSTRUCT* pdis
602 + );
603 +
604 +/********************************************************************
605 + ThemeHoverControl - mark a control as hover.
606 +
607 +*******************************************************************/
608 +DAPI_(BOOL) ThemeHoverControl(
609 + __in THEME* pTheme,
610 + __in HWND hwndParent,
611 + __in HWND hwndControl
612 + );
613 +
614 +/********************************************************************
615 + ThemeIsControlChecked - gets whether a control is checked. Only
616 + really useful for checkbox controls.
617 +
618 +*******************************************************************/
619 +DAPI_(BOOL) ThemeIsControlChecked(
620 + __in THEME* pTheme,
621 + __in DWORD dwControl
622 + );
623 +
624 +/********************************************************************
625 + ThemeSetControlColor - sets the color of text for a control.
626 +
627 +*******************************************************************/
628 +DAPI_(BOOL) ThemeSetControlColor(
629 + __in THEME* pTheme,
630 + __in HDC hdc,
631 + __in HWND hWnd,
632 + __out HBRUSH* phBackgroundBrush
633 + );
634 +
635 +/********************************************************************
636 + ThemeSetProgressControl - sets the current percentage complete in a
637 + progress bar control.
638 +
639 +*******************************************************************/
640 +DAPI_(HRESULT) ThemeSetProgressControl(
641 + __in THEME* pTheme,
642 + __in DWORD dwControl,
643 + __in DWORD dwProgressPercentage
644 + );
645 +
646 +/********************************************************************
647 + ThemeSetProgressControlColor - sets the current color of a
648 + progress bar control.
649 +
650 +*******************************************************************/
651 +DAPI_(HRESULT) ThemeSetProgressControlColor(
652 + __in THEME* pTheme,
653 + __in DWORD dwControl,
654 + __in DWORD dwColorIndex
655 + );
656 +
657 +/********************************************************************
658 + ThemeSetTextControl - sets the text of a control.
659 +
660 +*******************************************************************/
661 +DAPI_(HRESULT) ThemeSetTextControl(
662 + __in const THEME* pTheme,
663 + __in DWORD dwControl,
664 + __in_z_opt LPCWSTR wzText
665 + );
666 +
667 +/********************************************************************
668 +ThemeSetTextControl - sets the text of a control and optionally
669 + invalidates the control.
670 +
671 +*******************************************************************/
672 +DAPI_(HRESULT) ThemeSetTextControlEx(
673 + __in const THEME* pTheme,
674 + __in DWORD dwControl,
675 + __in BOOL fUpdate,
676 + __in_z_opt LPCWSTR wzText
677 + );
678 +
679 +/********************************************************************
680 + ThemeGetTextControl - gets the text of a control.
681 +
682 +*******************************************************************/
683 +DAPI_(HRESULT) ThemeGetTextControl(
684 + __in const THEME* pTheme,
685 + __in DWORD dwControl,
686 + __out_z LPWSTR* psczText
687 + );
688 +
689 +/********************************************************************
690 + ThemeUpdateCaption - updates the caption in the theme.
691 +
692 +*******************************************************************/
693 +DAPI_(HRESULT) ThemeUpdateCaption(
694 + __in THEME* pTheme,
695 + __in_z LPCWSTR wzCaption
696 + );
697 +
698 +/********************************************************************
699 + ThemeSetFocus - set the focus to the control supplied or the next
700 + enabled control if it is disabled.
701 +
702 +*******************************************************************/
703 +DAPI_(void) ThemeSetFocus(
704 + __in THEME* pTheme,
705 + __in DWORD dwControl
706 + );
707 +
708 +#ifdef __cplusplus
709 +}
710 +#endif
711 +
src/dutil/inc/timeutil.h new
+38
@@ -0,0 +1,38 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +HRESULT DAPI TimeFromString(
10 + __in_z LPCWSTR wzTime,
11 + __out FILETIME* pFileTime
12 + );
13 +HRESULT DAPI TimeFromString3339(
14 + __in_z LPCWSTR wzTime,
15 + __out FILETIME* pFileTime
16 + );
17 +HRESULT DAPI TimeCurrentTime(
18 + __deref_out_z LPWSTR* ppwz,
19 + __in BOOL fGMT
20 + );
21 +HRESULT DAPI TimeCurrentDateTime(
22 + __deref_out_z LPWSTR* ppwz,
23 + __in BOOL fGMT
24 + );
25 +HRESULT DAPI TimeSystemDateTime(
26 + __deref_out_z LPWSTR* ppwz,
27 + __in const SYSTEMTIME *pst,
28 + __in BOOL fGMT
29 + );
30 +HRESULT DAPI TimeSystemToDateTimeString(
31 + __deref_out_z LPWSTR* ppwz,
32 + __in const SYSTEMTIME *pst,
33 + __in LCID locale
34 + );
35 +
36 +#ifdef __cplusplus
37 +}
38 +#endif
src/dutil/inc/uncutil.h new
+20
@@ -0,0 +1,20 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +/*******************************************************************
10 + UncConvertFromMountedDrive - Converts the string in-place from a
11 + mounted drive path to a UNC path
12 +*******************************************************************/
13 +DAPI_(HRESULT) UncConvertFromMountedDrive(
14 + __inout LPWSTR *psczUNCPath,
15 + __in LPCWSTR sczMountedDrivePath
16 + );
17 +
18 +#ifdef __cplusplus
19 +}
20 +#endif
src/dutil/inc/uriutil.h new
+100
@@ -0,0 +1,100 @@
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 "wininet.h"
6 +
7 +
8 +#ifdef __cplusplus
9 +extern "C" {
10 +#endif
11 +
12 +typedef enum URI_PROTOCOL
13 +{
14 + URI_PROTOCOL_UNKNOWN,
15 + URI_PROTOCOL_FILE,
16 + URI_PROTOCOL_FTP,
17 + URI_PROTOCOL_HTTP,
18 + URI_PROTOCOL_HTTPS,
19 + URI_PROTOCOL_LOCAL,
20 + URI_PROTOCOL_UNC
21 +} URI_PROTOCOL;
22 +
23 +typedef struct _URI_INFO
24 +{
25 + INTERNET_SCHEME scheme;
26 + LPWSTR sczHostName;
27 + INTERNET_PORT port;
28 + LPWSTR sczUser;
29 + LPWSTR sczPassword;
30 + LPWSTR sczPath;
31 + LPWSTR sczQueryString;
32 +} URI_INFO;
33 +
34 +
35 +HRESULT DAPI UriCanonicalize(
36 + __inout_z LPWSTR* psczUri
37 + );
38 +
39 +HRESULT DAPI UriCrack(
40 + __in_z LPCWSTR wzUri,
41 + __out_opt INTERNET_SCHEME* pScheme,
42 + __deref_opt_out_z LPWSTR* psczHostName,
43 + __out_opt INTERNET_PORT* pPort,
44 + __deref_opt_out_z LPWSTR* psczUser,
45 + __deref_opt_out_z LPWSTR* psczPassword,
46 + __deref_opt_out_z LPWSTR* psczPath,
47 + __deref_opt_out_z LPWSTR* psczQueryString
48 + );
49 +
50 +HRESULT DAPI UriCrackEx(
51 + __in_z LPCWSTR wzUri,
52 + __in URI_INFO* pUriInfo
53 + );
54 +
55 +void DAPI UriInfoUninitialize(
56 + __in URI_INFO* pUriInfo
57 + );
58 +
59 +HRESULT DAPI UriCreate(
60 + __inout_z LPWSTR* psczUri,
61 + __in INTERNET_SCHEME scheme,
62 + __in_z_opt LPWSTR wzHostName,
63 + __in INTERNET_PORT port,
64 + __in_z_opt LPWSTR wzUser,
65 + __in_z_opt LPWSTR wzPassword,
66 + __in_z_opt LPWSTR wzPath,
67 + __in_z_opt LPWSTR wzQueryString
68 + );
69 +
70 +HRESULT DAPI UriCanonicalize(
71 + __inout_z LPWSTR* psczUri
72 + );
73 +
74 +HRESULT DAPI UriFile(
75 + __deref_out_z LPWSTR* psczFile,
76 + __in_z LPCWSTR wzUri
77 + );
78 +
79 +HRESULT DAPI UriProtocol(
80 + __in_z LPCWSTR wzUri,
81 + __out URI_PROTOCOL* pProtocol
82 + );
83 +
84 +HRESULT DAPI UriRoot(
85 + __in_z LPCWSTR wzUri,
86 + __out LPWSTR* ppwzRoot,
87 + __out_opt URI_PROTOCOL* pProtocol
88 + );
89 +
90 +HRESULT DAPI UriResolve(
91 + __in_z LPCWSTR wzUri,
92 + __in_opt LPCWSTR wzBaseUri,
93 + __out LPWSTR* ppwzResolvedUri,
94 + __out_opt const URI_PROTOCOL* pResolvedProtocol
95 + );
96 +
97 +#ifdef __cplusplus
98 +}
99 +#endif
100 +
src/dutil/inc/userutil.h new
+32
@@ -0,0 +1,32 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +HRESULT DAPI UserBuildDomainUserName(
10 + __out_ecount_z(cchDest) LPWSTR wzDest,
11 + __in int cchDest,
12 + __in_z LPCWSTR pwzName,
13 + __in_z LPCWSTR pwzDomain
14 + );
15 +
16 +HRESULT DAPI UserCheckIsMember(
17 + __in_z LPCWSTR pwzName,
18 + __in_z LPCWSTR pwzDomain,
19 + __in_z LPCWSTR pwzGroupName,
20 + __in_z LPCWSTR pwzGroupDomain,
21 + __out LPBOOL lpfMember
22 + );
23 +
24 +HRESULT DAPI UserCreateADsPath(
25 + __in_z LPCWSTR wzObjectDomain,
26 + __in_z LPCWSTR wzObjectName,
27 + __out BSTR *pbstrAdsPath
28 + );
29 +
30 +#ifdef __cplusplus
31 +}
32 +#endif
src/dutil/inc/varutil.h new
+126
@@ -0,0 +1,126 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseVariables(vh) if (vh) { VarDestroy(vh, NULL); }
10 +#define ReleaseVariableValue(v) if (v) { VarFreeValue(v); }
11 +#define ReleaseNullVariables(vh) if (vh) { VarDestroy(vh, NULL); vh = NULL; }
12 +#define ReleaseNullVariableValue(v) if (v) { VarFreeValue(v); v = NULL; }
13 +
14 +typedef void* VARIABLE_ENUM_HANDLE;
15 +typedef void* VARIABLES_HANDLE;
16 +typedef const void* C_VARIABLES_HANDLE;
17 +
18 +extern const int VARIABLE_ENUM_HANDLE_BYTES;
19 +extern const int VARIABLES_HANDLE_BYTES;
20 +
21 +typedef void(*PFN_FREEVARIABLECONTEXT)(
22 + __in LPVOID pvContext
23 + );
24 +
25 +typedef enum VARIABLE_VALUE_TYPE
26 +{
27 + VARIABLE_VALUE_TYPE_NONE,
28 + VARIABLE_VALUE_TYPE_NUMERIC,
29 + VARIABLE_VALUE_TYPE_STRING,
30 + VARIABLE_VALUE_TYPE_VERSION,
31 +} VARIABLE_VALUE_TYPE;
32 +
33 +typedef struct _VARIABLE_VALUE
34 +{
35 + VARIABLE_VALUE_TYPE type;
36 + union
37 + {
38 + LONGLONG llValue;
39 + DWORD64 qwValue;
40 + LPWSTR sczValue;
41 + };
42 + BOOL fHidden;
43 + LPVOID pvContext;
44 +} VARIABLE_VALUE;
45 +
46 +HRESULT DAPI VarCreate(
47 + __out_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE* ppVariables
48 + );
49 +void DAPI VarDestroy(
50 + __in_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE pVariables,
51 + __in_opt PFN_FREEVARIABLECONTEXT vpfFreeVariableContext
52 + );
53 +void DAPI VarFreeValue(
54 + __in VARIABLE_VALUE* pValue
55 + );
56 +HRESULT DAPI VarEscapeString(
57 + __in_z LPCWSTR wzIn,
58 + __out_z LPWSTR* psczOut
59 + );
60 +HRESULT DAPI VarFormatString(
61 + __in C_VARIABLES_HANDLE pVariables,
62 + __in_z LPCWSTR wzIn,
63 + __out_z_opt LPWSTR* psczOut,
64 + __out_opt DWORD* pcchOut
65 + );
66 +HRESULT DAPI VarGetFormatted(
67 + __in C_VARIABLES_HANDLE pVariables,
68 + __in_z LPCWSTR wzVariable,
69 + __out_z LPWSTR* psczValue
70 + );
71 +HRESULT DAPI VarGetNumeric(
72 + __in C_VARIABLES_HANDLE pVariables,
73 + __in_z LPCWSTR wzVariable,
74 + __out LONGLONG* pllValue
75 + );
76 +HRESULT DAPI VarGetString(
77 + __in C_VARIABLES_HANDLE pVariables,
78 + __in_z LPCWSTR wzVariable,
79 + __out_z LPWSTR* psczValue
80 + );
81 +HRESULT DAPI VarGetVersion(
82 + __in C_VARIABLES_HANDLE pVariables,
83 + __in_z LPCWSTR wzVariable,
84 + __in DWORD64* pqwValue
85 + );
86 +HRESULT DAPI VarGetValue(
87 + __in C_VARIABLES_HANDLE pVariables,
88 + __in_z LPCWSTR wzVariable,
89 + __out VARIABLE_VALUE** ppValue
90 + );
91 +HRESULT DAPI VarSetNumeric(
92 + __in VARIABLES_HANDLE pVariables,
93 + __in_z LPCWSTR wzVariable,
94 + __in LONGLONG llValue
95 + );
96 +HRESULT DAPI VarSetString(
97 + __in VARIABLES_HANDLE pVariables,
98 + __in_z LPCWSTR wzVariable,
99 + __in_z_opt LPCWSTR wzValue
100 + );
101 +HRESULT DAPI VarSetVersion(
102 + __in VARIABLES_HANDLE pVariables,
103 + __in_z LPCWSTR wzVariable,
104 + __in DWORD64 qwValue
105 + );
106 +HRESULT DAPI VarSetValue(
107 + __in VARIABLES_HANDLE pVariables,
108 + __in_z LPCWSTR wzVariable,
109 + __in VARIABLE_VALUE* pValue
110 + );
111 +HRESULT DAPI VarStartEnum(
112 + __in VARIABLES_HANDLE pVariables,
113 + __out_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE* ppEnum,
114 + __out VARIABLE_VALUE** ppValue
115 + );
116 +HRESULT DAPI VarNextVariable(
117 + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum,
118 + __out VARIABLE_VALUE** ppValue
119 + );
120 +void DAPI VarFinishEnum(
121 + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum
122 + );
123 +
124 +#if defined(__cplusplus)
125 +}
126 +#endif
src/dutil/inc/wiutil.h new
+373
@@ -0,0 +1,373 @@
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 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +// constants
10 +
11 +#define IDNOACTION 0
12 +#define WIU_MB_OKIGNORECANCELRETRY 0xE
13 +
14 +#define MAX_DARWIN_KEY 73
15 +#define MAX_DARWIN_COLUMN 255
16 +
17 +#define WIU_LOG_DEFAULT INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | \
18 + INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_RESOLVESOURCE | \
19 + INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | \
20 + INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROPERTYDUMP
21 +
22 +#define ReleaseMsi(h) if (h) { ::MsiCloseHandle(h); }
23 +#define ReleaseNullMsi(h) if (h) { ::MsiCloseHandle(h); h = NULL; }
24 +
25 +
26 +typedef enum WIU_RESTART
27 +{
28 + WIU_RESTART_NONE,
29 + WIU_RESTART_REQUIRED,
30 + WIU_RESTART_INITIATED,
31 +} WIU_RESTART;
32 +
33 +typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE
34 +{
35 + WIU_MSI_EXECUTE_MESSAGE_NONE,
36 + WIU_MSI_EXECUTE_MESSAGE_PROGRESS,
37 + WIU_MSI_EXECUTE_MESSAGE_ERROR,
38 + WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE,
39 + WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE,
40 +} WIU_MSI_EXECUTE_MESSAGE_TYPE;
41 +
42 +
43 +// structures
44 +
45 +typedef struct _WIU_MSI_EXECUTE_MESSAGE
46 +{
47 + WIU_MSI_EXECUTE_MESSAGE_TYPE type;
48 + DWORD dwAllowedResults;
49 +
50 + DWORD cData;
51 + LPCWSTR* rgwzData;
52 +
53 + INT nResultRecommendation; // recommended return result for this message based on analysis of real world installs.
54 +
55 + union
56 + {
57 + struct
58 + {
59 + DWORD dwPercentage;
60 + } progress;
61 + struct
62 + {
63 + DWORD dwErrorCode;
64 + LPCWSTR wzMessage;
65 + } error;
66 + struct
67 + {
68 + INSTALLMESSAGE mt;
69 + LPCWSTR wzMessage;
70 + } msiMessage;
71 + struct
72 + {
73 + DWORD cFiles;
74 + LPCWSTR* rgwzFiles;
75 + } msiFilesInUse;
76 + };
77 +} WIU_MSI_EXECUTE_MESSAGE;
78 +
79 +typedef struct _WIU_MSI_PROGRESS
80 +{
81 + DWORD dwTotal;
82 + DWORD dwCompleted;
83 + DWORD dwStep;
84 + BOOL fMoveForward;
85 + BOOL fEnableActionData;
86 + BOOL fScriptInProgress;
87 +} WIU_MSI_PROGRESS;
88 +
89 +
90 +typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)(
91 + __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
92 + __in_opt LPVOID pvContext
93 + );
94 +
95 +typedef struct _WIU_MSI_EXECUTE_CONTEXT
96 +{
97 + BOOL fRollback;
98 + PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler;
99 + LPVOID pvContext;
100 + WIU_MSI_PROGRESS rgMsiProgress[64];
101 + DWORD dwCurrentProgressIndex;
102 +
103 + INSTALLUILEVEL previousInstallUILevel;
104 + HWND hwndPreviousParentWindow;
105 + INSTALLUI_HANDLERW pfnPreviousExternalUI;
106 + INSTALLUI_HANDLER_RECORD pfnPreviousExternalUIRecord;
107 +
108 + BOOL fSetPreviousExternalUIRecord;
109 + BOOL fSetPreviousExternalUI;
110 +} WIU_MSI_EXECUTE_CONTEXT;
111 +
112 +
113 +// typedefs
114 +typedef UINT (WINAPI *PFN_MSIENABLELOGW)(
115 + __in DWORD dwLogMode,
116 + __in_z LPCWSTR szLogFile,
117 + __in DWORD dwLogAttributes
118 + );
119 +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)(
120 + __in LPCWSTR szProductCode,
121 + __in LPCWSTR szProperty,
122 + __out_ecount_opt(*pcchValue) LPWSTR szValue,
123 + __inout LPDWORD pcchValue
124 + );
125 +typedef INSTALLSTATE (WINAPI *PFN_MSIGETCOMPONENTPATHW)(
126 + __in LPCWSTR szProduct,
127 + __in LPCWSTR szComponent,
128 + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
129 + __inout_opt LPDWORD pcchBuf
130 + );
131 +typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)(
132 + __in LPCWSTR szComponent,
133 + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
134 + __inout_opt LPDWORD pcchBuf
135 + );
136 +typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOEXW)(
137 + __in LPCWSTR szProductCode,
138 + __in_opt LPCWSTR szUserSid,
139 + __in MSIINSTALLCONTEXT dwContext,
140 + __in LPCWSTR szProperty,
141 + __out_ecount_opt(*pcchValue) LPWSTR szValue,
142 + __inout_opt LPDWORD pcchValue
143 + );
144 +typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)(
145 + __in LPCWSTR szProduct,
146 + __in LPCWSTR szFeature
147 + );
148 +typedef UINT (WINAPI *PFN_MSIGETPATCHINFOEXW)(
149 + __in_z LPCWSTR wzPatchCode,
150 + __in_z LPCWSTR wzProductCode,
151 + __in_z_opt LPCWSTR wzUserSid,
152 + __in MSIINSTALLCONTEXT dwContext,
153 + __in_z LPCWSTR wzProperty,
154 + __out_opt LPWSTR wzValue,
155 + __inout DWORD* pcchValue
156 + );
157 +typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)(
158 + __in_z LPCWSTR wzProductCode,
159 + __in_z_opt LPCWSTR wzUserSid,
160 + __in MSIINSTALLCONTEXT context,
161 + __in DWORD cPatchInfo,
162 + __in PMSIPATCHSEQUENCEINFOW pPatchInfo
163 + );
164 +typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)(
165 + __in_z LPCWSTR wzProductPackagePath,
166 + __in DWORD cPatchInfo,
167 + __in PMSIPATCHSEQUENCEINFOW pPatchInfo
168 + );
169 +typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)(
170 + __in LPCWSTR szPackagePath,
171 + __in_opt LPCWSTR szCommandLine
172 + );
173 +typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)(
174 + __in LPCWSTR szProduct,
175 + __in int iInstallLevel,
176 + __in INSTALLSTATE eInstallState,
177 + __in_opt LPCWSTR szCommandLine
178 + );
179 +typedef UINT (WINAPI *PFN_MSIREMOVEPATCHESW)(
180 + __in_z LPCWSTR wzPatchList,
181 + __in_z LPCWSTR wzProductCode,
182 + __in INSTALLTYPE eUninstallType,
183 + __in_z_opt LPCWSTR szPropertyList
184 + );
185 +typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)(
186 + __in INSTALLUILEVEL dwUILevel,
187 + __inout_opt HWND *phWnd
188 + );
189 +typedef UINT (WINAPI *PFN_MSISETEXTERNALUIRECORD)(
190 + __in_opt INSTALLUI_HANDLER_RECORD puiHandler,
191 + __in DWORD dwMessageFilter,
192 + __in_opt LPVOID pvContext,
193 + __out_opt PINSTALLUI_HANDLER_RECORD ppuiPrevHandler
194 + );
195 +typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)(
196 + __in_opt INSTALLUI_HANDLERW puiHandler,
197 + __in DWORD dwMessageFilter,
198 + __in_opt LPVOID pvContext
199 + );
200 +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)(
201 + __in DWORD iProductIndex,
202 + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf
203 + );
204 +typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSEXW)(
205 + __in_z_opt LPCWSTR wzProductCode,
206 + __in_z_opt LPCWSTR wzUserSid,
207 + __in DWORD dwContext,
208 + __in DWORD dwIndex,
209 + __out_opt WCHAR wzInstalledProductCode[39],
210 + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext,
211 + __out_opt LPWSTR wzSid,
212 + __inout_opt LPDWORD pcchSid
213 + );
214 +
215 +typedef UINT (WINAPI *PFN_MSIENUMRELATEDPRODUCTSW)(
216 + __in LPCWSTR lpUpgradeCode,
217 + __reserved DWORD dwReserved,
218 + __in DWORD iProductIndex,
219 + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf
220 + );
221 +typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)(
222 + __in LPCWSTR szProductCodeOrPatchCode,
223 + __in_opt LPCWSTR szUserSid,
224 + __in MSIINSTALLCONTEXT dwContext,
225 + __in DWORD dwOptions,
226 + __in LPCWSTR szSource,
227 + __in_opt DWORD dwIndex
228 + );
229 +
230 +
231 +HRESULT DAPI WiuInitialize(
232 + );
233 +void DAPI WiuUninitialize(
234 + );
235 +void DAPI WiuFunctionOverride(
236 + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW,
237 + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW,
238 + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW,
239 + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW,
240 + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW,
241 + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW,
242 + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW,
243 + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW,
244 + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI,
245 + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW,
246 + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW,
247 + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord,
248 + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW
249 + );
250 +HRESULT DAPI WiuGetComponentPath(
251 + __in_z LPCWSTR wzProductCode,
252 + __in_z LPCWSTR wzComponentId,
253 + __out INSTALLSTATE* pInstallState,
254 + __out_z LPWSTR* psczValue
255 + );
256 +HRESULT DAPI WiuLocateComponent(
257 + __in_z LPCWSTR wzComponentId,
258 + __out INSTALLSTATE* pInstallState,
259 + __out_z LPWSTR* psczValue
260 + );
261 +HRESULT DAPI WiuQueryFeatureState(
262 + __in_z LPCWSTR wzProduct,
263 + __in_z LPCWSTR wzFeature,
264 + __out INSTALLSTATE* pInstallState
265 + );
266 +HRESULT DAPI WiuGetProductInfo(
267 + __in_z LPCWSTR wzProductCode,
268 + __in_z LPCWSTR wzProperty,
269 + __out LPWSTR* psczValue
270 + );
271 +HRESULT DAPI WiuGetProductInfoEx(
272 + __in_z LPCWSTR wzProductCode,
273 + __in_z_opt LPCWSTR wzUserSid,
274 + __in MSIINSTALLCONTEXT dwContext,
275 + __in_z LPCWSTR wzProperty,
276 + __out LPWSTR* psczValue
277 + );
278 +HRESULT DAPI WiuGetProductProperty(
279 + __in MSIHANDLE hProduct,
280 + __in_z LPCWSTR wzProperty,
281 + __out LPWSTR* psczValue
282 + );
283 +HRESULT DAPI WiuGetPatchInfoEx(
284 + __in_z LPCWSTR wzPatchCode,
285 + __in_z LPCWSTR wzProductCode,
286 + __in_z_opt LPCWSTR wzUserSid,
287 + __in MSIINSTALLCONTEXT dwContext,
288 + __in_z LPCWSTR wzProperty,
289 + __out LPWSTR* psczValue
290 + );
291 +HRESULT DAPI WiuDeterminePatchSequence(
292 + __in_z LPCWSTR wzProductCode,
293 + __in_z_opt LPCWSTR wzUserSid,
294 + __in MSIINSTALLCONTEXT context,
295 + __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
296 + __in DWORD cPatchInfo
297 + );
298 +HRESULT DAPI WiuDetermineApplicablePatches(
299 + __in_z LPCWSTR wzProductPackagePath,
300 + __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
301 + __in DWORD cPatchInfo
302 + );
303 +HRESULT DAPI WiuEnumProducts(
304 + __in DWORD iProductIndex,
305 + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
306 + );
307 +HRESULT DAPI WiuEnumProductsEx(
308 + __in_z_opt LPCWSTR wzProductCode,
309 + __in_z_opt LPCWSTR wzUserSid,
310 + __in DWORD dwContext,
311 + __in DWORD dwIndex,
312 + __out_opt WCHAR wzInstalledProductCode[39],
313 + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext,
314 + __out_opt LPWSTR wzSid,
315 + __inout_opt LPDWORD pcchSid
316 + );
317 +HRESULT DAPI WiuEnumRelatedProducts(
318 + __in_z LPCWSTR wzUpgradeCode,
319 + __in DWORD iProductIndex,
320 + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
321 + );
322 +HRESULT DAPI WiuEnumRelatedProductCodes(
323 + __in_z LPCWSTR wzUpgradeCode,
324 + __deref_out_ecount_opt(pcRelatedProducts) LPWSTR** prgsczProductCodes,
325 + __out DWORD* pcRelatedProducts,
326 + __in BOOL fReturnHighestVersionOnly
327 + );
328 +HRESULT DAPI WiuEnableLog(
329 + __in DWORD dwLogMode,
330 + __in_z LPCWSTR wzLogFile,
331 + __in DWORD dwLogAttributes
332 + );
333 +HRESULT DAPI WiuInitializeExternalUI(
334 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
335 + __in INSTALLUILEVEL internalUILevel,
336 + __in HWND hwndParent,
337 + __in LPVOID pvContext,
338 + __in BOOL fRollback,
339 + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
340 + );
341 +void DAPI WiuUninitializeExternalUI(
342 + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
343 + );
344 +HRESULT DAPI WiuConfigureProductEx(
345 + __in_z LPCWSTR wzProduct,
346 + __in int iInstallLevel,
347 + __in INSTALLSTATE eInstallState,
348 + __in_z LPCWSTR wzCommandLine,
349 + __out WIU_RESTART* pRestart
350 + );
351 +HRESULT DAPI WiuInstallProduct(
352 + __in_z LPCWSTR wzPackagPath,
353 + __in_z LPCWSTR wzCommandLine,
354 + __out WIU_RESTART* pRestart
355 + );
356 +HRESULT DAPI WiuRemovePatches(
357 + __in_z LPCWSTR wzPatchList,
358 + __in_z LPCWSTR wzProductCode,
359 + __in_z LPCWSTR wzPropertyList,
360 + __out WIU_RESTART* pRestart
361 + );
362 +HRESULT DAPI WiuSourceListAddSourceEx(
363 + __in_z LPCWSTR wzProductCodeOrPatchCode,
364 + __in_z_opt LPCWSTR wzUserSid,
365 + __in MSIINSTALLCONTEXT dwContext,
366 + __in DWORD dwCode,
367 + __in_z LPCWSTR wzSource,
368 + __in_opt DWORD dwIndex
369 + );
370 +
371 +#ifdef __cplusplus
372 +}
373 +#endif
src/dutil/inc/wuautil.h new
+19
@@ -0,0 +1,19 @@
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 +#if defined(__cplusplus)
6 +extern "C" {
7 +#endif
8 +
9 +HRESULT DAPI WuaPauseAutomaticUpdates();
10 +
11 +HRESULT DAPI WuaResumeAutomaticUpdates();
12 +
13 +HRESULT DAPI WuaRestartRequired(
14 + __out BOOL* pfRestartRequired
15 + );
16 +
17 +#if defined(__cplusplus)
18 +}
19 +#endif
src/dutil/inc/xmlutil.h new
+167
@@ -0,0 +1,167 @@
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 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
6 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}};
7 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}};
8 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}};
9 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
10 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
11 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
12 +extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
13 +
14 +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
15 +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
16 +extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}};
17 +
18 +typedef enum XML_LOAD_ATTRIBUTE
19 +{
20 + XML_LOAD_PRESERVE_WHITESPACE = 1,
21 +} XML_LOAD_ATTRIBUTE;
22 +
23 +
24 +#ifdef __cplusplus
25 +extern "C" {
26 +#endif
27 +
28 +HRESULT DAPI XmlInitialize();
29 +void DAPI XmlUninitialize();
30 +
31 +HRESULT DAPI XmlCreateElement(
32 + __in IXMLDOMDocument *pixdDocument,
33 + __in_z LPCWSTR wzElementName,
34 + __out IXMLDOMElement **ppixnElement
35 + );
36 +HRESULT DAPI XmlCreateDocument(
37 + __in_opt LPCWSTR pwzElementName,
38 + __out IXMLDOMDocument** ppixdDocument,
39 + __out_opt IXMLDOMElement** ppixeRootElement = NULL
40 + );
41 +HRESULT DAPI XmlLoadDocument(
42 + __in_z LPCWSTR wzDocument,
43 + __out IXMLDOMDocument** ppixdDocument
44 + );
45 +HRESULT DAPI XmlLoadDocumentEx(
46 + __in_z LPCWSTR wzDocument,
47 + __in DWORD dwAttributes,
48 + __out IXMLDOMDocument** ppixdDocument
49 + );
50 +HRESULT DAPI XmlLoadDocumentFromFile(
51 + __in_z LPCWSTR wzPath,
52 + __out IXMLDOMDocument** ppixdDocument
53 + );
54 +HRESULT DAPI XmlLoadDocumentFromBuffer(
55 + __in_bcount(cbSource) const BYTE* pbSource,
56 + __in DWORD cbSource,
57 + __out IXMLDOMDocument** ppixdDocument
58 + );
59 +HRESULT DAPI XmlLoadDocumentFromFileEx(
60 + __in_z LPCWSTR wzPath,
61 + __in DWORD dwAttributes,
62 + __out IXMLDOMDocument** ppixdDocument
63 + );
64 +HRESULT DAPI XmlSelectSingleNode(
65 + __in IXMLDOMNode* pixnParent,
66 + __in_z LPCWSTR wzXPath,
67 + __out IXMLDOMNode **ppixnChild
68 + );
69 +HRESULT DAPI XmlSetAttribute(
70 + __in IXMLDOMNode* pixnNode,
71 + __in_z LPCWSTR pwzAttribute,
72 + __in_z LPCWSTR pwzAttributeValue
73 + );
74 +HRESULT DAPI XmlCreateTextNode(
75 + __in IXMLDOMDocument *pixdDocument,
76 + __in_z LPCWSTR wzText,
77 + __out IXMLDOMText **ppixnTextNode
78 + );
79 +HRESULT DAPI XmlGetText(
80 + __in IXMLDOMNode* pixnNode,
81 + __deref_out_z BSTR* pbstrText
82 + );
83 +HRESULT DAPI XmlGetAttribute(
84 + __in IXMLDOMNode* pixnNode,
85 + __in_z LPCWSTR pwzAttribute,
86 + __deref_out_z BSTR* pbstrAttributeValue
87 + );
88 +HRESULT DAPI XmlGetAttributeEx(
89 + __in IXMLDOMNode* pixnNode,
90 + __in_z LPCWSTR wzAttribute,
91 + __deref_out_z LPWSTR* psczAttributeValue
92 + );
93 +HRESULT DAPI XmlGetYesNoAttribute(
94 + __in IXMLDOMNode* pixnNode,
95 + __in_z LPCWSTR wzAttribute,
96 + __out BOOL* pfYes
97 + );
98 +HRESULT DAPI XmlGetAttributeNumber(
99 + __in IXMLDOMNode* pixnNode,
100 + __in_z LPCWSTR pwzAttribute,
101 + __out DWORD* pdwValue
102 + );
103 +HRESULT DAPI XmlGetAttributeNumberBase(
104 + __in IXMLDOMNode* pixnNode,
105 + __in_z LPCWSTR pwzAttribute,
106 + __in int nBase,
107 + __out DWORD* pdwValue
108 + );
109 +HRESULT DAPI XmlGetAttributeLargeNumber(
110 + __in IXMLDOMNode* pixnNode,
111 + __in_z LPCWSTR pwzAttribute,
112 + __out DWORD64* pdw64Value
113 + );
114 +HRESULT DAPI XmlGetNamedItem(
115 + __in IXMLDOMNamedNodeMap *pixnmAttributes,
116 + __in_opt LPCWSTR wzName,
117 + __out IXMLDOMNode **ppixnNamedItem
118 + );
119 +HRESULT DAPI XmlSetText(
120 + __in IXMLDOMNode* pixnNode,
121 + __in_z LPCWSTR pwzText
122 + );
123 +HRESULT DAPI XmlSetTextNumber(
124 + __in IXMLDOMNode *pixnNode,
125 + __in DWORD dwValue
126 + );
127 +HRESULT DAPI XmlCreateChild(
128 + __in IXMLDOMNode* pixnParent,
129 + __in_z LPCWSTR pwzElementType,
130 + __out IXMLDOMNode** ppixnChild
131 + );
132 +HRESULT DAPI XmlRemoveAttribute(
133 + __in IXMLDOMNode* pixnNode,
134 + __in_z LPCWSTR pwzAttribute
135 + );
136 +HRESULT DAPI XmlSelectNodes(
137 + __in IXMLDOMNode* pixnParent,
138 + __in_z LPCWSTR wzXPath,
139 + __out IXMLDOMNodeList **ppixnChild
140 + );
141 +HRESULT DAPI XmlNextAttribute(
142 + __in IXMLDOMNamedNodeMap* pixnnm,
143 + __out IXMLDOMNode** pixnAttribute,
144 + __deref_opt_out_z_opt BSTR* pbstrAttribute
145 + );
146 +HRESULT DAPI XmlNextElement(
147 + __in IXMLDOMNodeList* pixnl,
148 + __out IXMLDOMNode** pixnElement,
149 + __deref_opt_out_z_opt BSTR* pbstrElement
150 + );
151 +HRESULT DAPI XmlRemoveChildren(
152 + __in IXMLDOMNode* pixnSource,
153 + __in_z LPCWSTR pwzXPath
154 + );
155 +HRESULT DAPI XmlSaveDocument(
156 + __in IXMLDOMDocument* pixdDocument,
157 + __inout LPCWSTR wzPath
158 + );
159 +HRESULT DAPI XmlSaveDocumentToBuffer(
160 + __in IXMLDOMDocument* pixdDocument,
161 + __deref_out_bcount(*pcbDest) BYTE** ppbDest,
162 + __out DWORD* pcbDest
163 + );
164 +
165 +#ifdef __cplusplus
166 +}
167 +#endif
src/dutil/inetutil.cpp new
+137
@@ -0,0 +1,137 @@
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 +
5 +
6 +/*******************************************************************
7 + InternetGetSizeByHandle - returns size of file by url handle
8 +
9 +*******************************************************************/
10 +extern "C" HRESULT DAPI InternetGetSizeByHandle(
11 + __in HINTERNET hiFile,
12 + __out LONGLONG* pllSize
13 + )
14 +{
15 + Assert(pllSize);
16 +
17 + HRESULT hr = S_OK;
18 + DWORD dwSize;
19 + DWORD cb;
20 +
21 + cb = sizeof(dwSize);
22 + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast<LPVOID>(&dwSize), &cb, NULL))
23 + {
24 + ExitOnLastError(hr, "Failed to get size for internet file handle");
25 + }
26 +
27 + *pllSize = dwSize;
28 +LExit:
29 + return hr;
30 +}
31 +
32 +
33 +/*******************************************************************
34 + InetGetCreateTimeByHandle - returns url creation time
35 +
36 +******************************************************************/
37 +extern "C" HRESULT DAPI InternetGetCreateTimeByHandle(
38 + __in HINTERNET hiFile,
39 + __out LPFILETIME pft
40 + )
41 +{
42 + Assert(pft);
43 +
44 + HRESULT hr = S_OK;
45 + SYSTEMTIME st = {0 };
46 + DWORD cb = sizeof(SYSTEMTIME);
47 +
48 + if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast<LPVOID>(&st), &cb, NULL))
49 + {
50 + ExitWithLastError(hr, "failed to get create time for internet file handle");
51 + }
52 +
53 + if (!::SystemTimeToFileTime(&st, pft))
54 + {
55 + ExitWithLastError(hr, "failed to convert system time to file time");
56 + }
57 +
58 +LExit:
59 + return hr;
60 +}
61 +
62 +
63 +/*******************************************************************
64 + InternetQueryInfoString - query info string
65 +
66 +*******************************************************************/
67 +extern "C" HRESULT DAPI InternetQueryInfoString(
68 + __in HINTERNET hRequest,
69 + __in DWORD dwInfo,
70 + __deref_out_z LPWSTR* psczValue
71 + )
72 +{
73 + HRESULT hr = S_OK;
74 + DWORD_PTR cbValue = 0;
75 + DWORD dwIndex = 0;
76 +
77 + // If nothing was provided start off with some arbitrary size.
78 + if (!*psczValue)
79 + {
80 + hr = StrAlloc(psczValue, 64);
81 + ExitOnFailure(hr, "Failed to allocate memory for value.");
82 + }
83 +
84 + hr = StrSize(*psczValue, &cbValue);
85 + ExitOnFailure(hr, "Failed to get size of value.");
86 +
87 + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast<void*>(*psczValue), reinterpret_cast<DWORD*>(&cbValue), &dwIndex))
88 + {
89 + DWORD er = ::GetLastError();
90 + if (ERROR_INSUFFICIENT_BUFFER == er)
91 + {
92 + cbValue += sizeof(WCHAR); // add one character for the null terminator.
93 +
94 + hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR));
95 + ExitOnFailure(hr, "Failed to allocate value.");
96 +
97 + if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast<void*>(*psczValue), reinterpret_cast<DWORD*>(&cbValue), &dwIndex))
98 + {
99 + er = ::GetLastError();
100 + }
101 + else
102 + {
103 + er = ERROR_SUCCESS;
104 + }
105 + }
106 +
107 + hr = HRESULT_FROM_WIN32(er);
108 + ExitOnRootFailure(hr, "Failed to get query information.");
109 + }
110 +
111 +LExit:
112 + return hr;
113 +}
114 +
115 +
116 +/*******************************************************************
117 + InternetQueryInfoNumber - query info number
118 +
119 +*******************************************************************/
120 +extern "C" HRESULT DAPI InternetQueryInfoNumber(
121 + __in HINTERNET hRequest,
122 + __in DWORD dwInfo,
123 + __out LONG* plInfo
124 + )
125 +{
126 + HRESULT hr = S_OK;
127 + DWORD cbCode = sizeof(LONG);
128 + DWORD dwIndex = 0;
129 +
130 + if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast<void*>(plInfo), &cbCode, &dwIndex))
131 + {
132 + ExitWithLastError(hr, "Failed to get query information.");
133 + }
134 +
135 +LExit:
136 + return hr;
137 +}
src/dutil/iniutil.cpp new
+753
@@ -0,0 +1,753 @@
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 +
5 +const LPCWSTR wzSectionSeparator = L"\\";
6 +
7 +struct INI_STRUCT
8 +{
9 + LPWSTR sczPath; // the path to the INI file to be parsed
10 +
11 + LPWSTR sczOpenTagPrefix; // For regular ini, this would be '['
12 + LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']'
13 +
14 + LPWSTR sczValuePrefix; // for regular ini, this would be NULL
15 + LPWSTR sczValueSeparator; // for regular ini, this would be '='
16 +
17 + LPWSTR *rgsczValueSeparatorExceptions;
18 + DWORD cValueSeparatorExceptions;
19 +
20 + LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';'
21 +
22 + INI_VALUE *rgivValues;
23 + DWORD cValues;
24 +
25 + LPWSTR *rgsczLines;
26 + DWORD cLines;
27 +
28 + FILE_ENCODING feEncoding;
29 + BOOL fModified;
30 +};
31 +
32 +const int INI_HANDLE_BYTES = sizeof(INI_STRUCT);
33 +
34 +static HRESULT GetSectionPrefixFromName(
35 + __in_z LPCWSTR wzName,
36 + __deref_out_z LPWSTR* psczOutput
37 + );
38 +static void UninitializeIniValue(
39 + INI_VALUE *pivValue
40 + );
41 +
42 +extern "C" HRESULT DAPI IniInitialize(
43 + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle
44 + )
45 +{
46 + HRESULT hr = S_OK;
47 +
48 + // Allocate the handle
49 + *piHandle = static_cast<INI_HANDLE>(MemAlloc(sizeof(INI_STRUCT), TRUE));
50 + ExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object");
51 +
52 +LExit:
53 + return hr;
54 +}
55 +
56 +extern "C" void DAPI IniUninitialize(
57 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle
58 + )
59 +{
60 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
61 +
62 + ReleaseStr(pi->sczPath);
63 + ReleaseStr(pi->sczOpenTagPrefix);
64 + ReleaseStr(pi->sczOpenTagPostfix);
65 + ReleaseStr(pi->sczValuePrefix);
66 + ReleaseStr(pi->sczValueSeparator);
67 +
68 + for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i)
69 + {
70 + ReleaseStr(pi->rgsczValueSeparatorExceptions + i);
71 + }
72 +
73 + ReleaseStr(pi->sczCommentLinePrefix);
74 +
75 + for (DWORD i = 0; i < pi->cValues; ++i)
76 + {
77 + UninitializeIniValue(pi->rgivValues + i);
78 + }
79 + ReleaseMem(pi->rgivValues);
80 +
81 + ReleaseStrArray(pi->rgsczLines, pi->cLines);
82 +
83 + ReleaseMem(pi);
84 +}
85 +
86 +extern "C" HRESULT DAPI IniSetOpenTag(
87 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
88 + __in_z_opt LPCWSTR wzOpenTagPrefix,
89 + __in_z_opt LPCWSTR wzOpenTagPostfix
90 + )
91 +{
92 + HRESULT hr = S_OK;
93 +
94 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
95 +
96 + if (wzOpenTagPrefix)
97 + {
98 + hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0);
99 + ExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix);
100 + }
101 + else
102 + {
103 + ReleaseNullStr(pi->sczOpenTagPrefix);
104 + }
105 +
106 + if (wzOpenTagPostfix)
107 + {
108 + hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0);
109 + ExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix);
110 + }
111 + else
112 + {
113 + ReleaseNullStr(pi->sczOpenTagPrefix);
114 + }
115 +
116 +LExit:
117 + return hr;
118 +}
119 +
120 +extern "C" HRESULT DAPI IniSetValueStyle(
121 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
122 + __in_z_opt LPCWSTR wzValuePrefix,
123 + __in_z_opt LPCWSTR wzValueSeparator
124 + )
125 +{
126 + HRESULT hr = S_OK;
127 +
128 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
129 +
130 + if (wzValuePrefix)
131 + {
132 + hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0);
133 + ExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix);
134 + }
135 + else
136 + {
137 + ReleaseNullStr(pi->sczValuePrefix);
138 + }
139 +
140 + if (wzValueSeparator)
141 + {
142 + hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0);
143 + ExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator);
144 + }
145 + else
146 + {
147 + ReleaseNullStr(pi->sczValueSeparator);
148 + }
149 +
150 +LExit:
151 + return hr;
152 +}
153 +
154 +extern "C" HRESULT DAPI IniSetValueSeparatorException(
155 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
156 + __in_z LPCWSTR wzValueNamePrefix
157 + )
158 +{
159 + HRESULT hr = S_OK;
160 + DWORD dwInsertedIndex = 0;
161 +
162 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
163 +
164 + hr = MemEnsureArraySize(reinterpret_cast<void **>(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5);
165 + ExitOnFailure(hr, "Failed to increase array size for value separator exceptions");
166 + dwInsertedIndex = pi->cValueSeparatorExceptions;
167 + ++pi->cValueSeparatorExceptions;
168 +
169 + hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0);
170 + ExitOnFailure(hr, "Failed to copy value separator exception");
171 +
172 +LExit:
173 + return hr;
174 +}
175 +
176 +extern "C" HRESULT DAPI IniSetCommentStyle(
177 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
178 + __in_z_opt LPCWSTR wzLinePrefix
179 + )
180 +{
181 + HRESULT hr = S_OK;
182 +
183 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
184 +
185 + if (wzLinePrefix)
186 + {
187 + hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0);
188 + ExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix);
189 + }
190 + else
191 + {
192 + ReleaseNullStr(pi->sczCommentLinePrefix);
193 + }
194 +
195 +LExit:
196 + return hr;
197 +}
198 +
199 +extern "C" HRESULT DAPI IniParse(
200 + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
201 + __in LPCWSTR wzPath,
202 + __out_opt FILE_ENCODING *pfeEncodingFound
203 + )
204 +{
205 + HRESULT hr = S_OK;
206 + DWORD dwValuePrefixLength = 0;
207 + DWORD dwValueSeparatorExceptionLength = 0;
208 + LPWSTR sczContents = NULL;
209 + LPWSTR sczCurrentSection = NULL;
210 + LPWSTR sczName = NULL;
211 + LPWSTR sczNameTrimmed = NULL;
212 + LPWSTR sczValue = NULL;
213 + LPWSTR sczValueTrimmed = NULL;
214 + LPWSTR wzOpenTagPrefix = NULL;
215 + LPWSTR wzOpenTagPostfix = NULL;
216 + LPWSTR wzValuePrefix = NULL;
217 + LPWSTR wzValueNameStart = NULL;
218 + LPWSTR wzValueSeparator = NULL;
219 + LPWSTR wzCommentLinePrefix = NULL;
220 + LPWSTR wzValueBegin = NULL;
221 + LPCWSTR wzTemp = NULL;
222 +
223 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
224 +
225 + BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix);
226 + BOOL fValuePrefix = (NULL != pi->sczValuePrefix);
227 +
228 + hr = StrAllocString(&pi->sczPath, wzPath, 0);
229 + ExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath);
230 +
231 + hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding);
232 + ExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath);
233 +
234 + if (pfeEncodingFound)
235 + {
236 + *pfeEncodingFound = pi->feEncoding;
237 + }
238 +
239 + if (!sczContents || !*sczContents)
240 + {
241 + // Empty string, nothing to parse
242 + ExitFunction1(hr = S_OK);
243 + }
244 +
245 + dwValuePrefixLength = lstrlenW(pi->sczValuePrefix);
246 + hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast<UINT *>(&pi->cLines), sczContents, L"\n");
247 + ExitOnFailure(hr, "Failed to split INI file into lines");
248 +
249 + for (DWORD i = 0; i < pi->cLines; ++i)
250 + {
251 + if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i])
252 + {
253 + continue;
254 + }
255 +
256 + if (pi->sczCommentLinePrefix)
257 + {
258 + wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix);
259 +
260 + if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1)
261 + {
262 + continue;
263 + }
264 + }
265 +
266 + if (pi->sczOpenTagPrefix)
267 + {
268 + wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix);
269 + if (wzOpenTagPrefix)
270 + {
271 + // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix
272 + // This is important, for example, to support values with names like "Array[0]=blah" in INI format
273 + for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp)
274 + {
275 + if (*wzTemp != L' ' && *wzTemp != L'\t')
276 + {
277 + wzOpenTagPrefix = NULL;
278 + break;
279 + }
280 + }
281 + }
282 + }
283 +
284 + if (pi->sczOpenTagPostfix)
285 + {
286 + wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix);
287 + }
288 +
289 + if (pi->sczValuePrefix)
290 + {
291 + wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix);
292 + if (wzValuePrefix != NULL)
293 + {
294 + wzValueNameStart = wzValuePrefix + dwValuePrefixLength;
295 + }
296 + }
297 + else
298 + {
299 + wzValueNameStart = pi->rgsczLines[i];
300 + }
301 +
302 + if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0')
303 + {
304 + dwValueSeparatorExceptionLength = 0;
305 + for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j)
306 + {
307 + if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j]))
308 + {
309 + dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]);
310 + break;
311 + }
312 + }
313 +
314 + wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator);
315 + }
316 +
317 + // Don't keep the endline
318 + if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r')
319 + {
320 + pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0';
321 + }
322 +
323 + if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix))
324 + {
325 + // There is an section starting here, let's keep track of it and move on
326 + hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix)));
327 + ExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath);
328 +
329 + // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output
330 + ReleaseNullStr(pi->rgsczLines[i]);
331 + }
332 + else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix)
333 + && (!fValuePrefix || wzValuePrefix))
334 + {
335 + if (fValuePrefix)
336 + {
337 + wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix);
338 + }
339 + else
340 + {
341 + wzValueBegin = pi->rgsczLines[i];
342 + }
343 +
344 + hr = MemEnsureArraySize(reinterpret_cast<void **>(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100);
345 + ExitOnFailure(hr, "Failed to increase array size for value array");
346 +
347 + if (sczCurrentSection)
348 + {
349 + hr = StrAllocString(&sczName, sczCurrentSection, 0);
350 + ExitOnFailure(hr, "Failed to copy current section name");
351 +
352 + hr = StrAllocConcat(&sczName, wzSectionSeparator, 0);
353 + ExitOnFailure(hr, "Failed to copy current section name");
354 + }
355 +
356 + hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin);
357 + ExitOnFailure(hr, "Failed to copy name");
358 +
359 + hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0);
360 + ExitOnFailure(hr, "Failed to copy value");
361 +
362 + hr = StrTrimWhitespace(&sczNameTrimmed, sczName);
363 + ExitOnFailure(hr, "Failed to trim whitespace from name");
364 +
365 + hr = StrTrimWhitespace(&sczValueTrimmed, sczValue);
366 + ExitOnFailure(hr, "Failed to trim whitespace from value");
367 +
368 + pi->rgivValues[pi->cValues].wzName = const_cast<LPCWSTR>(sczNameTrimmed);
369 + sczNameTrimmed = NULL;
370 + pi->rgivValues[pi->cValues].wzValue = const_cast<LPCWSTR>(sczValueTrimmed);
371 + sczValueTrimmed = NULL;
372 + pi->rgivValues[pi->cValues].dwLineNumber = i + 1;
373 +
374 + ++pi->cValues;
375 +
376 + // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output
377 + ReleaseNullStr(pi->rgsczLines[i]);
378 + }
379 + else
380 + {
381 + // Must be a comment, so ignore it and keep it in the list to output
382 + }
383 +
384 + ReleaseNullStr(sczName);
385 + }
386 +
387 +LExit:
388 + ReleaseStr(sczCurrentSection);
389 + ReleaseStr(sczContents);
390 + ReleaseStr(sczName);
391 + ReleaseStr(sczNameTrimmed);
392 + ReleaseStr(sczValue);
393 + ReleaseStr(sczValueTrimmed);
394 +
395 + return hr;
396 +}
397 +
398 +extern "C" HRESULT DAPI IniGetValueList(
399 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
400 + __deref_out_ecount_opt(pcValues) INI_VALUE** prgivValues,
401 + __out DWORD *pcValues
402 + )
403 +{
404 + HRESULT hr = S_OK;
405 +
406 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
407 +
408 + *prgivValues = pi->rgivValues;
409 + *pcValues = pi->cValues;
410 +
411 + return hr;
412 +}
413 +
414 +extern "C" HRESULT DAPI IniGetValue(
415 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
416 + __in LPCWSTR wzValueName,
417 + __deref_out_z LPWSTR* psczValue
418 + )
419 +{
420 + HRESULT hr = S_OK;
421 +
422 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
423 + INI_VALUE *pValue = NULL;
424 +
425 + for (DWORD i = 0; i < pi->cValues; ++i)
426 + {
427 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1))
428 + {
429 + pValue = pi->rgivValues + i;
430 + break;
431 + }
432 + }
433 +
434 + if (NULL == pValue)
435 + {
436 + hr = E_NOTFOUND;
437 + ExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName);
438 + }
439 +
440 + if (NULL == pValue->wzValue)
441 + {
442 + ExitFunction1(hr = E_NOTFOUND);
443 + }
444 +
445 + hr = StrAllocString(psczValue, pValue->wzValue, 0);
446 + ExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName);
447 +
448 +LExit:
449 + return hr;
450 +}
451 +
452 +extern "C" HRESULT DAPI IniSetValue(
453 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
454 + __in LPCWSTR wzValueName,
455 + __in_z_opt LPCWSTR wzValue
456 + )
457 +{
458 + HRESULT hr = S_OK;
459 + LPWSTR sczSectionPrefix = NULL; // includes section name and backslash
460 + LPWSTR sczName = NULL;
461 + LPWSTR sczValue = NULL;
462 + DWORD dwInsertIndex = DWORD_MAX;
463 +
464 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
465 + INI_VALUE *pValue = NULL;
466 +
467 + for (DWORD i = 0; i < pi->cValues; ++i)
468 + {
469 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1))
470 + {
471 + pValue = pi->rgivValues + i;
472 + break;
473 + }
474 + }
475 +
476 + // We're killing the value
477 + if (NULL == wzValue)
478 + {
479 + if (pValue && pValue->wzValue)
480 + {
481 + pi->fModified = TRUE;
482 + sczValue = const_cast<LPWSTR>(pValue->wzValue);
483 + pValue->wzValue = NULL;
484 + ReleaseNullStr(sczValue);
485 + }
486 +
487 + ExitFunction();
488 + }
489 + else
490 + {
491 + if (pValue)
492 + {
493 + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1))
494 + {
495 + pi->fModified = TRUE;
496 + hr = StrAllocString(const_cast<LPWSTR *>(&pValue->wzValue), wzValue, 0);
497 + ExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName);
498 + }
499 +
500 + ExitFunction1(hr = S_OK);
501 + }
502 + else
503 + {
504 + if (wzValueName)
505 + {
506 + hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix);
507 + ExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName);
508 + }
509 +
510 + // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in)
511 + if (sczSectionPrefix)
512 + {
513 + for (DWORD i = 0; i < pi->cValues; ++i)
514 + {
515 + if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix)))
516 + {
517 + dwInsertIndex = i;
518 + }
519 + else if (DWORD_MAX != dwInsertIndex)
520 + {
521 + break;
522 + }
523 + }
524 + }
525 + else
526 + {
527 + for (DWORD i = 0; i < pi->cValues; ++i)
528 + {
529 + if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator))
530 + {
531 + dwInsertIndex = i;
532 + }
533 + else if (DWORD_MAX != dwInsertIndex)
534 + {
535 + break;
536 + }
537 + }
538 + }
539 +
540 + // Otherwise, just add it to the end
541 + if (DWORD_MAX == dwInsertIndex)
542 + {
543 + dwInsertIndex = pi->cValues;
544 + }
545 +
546 + pi->fModified = TRUE;
547 + hr = MemInsertIntoArray(reinterpret_cast<void **>(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100);
548 + ExitOnFailure(hr, "Failed to insert value into array");
549 +
550 + hr = StrAllocString(&sczName, wzValueName, 0);
551 + ExitOnFailure(hr, "Failed to copy name");
552 +
553 + hr = StrAllocString(&sczValue, wzValue, 0);
554 + ExitOnFailure(hr, "Failed to copy value");
555 +
556 + pi->rgivValues[dwInsertIndex].wzName = const_cast<LPCWSTR>(sczName);
557 + sczName = NULL;
558 + pi->rgivValues[dwInsertIndex].wzValue = const_cast<LPCWSTR>(sczValue);
559 + sczValue = NULL;
560 +
561 + ++pi->cValues;
562 + }
563 + }
564 +
565 +LExit:
566 + ReleaseStr(sczName);
567 + ReleaseStr(sczValue);
568 +
569 + return hr;
570 +}
571 +
572 +extern "C" HRESULT DAPI IniWriteFile(
573 + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
574 + __in_z_opt LPCWSTR wzPath,
575 + __in FILE_ENCODING feOverrideEncoding
576 + )
577 +{
578 + HRESULT hr = S_OK;
579 + LPWSTR sczCurrentSectionPrefix = NULL;
580 + LPWSTR sczNewSectionPrefix = NULL;
581 + LPWSTR sczContents = NULL;
582 + LPCWSTR wzName = NULL;
583 + DWORD dwLineArrayIndex = 1;
584 + FILE_ENCODING feEncoding;
585 +
586 + INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
587 +
588 + if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding)
589 + {
590 + feEncoding = pi->feEncoding;
591 + }
592 + else
593 + {
594 + feEncoding = feOverrideEncoding;
595 + }
596 +
597 + if (FILE_ENCODING_UNSPECIFIED == feEncoding)
598 + {
599 + feEncoding = FILE_ENCODING_UTF16_WITH_BOM;
600 + }
601 +
602 + if (!pi->fModified)
603 + {
604 + ExitFunction1(hr = S_OK);
605 + }
606 + if (NULL == wzPath && NULL == pi->sczPath)
607 + {
608 + ExitFunction1(hr = E_NOTFOUND);
609 + }
610 +
611 + BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix);
612 +
613 + hr = StrAllocString(&sczContents, L"", 0);
614 + ExitOnFailure(hr, "Failed to begin contents string as empty string");
615 +
616 + // Insert any beginning lines we didn't understand like comments
617 + if (0 < pi->cLines)
618 + {
619 + while (pi->rgsczLines[dwLineArrayIndex])
620 + {
621 + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0);
622 + ExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory");
623 +
624 + hr = StrAllocConcat(&sczContents, L"\r\n", 2);
625 + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
626 +
627 + ++dwLineArrayIndex;
628 + }
629 + }
630 +
631 + for (DWORD i = 0; i < pi->cValues; ++i)
632 + {
633 + // Skip if this value was killed off
634 + if (NULL == pi->rgivValues[i].wzValue)
635 + {
636 + continue;
637 + }
638 +
639 + // Now generate any lines for the current value like value line and maybe also a new section line before it
640 +
641 + // First see if we need to write a section line
642 + hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix);
643 + ExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName);
644 +
645 + // If the new section prefix is different, write a section out for it
646 + if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1)))
647 + {
648 + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0);
649 + ExitOnFailure(hr, "Failed to concat open tag prefix to string");
650 +
651 + // Exclude section separator (i.e. backslash) from new section prefix
652 + hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator));
653 + ExitOnFailure(hr, "Failed to concat section name to string");
654 +
655 + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0);
656 + ExitOnFailure(hr, "Failed to concat open tag postfix to string");
657 +
658 + hr = StrAllocConcat(&sczContents, L"\r\n", 2);
659 + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
660 +
661 + ReleaseNullStr(sczCurrentSectionPrefix);
662 + sczCurrentSectionPrefix = sczNewSectionPrefix;
663 + sczNewSectionPrefix = NULL;
664 + }
665 +
666 + // Inserting lines we read before the current value if appropriate
667 + while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex)
668 + {
669 + // Skip any lines were purposely forgot
670 + if (NULL == pi->rgsczLines[dwLineArrayIndex])
671 + {
672 + ++dwLineArrayIndex;
673 + continue;
674 + }
675 +
676 + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0);
677 + ExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory");
678 +
679 + hr = StrAllocConcat(&sczContents, L"\r\n", 2);
680 + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
681 + }
682 +
683 + wzName = pi->rgivValues[i].wzName;
684 + if (fSections)
685 + {
686 + wzName += lstrlenW(sczCurrentSectionPrefix);
687 + }
688 +
689 + // OK, now just write the name/value pair, if it isn't deleted
690 + if (pi->sczValuePrefix)
691 + {
692 + hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0);
693 + ExitOnFailure(hr, "Failed to concat value prefix to ini output buffer");
694 + }
695 +
696 + hr = StrAllocConcat(&sczContents, wzName, 0);
697 + ExitOnFailure(hr, "Failed to concat value name to ini output buffer");
698 +
699 + hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0);
700 + ExitOnFailure(hr, "Failed to concat value separator to ini output buffer");
701 +
702 + hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0);
703 + ExitOnFailure(hr, "Failed to concat value to ini output buffer");
704 +
705 + hr = StrAllocConcat(&sczContents, L"\r\n", 2);
706 + ExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
707 + }
708 +
709 + // If no path was specified, use the path to the file we parsed
710 + if (NULL == wzPath)
711 + {
712 + wzPath = pi->sczPath;
713 + }
714 +
715 + hr = FileFromString(wzPath, 0, sczContents, feEncoding);
716 + ExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath);
717 +
718 +LExit:
719 + ReleaseStr(sczContents);
720 + ReleaseStr(sczCurrentSectionPrefix);
721 + ReleaseStr(sczNewSectionPrefix);
722 +
723 + return hr;
724 +}
725 +
726 +static void UninitializeIniValue(
727 + INI_VALUE *pivValue
728 + )
729 +{
730 + ReleaseStr(const_cast<LPWSTR>(pivValue->wzName));
731 + ReleaseStr(const_cast<LPWSTR>(pivValue->wzValue));
732 +}
733 +
734 +static HRESULT GetSectionPrefixFromName(
735 + __in_z LPCWSTR wzName,
736 + __deref_out_z LPWSTR* psczOutput
737 + )
738 +{
739 + HRESULT hr = S_OK;
740 + LPCWSTR wzSectionDelimiter = NULL;
741 +
742 + ReleaseNullStr(*psczOutput);
743 +
744 + wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator);
745 + if (wzSectionDelimiter && wzSectionDelimiter != wzName)
746 + {
747 + hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1);
748 + ExitOnFailure(hr, "Failed to copy section prefix");
749 + }
750 +
751 +LExit:
752 + return hr;
753 +}
src/dutil/jsonutil.cpp new
+672
@@ -0,0 +1,672 @@
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 +
5 +const DWORD JSON_STACK_INCREMENT = 5;
6 +
7 +// Prototypes
8 +static HRESULT DoStart(
9 + __in JSON_WRITER* pWriter,
10 + __in JSON_TOKEN tokenStart,
11 + __in_z LPCWSTR wzStartString
12 + );
13 +static HRESULT DoEnd(
14 + __in JSON_WRITER* pWriter,
15 + __in JSON_TOKEN tokenEnd,
16 + __in_z LPCWSTR wzEndString
17 + );
18 +static HRESULT DoKey(
19 + __in JSON_WRITER* pWriter,
20 + __in_z LPCWSTR wzKey
21 + );
22 +static HRESULT DoValue(
23 + __in JSON_WRITER* pWriter,
24 + __in_z_opt LPCWSTR wzValue
25 + );
26 +static HRESULT EnsureTokenStack(
27 + __in JSON_WRITER* pWriter
28 + );
29 +static HRESULT SerializeJsonString(
30 + __out_z LPWSTR* psczJsonString,
31 + __in_z LPCWSTR wzString
32 + );
33 +
34 +
35 +
36 +DAPI_(HRESULT) JsonInitializeReader(
37 + __in_z LPCWSTR wzJson,
38 + __in JSON_READER* pReader
39 + )
40 +{
41 + HRESULT hr = S_OK;
42 +
43 + memset(pReader, 0, sizeof(JSON_READER));
44 + ::InitializeCriticalSection(&pReader->cs);
45 +
46 + hr = StrAllocString(&pReader->sczJson, wzJson, 0);
47 + ExitOnFailure(hr, "Failed to allocate json string.");
48 +
49 + pReader->pwz = pReader->sczJson;
50 +
51 +LExit:
52 + return hr;
53 +}
54 +
55 +
56 +DAPI_(void) JsonUninitializeReader(
57 + __in JSON_READER* pReader
58 + )
59 +{
60 + ReleaseStr(pReader->sczJson);
61 +
62 + ::DeleteCriticalSection(&pReader->cs);
63 + memset(pReader, 0, sizeof(JSON_READER));
64 +}
65 +
66 +
67 +static HRESULT NextToken(
68 + __in JSON_READER* pReader,
69 + __out JSON_TOKEN* pToken
70 + )
71 +{
72 + HRESULT hr = S_OK;
73 +
74 + // Skip whitespace.
75 + while (L' ' == *pReader->pwz ||
76 + L'\t' == *pReader->pwz ||
77 + L'\r' == *pReader->pwz ||
78 + L'\n' == *pReader->pwz ||
79 + L',' == *pReader->pwz)
80 + {
81 + ++pReader->pwz;
82 + }
83 +
84 + switch (*pReader->pwz)
85 + {
86 + case L'{':
87 + *pToken = JSON_TOKEN_OBJECT_START;
88 + break;
89 +
90 + case L':': // begin object value
91 + *pToken = JSON_TOKEN_OBJECT_VALUE;
92 + break;
93 +
94 + case L'}': // end object
95 + *pToken = JSON_TOKEN_OBJECT_END;
96 + break;
97 +
98 + case L'[': // begin array
99 + *pToken = JSON_TOKEN_ARRAY_START;
100 + break;
101 +
102 + case L']': // end array
103 + *pToken = JSON_TOKEN_ARRAY_END;
104 + break;
105 +
106 + case L'"': // string
107 + *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY;
108 + break;
109 +
110 + case L't': // true
111 + case L'f': // false
112 + case L'n': // null
113 + case L'-': // number
114 + case L'0':
115 + case L'1':
116 + case L'2':
117 + case L'3':
118 + case L'4':
119 + case L'5':
120 + case L'6':
121 + case L'7':
122 + case L'8':
123 + case L'9':
124 + *pToken = JSON_TOKEN_VALUE;
125 + break;
126 +
127 + case L'\0':
128 + hr = E_NOMOREITEMS;
129 + break;
130 +
131 + default:
132 + hr = E_INVALIDSTATE;
133 + }
134 +
135 + return hr;
136 +}
137 +
138 +
139 +DAPI_(HRESULT) JsonReadNext(
140 + __in JSON_READER* pReader,
141 + __out JSON_TOKEN* pToken,
142 + __out JSON_VALUE* pValue
143 + )
144 +{
145 + HRESULT hr = S_OK;
146 + //WCHAR wz;
147 + //JSON_TOKEN token = JSON_TOKEN_NONE;
148 +
149 + ::EnterCriticalSection(&pReader->cs);
150 +
151 + hr = NextToken(pReader, pToken);
152 + if (E_NOMOREITEMS == hr)
153 + {
154 + ExitFunction();
155 + }
156 + ExitOnFailure(hr, "Failed to get next token.");
157 +
158 + if (JSON_TOKEN_VALUE == *pToken)
159 + {
160 + hr = JsonReadValue(pReader, pValue);
161 + }
162 + else
163 + {
164 + ++pReader->pwz;
165 + }
166 +
167 +LExit:
168 + ::LeaveCriticalSection(&pReader->cs);
169 + return hr;
170 +}
171 +
172 +
173 +DAPI_(HRESULT) JsonReadValue(
174 + __in JSON_READER* /*pReader*/,
175 + __in JSON_VALUE* /*pValue*/
176 + )
177 +{
178 + HRESULT hr = S_OK;
179 +
180 +//LExit:
181 + return hr;
182 +}
183 +
184 +
185 +DAPI_(HRESULT) JsonInitializeWriter(
186 + __in JSON_WRITER* pWriter
187 + )
188 +{
189 + memset(pWriter, 0, sizeof(JSON_WRITER));
190 + ::InitializeCriticalSection(&pWriter->cs);
191 +
192 + return S_OK;
193 +}
194 +
195 +
196 +DAPI_(void) JsonUninitializeWriter(
197 + __in JSON_WRITER* pWriter
198 + )
199 +{
200 + ReleaseMem(pWriter->rgTokenStack);
201 + ReleaseStr(pWriter->sczJson);
202 +
203 + ::DeleteCriticalSection(&pWriter->cs);
204 + memset(pWriter, 0, sizeof(JSON_WRITER));
205 +}
206 +
207 +
208 +DAPI_(HRESULT) JsonWriteBool(
209 + __in JSON_WRITER* pWriter,
210 + __in BOOL fValue
211 + )
212 +{
213 + HRESULT hr = S_OK;
214 + LPWSTR sczValue = NULL;
215 +
216 + hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0);
217 + ExitOnFailure(hr, "Failed to convert boolean to string.");
218 +
219 + hr = DoValue(pWriter, sczValue);
220 + ExitOnFailure(hr, "Failed to add boolean to JSON.");
221 +
222 +LExit:
223 + ReleaseStr(sczValue);
224 + return hr;
225 +}
226 +
227 +
228 +DAPI_(HRESULT) JsonWriteNumber(
229 + __in JSON_WRITER* pWriter,
230 + __in DWORD dwValue
231 + )
232 +{
233 + HRESULT hr = S_OK;
234 + LPWSTR sczValue = NULL;
235 +
236 + hr = StrAllocFormatted(&sczValue, L"%u", dwValue);
237 + ExitOnFailure(hr, "Failed to convert number to string.");
238 +
239 + hr = DoValue(pWriter, sczValue);
240 + ExitOnFailure(hr, "Failed to add number to JSON.");
241 +
242 +LExit:
243 + ReleaseStr(sczValue);
244 + return hr;
245 +}
246 +
247 +
248 +DAPI_(HRESULT) JsonWriteString(
249 + __in JSON_WRITER* pWriter,
250 + __in_z LPCWSTR wzValue
251 + )
252 +{
253 + HRESULT hr = S_OK;
254 + LPWSTR sczJsonString = NULL;
255 +
256 + hr = SerializeJsonString(&sczJsonString, wzValue);
257 + ExitOnFailure(hr, "Failed to allocate string JSON.");
258 +
259 + hr = DoValue(pWriter, sczJsonString);
260 + ExitOnFailure(hr, "Failed to add string to JSON.");
261 +
262 +LExit:
263 + ReleaseStr(sczJsonString);
264 + return hr;
265 +}
266 +
267 +
268 +DAPI_(HRESULT) JsonWriteArrayStart(
269 + __in JSON_WRITER* pWriter
270 + )
271 +{
272 + HRESULT hr = S_OK;
273 +
274 + hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"[");
275 + ExitOnFailure(hr, "Failed to start JSON array.");
276 +
277 +LExit:
278 + return hr;
279 +}
280 +
281 +
282 +DAPI_(HRESULT) JsonWriteArrayEnd(
283 + __in JSON_WRITER* pWriter
284 + )
285 +{
286 + HRESULT hr = S_OK;
287 +
288 + hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]");
289 + ExitOnFailure(hr, "Failed to end JSON array.");
290 +
291 +LExit:
292 + return hr;
293 +}
294 +
295 +
296 +DAPI_(HRESULT) JsonWriteObjectStart(
297 + __in JSON_WRITER* pWriter
298 + )
299 +{
300 + HRESULT hr = S_OK;
301 +
302 + hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{");
303 + ExitOnFailure(hr, "Failed to start JSON object.");
304 +
305 +LExit:
306 + return hr;
307 +}
308 +
309 +
310 +DAPI_(HRESULT) JsonWriteObjectKey(
311 + __in JSON_WRITER* pWriter,
312 + __in_z LPCWSTR wzKey
313 + )
314 +{
315 + HRESULT hr = S_OK;
316 + LPWSTR sczObjectKey = NULL;
317 +
318 + hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey);
319 + ExitOnFailure(hr, "Failed to allocate JSON object key.");
320 +
321 + hr = DoKey(pWriter, sczObjectKey);
322 + ExitOnFailure(hr, "Failed to add object key to JSON.");
323 +
324 +LExit:
325 + ReleaseStr(sczObjectKey);
326 + return hr;
327 +}
328 +
329 +
330 +DAPI_(HRESULT) JsonWriteObjectEnd(
331 + __in JSON_WRITER* pWriter
332 + )
333 +{
334 + HRESULT hr = S_OK;
335 +
336 + hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}");
337 + ExitOnFailure(hr, "Failed to end JSON object.");
338 +
339 +LExit:
340 + return hr;
341 +}
342 +
343 +
344 +static HRESULT DoStart(
345 + __in JSON_WRITER* pWriter,
346 + __in JSON_TOKEN tokenStart,
347 + __in_z LPCWSTR wzStartString
348 + )
349 +{
350 + Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart);
351 +
352 + HRESULT hr = S_OK;
353 + JSON_TOKEN token = JSON_TOKEN_NONE;
354 + BOOL fNeedComma = FALSE;
355 + BOOL fPushToken = TRUE;
356 +
357 + ::EnterCriticalSection(&pWriter->cs);
358 +
359 + hr = EnsureTokenStack(pWriter);
360 + ExitOnFailure(hr, "Failed to ensure token stack for start.");
361 +
362 + token = pWriter->rgTokenStack[pWriter->cTokens - 1];
363 + switch (token)
364 + {
365 + case JSON_TOKEN_NONE:
366 + token = tokenStart;
367 + fPushToken = FALSE;
368 + break;
369 +
370 + case JSON_TOKEN_ARRAY_START: // array start changes to array value.
371 + token = JSON_TOKEN_ARRAY_VALUE;
372 + break;
373 +
374 + case JSON_TOKEN_ARRAY_VALUE:
375 + case JSON_TOKEN_ARRAY_END:
376 + case JSON_TOKEN_OBJECT_END:
377 + fNeedComma = TRUE;
378 + break;
379 +
380 + default: // everything else is not allowed.
381 + hr = E_UNEXPECTED;
382 + break;
383 + }
384 + ExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now.");
385 +
386 + if (fNeedComma)
387 + {
388 + hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
389 + ExitOnFailure(hr, "Failed to add comma for start array or object to JSON.");
390 + }
391 +
392 + hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0);
393 + ExitOnFailure(hr, "Failed to start JSON array or object.");
394 +
395 + pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
396 + if (fPushToken)
397 + {
398 + pWriter->rgTokenStack[pWriter->cTokens] = tokenStart;
399 + ++pWriter->cTokens;
400 + }
401 +
402 +LExit:
403 + ::LeaveCriticalSection(&pWriter->cs);
404 + return hr;
405 +}
406 +
407 +
408 +static HRESULT DoEnd(
409 + __in JSON_WRITER* pWriter,
410 + __in JSON_TOKEN tokenEnd,
411 + __in_z LPCWSTR wzEndString
412 + )
413 +{
414 + HRESULT hr = S_OK;
415 +
416 + ::EnterCriticalSection(&pWriter->cs);
417 +
418 + if (!pWriter->rgTokenStack || 0 == pWriter->cTokens)
419 + {
420 + hr = E_UNEXPECTED;
421 + ExitOnRootFailure(hr, "Failure to pop token because the stack is empty.");
422 + }
423 + else
424 + {
425 + JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1];
426 + if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) ||
427 + (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token))
428 + {
429 + hr = E_UNEXPECTED;
430 + ExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd);
431 + }
432 + }
433 +
434 + hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0);
435 + ExitOnFailure(hr, "Failed to end JSON array or object.");
436 +
437 + --pWriter->cTokens;
438 +
439 +LExit:
440 + ::LeaveCriticalSection(&pWriter->cs);
441 + return hr;
442 +}
443 +
444 +
445 +static HRESULT DoKey(
446 + __in JSON_WRITER* pWriter,
447 + __in_z LPCWSTR wzKey
448 + )
449 +{
450 + HRESULT hr = S_OK;
451 + JSON_TOKEN token = JSON_TOKEN_NONE;
452 + BOOL fNeedComma = FALSE;
453 +
454 + ::EnterCriticalSection(&pWriter->cs);
455 +
456 + hr = EnsureTokenStack(pWriter);
457 + ExitOnFailure(hr, "Failed to ensure token stack for key.");
458 +
459 + token = pWriter->rgTokenStack[pWriter->cTokens - 1];
460 + switch (token)
461 + {
462 + case JSON_TOKEN_OBJECT_START:
463 + token = JSON_TOKEN_OBJECT_KEY;
464 + break;
465 +
466 + case JSON_TOKEN_OBJECT_VALUE:
467 + token = JSON_TOKEN_OBJECT_KEY;
468 + fNeedComma = TRUE;
469 + break;
470 +
471 + default: // everything else is not allowed.
472 + hr = E_UNEXPECTED;
473 + break;
474 + }
475 + ExitOnRootFailure(hr, "Cannot add key to JSON serializer now.");
476 +
477 + if (fNeedComma)
478 + {
479 + hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
480 + ExitOnFailure(hr, "Failed to add comma for key to JSON.");
481 + }
482 +
483 + hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0);
484 + ExitOnFailure(hr, "Failed to add key to JSON.");
485 +
486 + pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
487 +
488 +LExit:
489 + ::LeaveCriticalSection(&pWriter->cs);
490 + return hr;
491 +}
492 +
493 +
494 +static HRESULT DoValue(
495 + __in JSON_WRITER* pWriter,
496 + __in_z_opt LPCWSTR wzValue
497 + )
498 +{
499 + HRESULT hr = S_OK;
500 + JSON_TOKEN token = JSON_TOKEN_NONE;
501 + BOOL fNeedComma = FALSE;
502 +
503 + ::EnterCriticalSection(&pWriter->cs);
504 +
505 + hr = EnsureTokenStack(pWriter);
506 + ExitOnFailure(hr, "Failed to ensure token stack for value.");
507 +
508 + token = pWriter->rgTokenStack[pWriter->cTokens - 1];
509 + switch (token)
510 + {
511 + case JSON_TOKEN_ARRAY_START:
512 + token = JSON_TOKEN_ARRAY_VALUE;
513 + break;
514 +
515 + case JSON_TOKEN_ARRAY_VALUE:
516 + fNeedComma = TRUE;
517 + break;
518 +
519 + case JSON_TOKEN_OBJECT_KEY:
520 + token = JSON_TOKEN_OBJECT_VALUE;
521 + break;
522 +
523 + case JSON_TOKEN_NONE:
524 + token = JSON_TOKEN_VALUE;
525 + break;
526 +
527 + default: // everything else is not allowed.
528 + hr = E_UNEXPECTED;
529 + break;
530 + }
531 + ExitOnRootFailure(hr, "Cannot add value to JSON serializer now.");
532 +
533 + if (fNeedComma)
534 + {
535 + hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
536 + ExitOnFailure(hr, "Failed to add comma for value to JSON.");
537 + }
538 +
539 + if (wzValue)
540 + {
541 + hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0);
542 + ExitOnFailure(hr, "Failed to add value to JSON.");
543 + }
544 + else
545 + {
546 + hr = StrAllocConcat(&pWriter->sczJson, L"null", 0);
547 + ExitOnFailure(hr, "Failed to add null value to JSON.");
548 + }
549 +
550 + pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
551 +
552 +LExit:
553 + ::LeaveCriticalSection(&pWriter->cs);
554 + return hr;
555 +}
556 +
557 +
558 +static HRESULT EnsureTokenStack(
559 + __in JSON_WRITER* pWriter
560 + )
561 +{
562 + HRESULT hr = S_OK;
563 + DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0;
564 +
565 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT);
566 + ExitOnFailure(hr, "Failed to allocate JSON token stack.");
567 +
568 + if (0 == pWriter->cTokens)
569 + {
570 + pWriter->rgTokenStack[0] = JSON_TOKEN_NONE;
571 + ++pWriter->cTokens;
572 + }
573 +
574 +LExit:
575 + return hr;
576 +}
577 +
578 +
579 +static HRESULT SerializeJsonString(
580 + __out_z LPWSTR* psczJsonString,
581 + __in_z LPCWSTR wzString
582 + )
583 +{
584 + HRESULT hr = S_OK;
585 + DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0)
586 +
587 + for (LPCWSTR pch = wzString; *pch; ++pch)
588 + {
589 + // If it is a special JSON character, add space for the escape backslash.
590 + if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch)
591 + {
592 + ++cchRequired;
593 + }
594 +
595 + ++cchRequired;
596 + }
597 +
598 + hr = StrAlloc(psczJsonString, cchRequired);
599 + ExitOnFailure(hr, "Failed to allocate space for JSON string.");
600 +
601 + LPWSTR pchTarget = *psczJsonString;
602 +
603 + *pchTarget = L'\"';
604 + ++pchTarget;
605 +
606 + for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget)
607 + {
608 + // If it is a special JSON character, handle it or just add the character as is.
609 + switch (*pch)
610 + {
611 + case L'"':
612 + *pchTarget = L'\\';
613 + ++pchTarget;
614 + *pchTarget = L'"';
615 + break;
616 +
617 + case L'\\':
618 + *pchTarget = L'\\';
619 + ++pchTarget;
620 + *pchTarget = L'\\';
621 + break;
622 +
623 + case L'/':
624 + *pchTarget = L'\\';
625 + ++pchTarget;
626 + *pchTarget = L'/';
627 + break;
628 +
629 + case L'\b':
630 + *pchTarget = L'\\';
631 + ++pchTarget;
632 + *pchTarget = L'b';
633 + break;
634 +
635 + case L'\f':
636 + *pchTarget = L'\\';
637 + ++pchTarget;
638 + *pchTarget = L'f';
639 + break;
640 +
641 + case L'\n':
642 + *pchTarget = L'\\';
643 + ++pchTarget;
644 + *pchTarget = L'n';
645 + break;
646 +
647 + case L'\r':
648 + *pchTarget = L'\\';
649 + ++pchTarget;
650 + *pchTarget = L'r';
651 + break;
652 +
653 + case L'\t':
654 + *pchTarget = L'\\';
655 + ++pchTarget;
656 + *pchTarget = L't';
657 + break;
658 +
659 + default:
660 + *pchTarget = *pch;
661 + break;
662 + }
663 +
664 + }
665 +
666 + *pchTarget = L'\"';
667 + ++pchTarget;
668 + *pchTarget = L'\0';
669 +
670 +LExit:
671 + return hr;
672 +}
src/dutil/locutil.cpp new
+613
@@ -0,0 +1,613 @@
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 +
5 +// prototypes
6 +static HRESULT ParseWxl(
7 + __in IXMLDOMDocument* pixd,
8 + __out WIX_LOCALIZATION** ppWixLoc
9 + );
10 +static HRESULT ParseWxlStrings(
11 + __in IXMLDOMElement* pElement,
12 + __in WIX_LOCALIZATION* pWixLoc
13 + );
14 +static HRESULT ParseWxlControls(
15 + __in IXMLDOMElement* pElement,
16 + __in WIX_LOCALIZATION* pWixLoc
17 + );
18 +static HRESULT ParseWxlString(
19 + __in IXMLDOMNode* pixn,
20 + __in DWORD dwIdx,
21 + __in WIX_LOCALIZATION* pWixLoc
22 + );
23 +static HRESULT ParseWxlControl(
24 + __in IXMLDOMNode* pixn,
25 + __in DWORD dwIdx,
26 + __in WIX_LOCALIZATION* pWixLoc
27 + );
28 +
29 +// from Winnls.h
30 +#ifndef MUI_LANGUAGE_ID
31 +#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention
32 +#endif
33 +#ifndef MUI_MERGE_USER_FALLBACK
34 +#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages
35 +#endif
36 +#ifndef MUI_MERGE_SYSTEM_FALLBACK
37 +#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages
38 +#endif
39 +typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) (
40 + __in DWORD dwFlags,
41 + __out PULONG pulNumLanguages,
42 + __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer,
43 + __inout PULONG pcchLanguagesBuffer
44 +);
45 +
46 +extern "C" HRESULT DAPI LocProbeForFile(
47 + __in_z LPCWSTR wzBasePath,
48 + __in_z LPCWSTR wzLocFileName,
49 + __in_z_opt LPCWSTR wzLanguage,
50 + __inout LPWSTR* psczPath
51 + )
52 +{
53 + HRESULT hr = S_OK;
54 + LPWSTR sczProbePath = NULL;
55 + LANGID langid = 0;
56 + LPWSTR sczLangIdFile = NULL;
57 + LPWSTR sczLangsBuff = NULL;
58 + GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages =
59 + reinterpret_cast<GET_THREAD_PREFERRED_UI_LANGUAGES>(
60 + GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages"));
61 +
62 + // If a language was specified, look for a loc file in that as a directory.
63 + if (wzLanguage && *wzLanguage)
64 + {
65 + hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath);
66 + ExitOnFailure(hr, "Failed to concat base path to language.");
67 +
68 + hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath);
69 + ExitOnFailure(hr, "Failed to concat loc file name to probe path.");
70 +
71 + if (FileExistsEx(sczProbePath, NULL))
72 + {
73 + ExitFunction();
74 + }
75 + }
76 +
77 + if (pvfnGetThreadPreferredUILanguages)
78 + {
79 + ULONG nLangs;
80 + ULONG cchLangs = 0;
81 + DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK;
82 + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs))
83 + {
84 + ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size.");
85 + }
86 +
87 + hr = StrAlloc(&sczLangsBuff, cchLangs);
88 + ExitOnFailure(hr, "Failed to allocate buffer for languages");
89 +
90 + nLangs = 0;
91 + if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs))
92 + {
93 + ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list.");
94 + }
95 +
96 + LPWSTR szLangs = sczLangsBuff;
97 + for (ULONG i = 0; i < nLangs; ++i, szLangs += 5)
98 + {
99 + // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value.
100 + hr = StrHexDecode(szLangs, reinterpret_cast<BYTE*>(&langid), sizeof(langid));
101 + ExitOnFailure(hr, "Failed to parse langId.");
102 +
103 + langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid));
104 + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
105 + ExitOnFailure(hr, "Failed to format user preferred langid.");
106 +
107 + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
108 + ExitOnFailure(hr, "Failed to concat user preferred langid file name to base path.");
109 +
110 + if (FileExistsEx(sczProbePath, NULL))
111 + {
112 + ExitFunction();
113 + }
114 + }
115 + }
116 +
117 + langid = ::GetUserDefaultUILanguage();
118 +
119 + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
120 + ExitOnFailure(hr, "Failed to format user langid.");
121 +
122 + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
123 + ExitOnFailure(hr, "Failed to concat user langid file name to base path.");
124 +
125 + if (FileExistsEx(sczProbePath, NULL))
126 + {
127 + ExitFunction();
128 + }
129 +
130 + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid)
131 + {
132 + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT);
133 +
134 + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
135 + ExitOnFailure(hr, "Failed to format user langid (default sublang).");
136 +
137 + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
138 + ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang).");
139 +
140 + if (FileExistsEx(sczProbePath, NULL))
141 + {
142 + ExitFunction();
143 + }
144 + }
145 +
146 + langid = ::GetSystemDefaultUILanguage();
147 +
148 + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
149 + ExitOnFailure(hr, "Failed to format system langid.");
150 +
151 + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
152 + ExitOnFailure(hr, "Failed to concat system langid file name to base path.");
153 +
154 + if (FileExistsEx(sczProbePath, NULL))
155 + {
156 + ExitFunction();
157 + }
158 +
159 + if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid)
160 + {
161 + langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT);
162 +
163 + hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
164 + ExitOnFailure(hr, "Failed to format user langid (default sublang).");
165 +
166 + hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
167 + ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang).");
168 +
169 + if (FileExistsEx(sczProbePath, NULL))
170 + {
171 + ExitFunction();
172 + }
173 + }
174 +
175 + // Finally, look for the loc file in the base path.
176 + hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath);
177 + ExitOnFailure(hr, "Failed to concat loc file name to base path.");
178 +
179 + if (!FileExistsEx(sczProbePath, NULL))
180 + {
181 + hr = E_FILENOTFOUND;
182 + }
183 +
184 +LExit:
185 + if (SUCCEEDED(hr))
186 + {
187 + hr = StrAllocString(psczPath, sczProbePath, 0);
188 + }
189 +
190 + ReleaseStr(sczLangIdFile);
191 + ReleaseStr(sczProbePath);
192 + ReleaseStr(sczLangsBuff);
193 +
194 + return hr;
195 +}
196 +
197 +extern "C" HRESULT DAPI LocLoadFromFile(
198 + __in_z LPCWSTR wzWxlFile,
199 + __out WIX_LOCALIZATION** ppWixLoc
200 + )
201 +{
202 + HRESULT hr = S_OK;
203 + IXMLDOMDocument* pixd = NULL;
204 +
205 + hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd);
206 + ExitOnFailure(hr, "Failed to load WXL file as XML document.");
207 +
208 + hr = ParseWxl(pixd, ppWixLoc);
209 + ExitOnFailure(hr, "Failed to parse WXL.");
210 +
211 +LExit:
212 + ReleaseObject(pixd);
213 +
214 + return hr;
215 +}
216 +
217 +extern "C" HRESULT DAPI LocLoadFromResource(
218 + __in HMODULE hModule,
219 + __in_z LPCSTR szResource,
220 + __out WIX_LOCALIZATION** ppWixLoc
221 + )
222 +{
223 + HRESULT hr = S_OK;
224 + LPVOID pvResource = NULL;
225 + DWORD cbResource = 0;
226 + LPWSTR sczXml = NULL;
227 + IXMLDOMDocument* pixd = NULL;
228 +
229 + hr = ResReadData(hModule, szResource, &pvResource, &cbResource);
230 + ExitOnFailure(hr, "Failed to read theme from resource.");
231 +
232 + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast<LPCSTR>(pvResource), cbResource, CP_UTF8);
233 + ExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string.");
234 +
235 + hr = XmlLoadDocument(sczXml, &pixd);
236 + ExitOnFailure(hr, "Failed to load theme resource as XML document.");
237 +
238 + hr = ParseWxl(pixd, ppWixLoc);
239 + ExitOnFailure(hr, "Failed to parse WXL.");
240 +
241 +LExit:
242 + ReleaseObject(pixd);
243 + ReleaseStr(sczXml);
244 +
245 + return hr;
246 +}
247 +
248 +extern "C" void DAPI LocFree(
249 + __in_opt WIX_LOCALIZATION* pWixLoc
250 + )
251 +{
252 + if (pWixLoc)
253 + {
254 + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx)
255 + {
256 + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId);
257 + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText);
258 + }
259 +
260 + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx)
261 + {
262 + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl);
263 + ReleaseStr(pWixLoc->rgLocControls[idx].wzText);
264 + }
265 +
266 + ReleaseMem(pWixLoc->rgLocStrings);
267 + ReleaseMem(pWixLoc->rgLocControls);
268 + ReleaseMem(pWixLoc);
269 + }
270 +}
271 +
272 +extern "C" HRESULT DAPI LocLocalizeString(
273 + __in const WIX_LOCALIZATION* pWixLoc,
274 + __inout LPWSTR* ppsczInput
275 + )
276 +{
277 + Assert(ppsczInput && pWixLoc);
278 + HRESULT hr = S_OK;
279 +
280 + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i)
281 + {
282 + hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText);
283 + ExitOnFailure(hr, "Localizing string failed.");
284 + }
285 +
286 +LExit:
287 + return hr;
288 +}
289 +
290 +extern "C" HRESULT DAPI LocGetControl(
291 + __in const WIX_LOCALIZATION* pWixLoc,
292 + __in_z LPCWSTR wzId,
293 + __out LOC_CONTROL** ppLocControl
294 + )
295 +{
296 + HRESULT hr = S_OK;
297 + LOC_CONTROL* pLocControl = NULL;
298 +
299 + for (DWORD i = 0; i < pWixLoc->cLocControls; ++i)
300 + {
301 + pLocControl = &pWixLoc->rgLocControls[i];
302 +
303 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1))
304 + {
305 + *ppLocControl = pLocControl;
306 + ExitFunction1(hr = S_OK);
307 + }
308 + }
309 +
310 + hr = E_NOTFOUND;
311 +
312 +LExit:
313 + return hr;
314 +}
315 +
316 +extern "C" HRESULT DAPI LocGetString(
317 + __in const WIX_LOCALIZATION* pWixLoc,
318 + __in_z LPCWSTR wzId,
319 + __out LOC_STRING** ppLocString
320 + )
321 +{
322 + HRESULT hr = E_NOTFOUND;
323 + LOC_STRING* pLocString = NULL;
324 +
325 + for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i)
326 + {
327 + pLocString = pWixLoc->rgLocStrings + i;
328 +
329 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1))
330 + {
331 + *ppLocString = pLocString;
332 + hr = S_OK;
333 + break;
334 + }
335 + }
336 +
337 + return hr;
338 +}
339 +
340 +extern "C" HRESULT DAPI LocAddString(
341 + __in WIX_LOCALIZATION* pWixLoc,
342 + __in_z LPCWSTR wzId,
343 + __in_z LPCWSTR wzLocString,
344 + __in BOOL bOverridable
345 + )
346 +{
347 + HRESULT hr = S_OK;
348 +
349 + ++pWixLoc->cLocStrings;
350 + pWixLoc->rgLocStrings = static_cast<LOC_STRING*>(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE));
351 + ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings.");
352 +
353 + LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1);
354 +
355 + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId);
356 + ExitOnFailure(hr, "Failed to set localization string Id.");
357 +
358 + hr = StrAllocString(&pLocString->wzText, wzLocString, 0);
359 + ExitOnFailure(hr, "Failed to set localization string Text.");
360 +
361 + pLocString->bOverridable = bOverridable;
362 +
363 +LExit:
364 + return hr;
365 +}
366 +
367 +// helper functions
368 +
369 +static HRESULT ParseWxl(
370 + __in IXMLDOMDocument* pixd,
371 + __out WIX_LOCALIZATION** ppWixLoc
372 + )
373 +{
374 + HRESULT hr = S_OK;
375 + IXMLDOMElement *pWxlElement = NULL;
376 + WIX_LOCALIZATION* pWixLoc = NULL;
377 +
378 + pWixLoc = static_cast<WIX_LOCALIZATION*>(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE));
379 + ExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file.");
380 +
381 + // read the WixLocalization tag
382 + hr = pixd->get_documentElement(&pWxlElement);
383 + ExitOnFailure(hr, "Failed to get localization element.");
384 +
385 + // get the Language attribute if present
386 + pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET;
387 + hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId);
388 + if (S_FALSE == hr)
389 + {
390 + hr = S_OK;
391 + }
392 + ExitOnFailure(hr, "Failed to get Language value.");
393 +
394 + // store the strings and controls in a node list
395 + hr = ParseWxlStrings(pWxlElement, pWixLoc);
396 + ExitOnFailure(hr, "Parsing localization strings failed.");
397 +
398 + hr = ParseWxlControls(pWxlElement, pWixLoc);
399 + ExitOnFailure(hr, "Parsing localization controls failed.");
400 +
401 + *ppWixLoc = pWixLoc;
402 + pWixLoc = NULL;
403 +
404 +LExit:
405 + ReleaseObject(pWxlElement);
406 + ReleaseMem(pWixLoc);
407 +
408 + return hr;
409 +}
410 +
411 +
412 +static HRESULT ParseWxlStrings(
413 + __in IXMLDOMElement* pElement,
414 + __in WIX_LOCALIZATION* pWixLoc
415 + )
416 +{
417 + HRESULT hr = S_OK;
418 + IXMLDOMNode* pixn = NULL;
419 + IXMLDOMNodeList* pixnl = NULL;
420 + DWORD dwIdx = 0;
421 +
422 + hr = XmlSelectNodes(pElement, L"String", &pixnl);
423 + ExitOnLastError(hr, "Failed to get String child nodes of Wxl File.");
424 +
425 + hr = pixnl->get_length(reinterpret_cast<long*>(&pWixLoc->cLocStrings));
426 + ExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File.");
427 +
428 + if (0 < pWixLoc->cLocStrings)
429 + {
430 + pWixLoc->rgLocStrings = static_cast<LOC_STRING*>(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE));
431 + ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings.");
432 +
433 + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
434 + {
435 + hr = ParseWxlString(pixn, dwIdx, pWixLoc);
436 + ExitOnFailure(hr, "Failed to parse localization string.");
437 +
438 + ++dwIdx;
439 + ReleaseNullObject(pixn);
440 + }
441 +
442 + hr = S_OK;
443 + ExitOnFailure(hr, "Failed to enumerate all localization strings.");
444 + }
445 +
446 +LExit:
447 + if (FAILED(hr) && pWixLoc->rgLocStrings)
448 + {
449 + for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx)
450 + {
451 + ReleaseStr(pWixLoc->rgLocStrings[idx].wzId);
452 + ReleaseStr(pWixLoc->rgLocStrings[idx].wzText);
453 + }
454 +
455 + ReleaseMem(pWixLoc->rgLocStrings);
456 + }
457 +
458 + ReleaseObject(pixn);
459 + ReleaseObject(pixnl);
460 +
461 + return hr;
462 +}
463 +
464 +static HRESULT ParseWxlControls(
465 + __in IXMLDOMElement* pElement,
466 + __in WIX_LOCALIZATION* pWixLoc
467 + )
468 +{
469 + HRESULT hr = S_OK;
470 + IXMLDOMNode* pixn = NULL;
471 + IXMLDOMNodeList* pixnl = NULL;
472 + DWORD dwIdx = 0;
473 +
474 + hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl);
475 + ExitOnLastError(hr, "Failed to get UI child nodes of Wxl File.");
476 +
477 + hr = pixnl->get_length(reinterpret_cast<long*>(&pWixLoc->cLocControls));
478 + ExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File.");
479 +
480 + if (0 < pWixLoc->cLocControls)
481 + {
482 + pWixLoc->rgLocControls = static_cast<LOC_CONTROL*>(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE));
483 + ExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls.");
484 +
485 + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
486 + {
487 + hr = ParseWxlControl(pixn, dwIdx, pWixLoc);
488 + ExitOnFailure(hr, "Failed to parse localized control.");
489 +
490 + ++dwIdx;
491 + ReleaseNullObject(pixn);
492 + }
493 +
494 + hr = S_OK;
495 + ExitOnFailure(hr, "Failed to enumerate all localized controls.");
496 + }
497 +
498 +LExit:
499 + if (FAILED(hr) && pWixLoc->rgLocControls)
500 + {
501 + for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx)
502 + {
503 + ReleaseStr(pWixLoc->rgLocControls[idx].wzControl);
504 + ReleaseStr(pWixLoc->rgLocControls[idx].wzText);
505 + }
506 +
507 + ReleaseMem(pWixLoc->rgLocControls);
508 + }
509 +
510 + ReleaseObject(pixn);
511 + ReleaseObject(pixnl);
512 +
513 + return hr;
514 +}
515 +
516 +static HRESULT ParseWxlString(
517 + __in IXMLDOMNode* pixn,
518 + __in DWORD dwIdx,
519 + __in WIX_LOCALIZATION* pWixLoc
520 + )
521 +{
522 + HRESULT hr = S_OK;
523 + LOC_STRING* pLocString = NULL;
524 + BSTR bstrText = NULL;
525 +
526 + pLocString = pWixLoc->rgLocStrings + dwIdx;
527 +
528 + // Id
529 + hr = XmlGetAttribute(pixn, L"Id", &bstrText);
530 + ExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file.");
531 +
532 + hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText);
533 + ExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file.");
534 +
535 + ReleaseNullBSTR(bstrText);
536 +
537 + // Overrideable
538 + hr = XmlGetAttribute(pixn, L"Overridable", &bstrText);
539 + ExitOnFailure(hr, "Failed to get Xml attribute Overridable.");
540 +
541 + if (S_OK == hr)
542 + {
543 + pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1);
544 + }
545 +
546 + ReleaseNullBSTR(bstrText);
547 +
548 + // Text
549 + hr = XmlGetText(pixn, &bstrText);
550 + ExitOnFailure(hr, "Failed to get Xml text in Wxl file.");
551 +
552 + hr = StrAllocString(&pLocString->wzText, bstrText, 0);
553 + ExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file.");
554 +
555 +LExit:
556 + ReleaseBSTR(bstrText);
557 +
558 + return hr;
559 +}
560 +
561 +static HRESULT ParseWxlControl(
562 + __in IXMLDOMNode* pixn,
563 + __in DWORD dwIdx,
564 + __in WIX_LOCALIZATION* pWixLoc
565 + )
566 +{
567 + HRESULT hr = S_OK;
568 + LOC_CONTROL* pLocControl = NULL;
569 + BSTR bstrText = NULL;
570 +
571 + pLocControl = pWixLoc->rgLocControls + dwIdx;
572 +
573 + // Id
574 + hr = XmlGetAttribute(pixn, L"Control", &bstrText);
575 + ExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file.");
576 +
577 + hr = StrAllocString(&pLocControl->wzControl, bstrText, 0);
578 + ExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file.");
579 +
580 + ReleaseNullBSTR(bstrText);
581 +
582 + // X
583 + pLocControl->nX = LOC_CONTROL_NOT_SET;
584 + hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast<DWORD*>(&pLocControl->nX));
585 + ExitOnFailure(hr, "Failed to get control X attribute.");
586 +
587 + // Y
588 + pLocControl->nY = LOC_CONTROL_NOT_SET;
589 + hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast<DWORD*>(&pLocControl->nY));
590 + ExitOnFailure(hr, "Failed to get control Y attribute.");
591 +
592 + // Width
593 + pLocControl->nWidth = LOC_CONTROL_NOT_SET;
594 + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast<DWORD*>(&pLocControl->nWidth));
595 + ExitOnFailure(hr, "Failed to get control width attribute.");
596 +
597 + // Height
598 + pLocControl->nHeight = LOC_CONTROL_NOT_SET;
599 + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&pLocControl->nHeight));
600 + ExitOnFailure(hr, "Failed to get control height attribute.");
601 +
602 + // Text
603 + hr = XmlGetText(pixn, &bstrText);
604 + ExitOnFailure(hr, "Failed to get control text in Wxl file.");
605 +
606 + hr = StrAllocString(&pLocControl->wzText, bstrText, 0);
607 + ExitOnFailure(hr, "Failed to duplicate control text in Wxl file.");
608 +
609 +LExit:
610 + ReleaseBSTR(bstrText);
611 +
612 + return hr;
613 +}
src/dutil/logutil.cpp new
+942
@@ -0,0 +1,942 @@
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 +
5 +// globals
6 +static HMODULE LogUtil_hModule = NULL;
7 +static BOOL LogUtil_fDisabled = FALSE;
8 +static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE;
9 +static LPWSTR LogUtil_sczLogPath = NULL;
10 +static LPSTR LogUtil_sczPreInitBuffer = NULL;
11 +static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD;
12 +static CRITICAL_SECTION LogUtil_csLog = { };
13 +static BOOL LogUtil_fInitializedCriticalSection = FALSE;
14 +
15 +// Customization of certain parts of the string, within a line
16 +static LPWSTR LogUtil_sczSpecialBeginLine = NULL;
17 +static LPWSTR LogUtil_sczSpecialEndLine = NULL;
18 +static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL;
19 +
20 +static LPCSTR LOGUTIL_UNKNOWN = "unknown";
21 +static LPCSTR LOGUTIL_WARNING = "warning";
22 +static LPCSTR LOGUTIL_STANDARD = "standard";
23 +static LPCSTR LOGUTIL_VERBOSE = "verbose";
24 +static LPCSTR LOGUTIL_DEBUG = "debug";
25 +static LPCSTR LOGUTIL_NONE = "none";
26 +
27 +// prototypes
28 +static HRESULT LogIdWork(
29 + __in REPORT_LEVEL rl,
30 + __in_opt HMODULE hModule,
31 + __in DWORD dwLogId,
32 + __in va_list args,
33 + __in BOOL fLOGUTIL_NEWLINE
34 + );
35 +static HRESULT LogStringWorkArgs(
36 + __in REPORT_LEVEL rl,
37 + __in_z __format_string LPCSTR szFormat,
38 + __in va_list args,
39 + __in BOOL fLOGUTIL_NEWLINE
40 + );
41 +static HRESULT LogStringWork(
42 + __in REPORT_LEVEL rl,
43 + __in DWORD dwLogId,
44 + __in_z LPCWSTR sczString,
45 + __in BOOL fLOGUTIL_NEWLINE
46 + );
47 +
48 +// Hook to allow redirecting LogStringWorkRaw function calls
49 +static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL;
50 +static LPVOID s_vpvLogStringWorkRawContext = NULL;
51 +
52 +
53 +/********************************************************************
54 + IsLogInitialized - Checks if log is currently initialized.
55 +********************************************************************/
56 +extern "C" BOOL DAPI IsLogInitialized()
57 +{
58 + return LogUtil_fInitializedCriticalSection;
59 +}
60 +
61 +/********************************************************************
62 + IsLogOpen - Checks if log is currently initialized and open.
63 +********************************************************************/
64 +extern "C" BOOL DAPI IsLogOpen()
65 +{
66 + return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath);
67 +}
68 +
69 +
70 +/********************************************************************
71 + LogInitialize - initializes the logutil API
72 +
73 +********************************************************************/
74 +extern "C" void DAPI LogInitialize(
75 + __in HMODULE hModule
76 + )
77 +{
78 + AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called.");
79 +
80 + LogUtil_hModule = hModule;
81 + LogUtil_fDisabled = FALSE;
82 +
83 + ::InitializeCriticalSection(&LogUtil_csLog);
84 + LogUtil_fInitializedCriticalSection = TRUE;
85 +}
86 +
87 +
88 +/********************************************************************
89 + LogOpen - creates an application log file
90 +
91 + NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name
92 +********************************************************************/
93 +extern "C" HRESULT DAPI LogOpen(
94 + __in_z_opt LPCWSTR wzDirectory,
95 + __in_z LPCWSTR wzLog,
96 + __in_z_opt LPCWSTR wzPostfix,
97 + __in_z_opt LPCWSTR wzExt,
98 + __in BOOL fAppend,
99 + __in BOOL fHeader,
100 + __out_z_opt LPWSTR* psczLogPath
101 + )
102 +{
103 + HRESULT hr = S_OK;
104 + BOOL fEnteredCriticalSection = FALSE;
105 + LPWSTR sczLogDirectory = NULL;
106 +
107 + ::EnterCriticalSection(&LogUtil_csLog);
108 + fEnteredCriticalSection = TRUE;
109 +
110 + if (wzExt && *wzExt)
111 + {
112 + hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog);
113 + ExitOnFailure(hr, "Failed to create log based on current system time.");
114 + }
115 + else
116 + {
117 + hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath);
118 + ExitOnFailure(hr, "Failed to combine the log path.");
119 +
120 + hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory);
121 + ExitOnFailure(hr, "Failed to get log directory.");
122 +
123 + hr = DirEnsureExists(sczLogDirectory, NULL);
124 + ExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory);
125 +
126 + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
127 + if (INVALID_HANDLE_VALUE == LogUtil_hLog)
128 + {
129 + ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
130 + }
131 +
132 + if (fAppend)
133 + {
134 + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
135 + }
136 + }
137 +
138 + if (fHeader)
139 + {
140 + LogHeader();
141 + }
142 +
143 + if (NULL != LogUtil_sczPreInitBuffer)
144 + {
145 + // Log anything that was logged before LogOpen() was called.
146 + LogStringWorkRaw(LogUtil_sczPreInitBuffer);
147 + ReleaseNullStr(LogUtil_sczPreInitBuffer);
148 + }
149 +
150 + if (psczLogPath)
151 + {
152 + hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0);
153 + ExitOnFailure(hr, "Failed to copy log path.");
154 + }
155 +
156 + LogUtil_fDisabled = FALSE;
157 +
158 +LExit:
159 + if (fEnteredCriticalSection)
160 + {
161 + ::LeaveCriticalSection(&LogUtil_csLog);
162 + }
163 +
164 + ReleaseStr(sczLogDirectory);
165 +
166 + return hr;
167 +}
168 +
169 +
170 +/********************************************************************
171 + LogDisable - closes any open files and disables in memory logging.
172 +
173 +********************************************************************/
174 +void DAPI LogDisable()
175 +{
176 + ::EnterCriticalSection(&LogUtil_csLog);
177 +
178 + LogUtil_fDisabled = TRUE;
179 +
180 + ReleaseFileHandle(LogUtil_hLog);
181 + ReleaseNullStr(LogUtil_sczLogPath);
182 + ReleaseNullStr(LogUtil_sczPreInitBuffer);
183 +
184 + ::LeaveCriticalSection(&LogUtil_csLog);
185 +}
186 +
187 +
188 +/********************************************************************
189 + LogRedirect - Redirects all logging strings to the specified
190 + function - or set NULL to disable the hook
191 +********************************************************************/
192 +void DAPI LogRedirect(
193 + __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw,
194 + __in_opt LPVOID pvContext
195 + )
196 +{
197 + s_vpfLogStringWorkRaw = vpfLogStringWorkRaw;
198 + s_vpvLogStringWorkRawContext = pvContext;
199 +}
200 +
201 +
202 +/********************************************************************
203 + LogRename - Renames a logfile, moving its contents to a new path,
204 + and re-opening the file for appending at the new
205 + location
206 +********************************************************************/
207 +HRESULT DAPI LogRename(
208 + __in_z LPCWSTR wzNewPath
209 + )
210 +{
211 + HRESULT hr = S_OK;
212 + BOOL fEnteredCriticalSection = FALSE;
213 +
214 + ::EnterCriticalSection(&LogUtil_csLog);
215 + fEnteredCriticalSection = TRUE;
216 +
217 + ReleaseFileHandle(LogUtil_hLog);
218 +
219 + hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE);
220 + ExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath);
221 +
222 + hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0);
223 + ExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath);
224 +
225 + LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
226 + if (INVALID_HANDLE_VALUE == LogUtil_hLog)
227 + {
228 + ExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
229 + }
230 +
231 + // Enable "append" mode by moving file pointer to the end
232 + ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
233 +
234 +LExit:
235 + if (fEnteredCriticalSection)
236 + {
237 + ::LeaveCriticalSection(&LogUtil_csLog);
238 + }
239 +
240 + return hr;
241 +}
242 +
243 +
244 +extern "C" void DAPI LogClose(
245 + __in BOOL fFooter
246 + )
247 +{
248 + if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter)
249 + {
250 + LogFooter();
251 + }
252 +
253 + ReleaseFileHandle(LogUtil_hLog);
254 + ReleaseNullStr(LogUtil_sczLogPath);
255 + ReleaseNullStr(LogUtil_sczPreInitBuffer);
256 +}
257 +
258 +
259 +extern "C" void DAPI LogUninitialize(
260 + __in BOOL fFooter
261 + )
262 +{
263 + LogClose(fFooter);
264 +
265 + if (LogUtil_fInitializedCriticalSection)
266 + {
267 + ::DeleteCriticalSection(&LogUtil_csLog);
268 + LogUtil_fInitializedCriticalSection = FALSE;
269 + }
270 +
271 + LogUtil_hModule = NULL;
272 + LogUtil_fDisabled = FALSE;
273 +
274 + ReleaseNullStr(LogUtil_sczSpecialBeginLine);
275 + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
276 + ReleaseNullStr(LogUtil_sczSpecialEndLine);
277 +}
278 +
279 +
280 +/********************************************************************
281 + LogIsOpen - returns whether log file is open or note
282 +
283 +********************************************************************/
284 +extern "C" BOOL DAPI LogIsOpen()
285 +{
286 + return INVALID_HANDLE_VALUE != LogUtil_hLog;
287 +}
288 +
289 +
290 +/********************************************************************
291 + LogSetSpecialParams - sets a special beginline string, endline
292 + string, post-timestamp string, etc.
293 +********************************************************************/
294 +HRESULT DAPI LogSetSpecialParams(
295 + __in_z_opt LPCWSTR wzSpecialBeginLine,
296 + __in_z_opt LPCWSTR wzSpecialAfterTimeStamp,
297 + __in_z_opt LPCWSTR wzSpecialEndLine
298 + )
299 +{
300 + HRESULT hr = S_OK;
301 +
302 + // Handle special string to be prepended before every full line
303 + if (NULL == wzSpecialBeginLine)
304 + {
305 + ReleaseNullStr(LogUtil_sczSpecialBeginLine);
306 + }
307 + else
308 + {
309 + hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0);
310 + ExitOnFailure(hr, "Failed to allocate copy of special beginline string");
311 + }
312 +
313 + // Handle special string to be appended to every time stamp
314 + if (NULL == wzSpecialAfterTimeStamp)
315 + {
316 + ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
317 + }
318 + else
319 + {
320 + hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0);
321 + ExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string");
322 + }
323 +
324 + // Handle special string to be appended before every full line
325 + if (NULL == wzSpecialEndLine)
326 + {
327 + ReleaseNullStr(LogUtil_sczSpecialEndLine);
328 + }
329 + else
330 + {
331 + hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0);
332 + ExitOnFailure(hr, "Failed to allocate copy of special endline string");
333 + }
334 +
335 +LExit:
336 + return hr;
337 +}
338 +
339 +/********************************************************************
340 + LogSetLevel - sets the logging level
341 +
342 + NOTE: returns previous logging level
343 +********************************************************************/
344 +extern "C" REPORT_LEVEL DAPI LogSetLevel(
345 + __in REPORT_LEVEL rl,
346 + __in BOOL fLogChange
347 + )
348 +{
349 + AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set");
350 +
351 + REPORT_LEVEL rlPrev = LogUtil_rlCurrent;
352 +
353 + if (LogUtil_rlCurrent != rl)
354 + {
355 + LogUtil_rlCurrent = rl;
356 +
357 + if (fLogChange)
358 + {
359 + LPCSTR szLevel = LOGUTIL_UNKNOWN;
360 + switch (LogUtil_rlCurrent)
361 + {
362 + case REPORT_WARNING:
363 + szLevel = LOGUTIL_WARNING;
364 + break;
365 + case REPORT_STANDARD:
366 + szLevel = LOGUTIL_STANDARD;
367 + break;
368 + case REPORT_VERBOSE:
369 + szLevel = LOGUTIL_VERBOSE;
370 + break;
371 + case REPORT_DEBUG:
372 + szLevel = LOGUTIL_DEBUG;
373 + break;
374 + case REPORT_NONE:
375 + szLevel = LOGUTIL_NONE;
376 + break;
377 + }
378 +
379 + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
380 + }
381 + }
382 +
383 + return rlPrev;
384 +}
385 +
386 +
387 +/********************************************************************
388 + LogGetLevel - gets the current logging level
389 +
390 +********************************************************************/
391 +extern "C" REPORT_LEVEL DAPI LogGetLevel()
392 +{
393 + return LogUtil_rlCurrent;
394 +}
395 +
396 +
397 +/********************************************************************
398 + LogGetPath - gets the current log path
399 +
400 +********************************************************************/
401 +extern "C" HRESULT DAPI LogGetPath(
402 + __out_ecount_z(cchLogPath) LPWSTR pwzLogPath,
403 + __in DWORD cchLogPath
404 + )
405 +{
406 + Assert(pwzLogPath);
407 +
408 + HRESULT hr = S_OK;
409 +
410 + if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one!
411 + {
412 + ExitFunction1(hr = E_UNEXPECTED);
413 + }
414 +
415 + hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath);
416 +
417 +LExit:
418 + return hr;
419 +}
420 +
421 +
422 +/********************************************************************
423 + LogGetHandle - gets the current log file handle
424 +
425 +********************************************************************/
426 +extern "C" HANDLE DAPI LogGetHandle()
427 +{
428 + return LogUtil_hLog;
429 +}
430 +
431 +
432 +/********************************************************************
433 + LogString - write a string to the log
434 +
435 + NOTE: use printf formatting ("%ls", "%d", etc.)
436 +********************************************************************/
437 +extern "C" HRESULT DAPIV LogString(
438 + __in REPORT_LEVEL rl,
439 + __in_z __format_string LPCSTR szFormat,
440 + ...
441 + )
442 +{
443 + HRESULT hr = S_OK;
444 + va_list args;
445 +
446 + va_start(args, szFormat);
447 + hr = LogStringArgs(rl, szFormat, args);
448 + va_end(args);
449 +
450 + return hr;
451 +}
452 +
453 +extern "C" HRESULT DAPI LogStringArgs(
454 + __in REPORT_LEVEL rl,
455 + __in_z __format_string LPCSTR szFormat,
456 + __in va_list args
457 + )
458 +{
459 + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
460 + HRESULT hr = S_OK;
461 +
462 + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
463 + {
464 + ExitFunction1(hr = S_FALSE);
465 + }
466 +
467 + hr = LogStringWorkArgs(rl, szFormat, args, FALSE);
468 +
469 +LExit:
470 + return hr;
471 +}
472 +
473 +/********************************************************************
474 + LogStringLine - write a string plus LOGUTIL_NEWLINE to the log
475 +
476 + NOTE: use printf formatting ("%ls", "%d", etc.)
477 +********************************************************************/
478 +extern "C" HRESULT DAPIV LogStringLine(
479 + __in REPORT_LEVEL rl,
480 + __in_z __format_string LPCSTR szFormat,
481 + ...
482 + )
483 +{
484 + HRESULT hr = S_OK;
485 + va_list args;
486 +
487 + va_start(args, szFormat);
488 + hr = LogStringLineArgs(rl, szFormat, args);
489 + va_end(args);
490 +
491 + return hr;
492 +}
493 +
494 +extern "C" HRESULT DAPI LogStringLineArgs(
495 + __in REPORT_LEVEL rl,
496 + __in_z __format_string LPCSTR szFormat,
497 + __in va_list args
498 + )
499 +{
500 + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
501 + HRESULT hr = S_OK;
502 +
503 + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
504 + {
505 + ExitFunction1(hr = S_FALSE);
506 + }
507 +
508 + hr = LogStringWorkArgs(rl, szFormat, args, TRUE);
509 +
510 +LExit:
511 + return hr;
512 +}
513 +
514 +/********************************************************************
515 + LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log
516 +
517 + NOTE: uses format string from MESSAGETABLE resource
518 +********************************************************************/
519 +
520 +extern "C" HRESULT DAPI LogIdModuleArgs(
521 + __in REPORT_LEVEL rl,
522 + __in DWORD dwLogId,
523 + __in_opt HMODULE hModule,
524 + __in va_list args
525 + )
526 +{
527 + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
528 + HRESULT hr = S_OK;
529 +
530 + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
531 + {
532 + ExitFunction1(hr = S_FALSE);
533 + }
534 +
535 + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
536 +
537 +LExit:
538 + return hr;
539 +}
540 +
541 +extern "C" HRESULT DAPI LogIdModule(
542 + __in REPORT_LEVEL rl,
543 + __in DWORD dwLogId,
544 + __in_opt HMODULE hModule,
545 + ...
546 + )
547 +{
548 + AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
549 + HRESULT hr = S_OK;
550 + va_list args;
551 +
552 + if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
553 + {
554 + ExitFunction1(hr = S_FALSE);
555 + }
556 +
557 + va_start(args, hModule);
558 + hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
559 + va_end(args);
560 +
561 +LExit:
562 + return hr;
563 +}
564 +
565 +
566 +
567 +
568 +/********************************************************************
569 + LogError - write an error to the log
570 +
571 + NOTE: use printf formatting ("%ls", "%d", etc.)
572 +********************************************************************/
573 +extern "C" HRESULT DAPIV LogErrorString(
574 + __in HRESULT hrError,
575 + __in_z __format_string LPCSTR szFormat,
576 + ...
577 + )
578 +{
579 + HRESULT hr = S_OK;
580 +
581 + va_list args;
582 + va_start(args, szFormat);
583 + hr = LogErrorStringArgs(hrError, szFormat, args);
584 + va_end(args);
585 +
586 + return hr;
587 +}
588 +
589 +extern "C" HRESULT DAPI LogErrorStringArgs(
590 + __in HRESULT hrError,
591 + __in_z __format_string LPCSTR szFormat,
592 + __in va_list args
593 + )
594 +{
595 + HRESULT hr = S_OK;
596 + LPWSTR sczFormat = NULL;
597 + LPWSTR sczMessage = NULL;
598 +
599 + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
600 + ExitOnFailure(hr, "Failed to convert format string to wide character string");
601 +
602 + // format the string as a unicode string - this is necessary to be able to include
603 + // international characters in our output string. This does have the counterintuitive effect
604 + // that the caller's "%s" is interpreted differently
605 + // (so callers should use %hs for LPSTR and %ls for LPWSTR)
606 + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
607 + ExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat);
608 +
609 + hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage);
610 +
611 +LExit:
612 + ReleaseStr(sczFormat);
613 + ReleaseStr(sczMessage);
614 +
615 + return hr;
616 +}
617 +
618 +
619 +/********************************************************************
620 + LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log
621 +
622 + NOTE: uses format string from MESSAGETABLE resource
623 + can log no more than three strings in the error message
624 +********************************************************************/
625 +extern "C" HRESULT DAPI LogErrorIdModule(
626 + __in HRESULT hrError,
627 + __in DWORD dwLogId,
628 + __in_opt HMODULE hModule,
629 + __in_z_opt LPCWSTR wzString1 = NULL,
630 + __in_z_opt LPCWSTR wzString2 = NULL,
631 + __in_z_opt LPCWSTR wzString3 = NULL
632 + )
633 +{
634 + HRESULT hr = S_OK;
635 + WCHAR wzError[11];
636 + WORD cStrings = 1; // guaranteed wzError is in the list
637 +
638 + hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError);
639 + ExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError);
640 +
641 + cStrings += wzString1 ? 1 : 0;
642 + cStrings += wzString2 ? 1 : 0;
643 + cStrings += wzString3 ? 1 : 0;
644 +
645 + hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3);
646 + ExitOnFailure(hr, "Failed to log id module.");
647 +
648 +LExit:
649 + return hr;
650 +}
651 +
652 +/********************************************************************
653 + LogHeader - write a standard header to the log
654 +
655 +********************************************************************/
656 +extern "C" HRESULT DAPI LogHeader()
657 +{
658 + HRESULT hr = S_OK;
659 + WCHAR wzComputerName[MAX_PATH];
660 + DWORD cchComputerName = countof(wzComputerName);
661 + WCHAR wzPath[MAX_PATH];
662 + DWORD dwMajorVersion = 0;
663 + DWORD dwMinorVersion = 0;
664 + LPCSTR szLevel = LOGUTIL_UNKNOWN;
665 + LPWSTR sczCurrentDateTime = NULL;
666 +
667 + //
668 + // get the interesting data
669 + //
670 + if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath)))
671 + {
672 + memset(wzPath, 0, sizeof(wzPath));
673 + }
674 +
675 + hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
676 + if (FAILED(hr))
677 + {
678 + dwMajorVersion = 0;
679 + dwMinorVersion = 0;
680 + }
681 +
682 + if (!::GetComputerNameW(wzComputerName, &cchComputerName))
683 + {
684 + ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName));
685 + }
686 +
687 + TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
688 +
689 + //
690 + // write data to the log
691 + //
692 + LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime);
693 + LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF);
694 + LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName);
695 + switch (LogUtil_rlCurrent)
696 + {
697 + case REPORT_WARNING:
698 + szLevel = LOGUTIL_WARNING;
699 + break;
700 + case REPORT_STANDARD:
701 + szLevel = LOGUTIL_STANDARD;
702 + break;
703 + case REPORT_VERBOSE:
704 + szLevel = LOGUTIL_VERBOSE;
705 + break;
706 + case REPORT_DEBUG:
707 + szLevel = LOGUTIL_DEBUG;
708 + break;
709 + case REPORT_NONE:
710 + szLevel = LOGUTIL_NONE;
711 + break;
712 + }
713 + LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
714 +
715 + hr = S_OK;
716 +
717 + ReleaseStr(sczCurrentDateTime);
718 +
719 + return hr;
720 +}
721 +
722 +
723 +/********************************************************************
724 + LogFooterWork - write a standard footer to the log
725 +
726 +********************************************************************/
727 +
728 +static HRESULT LogFooterWork(
729 + __in_z __format_string LPCSTR szFormat,
730 + ...
731 + )
732 +{
733 + HRESULT hr = S_OK;
734 +
735 + va_list args;
736 + va_start(args, szFormat);
737 + hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE);
738 + va_end(args);
739 +
740 + return hr;
741 +}
742 +
743 +extern "C" HRESULT DAPI LogFooter()
744 +{
745 + HRESULT hr = S_OK;
746 + LPWSTR sczCurrentDateTime = NULL;
747 + TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
748 + hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime);
749 + ReleaseStr(sczCurrentDateTime);
750 + return hr;
751 +}
752 +
753 +/********************************************************************
754 + LogStringWorkRaw - Write a raw, unformatted string to the log
755 +
756 +********************************************************************/
757 +extern "C" HRESULT LogStringWorkRaw(
758 + __in_z LPCSTR szLogData
759 + )
760 +{
761 + Assert(szLogData && *szLogData);
762 +
763 + HRESULT hr = S_OK;
764 + DWORD cbLogData = 0;
765 + DWORD cbTotal = 0;
766 + DWORD cbWrote = 0;
767 +
768 + cbLogData = lstrlenA(szLogData);
769 +
770 + // If the log hasn't been initialized yet, store it in a buffer
771 + if (INVALID_HANDLE_VALUE == LogUtil_hLog)
772 + {
773 + hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0);
774 + ExitOnFailure(hr, "Failed to concatenate string to pre-init buffer");
775 +
776 + ExitFunction1(hr = S_OK);
777 + }
778 +
779 + // write the string
780 + while (cbTotal < cbLogData)
781 + {
782 + if (!::WriteFile(LogUtil_hLog, reinterpret_cast<const BYTE*>(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL))
783 + {
784 + ExitOnLastError(hr, "Failed to write output to log: %ls - %ls", LogUtil_sczLogPath, szLogData);
785 + }
786 +
787 + cbTotal += cbWrote;
788 + }
789 +
790 +LExit:
791 + return hr;
792 +}
793 +
794 +//
795 +// private worker functions
796 +//
797 +static HRESULT LogIdWork(
798 + __in REPORT_LEVEL rl,
799 + __in_opt HMODULE hModule,
800 + __in DWORD dwLogId,
801 + __in va_list args,
802 + __in BOOL fLOGUTIL_NEWLINE
803 + )
804 +{
805 + HRESULT hr = S_OK;
806 + LPWSTR pwz = NULL;
807 + DWORD cch = 0;
808 +
809 + // get the string for the id
810 +#pragma prefast(push)
811 +#pragma prefast(disable:25028)
812 +#pragma prefast(disable:25068)
813 + cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
814 + static_cast<LPCVOID>(hModule), dwLogId, 0, reinterpret_cast<LPWSTR>(&pwz), 0, &args);
815 +#pragma prefast(pop)
816 +
817 + if (0 == cch)
818 + {
819 + ExitOnLastError(hr, "failed to log id: %d", dwLogId);
820 + }
821 +
822 + if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1])
823 + {
824 + pwz[cch-2] = L'\0'; // remove newline from message table
825 + }
826 +
827 + LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE);
828 +
829 +LExit:
830 + if (pwz)
831 + {
832 + ::LocalFree(pwz);
833 + }
834 +
835 + return hr;
836 +}
837 +
838 +
839 +static HRESULT LogStringWorkArgs(
840 + __in REPORT_LEVEL rl,
841 + __in_z __format_string LPCSTR szFormat,
842 + __in va_list args,
843 + __in BOOL fLOGUTIL_NEWLINE
844 + )
845 +{
846 + Assert(szFormat && *szFormat);
847 +
848 + HRESULT hr = S_OK;
849 + LPWSTR sczFormat = NULL;
850 + LPWSTR sczMessage = NULL;
851 +
852 + hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
853 + ExitOnFailure(hr, "Failed to convert format string to wide character string");
854 +
855 + // format the string as a unicode string
856 + hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
857 + ExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat);
858 +
859 + hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE);
860 + ExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage);
861 +
862 +LExit:
863 + ReleaseStr(sczFormat);
864 + ReleaseStr(sczMessage);
865 +
866 + return hr;
867 +}
868 +
869 +
870 +static HRESULT LogStringWork(
871 + __in REPORT_LEVEL rl,
872 + __in DWORD dwLogId,
873 + __in_z LPCWSTR sczString,
874 + __in BOOL fLOGUTIL_NEWLINE
875 + )
876 +{
877 + Assert(sczString && *sczString);
878 +
879 + HRESULT hr = S_OK;
880 + BOOL fEnteredCriticalSection = FALSE;
881 + LPWSTR scz = NULL;
882 + LPCWSTR wzLogData = NULL;
883 + LPSTR sczMultiByte = NULL;
884 +
885 + // If logging is disabled, just bail.
886 + if (LogUtil_fDisabled)
887 + {
888 + ExitFunction();
889 + }
890 +
891 + ::EnterCriticalSection(&LogUtil_csLog);
892 + fEnteredCriticalSection = TRUE;
893 +
894 + if (fLOGUTIL_NEWLINE)
895 + {
896 + // get the process and thread id.
897 + DWORD dwProcessId = ::GetCurrentProcessId();
898 + DWORD dwThreadId = ::GetCurrentThreadId();
899 +
900 + // get the time relative to GMT.
901 + SYSTEMTIME st = { };
902 + ::GetLocalTime(&st);
903 +
904 + DWORD dwId = dwLogId & 0xFFFFFFF;
905 + DWORD dwType = dwLogId & 0xF0000000;
906 + LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i";
907 +
908 + // add line prefix and trailing newline
909 + hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"",
910 + dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId,
911 + LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n");
912 + ExitOnFailure(hr, "Failed to format line prefix.");
913 + }
914 +
915 + wzLogData = scz ? scz : sczString;
916 +
917 + // Convert to UTF-8 before writing out to the log file
918 + hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8);
919 + ExitOnFailure(hr, "Failed to convert log string to UTF-8");
920 +
921 + if (s_vpfLogStringWorkRaw)
922 + {
923 + hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext);
924 + ExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString);
925 + }
926 + else
927 + {
928 + hr = LogStringWorkRaw(sczMultiByte);
929 + ExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString);
930 + }
931 +
932 +LExit:
933 + if (fEnteredCriticalSection)
934 + {
935 + ::LeaveCriticalSection(&LogUtil_csLog);
936 + }
937 +
938 + ReleaseStr(scz);
939 + ReleaseStr(sczMultiByte);
940 +
941 + return hr;
942 +}
src/dutil/memutil.cpp new
+323
@@ -0,0 +1,323 @@
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 "precomp.h"
6 +
7 +
8 +#if DEBUG
9 +static BOOL vfMemInitialized = FALSE;
10 +#endif
11 +
12 +extern "C" HRESULT DAPI MemInitialize()
13 +{
14 +#if DEBUG
15 + vfMemInitialized = TRUE;
16 +#endif
17 + return S_OK;
18 +}
19 +
20 +extern "C" void DAPI MemUninitialize()
21 +{
22 +#if DEBUG
23 + vfMemInitialized = FALSE;
24 +#endif
25 +}
26 +
27 +extern "C" LPVOID DAPI MemAlloc(
28 + __in SIZE_T cbSize,
29 + __in BOOL fZero
30 + )
31 +{
32 +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
33 + AssertSz(0 < cbSize, "MemAlloc() called with invalid size");
34 + return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize);
35 +}
36 +
37 +
38 +extern "C" LPVOID DAPI MemReAlloc(
39 + __in LPVOID pv,
40 + __in SIZE_T cbSize,
41 + __in BOOL fZero
42 + )
43 +{
44 +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
45 + AssertSz(0 < cbSize, "MemReAlloc() called with invalid size");
46 + return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize);
47 +}
48 +
49 +
50 +extern "C" HRESULT DAPI MemReAllocSecure(
51 + __in LPVOID pv,
52 + __in SIZE_T cbSize,
53 + __in BOOL fZero,
54 + __out LPVOID* ppvNew
55 + )
56 +{
57 +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
58 + AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer");
59 + AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size");
60 +
61 + HRESULT hr = S_OK;
62 + DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY;
63 + LPVOID pvNew = NULL;
64 +
65 + dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0;
66 + pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize);
67 + if (!pvNew)
68 + {
69 + pvNew = MemAlloc(cbSize, fZero);
70 + if (pvNew)
71 + {
72 + const SIZE_T cbCurrent = MemSize(pv);
73 + if (-1 == cbCurrent)
74 + {
75 + ExitOnFailure(hr = E_INVALIDARG, "Failed to get memory size");
76 + }
77 +
78 + // HeapReAlloc may allocate more memory than requested.
79 + const SIZE_T cbNew = MemSize(pvNew);
80 + if (-1 == cbNew)
81 + {
82 + ExitOnFailure(hr = E_INVALIDARG, "Failed to get memory size");
83 + }
84 +
85 + cbSize = cbNew;
86 + if (cbSize > cbCurrent)
87 + {
88 + cbSize = cbCurrent;
89 + }
90 +
91 + memcpy_s(pvNew, cbNew, pv, cbSize);
92 +
93 + SecureZeroMemory(pv, cbCurrent);
94 + MemFree(pv);
95 + }
96 + }
97 + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory");
98 +
99 + *ppvNew = pvNew;
100 + pvNew = NULL;
101 +
102 +LExit:
103 + ReleaseMem(pvNew);
104 +
105 + return hr;
106 +}
107 +
108 +
109 +extern "C" HRESULT DAPI MemAllocArray(
110 + __inout LPVOID* ppvArray,
111 + __in SIZE_T cbArrayType,
112 + __in DWORD dwItemCount
113 + )
114 +{
115 + return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount);
116 +}
117 +
118 +
119 +extern "C" HRESULT DAPI MemReAllocArray(
120 + __inout LPVOID* ppvArray,
121 + __in DWORD cArray,
122 + __in SIZE_T cbArrayType,
123 + __in DWORD dwNewItemCount
124 + )
125 +{
126 + HRESULT hr = S_OK;
127 + DWORD cNew = 0;
128 + LPVOID pvNew = NULL;
129 + SIZE_T cbNew = 0;
130 +
131 + hr = ::DWordAdd(cArray, dwNewItemCount, &cNew);
132 + ExitOnFailure(hr, "Integer overflow when calculating new element count.");
133 +
134 + hr = ::SIZETMult(cNew, cbArrayType, &cbNew);
135 + ExitOnFailure(hr, "Integer overflow when calculating new block size.");
136 +
137 + if (*ppvArray)
138 + {
139 + SIZE_T cbCurrent = MemSize(*ppvArray);
140 + if (cbCurrent < cbNew)
141 + {
142 + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE);
143 + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array.");
144 +
145 + *ppvArray = pvNew;
146 + }
147 + }
148 + else
149 + {
150 + pvNew = MemAlloc(cbNew, TRUE);
151 + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array.");
152 +
153 + *ppvArray = pvNew;
154 + }
155 +
156 +LExit:
157 + return hr;
158 +}
159 +
160 +
161 +extern "C" HRESULT DAPI MemEnsureArraySize(
162 + __deref_out_bcount(cArray * cbArrayType) LPVOID* ppvArray,
163 + __in DWORD cArray,
164 + __in SIZE_T cbArrayType,
165 + __in DWORD dwGrowthCount
166 + )
167 +{
168 + HRESULT hr = S_OK;
169 + DWORD cNew = 0;
170 + LPVOID pvNew = NULL;
171 + SIZE_T cbNew = 0;
172 +
173 + hr = ::DWordAdd(cArray, dwGrowthCount, &cNew);
174 + ExitOnFailure(hr, "Integer overflow when calculating new element count.");
175 +
176 + hr = ::SIZETMult(cNew, cbArrayType, &cbNew);
177 + ExitOnFailure(hr, "Integer overflow when calculating new block size.");
178 +
179 + if (*ppvArray)
180 + {
181 + SIZE_T cbUsed = cArray * cbArrayType;
182 + SIZE_T cbCurrent = MemSize(*ppvArray);
183 + if (cbCurrent < cbUsed)
184 + {
185 + pvNew = MemReAlloc(*ppvArray, cbNew, TRUE);
186 + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger.");
187 +
188 + *ppvArray = pvNew;
189 + }
190 + }
191 + else
192 + {
193 + pvNew = MemAlloc(cbNew, TRUE);
194 + ExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array.");
195 +
196 + *ppvArray = pvNew;
197 + }
198 +
199 +LExit:
200 + return hr;
201 +}
202 +
203 +
204 +extern "C" HRESULT DAPI MemInsertIntoArray(
205 + __deref_out_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray,
206 + __in DWORD dwInsertIndex,
207 + __in DWORD cInsertItems,
208 + __in DWORD cExistingArray,
209 + __in SIZE_T cbArrayType,
210 + __in DWORD dwGrowthCount
211 + )
212 +{
213 + HRESULT hr = S_OK;
214 + DWORD i;
215 + BYTE *pbArray = NULL;
216 +
217 + if (0 == cInsertItems)
218 + {
219 + ExitFunction1(hr = S_OK);
220 + }
221 +
222 + hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount);
223 + ExitOnFailure(hr, "Failed to resize array while inserting items");
224 +
225 + pbArray = reinterpret_cast<BYTE *>(*ppvArray);
226 + for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i)
227 + {
228 + memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType);
229 + }
230 +
231 + // Zero out the newly-inserted items
232 + memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType);
233 +
234 +LExit:
235 + return hr;
236 +}
237 +
238 +extern "C" void DAPI MemRemoveFromArray(
239 + __inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID pvArray,
240 + __in DWORD dwRemoveIndex,
241 + __in DWORD cRemoveItems,
242 + __in DWORD cExistingArray,
243 + __in SIZE_T cbArrayType,
244 + __in BOOL fPreserveOrder
245 + )
246 +{
247 + BYTE *pbArray = static_cast<BYTE *>(pvArray);
248 + DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex);
249 +
250 + if (fPreserveOrder)
251 + {
252 + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType);
253 + }
254 + else
255 + {
256 + DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems);
257 + memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType);
258 + }
259 +
260 + ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType);
261 +}
262 +
263 +extern "C" void DAPI MemArraySwapItems(
264 + __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray,
265 + __in DWORD dwIndex1,
266 + __in DWORD dwIndex2,
267 + __in SIZE_T cbArrayType
268 + )
269 +{
270 + BYTE *pbArrayItem1 = static_cast<BYTE *>(pvArray) + dwIndex1 * cbArrayType;
271 + BYTE *pbArrayItem2 = static_cast<BYTE *>(pvArray) + dwIndex2 * cbArrayType;
272 + DWORD dwByteIndex = 0;
273 +
274 + if (dwIndex1 == dwIndex2)
275 + {
276 + return;
277 + }
278 +
279 + // Use XOR swapping to avoid the need for a temporary item
280 + while (dwByteIndex < cbArrayType)
281 + {
282 + // Try to do many bytes at a time in most cases
283 + if (cbArrayType - dwByteIndex > sizeof(DWORD64))
284 + {
285 + // x: X xor Y
286 + *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
287 + // y: X xor Y
288 + *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
289 + // x: X xor Y
290 + *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
291 +
292 + dwByteIndex += sizeof(DWORD64);
293 + }
294 + else
295 + {
296 + // x: X xor Y
297 + *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
298 + // y: X xor Y
299 + *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
300 + // x: X xor Y
301 + *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
302 +
303 + dwByteIndex += sizeof(unsigned char);
304 + }
305 + }
306 +}
307 +
308 +extern "C" HRESULT DAPI MemFree(
309 + __in LPVOID pv
310 + )
311 +{
312 +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
313 + return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
314 +}
315 +
316 +
317 +extern "C" SIZE_T DAPI MemSize(
318 + __in LPCVOID pv
319 + )
320 +{
321 +// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
322 + return ::HeapSize(::GetProcessHeap(), 0, pv);
323 +}
src/dutil/metautil.cpp new
+356
@@ -0,0 +1,356 @@
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 +
5 +// okay, this may look a little weird, but metautil.h cannot be in the
6 +// pre-compiled header because we need to #define these things so the
7 +// correct GUID's get pulled into this object file
8 +#include <initguid.h>
9 +#include "metautil.h"
10 +
11 +
12 +// prototypes
13 +static void Sort(
14 + __in_ecount(cArray) DWORD dwArray[],
15 + __in int cArray
16 + );
17 +
18 +
19 +/********************************************************************
20 + MetaFindWebBase - finds a metabase base string that matches IP, Port and Header
21 +
22 +********************************************************************/
23 +extern "C" HRESULT DAPI MetaFindWebBase(
24 + __in IMSAdminBaseW* piMetabase,
25 + __in_z LPCWSTR wzIP,
26 + __in int iPort,
27 + __in_z LPCWSTR wzHeader,
28 + __in BOOL fSecure,
29 + __out_ecount(cchWebBase) LPWSTR wzWebBase,
30 + __in DWORD cchWebBase
31 + )
32 +{
33 + Assert(piMetabase && cchWebBase);
34 +
35 + HRESULT hr = S_OK;
36 +
37 + BOOL fFound = FALSE;
38 +
39 + WCHAR wzKey[METADATA_MAX_NAME_LEN];
40 + WCHAR wzSubkey[METADATA_MAX_NAME_LEN];
41 + DWORD dwIndex = 0;
42 +
43 + METADATA_RECORD mr;
44 + METADATA_RECORD mrAddress;
45 +
46 + LPWSTR pwzExists = NULL;
47 + LPWSTR pwzIPExists = NULL;
48 + LPWSTR pwzPortExists = NULL;
49 + int iPortExists = 0;
50 + LPCWSTR pwzHeaderExists = NULL;
51 +
52 + memset(&mr, 0, sizeof(mr));
53 + mr.dwMDIdentifier = MD_KEY_TYPE;
54 + mr.dwMDAttributes = METADATA_INHERIT;
55 + mr.dwMDUserType = IIS_MD_UT_SERVER;
56 + mr.dwMDDataType = ALL_METADATA;
57 +
58 + memset(&mrAddress, 0, sizeof(mrAddress));
59 + mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS;
60 + mrAddress.dwMDAttributes = METADATA_INHERIT;
61 + mrAddress.dwMDUserType = IIS_MD_UT_SERVER;
62 + mrAddress.dwMDDataType = ALL_METADATA;
63 +
64 + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb
65 + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex)
66 + {
67 + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex);
68 + if (FAILED(hr))
69 + break;
70 +
71 + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey);
72 + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr);
73 + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
74 + {
75 + hr = S_FALSE; // didn't find anything, try next one
76 + continue;
77 + }
78 + ExitOnFailure(hr, "failed to get key from metabase while searching for web servers");
79 +
80 + // if we have an IIsWebServer store the key
81 + if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData))
82 + {
83 + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress);
84 + if (MD_ERROR_DATA_NOT_FOUND == hr)
85 + hr = S_FALSE;
86 + ExitOnFailure(hr, "failed to get address from metabase while searching for web servers");
87 +
88 + // break down the first address into parts
89 + pwzIPExists = reinterpret_cast<LPWSTR>(mrAddress.pbMDData);
90 + pwzExists = wcsstr(pwzIPExists, L":");
91 + if (NULL == pwzExists)
92 + continue;
93 +
94 + *pwzExists = L'\0';
95 +
96 + pwzPortExists = pwzExists + 1;
97 + pwzExists = wcsstr(pwzPortExists, L":");
98 + if (NULL == pwzExists)
99 + continue;
100 +
101 + *pwzExists = L'\0';
102 + iPortExists = wcstol(pwzPortExists, NULL, 10);
103 +
104 + pwzHeaderExists = pwzExists + 1;
105 +
106 + // compare the passed in address with the address listed for this web
107 + if (S_OK == hr &&
108 + (0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) &&
109 + iPort == iPortExists &&
110 + 0 == lstrcmpW(wzHeader, pwzHeaderExists))
111 + {
112 + // if the passed in buffer wasn't big enough
113 + hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey);
114 + ExitOnFailure(hr, "failed to copy in web base: %ls", wzKey);
115 +
116 + fFound = TRUE;
117 + break;
118 + }
119 + }
120 + }
121 +
122 + if (E_NOMOREITEMS == hr)
123 + {
124 + Assert(!fFound);
125 + hr = S_FALSE;
126 + }
127 +
128 +LExit:
129 + MetaFreeValue(&mrAddress);
130 + MetaFreeValue(&mr);
131 +
132 + if (!fFound && SUCCEEDED(hr))
133 + hr = S_FALSE;
134 +
135 + return hr;
136 +}
137 +
138 +
139 +/********************************************************************
140 + MetaFindFreeWebBase - finds the next metabase base string
141 +
142 +********************************************************************/
143 +extern "C" HRESULT DAPI MetaFindFreeWebBase(
144 + __in IMSAdminBaseW* piMetabase,
145 + __out_ecount(cchWebBase) LPWSTR wzWebBase,
146 + __in DWORD cchWebBase
147 + )
148 +{
149 + Assert(piMetabase);
150 +
151 + HRESULT hr = S_OK;
152 +
153 + WCHAR wzKey[METADATA_MAX_NAME_LEN];
154 + WCHAR wzSubkey[METADATA_MAX_NAME_LEN];
155 + DWORD dwSubKeys[100];
156 + int cSubKeys = 0;
157 + DWORD dwIndex = 0;
158 +
159 + int i;
160 + DWORD dwKey;
161 +
162 + METADATA_RECORD mr;
163 +
164 + memset(&mr, 0, sizeof(mr));
165 + mr.dwMDIdentifier = MD_KEY_TYPE;
166 + mr.dwMDAttributes = 0;
167 + mr.dwMDUserType = IIS_MD_UT_SERVER;
168 + mr.dwMDDataType = STRING_METADATA;
169 +
170 + // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb
171 + for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex)
172 + {
173 + hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex);
174 + if (FAILED(hr))
175 + break;
176 +
177 + ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey);
178 +
179 + hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr);
180 + if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
181 + {
182 + hr = S_FALSE; // didn't find anything, try next one
183 + continue;
184 + }
185 + ExitOnFailure(hr, "failed to get key from metabase while searching for free web root");
186 +
187 + // if we have a IIsWebServer get the address information
188 + if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast<LPCWSTR>(mr.pbMDData)))
189 + {
190 + if (cSubKeys >= countof(dwSubKeys))
191 + {
192 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
193 + ExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites");
194 + }
195 +
196 + dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10);
197 + ++cSubKeys;
198 + Sort(dwSubKeys, cSubKeys);
199 + }
200 + }
201 +
202 + if (E_NOMOREITEMS == hr)
203 + hr = S_OK;
204 + ExitOnFailure(hr, "failed to find free web root");
205 +
206 + // find the lowest free web root
207 + dwKey = 1;
208 + for (i = 0; i < cSubKeys; ++i)
209 + {
210 + if (dwKey < dwSubKeys[i])
211 + break;
212 +
213 + dwKey = dwSubKeys[i] + 1;
214 + }
215 +
216 + hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey);
217 +LExit:
218 + MetaFreeValue(&mr);
219 + return hr;
220 +}
221 +
222 +
223 +/********************************************************************
224 + MetaOpenKey - open key
225 +
226 +********************************************************************/
227 +extern "C" HRESULT DAPI MetaOpenKey(
228 + __in IMSAdminBaseW* piMetabase,
229 + __in METADATA_HANDLE mhKey,
230 + __in_z LPCWSTR wzKey,
231 + __in DWORD dwAccess,
232 + __in DWORD cRetries,
233 + __out METADATA_HANDLE* pmh
234 + )
235 +{
236 + Assert(piMetabase && pmh);
237 +
238 + HRESULT hr = S_OK;
239 +
240 + // loop while the key is busy
241 + do
242 + {
243 + hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh);
244 + if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr)
245 + ::SleepEx(1000, TRUE);
246 + } while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--);
247 +
248 + return hr;
249 +}
250 +
251 +
252 +/********************************************************************
253 + MetaGetValue - finds the next metabase base string
254 +
255 + NOTE: piMetabase is optional
256 +********************************************************************/
257 +extern "C" HRESULT DAPI MetaGetValue(
258 + __in IMSAdminBaseW* piMetabase,
259 + __in METADATA_HANDLE mhKey,
260 + __in_z LPCWSTR wzKey,
261 + __inout METADATA_RECORD* pmr
262 + )
263 +{
264 + Assert(pmr);
265 +
266 + HRESULT hr = S_OK;
267 + BOOL fInitialized = FALSE;
268 + DWORD cbRequired = 0;
269 +
270 + if (!piMetabase)
271 + {
272 + hr = ::CoInitialize(NULL);
273 + ExitOnFailure(hr, "failed to initialize COM");
274 + fInitialized = TRUE;
275 +
276 + hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast<LPVOID*>(&piMetabase));
277 + ExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object");
278 + }
279 +
280 + if (!pmr->pbMDData)
281 + {
282 + pmr->dwMDDataLen = 256;
283 + pmr->pbMDData = static_cast<BYTE*>(MemAlloc(pmr->dwMDDataLen, TRUE));
284 + ExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value");
285 + }
286 + else // set the size of the data to the actual size of the memory
287 + pmr->dwMDDataLen = (DWORD)MemSize(pmr->pbMDData);
288 +
289 + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired);
290 + if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
291 + {
292 + pmr->dwMDDataLen = cbRequired;
293 + BYTE* pb = static_cast<BYTE*>(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE));
294 + ExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value");
295 +
296 + pmr->pbMDData = pb;
297 + hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired);
298 + }
299 + ExitOnFailure(hr, "failed to get metabase data");
300 +
301 +LExit:
302 + if (fInitialized)
303 + {
304 + ReleaseObject(piMetabase);
305 + ::CoUninitialize();
306 + }
307 +
308 + return hr;
309 +}
310 +
311 +
312 +/********************************************************************
313 + MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue()
314 +
315 + NOTE: METADATA_RECORD must have been returned from MetaGetValue() above
316 +********************************************************************/
317 +extern "C" void DAPI MetaFreeValue(
318 + __in METADATA_RECORD* pmr
319 + )
320 +{
321 + Assert(pmr);
322 +
323 + ReleaseNullMem(pmr->pbMDData);
324 +}
325 +
326 +
327 +//
328 +// private
329 +//
330 +
331 +/********************************************************************
332 + Sort - quick and dirty insertion sort
333 +
334 +********************************************************************/
335 +static void Sort(
336 + __in_ecount(cArray) DWORD dwArray[],
337 + __in int cArray
338 + )
339 +{
340 + int i, j;
341 + DWORD dwData;
342 +
343 + for (i = 1; i < cArray; ++i)
344 + {
345 + dwData = dwArray[i];
346 +
347 + j = i - 1;
348 + while (0 <= j && dwArray[j] > dwData)
349 + {
350 + dwArray[j + 1] = dwArray[j];
351 + j--;
352 + }
353 +
354 + dwArray[j + 1] = dwData;
355 + }
356 +}
src/dutil/monutil.cpp new
+2004
@@ -0,0 +1,2004 @@
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 +
5 +const int MON_THREAD_GROWTH = 5;
6 +const int MON_ARRAY_GROWTH = 40;
7 +const int MON_MAX_MONITORS_PER_THREAD = 63;
8 +const int MON_THREAD_INIT_RETRIES = 1000;
9 +const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10;
10 +const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute
11 +const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently
12 +const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000;
13 +const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass";
14 +
15 +enum MON_MESSAGE
16 +{
17 + MON_MESSAGE_ADD = WM_APP + 1,
18 + MON_MESSAGE_REMOVE,
19 + MON_MESSAGE_REMOVED, // Sent by waiter thread back to coordinator thread to indicate a remove occurred
20 + MON_MESSAGE_NETWORK_WAIT_FAILED, // Sent by waiter thread back to coordinator thread to indicate a network wait failed. Coordinator thread will periodically trigger retries (via MON_MESSAGE_NETWORK_STATUS_UPDATE messages).
21 + MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, // Sent by waiter thread back to coordinator thread to indicate a previously failing network wait is now succeeding. Coordinator thread will stop triggering retries if no other failing waits exist.
22 + MON_MESSAGE_NETWORK_STATUS_UPDATE, // Some change to network connectivity occurred (a network connection was connected or disconnected for example)
23 + MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any successful network waits.
24 + // Annoyingly, this is necessary to catch the rare case that the remote server goes offline unexpectedly, such as by
25 + // network cable unplugged or power loss - in this case there is no local network status change, and the wait will just never fire.
26 + // So we very occasionally retry all successful network waits. When this occurs, we notify for changes, even though there may not have been any.
27 + // This is because we have no way to detect if the old wait had failed (and changes were lost) due to the remote server going offline during that time or not.
28 + // If we do this often, it can cause a lot of wasted work (which could be expensive for battery life), so the default is to do it very rarely (every 20 minutes).
29 + MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any failed network waits
30 + MON_MESSAGE_DRIVE_STATUS_UPDATE, // Some change to local drive has occurred (new drive created or plugged in, or removed)
31 + MON_MESSAGE_DRIVE_QUERY_REMOVE, // User wants to unplug a drive, which MonUtil will always allow
32 + MON_MESSAGE_STOP
33 +};
34 +
35 +enum MON_TYPE
36 +{
37 + MON_NONE = 0,
38 + MON_DIRECTORY = 1,
39 + MON_REGKEY = 2
40 +};
41 +
42 +struct MON_REQUEST
43 +{
44 + MON_TYPE type;
45 + DWORD dwMaxSilencePeriodInMs;
46 +
47 + // Handle to the main window for RegisterDeviceNotification() (same handle as owned by coordinator thread)
48 + HWND hwnd;
49 + // and handle to the notification (specific to this request)
50 + HDEVNOTIFY hNotify;
51 +
52 + BOOL fRecursive;
53 + void *pvContext;
54 +
55 + HRESULT hrStatus;
56 +
57 + LPWSTR sczOriginalPathRequest;
58 + BOOL fNetwork; // This reflects either a UNC or mounted drive original request
59 + DWORD dwPathHierarchyIndex;
60 + LPWSTR *rgsczPathHierarchy;
61 + DWORD cPathHierarchy;
62 +
63 + // If the notify fires, fPendingFire gets set to TRUE, and we wait to see if other writes are occurring, and only after the configured silence period do we notify of changes
64 + // after notification, we set fPendingFire back to FALSE
65 + BOOL fPendingFire;
66 + BOOL fSkipDeltaAdd;
67 + DWORD dwSilencePeriodInMs;
68 +
69 + union
70 + {
71 + struct
72 + {
73 + } directory;
74 + struct
75 + {
76 + HKEY hkRoot;
77 + HKEY hkSubKey;
78 + REG_KEY_BITNESS kbKeyBitness; // Only used to pass on 32-bit, 64-bit, or default parameter
79 + } regkey;
80 + };
81 +};
82 +
83 +struct MON_ADD_MESSAGE
84 +{
85 + MON_REQUEST request;
86 + HANDLE handle;
87 +};
88 +
89 +struct MON_REMOVE_MESSAGE
90 +{
91 + MON_TYPE type;
92 + BOOL fRecursive;
93 +
94 + union
95 + {
96 + struct
97 + {
98 + LPWSTR sczDirectory;
99 + } directory;
100 + struct
101 + {
102 + HKEY hkRoot;
103 + LPWSTR sczSubKey;
104 + REG_KEY_BITNESS kbKeyBitness;
105 + } regkey;
106 + };
107 +};
108 +
109 +struct MON_WAITER_CONTEXT
110 +{
111 + DWORD dwCoordinatorThreadId;
112 +
113 + HANDLE hWaiterThread;
114 + DWORD dwWaiterThreadId;
115 + BOOL fWaiterThreadMessageQueueInitialized;
116 +
117 + // Callbacks
118 + PFN_MONGENERAL vpfMonGeneral;
119 + PFN_MONDIRECTORY vpfMonDirectory;
120 + PFN_MONREGKEY vpfMonRegKey;
121 +
122 + // Context for callbacks
123 + LPVOID pvContext;
124 +
125 + // HANDLEs are in their own array for easy use with WaitForMultipleObjects()
126 + // After initialization, the very first handle is just to wake the listener thread to have it re-wait on a new list
127 + // Because this array is read by both coordinator thread and waiter thread, to avoid locking between both threads, it must start at the maximum size
128 + HANDLE *rgHandles;
129 + DWORD cHandles;
130 +
131 + // Requested things to monitor
132 + MON_REQUEST *rgRequests;
133 + DWORD cRequests;
134 +
135 + // Number of pending notifications
136 + DWORD cRequestsPending;
137 +
138 + // Number of requests in a failed state (couldn't initiate wait)
139 + DWORD cRequestsFailing;
140 +};
141 +
142 +// Info stored about each waiter by the coordinator
143 +struct MON_WAITER_INFO
144 +{
145 + DWORD cMonitorCount;
146 +
147 + MON_WAITER_CONTEXT *pWaiterContext;
148 +};
149 +
150 +// This struct is used when Thread A wants to send a task to another thread B (and get notified when the task finishes)
151 +// You typically declare this struct in a manner that a pointer to it is valid as long as a thread that could respond is still running
152 +// (even long after sender is no longer waiting, in case thread has huge message queue)
153 +// and you must send 2 parameters in the message:
154 +// 1) a pointer to this struct (which is always valid)
155 +// 2) the original value of dwIteration
156 +// The receiver of the message can compare the current value of dwSendIteration in the struct with what was sent in the message
157 +// If values are different, we're too late and thread A is no longer waiting on this response
158 +// otherwise, set dwResponseIteration to the same value, and call ::SetEvent() on hWait
159 +// Thread A will then wakeup, and must verify that dwResponseIteration == dwSendIteration to ensure it isn't an earlier out-of-date reply
160 +// replying to a newer wait
161 +// pvContext is used to send a misc parameter related to processing data
162 +struct MON_INTERNAL_TEMPORARY_WAIT
163 +{
164 + // Should be incremented each time sender sends a pointer to this struct, so each request has a different iteration
165 + DWORD dwSendIteration;
166 + DWORD dwReceiveIteration;
167 + HANDLE hWait;
168 + void *pvContext;
169 +};
170 +
171 +struct MON_STRUCT
172 +{
173 + HANDLE hCoordinatorThread;
174 + DWORD dwCoordinatorThreadId;
175 + BOOL fCoordinatorThreadMessageQueueInitialized;
176 +
177 + // Invisible window for receiving network status & drive added/removal messages
178 + HWND hwnd;
179 + // Used by window procedure for sending request and waiting for response from waiter threads
180 + // such as in event of a request to remove a device
181 + MON_INTERNAL_TEMPORARY_WAIT internalWait;
182 +
183 + // Callbacks
184 + PFN_MONGENERAL vpfMonGeneral;
185 + PFN_MONDRIVESTATUS vpfMonDriveStatus;
186 + PFN_MONDIRECTORY vpfMonDirectory;
187 + PFN_MONREGKEY vpfMonRegKey;
188 +
189 + // Context for callbacks
190 + LPVOID pvContext;
191 +
192 + // Waiter thread array
193 + MON_WAITER_INFO *rgWaiterThreads;
194 + DWORD cWaiterThreads;
195 +};
196 +
197 +const int MON_HANDLE_BYTES = sizeof(MON_STRUCT);
198 +
199 +static DWORD WINAPI CoordinatorThread(
200 + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext
201 + );
202 +// Initiates (or if *pHandle is non-null, continues) wait on the directory or subkey
203 +// if the directory or subkey doesn't exist, instead calls it on the first existing parent directory or subkey
204 +// writes to pRequest->dwPathHierarchyIndex with the array index that was waited on
205 +static HRESULT InitiateWait(
206 + __inout MON_REQUEST *pRequest,
207 + __inout HANDLE *pHandle
208 + );
209 +static DWORD WINAPI WaiterThread(
210 + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext
211 + );
212 +static void Notify(
213 + __in HRESULT hr,
214 + __in MON_WAITER_CONTEXT *pWaiterContext,
215 + __in MON_REQUEST *pRequest
216 + );
217 +static void MonRequestDestroy(
218 + __in MON_REQUEST *pRequest
219 + );
220 +static void MonAddMessageDestroy(
221 + __in MON_ADD_MESSAGE *pMessage
222 + );
223 +static void MonRemoveMessageDestroy(
224 + __in MON_REMOVE_MESSAGE *pMessage
225 + );
226 +static BOOL GetRecursiveFlag(
227 + __in MON_REQUEST *pRequest,
228 + __in DWORD dwIndex
229 + );
230 +static HRESULT FindRequestIndex(
231 + __in MON_WAITER_CONTEXT *pWaiterContext,
232 + __in MON_REMOVE_MESSAGE *pMessage,
233 + __out DWORD *pdwIndex
234 + );
235 +static HRESULT RemoveRequest(
236 + __inout MON_WAITER_CONTEXT *pWaiterContext,
237 + __in DWORD dwRequestIndex
238 + );
239 +static REGSAM GetRegKeyBitness(
240 + __in MON_REQUEST *pRequest
241 + );
242 +static HRESULT DuplicateRemoveMessage(
243 + __in MON_REMOVE_MESSAGE *pMessage,
244 + __out MON_REMOVE_MESSAGE **ppMessage
245 + );
246 +static LRESULT CALLBACK MonWndProc(
247 + __in HWND hWnd,
248 + __in UINT uMsg,
249 + __in WPARAM wParam,
250 + __in LPARAM lParam
251 + );
252 +static HRESULT CreateMonWindow(
253 + __in MON_STRUCT *pm,
254 + __out HWND *pHwnd
255 + );
256 +// if *phMonitor is non-NULL, closes the old wait before re-starting the new wait
257 +static HRESULT WaitForNetworkChanges(
258 + __inout HANDLE *phMonitor,
259 + __in MON_STRUCT *pm
260 + );
261 +static HRESULT UpdateWaitStatus(
262 + __in HRESULT hrNewStatus,
263 + __inout MON_WAITER_CONTEXT *pWaiterContext,
264 + __in DWORD dwRequestIndex,
265 + __out DWORD *pdwNewRequestIndex
266 + );
267 +
268 +extern "C" HRESULT DAPI MonCreate(
269 + __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle,
270 + __in PFN_MONGENERAL vpfMonGeneral,
271 + __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus,
272 + __in_opt PFN_MONDIRECTORY vpfMonDirectory,
273 + __in_opt PFN_MONREGKEY vpfMonRegKey,
274 + __in_opt LPVOID pvContext
275 + )
276 +{
277 + HRESULT hr = S_OK;
278 + DWORD dwRetries = MON_THREAD_INIT_RETRIES;
279 +
280 + ExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor");
281 +
282 + // Allocate the struct
283 + *pHandle = static_cast<MON_HANDLE>(MemAlloc(sizeof(MON_STRUCT), TRUE));
284 + ExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object");
285 +
286 + MON_STRUCT *pm = static_cast<MON_STRUCT *>(*pHandle);
287 +
288 + pm->vpfMonGeneral = vpfMonGeneral;
289 + pm->vpfMonDriveStatus = vpfMonDriveStatus;
290 + pm->vpfMonDirectory = vpfMonDirectory;
291 + pm->vpfMonRegKey = vpfMonRegKey;
292 + pm->pvContext = pvContext;
293 +
294 + pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId);
295 + if (!pm->hCoordinatorThread)
296 + {
297 + ExitWithLastError(hr, "Failed to create waiter thread.");
298 + }
299 +
300 + // Ensure the created thread initializes its message queue. It does this first thing, so if it doesn't within 10 seconds, there must be a huge problem.
301 + while (!pm->fCoordinatorThreadMessageQueueInitialized && 0 < dwRetries)
302 + {
303 + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS);
304 + --dwRetries;
305 + }
306 +
307 + if (0 == dwRetries)
308 + {
309 + hr = E_UNEXPECTED;
310 + ExitOnFailure(hr, "Waiter thread apparently never initialized its message queue.");
311 + }
312 +
313 +LExit:
314 + return hr;
315 +}
316 +
317 +extern "C" HRESULT DAPI MonAddDirectory(
318 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
319 + __in_z LPCWSTR wzDirectory,
320 + __in BOOL fRecursive,
321 + __in DWORD dwSilencePeriodInMs,
322 + __in_opt LPVOID pvDirectoryContext
323 + )
324 +{
325 + HRESULT hr = S_OK;
326 + MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
327 + LPWSTR sczDirectory = NULL;
328 + LPWSTR sczOriginalPathRequest = NULL;
329 + MON_ADD_MESSAGE *pMessage = NULL;
330 +
331 + hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0);
332 + ExitOnFailure(hr, "Failed to convert directory string to UNC path");
333 +
334 + hr = PathBackslashTerminate(&sczOriginalPathRequest);
335 + ExitOnFailure(hr, "Failed to ensure directory ends in backslash");
336 +
337 + pMessage = reinterpret_cast<MON_ADD_MESSAGE *>(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE));
338 + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
339 +
340 + if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\')
341 + {
342 + pMessage->request.fNetwork = TRUE;
343 + }
344 + else
345 + {
346 + hr = UncConvertFromMountedDrive(&sczDirectory, sczOriginalPathRequest);
347 + if (SUCCEEDED(hr))
348 + {
349 + pMessage->request.fNetwork = TRUE;
350 + }
351 + }
352 +
353 + if (NULL == sczDirectory)
354 + {
355 + // Likely not a mounted drive - just copy the request then
356 + hr = S_OK;
357 +
358 + hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0);
359 + ExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest);
360 + }
361 +
362 + pMessage->handle = INVALID_HANDLE_VALUE;
363 + pMessage->request.type = MON_DIRECTORY;
364 + pMessage->request.fRecursive = fRecursive;
365 + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs;
366 + pMessage->request.hwnd = pm->hwnd;
367 + pMessage->request.pvContext = pvDirectoryContext;
368 + pMessage->request.sczOriginalPathRequest = sczOriginalPathRequest;
369 + sczOriginalPathRequest = NULL;
370 +
371 + hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast<LPUINT>(&pMessage->request.cPathHierarchy));
372 + ExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory);
373 +
374 + if (0 < pMessage->request.cPathHierarchy)
375 + {
376 + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle);
377 + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast<WPARAM>(pMessage), 0))
378 + {
379 + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory);
380 + }
381 + pMessage = NULL;
382 + }
383 +
384 +LExit:
385 + ReleaseStr(sczDirectory);
386 + ReleaseStr(sczOriginalPathRequest);
387 + MonAddMessageDestroy(pMessage);
388 +
389 + return hr;
390 +}
391 +
392 +extern "C" HRESULT DAPI MonAddRegKey(
393 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
394 + __in HKEY hkRoot,
395 + __in_z LPCWSTR wzSubKey,
396 + __in REG_KEY_BITNESS kbKeyBitness,
397 + __in BOOL fRecursive,
398 + __in DWORD dwSilencePeriodInMs,
399 + __in_opt LPVOID pvRegKeyContext
400 + )
401 +{
402 + HRESULT hr = S_OK;
403 + MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
404 + LPWSTR sczSubKey = NULL;
405 + MON_ADD_MESSAGE *pMessage = NULL;
406 +
407 + hr = StrAllocString(&sczSubKey, wzSubKey, 0);
408 + ExitOnFailure(hr, "Failed to copy subkey string");
409 +
410 + hr = PathBackslashTerminate(&sczSubKey);
411 + ExitOnFailure(hr, "Failed to ensure subkey path ends in backslash");
412 +
413 + pMessage = reinterpret_cast<MON_ADD_MESSAGE *>(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE));
414 + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
415 +
416 + pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL);
417 + ExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor");
418 +
419 + pMessage->request.type = MON_REGKEY;
420 + pMessage->request.regkey.hkRoot = hkRoot;
421 + pMessage->request.regkey.kbKeyBitness = kbKeyBitness;
422 + pMessage->request.fRecursive = fRecursive;
423 + pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs,
424 + pMessage->request.hwnd = pm->hwnd;
425 + pMessage->request.pvContext = pvRegKeyContext;
426 +
427 + hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast<LPUINT>(&pMessage->request.cPathHierarchy));
428 + ExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey);
429 +
430 + if (0 < pMessage->request.cPathHierarchy)
431 + {
432 + pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle);
433 + ExitOnFailure(hr, "Failed to initiate wait");
434 +
435 + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast<WPARAM>(pMessage), 0))
436 + {
437 + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey);
438 + }
439 + pMessage = NULL;
440 + }
441 +
442 +LExit:
443 + ReleaseStr(sczSubKey);
444 + MonAddMessageDestroy(pMessage);
445 +
446 + return hr;
447 +}
448 +
449 +extern "C" HRESULT DAPI MonRemoveDirectory(
450 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
451 + __in_z LPCWSTR wzDirectory,
452 + __in BOOL fRecursive
453 + )
454 +{
455 + HRESULT hr = S_OK;
456 + MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
457 + LPWSTR sczDirectory = NULL;
458 + MON_REMOVE_MESSAGE *pMessage = NULL;
459 +
460 + hr = StrAllocString(&sczDirectory, wzDirectory, 0);
461 + ExitOnFailure(hr, "Failed to copy directory string");
462 +
463 + hr = PathBackslashTerminate(&sczDirectory);
464 + ExitOnFailure(hr, "Failed to ensure directory ends in backslash");
465 +
466 + pMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE));
467 + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
468 +
469 + pMessage->type = MON_DIRECTORY;
470 + pMessage->fRecursive = fRecursive;
471 +
472 + hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0);
473 + ExitOnFailure(hr, "Failed to allocate copy of directory string");
474 +
475 + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast<WPARAM>(pMessage), 0))
476 + {
477 + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory);
478 + }
479 + pMessage = NULL;
480 +
481 +LExit:
482 + MonRemoveMessageDestroy(pMessage);
483 +
484 + return hr;
485 +}
486 +
487 +extern "C" HRESULT DAPI MonRemoveRegKey(
488 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
489 + __in HKEY hkRoot,
490 + __in_z LPCWSTR wzSubKey,
491 + __in REG_KEY_BITNESS kbKeyBitness,
492 + __in BOOL fRecursive
493 + )
494 +{
495 + HRESULT hr = S_OK;
496 + MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
497 + LPWSTR sczSubKey = NULL;
498 + MON_REMOVE_MESSAGE *pMessage = NULL;
499 +
500 + hr = StrAllocString(&sczSubKey, wzSubKey, 0);
501 + ExitOnFailure(hr, "Failed to copy subkey string");
502 +
503 + hr = PathBackslashTerminate(&sczSubKey);
504 + ExitOnFailure(hr, "Failed to ensure subkey path ends in backslash");
505 +
506 + pMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE));
507 + ExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
508 +
509 + pMessage->type = MON_REGKEY;
510 + pMessage->regkey.hkRoot = hkRoot;
511 + pMessage->regkey.kbKeyBitness = kbKeyBitness;
512 + pMessage->fRecursive = fRecursive;
513 +
514 + hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0);
515 + ExitOnFailure(hr, "Failed to allocate copy of directory string");
516 +
517 + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast<WPARAM>(pMessage), 0))
518 + {
519 + ExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey);
520 + }
521 + pMessage = NULL;
522 +
523 +LExit:
524 + ReleaseStr(sczSubKey);
525 + MonRemoveMessageDestroy(pMessage);
526 +
527 + return hr;
528 +}
529 +
530 +extern "C" void DAPI MonDestroy(
531 + __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle
532 + )
533 +{
534 + HRESULT hr = S_OK;
535 + DWORD er = ERROR_SUCCESS;
536 + MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
537 +
538 + if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0))
539 + {
540 + er = ::GetLastError();
541 + if (ERROR_INVALID_THREAD_ID == er)
542 + {
543 + // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up
544 + er = ERROR_SUCCESS;
545 + }
546 + ExitOnWin32Error(er, hr, "Failed to send message to background thread to halt");
547 + }
548 +
549 + if (pm->hCoordinatorThread)
550 + {
551 + ::WaitForSingleObject(pm->hCoordinatorThread, INFINITE);
552 + ::CloseHandle(pm->hCoordinatorThread);
553 + }
554 +
555 +LExit:
556 + return;
557 +}
558 +
559 +static void MonRequestDestroy(
560 + __in MON_REQUEST *pRequest
561 + )
562 +{
563 + if (NULL != pRequest)
564 + {
565 + if (MON_REGKEY == pRequest->type)
566 + {
567 + ReleaseRegKey(pRequest->regkey.hkSubKey);
568 + }
569 + else if (MON_DIRECTORY == pRequest->type && pRequest->hNotify)
570 + {
571 + UnregisterDeviceNotification(pRequest->hNotify);
572 + pRequest->hNotify = NULL;
573 + }
574 + ReleaseStr(pRequest->sczOriginalPathRequest);
575 + ReleaseStrArray(pRequest->rgsczPathHierarchy, pRequest->cPathHierarchy);
576 + }
577 +}
578 +
579 +static void MonAddMessageDestroy(
580 + __in MON_ADD_MESSAGE *pMessage
581 + )
582 +{
583 + if (NULL != pMessage)
584 + {
585 + MonRequestDestroy(&pMessage->request);
586 + if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle)
587 + {
588 + ::FindCloseChangeNotification(pMessage->handle);
589 + }
590 + else if (MON_REGKEY == pMessage->request.type)
591 + {
592 + ReleaseHandle(pMessage->handle);
593 + }
594 +
595 + ReleaseMem(pMessage);
596 + }
597 +}
598 +
599 +static void MonRemoveMessageDestroy(
600 + __in MON_REMOVE_MESSAGE *pMessage
601 + )
602 +{
603 + if (NULL != pMessage)
604 + {
605 + switch (pMessage->type)
606 + {
607 + case MON_DIRECTORY:
608 + ReleaseStr(pMessage->directory.sczDirectory);
609 + break;
610 + case MON_REGKEY:
611 + ReleaseStr(pMessage->regkey.sczSubKey);
612 + break;
613 + default:
614 + Assert(false);
615 + }
616 +
617 + ReleaseMem(pMessage);
618 + }
619 +}
620 +
621 +static DWORD WINAPI CoordinatorThread(
622 + __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext
623 + )
624 +{
625 + HRESULT hr = S_OK;
626 + MSG msg = { };
627 + DWORD dwThreadIndex = DWORD_MAX;
628 + DWORD dwRetries;
629 + DWORD dwFailingNetworkWaits = 0;
630 + MON_WAITER_CONTEXT *pWaiterContext = NULL;
631 + MON_REMOVE_MESSAGE *pRemoveMessage = NULL;
632 + MON_REMOVE_MESSAGE *pTempRemoveMessage = NULL;
633 + MON_STRUCT *pm = reinterpret_cast<MON_STRUCT*>(pvContext);
634 + WSADATA wsaData = { };
635 + HANDLE hMonitor = NULL;
636 + BOOL fRet = FALSE;
637 + UINT_PTR uTimerSuccessfulNetworkRetry = 0;
638 + UINT_PTR uTimerFailedNetworkRetry = 0;
639 +
640 + // Ensure the thread has a message queue
641 + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
642 + pm->fCoordinatorThreadMessageQueueInitialized = TRUE;
643 +
644 + hr = CreateMonWindow(pm, &pm->hwnd);
645 + ExitOnFailure(hr, "Failed to create window for status update thread");
646 +
647 + ::WSAStartup(MAKEWORD(2, 2), &wsaData);
648 +
649 + hr = WaitForNetworkChanges(&hMonitor, pm);
650 + ExitOnFailure(hr, "Failed to wait for network changes");
651 +
652 + uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL);
653 + if (0 == uTimerSuccessfulNetworkRetry)
654 + {
655 + ExitWithLastError(hr, "Failed to set timer for network successful retry");
656 + }
657 +
658 + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
659 + {
660 + if (-1 == fRet)
661 + {
662 + hr = E_UNEXPECTED;
663 + ExitOnRootFailure(hr, "Unexpected return value from message pump.");
664 + }
665 + else
666 + {
667 + switch (msg.message)
668 + {
669 + case MON_MESSAGE_ADD:
670 + dwThreadIndex = DWORD_MAX;
671 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
672 + {
673 + if (pm->rgWaiterThreads[i].cMonitorCount < MON_MAX_MONITORS_PER_THREAD)
674 + {
675 + dwThreadIndex = i;
676 + break;
677 + }
678 + }
679 +
680 + if (dwThreadIndex < pm->cWaiterThreads)
681 + {
682 + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext;
683 + }
684 + else
685 + {
686 + hr = MemEnsureArraySize(reinterpret_cast<void **>(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH);
687 + ExitOnFailure(hr, "Failed to grow waiter thread array size");
688 + ++pm->cWaiterThreads;
689 +
690 + dwThreadIndex = pm->cWaiterThreads - 1;
691 + pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast<MON_WAITER_CONTEXT*>(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE));
692 + ExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct");
693 + pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext;
694 + pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId();
695 + pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral;
696 + pWaiterContext->vpfMonDirectory = pm->vpfMonDirectory;
697 + pWaiterContext->vpfMonRegKey = pm->vpfMonRegKey;
698 + pWaiterContext->pvContext = pm->pvContext;
699 +
700 + hr = MemEnsureArraySize(reinterpret_cast<void **>(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0);
701 + ExitOnFailure(hr, "Failed to allocate first handle");
702 + pWaiterContext->cHandles = 1;
703 +
704 + pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL);
705 + ExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event");
706 +
707 + pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId);
708 + if (!pWaiterContext->hWaiterThread)
709 + {
710 + ExitWithLastError(hr, "Failed to create waiter thread.");
711 + }
712 +
713 + dwRetries = MON_THREAD_INIT_RETRIES;
714 + while (!pWaiterContext->fWaiterThreadMessageQueueInitialized && 0 < dwRetries)
715 + {
716 + ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS);
717 + --dwRetries;
718 + }
719 +
720 + if (0 == dwRetries)
721 + {
722 + hr = E_UNEXPECTED;
723 + ExitOnFailure(hr, "Waiter thread apparently never initialized its message queue.");
724 + }
725 + }
726 +
727 + ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount;
728 + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0))
729 + {
730 + ExitWithLastError(hr, "Failed to send message to waiter thread to add monitor");
731 + }
732 +
733 + if (!::SetEvent(pWaiterContext->rgHandles[0]))
734 + {
735 + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message");
736 + }
737 + break;
738 +
739 + case MON_MESSAGE_REMOVE:
740 + // Send remove to all waiter threads. They'll ignore it if they don't have that monitor.
741 + // If they do have that monitor, they'll remove it from their list, and tell coordinator they have another
742 + // empty slot via MON_MESSAGE_REMOVED message
743 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
744 + {
745 + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
746 + pRemoveMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(msg.wParam);
747 +
748 + hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage);
749 + ExitOnFailure(hr, "Failed to duplicate remove message");
750 +
751 + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast<WPARAM>(pTempRemoveMessage), msg.lParam))
752 + {
753 + ExitWithLastError(hr, "Failed to send message to waiter thread to add monitor");
754 + }
755 + pTempRemoveMessage = NULL;
756 +
757 + if (!::SetEvent(pWaiterContext->rgHandles[0]))
758 + {
759 + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message");
760 + }
761 + }
762 + MonRemoveMessageDestroy(pRemoveMessage);
763 + pRemoveMessage = NULL;
764 + break;
765 +
766 + case MON_MESSAGE_REMOVED:
767 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
768 + {
769 + if (pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId == static_cast<DWORD>(msg.wParam))
770 + {
771 + Assert(pm->rgWaiterThreads[i].cMonitorCount > 0);
772 + --pm->rgWaiterThreads[i].cMonitorCount;
773 + if (0 == pm->rgWaiterThreads[i].cMonitorCount)
774 + {
775 + if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam))
776 + {
777 + ExitWithLastError(hr, "Failed to send message to waiter thread to stop");
778 + }
779 + MemRemoveFromArray(reinterpret_cast<LPVOID>(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE);
780 + --pm->cWaiterThreads;
781 + --i; // reprocess this index in the for loop, which will now contain the item after the one we removed
782 + }
783 + }
784 + }
785 + break;
786 +
787 + case MON_MESSAGE_NETWORK_WAIT_FAILED:
788 + if (0 == dwFailingNetworkWaits)
789 + {
790 + uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL);
791 + if (0 == uTimerFailedNetworkRetry)
792 + {
793 + ExitWithLastError(hr, "Failed to set timer for network fail retry");
794 + }
795 + }
796 + ++dwFailingNetworkWaits;
797 + break;
798 +
799 + case MON_MESSAGE_NETWORK_WAIT_SUCCEEDED:
800 + --dwFailingNetworkWaits;
801 + if (0 == dwFailingNetworkWaits)
802 + {
803 + if (!::KillTimer(NULL, uTimerFailedNetworkRetry))
804 + {
805 + ExitWithLastError(hr, "Failed to kill timer for network fail retry");
806 + }
807 + uTimerFailedNetworkRetry = 0;
808 + }
809 + break;
810 +
811 + case MON_MESSAGE_NETWORK_STATUS_UPDATE:
812 + hr = WaitForNetworkChanges(&hMonitor, pm);
813 + ExitOnFailure(hr, "Failed to re-wait for network changes");
814 +
815 + // Propagate any network status update messages to all waiter threads
816 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
817 + {
818 + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
819 +
820 + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0))
821 + {
822 + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update");
823 + }
824 +
825 + if (!::SetEvent(pWaiterContext->rgHandles[0]))
826 + {
827 + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message");
828 + }
829 + }
830 + break;
831 +
832 + case WM_TIMER:
833 + // Timer means some network wait is failing, and we need to retry every so often in case a remote server goes back up
834 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
835 + {
836 + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
837 +
838 + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0))
839 + {
840 + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update");
841 + }
842 +
843 + if (!::SetEvent(pWaiterContext->rgHandles[0]))
844 + {
845 + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message");
846 + }
847 + }
848 + break;
849 +
850 + case MON_MESSAGE_DRIVE_STATUS_UPDATE:
851 + // If user requested to be notified of drive status updates, notify!
852 + if (pm->vpfMonDriveStatus)
853 + {
854 + pm->vpfMonDriveStatus(static_cast<WCHAR>(msg.wParam), static_cast<BOOL>(msg.lParam), pm->pvContext);
855 + }
856 +
857 + // Propagate any drive status update messages to all waiter threads
858 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
859 + {
860 + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
861 +
862 + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam))
863 + {
864 + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update");
865 + }
866 +
867 + if (!::SetEvent(pWaiterContext->rgHandles[0]))
868 + {
869 + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message");
870 + }
871 + }
872 + break;
873 +
874 + case MON_MESSAGE_STOP:
875 + ExitFunction1(hr = static_cast<HRESULT>(msg.wParam));
876 +
877 + default:
878 + // This thread owns a window, so this handles all the other random messages we get
879 + ::TranslateMessage(&msg);
880 + ::DispatchMessageW(&msg);
881 + break;
882 + }
883 + }
884 + }
885 +
886 +LExit:
887 + if (uTimerFailedNetworkRetry)
888 + {
889 + fRet = ::KillTimer(NULL, uTimerFailedNetworkRetry);
890 + }
891 + if (uTimerSuccessfulNetworkRetry)
892 + {
893 + fRet = ::KillTimer(NULL, uTimerSuccessfulNetworkRetry);
894 + }
895 +
896 + if (pm->hwnd)
897 + {
898 + ::CloseWindow(pm->hwnd);
899 + }
900 +
901 + // Tell all waiter threads to shutdown
902 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
903 + {
904 + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
905 + if (NULL != pWaiterContext->rgHandles[0])
906 + {
907 + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam))
908 + {
909 + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to waiter thread to stop");
910 + }
911 +
912 + if (!::SetEvent(pWaiterContext->rgHandles[0]))
913 + {
914 + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify waiter thread of incoming message");
915 + }
916 + }
917 + }
918 +
919 + if (hMonitor != NULL)
920 + {
921 + ::WSALookupServiceEnd(hMonitor);
922 + }
923 +
924 + // Now confirm they're actually shut down before returning
925 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
926 + {
927 + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
928 + if (NULL != pWaiterContext->hWaiterThread)
929 + {
930 + ::WaitForSingleObject(pWaiterContext->hWaiterThread, INFINITE);
931 + ::CloseHandle(pWaiterContext->hWaiterThread);
932 + }
933 +
934 + // Waiter thread can't release these, because coordinator thread uses it to try communicating with waiter thread
935 + ReleaseHandle(pWaiterContext->rgHandles[0]);
936 + ReleaseMem(pWaiterContext->rgHandles);
937 +
938 + ReleaseMem(pWaiterContext);
939 + }
940 +
941 + if (FAILED(hr))
942 + {
943 + // If coordinator thread fails, notify general callback of an error
944 + Assert(pm->vpfMonGeneral);
945 + pm->vpfMonGeneral(hr, pm->pvContext);
946 + }
947 + MonRemoveMessageDestroy(pRemoveMessage);
948 + MonRemoveMessageDestroy(pTempRemoveMessage);
949 +
950 + ::WSACleanup();
951 +
952 + return hr;
953 +}
954 +
955 +static HRESULT InitiateWait(
956 + __inout MON_REQUEST *pRequest,
957 + __inout HANDLE *pHandle
958 + )
959 +{
960 + HRESULT hr = S_OK;
961 + HRESULT hrTemp = S_OK;
962 + DEV_BROADCAST_HANDLE dev = { };
963 + BOOL fRedo = FALSE;
964 + BOOL fHandleFound;
965 + DWORD er = ERROR_SUCCESS;
966 + DWORD dwIndex = 0;
967 + HKEY hk = NULL;
968 + HANDLE hTemp = INVALID_HANDLE_VALUE;
969 +
970 + if (pRequest->hNotify)
971 + {
972 + UnregisterDeviceNotification(pRequest->hNotify);
973 + pRequest->hNotify = NULL;
974 + }
975 +
976 + do
977 + {
978 + fRedo = FALSE;
979 + fHandleFound = FALSE;
980 +
981 + for (DWORD i = 0; i < pRequest->cPathHierarchy && !fHandleFound; ++i)
982 + {
983 + dwIndex = pRequest->cPathHierarchy - i - 1;
984 + switch (pRequest->type)
985 + {
986 + case MON_DIRECTORY:
987 + if (INVALID_HANDLE_VALUE != *pHandle)
988 + {
989 + ::FindCloseChangeNotification(*pHandle);
990 + *pHandle = INVALID_HANDLE_VALUE;
991 + }
992 +
993 + *pHandle = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex], GetRecursiveFlag(pRequest, dwIndex), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY);
994 + if (INVALID_HANDLE_VALUE == *pHandle)
995 + {
996 + hr = HRESULT_FROM_WIN32(::GetLastError());
997 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || E_ACCESSDENIED == hr)
998 + {
999 + continue;
1000 + }
1001 + ExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]);
1002 + }
1003 + else
1004 + {
1005 + fHandleFound = TRUE;
1006 + hr = S_OK;
1007 + }
1008 + break;
1009 + case MON_REGKEY:
1010 + ReleaseRegKey(pRequest->regkey.hkSubKey);
1011 + hr = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex], KEY_NOTIFY | GetRegKeyBitness(pRequest), &pRequest->regkey.hkSubKey);
1012 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1013 + {
1014 + continue;
1015 + }
1016 + ExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]);
1017 +
1018 + er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE);
1019 + ReleaseRegKey(hk);
1020 + hr = HRESULT_FROM_WIN32(er);
1021 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || HRESULT_FROM_WIN32(ERROR_KEY_DELETED) == hr)
1022 + {
1023 + continue;
1024 + }
1025 + else
1026 + {
1027 + ExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]);
1028 +
1029 + fHandleFound = TRUE;
1030 + }
1031 +
1032 + break;
1033 + default:
1034 + return E_INVALIDARG;
1035 + }
1036 + }
1037 +
1038 + pRequest->dwPathHierarchyIndex = dwIndex;
1039 +
1040 + // If we're monitoring a parent instead of the real path because the real path didn't exist, double-check the child hasn't been created since.
1041 + // If it has, restart the whole loop
1042 + if (dwIndex < pRequest->cPathHierarchy - 1)
1043 + {
1044 + switch (pRequest->type)
1045 + {
1046 + case MON_DIRECTORY:
1047 + hTemp = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex + 1], GetRecursiveFlag(pRequest, dwIndex + 1), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY);
1048 + if (INVALID_HANDLE_VALUE != hTemp)
1049 + {
1050 + ::FindCloseChangeNotification(hTemp);
1051 + fRedo = TRUE;
1052 + }
1053 + break;
1054 + case MON_REGKEY:
1055 + hrTemp = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex + 1], KEY_NOTIFY | GetRegKeyBitness(pRequest), &hk);
1056 + ReleaseRegKey(hk);
1057 + fRedo = SUCCEEDED(hrTemp);
1058 + break;
1059 + default:
1060 + Assert(false);
1061 + }
1062 + }
1063 + } while (fRedo);
1064 +
1065 + ExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]);
1066 +
1067 + if (MON_DIRECTORY == pRequest->type)
1068 + {
1069 + dev.dbch_size = sizeof(dev);
1070 + dev.dbch_devicetype = DBT_DEVTYP_HANDLE;
1071 + dev.dbch_handle = *pHandle;
1072 + // Ignore failure on this - some drives by design don't support it (like network paths), and the worst that can happen is a
1073 + // removable device will be left in use so user cannot gracefully remove
1074 + pRequest->hNotify = RegisterDeviceNotification(pRequest->hwnd, &dev, DEVICE_NOTIFY_WINDOW_HANDLE);
1075 + }
1076 +
1077 +LExit:
1078 + ReleaseRegKey(hk);
1079 +
1080 + return hr;
1081 +}
1082 +
1083 +static DWORD WINAPI WaiterThread(
1084 + __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext
1085 + )
1086 +{
1087 + HRESULT hr = S_OK;
1088 + HRESULT hrTemp = S_OK;
1089 + DWORD dwRet = 0;
1090 + BOOL fAgain = FALSE;
1091 + BOOL fContinue = TRUE;
1092 + BOOL fNotify = FALSE;
1093 + BOOL fRet = FALSE;
1094 + MSG msg = { };
1095 + MON_ADD_MESSAGE *pAddMessage = NULL;
1096 + MON_REMOVE_MESSAGE *pRemoveMessage = NULL;
1097 + MON_WAITER_CONTEXT *pWaiterContext = reinterpret_cast<MON_WAITER_CONTEXT *>(pvContext);
1098 + DWORD dwRequestIndex;
1099 + DWORD dwNewRequestIndex;
1100 + // If we have one or more requests pending notification, this is the period we intend to wait for multiple objects (shortest amount of time to next potential notify)
1101 + DWORD dwWait = 0;
1102 + DWORD uCurrentTime = 0;
1103 + DWORD uLastTimeInMs = ::GetTickCount();
1104 + DWORD uDeltaInMs = 0;
1105 + DWORD cRequestsPendingBeforeLoop = 0;
1106 + LPWSTR sczDirectory = NULL;
1107 + bool rgfProcessedIndex[MON_MAX_MONITORS_PER_THREAD + 1] = { };
1108 + MON_INTERNAL_TEMPORARY_WAIT * pInternalWait = NULL;
1109 +
1110 + // Ensure the thread has a message queue
1111 + ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
1112 + pWaiterContext->fWaiterThreadMessageQueueInitialized = TRUE;
1113 +
1114 + do
1115 + {
1116 + dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE);
1117 +
1118 + uCurrentTime = ::GetTickCount();
1119 + uDeltaInMs = uCurrentTime - uLastTimeInMs;
1120 + uLastTimeInMs = uCurrentTime;
1121 +
1122 + if (WAIT_OBJECT_0 == dwRet)
1123 + {
1124 + do
1125 + {
1126 + fRet = ::PeekMessage(&msg, reinterpret_cast<HWND>(-1), 0, 0, PM_REMOVE);
1127 + fAgain = fRet;
1128 + if (fRet)
1129 + {
1130 + switch (msg.message)
1131 + {
1132 + case MON_MESSAGE_ADD:
1133 + pAddMessage = reinterpret_cast<MON_ADD_MESSAGE *>(msg.wParam);
1134 +
1135 + // Don't just blindly put it at the end of the array - it must be before any failing requests
1136 + // for WaitForMultipleObjects() to succeed
1137 + dwNewRequestIndex = pWaiterContext->cRequests - pWaiterContext->cRequestsFailing;
1138 + if (FAILED(pAddMessage->request.hrStatus))
1139 + {
1140 + ++pWaiterContext->cRequestsFailing;
1141 + }
1142 +
1143 + hr = MemInsertIntoArray(reinterpret_cast<void **>(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH);
1144 + ExitOnFailure(hr, "Failed to insert additional handle");
1145 + ++pWaiterContext->cHandles;
1146 +
1147 + // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL
1148 + if (MON_DIRECTORY == pAddMessage->request.type)
1149 + {
1150 + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = INVALID_HANDLE_VALUE;
1151 + }
1152 +
1153 + hr = MemInsertIntoArray(reinterpret_cast<void **>(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH);
1154 + ExitOnFailure(hr, "Failed to insert additional request struct");
1155 + ++pWaiterContext->cRequests;
1156 +
1157 + pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request;
1158 + pWaiterContext->rgHandles[dwNewRequestIndex + 1] = pAddMessage->handle;
1159 +
1160 + ReleaseNullMem(pAddMessage);
1161 + break;
1162 +
1163 + case MON_MESSAGE_REMOVE:
1164 + pRemoveMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(msg.wParam);
1165 +
1166 + // Find the request to remove
1167 + hr = FindRequestIndex(pWaiterContext, pRemoveMessage, &dwRequestIndex);
1168 + if (E_NOTFOUND == hr)
1169 + {
1170 + // Coordinator sends removes blindly to all waiter threads, so maybe this one wasn't intended for us
1171 + hr = S_OK;
1172 + }
1173 + else
1174 + {
1175 + ExitOnFailure(hr, "Failed to find request index for remove message");
1176 +
1177 + hr = RemoveRequest(pWaiterContext, dwRequestIndex);
1178 + ExitOnFailure(hr, "Failed to remove request after request from coordinator thread.");
1179 + }
1180 +
1181 + MonRemoveMessageDestroy(pRemoveMessage);
1182 + pRemoveMessage = NULL;
1183 + break;
1184 +
1185 + case MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS:
1186 + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, PM_NOREMOVE))
1187 + {
1188 + // If there is another a pending retry failed wait message, skip this one
1189 + continue;
1190 + }
1191 +
1192 + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1193 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1194 + {
1195 + if (rgfProcessedIndex[i])
1196 + {
1197 + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1198 + continue;
1199 + }
1200 +
1201 + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && FAILED(pWaiterContext->rgRequests[i].hrStatus))
1202 + {
1203 + // This is not a failure, just record this in the request's status
1204 + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1205 +
1206 + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1207 + ExitOnFailure(hr, "Failed to update wait status");
1208 + hrTemp = S_OK;
1209 +
1210 + if (dwNewRequestIndex != i)
1211 + {
1212 + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1213 + rgfProcessedIndex[dwNewRequestIndex] = true;
1214 + --i;
1215 + }
1216 + }
1217 + }
1218 + break;
1219 +
1220 + case MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS:
1221 + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, PM_NOREMOVE))
1222 + {
1223 + // If there is another a pending retry successful wait message, skip this one
1224 + continue;
1225 + }
1226 +
1227 + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1228 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1229 + {
1230 + if (rgfProcessedIndex[i])
1231 + {
1232 + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1233 + continue;
1234 + }
1235 +
1236 + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && SUCCEEDED(pWaiterContext->rgRequests[i].hrStatus))
1237 + {
1238 + // This is not a failure, just record this in the request's status
1239 + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1240 +
1241 + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1242 + ExitOnFailure(hr, "Failed to update wait status");
1243 + hrTemp = S_OK;
1244 +
1245 + if (dwNewRequestIndex != i)
1246 + {
1247 + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1248 + rgfProcessedIndex[dwNewRequestIndex] = true;
1249 + --i;
1250 + }
1251 + }
1252 + }
1253 + break;
1254 +
1255 + case MON_MESSAGE_NETWORK_STATUS_UPDATE:
1256 + if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_STATUS_UPDATE, MON_MESSAGE_NETWORK_STATUS_UPDATE, PM_NOREMOVE))
1257 + {
1258 + // If there is another a pending network status update message, skip this one
1259 + continue;
1260 + }
1261 +
1262 + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1263 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1264 + {
1265 + if (rgfProcessedIndex[i])
1266 + {
1267 + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1268 + continue;
1269 + }
1270 +
1271 + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork)
1272 + {
1273 + // Failures here get recorded in the request's status
1274 + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1275 +
1276 + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1277 + ExitOnFailure(hr, "Failed to update wait status");
1278 + hrTemp = S_OK;
1279 +
1280 + if (dwNewRequestIndex != i)
1281 + {
1282 + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1283 + rgfProcessedIndex[dwNewRequestIndex] = true;
1284 + --i;
1285 + }
1286 + }
1287 + }
1288 + break;
1289 +
1290 + case MON_MESSAGE_DRIVE_STATUS_UPDATE:
1291 + ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1292 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1293 + {
1294 + if (rgfProcessedIndex[i])
1295 + {
1296 + // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1297 + continue;
1298 + }
1299 +
1300 + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].sczOriginalPathRequest[0] == static_cast<WCHAR>(msg.wParam))
1301 + {
1302 + // Failures here get recorded in the request's status
1303 + if (static_cast<BOOL>(msg.lParam))
1304 + {
1305 + hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1306 + }
1307 + else
1308 + {
1309 + // If the message says the drive is disconnected, don't even try to wait, just mark it as gone
1310 + hrTemp = E_PATHNOTFOUND;
1311 + }
1312 +
1313 + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1314 + ExitOnFailure(hr, "Failed to update wait status");
1315 + hrTemp = S_OK;
1316 +
1317 + if (dwNewRequestIndex != i)
1318 + {
1319 + // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1320 + rgfProcessedIndex[dwNewRequestIndex] = true;
1321 + --i;
1322 + }
1323 + }
1324 + }
1325 + break;
1326 +
1327 + case MON_MESSAGE_DRIVE_QUERY_REMOVE:
1328 + pInternalWait = reinterpret_cast<MON_INTERNAL_TEMPORARY_WAIT *>(msg.wParam);
1329 + // Only do any work if message is not yet out of date
1330 + // While it could become out of date while doing this processing, sending thread will check response to guard against this
1331 + if (pInternalWait->dwSendIteration == static_cast<DWORD>(msg.lParam))
1332 + {
1333 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1334 + {
1335 + if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgHandles[i + 1] == reinterpret_cast<HANDLE>(pInternalWait->pvContext))
1336 + {
1337 + // Release handles ASAP so the remove request will succeed
1338 + if (pWaiterContext->rgRequests[i].hNotify)
1339 + {
1340 + UnregisterDeviceNotification(pWaiterContext->rgRequests[i].hNotify);
1341 + pWaiterContext->rgRequests[i].hNotify = NULL;
1342 + }
1343 + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]);
1344 + pWaiterContext->rgHandles[i + 1] = INVALID_HANDLE_VALUE;
1345 +
1346 + // Reply to unblock our reply to the remove request
1347 + pInternalWait->dwReceiveIteration = static_cast<DWORD>(msg.lParam);
1348 + if (!::SetEvent(pInternalWait->hWait))
1349 + {
1350 + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify coordinator thread that removable device handle was released, this could be due to wndproc no longer waiting for waiter thread's response");
1351 + }
1352 +
1353 + // Drive is disconnecting, don't even try to wait, just mark it as gone
1354 + hrTemp = E_PATHNOTFOUND;
1355 +
1356 + hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1357 + ExitOnFailure(hr, "Failed to update wait status");
1358 + hrTemp = S_OK;
1359 + break;
1360 + }
1361 + }
1362 + }
1363 + break;
1364 +
1365 + case MON_MESSAGE_STOP:
1366 + // Stop requested, so abort the whole thread
1367 + Trace(REPORT_DEBUG, "Waiter thread was told to stop");
1368 + fAgain = FALSE;
1369 + fContinue = FALSE;
1370 + ExitFunction1(hr = static_cast<HRESULT>(msg.wParam));
1371 +
1372 + default:
1373 + Assert(false);
1374 + break;
1375 + }
1376 + }
1377 + } while (fAgain);
1378 + }
1379 + else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles)
1380 + {
1381 + // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist
1382 + dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1;
1383 + fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1);
1384 +
1385 + // Initiate re-waits before we notify callback, to ensure we don't miss a single update
1386 + hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1);
1387 + hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex);
1388 + ExitOnFailure(hr, "Failed to update wait status");
1389 + hrTemp = S_OK;
1390 +
1391 + // If there were no errors and we were already waiting on the right target, or if we weren't yet but are able to now, it's a successful notify
1392 + if (SUCCEEDED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus) && (fNotify || (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1)))
1393 + {
1394 + Trace(REPORT_DEBUG, "Changes detected, waiting for silence period index %u", dwRequestIndex);
1395 +
1396 + if (0 < pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs)
1397 + {
1398 + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs = 0;
1399 + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = TRUE;
1400 +
1401 + if (!pWaiterContext->rgRequests[dwRequestIndex].fPendingFire)
1402 + {
1403 + pWaiterContext->rgRequests[dwRequestIndex].fPendingFire = TRUE;
1404 + ++pWaiterContext->cRequestsPending;
1405 + }
1406 + }
1407 + else
1408 + {
1409 + // If no silence period, notify immediately
1410 + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex);
1411 + }
1412 + }
1413 + }
1414 + else if (WAIT_TIMEOUT != dwRet)
1415 + {
1416 + ExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet);
1417 + }
1418 +
1419 + // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire
1420 + // And set dwWait appropriately so we awaken at the right time to fire the next pending notification (in case no further writes occur during that time)
1421 + if (0 < pWaiterContext->cRequestsPending)
1422 + {
1423 + // Start at max value and find the lowest wait we can below that
1424 + dwWait = DWORD_MAX;
1425 + cRequestsPendingBeforeLoop = pWaiterContext->cRequestsPending;
1426 +
1427 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1428 + {
1429 + if (pWaiterContext->rgRequests[i].fPendingFire)
1430 + {
1431 + if (0 == cRequestsPendingBeforeLoop)
1432 + {
1433 + Assert(FALSE);
1434 + hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT);
1435 + ExitOnFailure(hr, "Phantom pending fires were found!");
1436 + }
1437 + --cRequestsPendingBeforeLoop;
1438 +
1439 + dwRequestIndex = i;
1440 +
1441 + if (pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd)
1442 + {
1443 + pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = FALSE;
1444 + }
1445 + else
1446 + {
1447 + pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs += uDeltaInMs;
1448 + }
1449 +
1450 + // silence period has elapsed without further notifications, so reset pending-related variables, and finally fire a notify!
1451 + if (pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs >= pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs)
1452 + {
1453 + Trace(REPORT_DEBUG, "Silence period surpassed, notifying %u ms late", pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs);
1454 + Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex);
1455 + }
1456 + else
1457 + {
1458 + // set dwWait to the shortest interval period so that if no changes occur, WaitForMultipleObjects
1459 + // wakes the thread back up when it's time to fire the next pending notification
1460 + if (dwWait > pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs)
1461 + {
1462 + dwWait = pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs;
1463 + }
1464 + }
1465 + }
1466 + }
1467 +
1468 + // Some post-loop list validation for sanity checking
1469 + if (0 < cRequestsPendingBeforeLoop)
1470 + {
1471 + Assert(FALSE);
1472 + hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA);
1473 + ExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait);
1474 + }
1475 + if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait)
1476 + {
1477 + Assert(FALSE);
1478 + hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT);
1479 + ExitOnFailure(hr, "Pending fires exist, but wait was infinite", cRequestsPendingBeforeLoop);
1480 + }
1481 + }
1482 + } while (fContinue);
1483 +
1484 + // Don't bother firing pending notifications. We were told to stop monitoring, so client doesn't care.
1485 +
1486 +LExit:
1487 + ReleaseStr(sczDirectory);
1488 + MonAddMessageDestroy(pAddMessage);
1489 + MonRemoveMessageDestroy(pRemoveMessage);
1490 +
1491 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1492 + {
1493 + MonRequestDestroy(pWaiterContext->rgRequests + i);
1494 +
1495 + switch (pWaiterContext->rgRequests[i].type)
1496 + {
1497 + case MON_DIRECTORY:
1498 + if (INVALID_HANDLE_VALUE != pWaiterContext->rgHandles[i + 1])
1499 + {
1500 + ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]);
1501 + }
1502 + break;
1503 + case MON_REGKEY:
1504 + ReleaseHandle(pWaiterContext->rgHandles[i + 1]);
1505 + break;
1506 + default:
1507 + Assert(false);
1508 + }
1509 + }
1510 +
1511 + if (FAILED(hr))
1512 + {
1513 + // If waiter thread fails, notify general callback of an error
1514 + Assert(pWaiterContext->vpfMonGeneral);
1515 + pWaiterContext->vpfMonGeneral(hr, pWaiterContext->pvContext);
1516 +
1517 + // And tell coordinator to shut all other waiters down
1518 + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0))
1519 + {
1520 + TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to coordinator thread to stop (due to general failure).");
1521 + }
1522 + }
1523 +
1524 + return hr;
1525 +}
1526 +
1527 +static void Notify(
1528 + __in HRESULT hr,
1529 + __in MON_WAITER_CONTEXT *pWaiterContext,
1530 + __in MON_REQUEST *pRequest
1531 + )
1532 +{
1533 + if (pRequest->fPendingFire)
1534 + {
1535 + --pWaiterContext->cRequestsPending;
1536 + }
1537 +
1538 + pRequest->fPendingFire = FALSE;
1539 + pRequest->fSkipDeltaAdd = FALSE;
1540 + pRequest->dwSilencePeriodInMs = 0;
1541 +
1542 + switch (pRequest->type)
1543 + {
1544 + case MON_DIRECTORY:
1545 + Assert(pWaiterContext->vpfMonDirectory);
1546 + pWaiterContext->vpfMonDirectory(hr, pRequest->sczOriginalPathRequest, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext);
1547 + break;
1548 + case MON_REGKEY:
1549 + Assert(pWaiterContext->vpfMonRegKey);
1550 + pWaiterContext->vpfMonRegKey(hr, pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1], pRequest->regkey.kbKeyBitness, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext);
1551 + break;
1552 + default:
1553 + Assert(false);
1554 + }
1555 +}
1556 +
1557 +static BOOL GetRecursiveFlag(
1558 + __in MON_REQUEST *pRequest,
1559 + __in DWORD dwIndex
1560 + )
1561 +{
1562 + if (pRequest->cPathHierarchy - 1 == dwIndex)
1563 + {
1564 + return pRequest->fRecursive;
1565 + }
1566 + else
1567 + {
1568 + return FALSE;
1569 + }
1570 +}
1571 +
1572 +static HRESULT FindRequestIndex(
1573 + __in MON_WAITER_CONTEXT *pWaiterContext,
1574 + __in MON_REMOVE_MESSAGE *pMessage,
1575 + __out DWORD *pdwIndex
1576 + )
1577 +{
1578 + HRESULT hr = S_OK;
1579 +
1580 + for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1581 + {
1582 + if (pWaiterContext->rgRequests[i].type == pMessage->type)
1583 + {
1584 + switch (pWaiterContext->rgRequests[i].type)
1585 + {
1586 + case MON_DIRECTORY:
1587 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->directory.sczDirectory, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive)
1588 + {
1589 + *pdwIndex = i;
1590 + ExitFunction1(hr = S_OK);
1591 + }
1592 + break;
1593 + case MON_REGKEY:
1594 + if (reinterpret_cast<DWORD_PTR>(pMessage->regkey.hkRoot) == reinterpret_cast<DWORD_PTR>(pWaiterContext->rgRequests[i].regkey.hkRoot) && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->regkey.sczSubKey, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive && pWaiterContext->rgRequests[i].regkey.kbKeyBitness == pMessage->regkey.kbKeyBitness)
1595 + {
1596 + *pdwIndex = i;
1597 + ExitFunction1(hr = S_OK);
1598 + }
1599 + break;
1600 + default:
1601 + Assert(false);
1602 + }
1603 + }
1604 + }
1605 +
1606 + hr = E_NOTFOUND;
1607 +
1608 +LExit:
1609 + return hr;
1610 +}
1611 +
1612 +static HRESULT RemoveRequest(
1613 + __inout MON_WAITER_CONTEXT *pWaiterContext,
1614 + __in DWORD dwRequestIndex
1615 + )
1616 +{
1617 + HRESULT hr = S_OK;
1618 +
1619 + MonRequestDestroy(pWaiterContext->rgRequests + dwRequestIndex);
1620 +
1621 + switch (pWaiterContext->rgRequests[dwRequestIndex].type)
1622 + {
1623 + case MON_DIRECTORY:
1624 + if (pWaiterContext->rgHandles[dwRequestIndex + 1] != INVALID_HANDLE_VALUE)
1625 + {
1626 + ::FindCloseChangeNotification(pWaiterContext->rgHandles[dwRequestIndex + 1]);
1627 + }
1628 + break;
1629 + case MON_REGKEY:
1630 + ReleaseHandle(pWaiterContext->rgHandles[dwRequestIndex + 1]);
1631 + break;
1632 + default:
1633 + Assert(false);
1634 + }
1635 +
1636 + if (pWaiterContext->rgRequests[dwRequestIndex].fPendingFire)
1637 + {
1638 + --pWaiterContext->cRequestsPending;
1639 + }
1640 +
1641 + if (FAILED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus))
1642 + {
1643 + --pWaiterContext->cRequestsFailing;
1644 + }
1645 +
1646 + MemRemoveFromArray(reinterpret_cast<void *>(pWaiterContext->rgHandles), dwRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), TRUE);
1647 + --pWaiterContext->cHandles;
1648 + MemRemoveFromArray(reinterpret_cast<void *>(pWaiterContext->rgRequests), dwRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), TRUE);
1649 + --pWaiterContext->cRequests;
1650 +
1651 + // Notify coordinator thread that a wait was removed
1652 + if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast<WPARAM>(::GetCurrentThreadId()), 0))
1653 + {
1654 + ExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed.");
1655 + }
1656 +
1657 +LExit:
1658 + return hr;
1659 +}
1660 +
1661 +static REGSAM GetRegKeyBitness(
1662 + __in MON_REQUEST *pRequest
1663 + )
1664 +{
1665 + if (REG_KEY_32BIT == pRequest->regkey.kbKeyBitness)
1666 + {
1667 + return KEY_WOW64_32KEY;
1668 + }
1669 + else if (REG_KEY_64BIT == pRequest->regkey.kbKeyBitness)
1670 + {
1671 + return KEY_WOW64_64KEY;
1672 + }
1673 + else
1674 + {
1675 + return 0;
1676 + }
1677 +}
1678 +
1679 +static HRESULT DuplicateRemoveMessage(
1680 + __in MON_REMOVE_MESSAGE *pMessage,
1681 + __out MON_REMOVE_MESSAGE **ppMessage
1682 + )
1683 +{
1684 + HRESULT hr = S_OK;
1685 +
1686 + *ppMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE));
1687 + ExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message");
1688 +
1689 + (*ppMessage)->type = pMessage->type;
1690 + (*ppMessage)->fRecursive = pMessage->fRecursive;
1691 +
1692 + switch (pMessage->type)
1693 + {
1694 + case MON_DIRECTORY:
1695 + hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0);
1696 + ExitOnFailure(hr, "Failed to copy directory");
1697 + break;
1698 + case MON_REGKEY:
1699 + (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot;
1700 + (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness;
1701 + hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0);
1702 + ExitOnFailure(hr, "Failed to copy subkey");
1703 + break;
1704 + default:
1705 + Assert(false);
1706 + break;
1707 + }
1708 +
1709 +LExit:
1710 + return hr;
1711 +}
1712 +
1713 +static LRESULT CALLBACK MonWndProc(
1714 + __in HWND hWnd,
1715 + __in UINT uMsg,
1716 + __in WPARAM wParam,
1717 + __in LPARAM lParam
1718 + )
1719 +{
1720 + HRESULT hr = S_OK;
1721 + DEV_BROADCAST_HDR *pHdr = NULL;
1722 + DEV_BROADCAST_HANDLE *pHandle = NULL;
1723 + DEV_BROADCAST_VOLUME *pVolume = NULL;
1724 + DWORD dwUnitMask = 0;
1725 + DWORD er = ERROR_SUCCESS;
1726 + WCHAR chDrive = L'\0';
1727 + BOOL fArrival = FALSE;
1728 + BOOL fReturnTrue = FALSE;
1729 + CREATESTRUCT *pCreateStruct = NULL;
1730 + MON_WAITER_CONTEXT *pWaiterContext = NULL;
1731 + MON_STRUCT *pm = NULL;
1732 +
1733 + // keep track of the MON_STRUCT pointer that was passed in on init, associate it with the window
1734 + if (WM_CREATE == uMsg)
1735 + {
1736 + pCreateStruct = reinterpret_cast<CREATESTRUCT *>(lParam);
1737 + if (pCreateStruct)
1738 + {
1739 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
1740 + }
1741 + }
1742 + else if (WM_NCDESTROY == uMsg)
1743 + {
1744 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
1745 + }
1746 +
1747 + // Note this message ONLY comes in through WndProc, it isn't visible from the GetMessage loop.
1748 + else if (WM_DEVICECHANGE == uMsg)
1749 + {
1750 + if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam)
1751 + {
1752 + fArrival = DBT_DEVICEARRIVAL == wParam;
1753 +
1754 + pHdr = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);
1755 + if (DBT_DEVTYP_VOLUME == pHdr->dbch_devicetype)
1756 + {
1757 + pVolume = reinterpret_cast<DEV_BROADCAST_VOLUME*>(lParam);
1758 + dwUnitMask = pVolume->dbcv_unitmask;
1759 + chDrive = L'a';
1760 + while (0 < dwUnitMask)
1761 + {
1762 + if (dwUnitMask & 0x1)
1763 + {
1764 + // This drive had a status update, so send it out to all threads
1765 + if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast<WPARAM>(chDrive), static_cast<LPARAM>(fArrival)))
1766 + {
1767 + ExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE");
1768 + }
1769 + }
1770 + dwUnitMask >>= 1;
1771 + ++chDrive;
1772 +
1773 + if (chDrive == 'z')
1774 + {
1775 + hr = E_UNEXPECTED;
1776 + ExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask);
1777 + }
1778 + }
1779 + }
1780 + }
1781 + // We can only process device query remove messages if we have a MON_STRUCT pointer
1782 + else if (DBT_DEVICEQUERYREMOVE == wParam)
1783 + {
1784 + pm = reinterpret_cast<MON_STRUCT*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
1785 + if (!pm)
1786 + {
1787 + hr = E_POINTER;
1788 + ExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored");
1789 + }
1790 +
1791 + fReturnTrue = TRUE;
1792 +
1793 + pHdr = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);
1794 + if (DBT_DEVTYP_HANDLE == pHdr->dbch_devicetype)
1795 + {
1796 + // We must wait for the actual wait handle to be released by waiter thread before telling windows to proceed with device removal, otherwise it could fail
1797 + // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread
1798 + pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL);
1799 + ExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed");
1800 +
1801 + pHandle = reinterpret_cast<DEV_BROADCAST_HANDLE*>(lParam);
1802 + pm->internalWait.pvContext = pHandle->dbch_handle;
1803 + pm->internalWait.dwReceiveIteration = pm->internalWait.dwSendIteration - 1;
1804 + // This drive had a status update, so send it out to all threads
1805 + for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
1806 + {
1807 + pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
1808 +
1809 + if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast<WPARAM>(&pm->internalWait), static_cast<LPARAM>(pm->internalWait.dwSendIteration)))
1810 + {
1811 + ExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove");
1812 + }
1813 +
1814 + if (!::SetEvent(pWaiterContext->rgHandles[0]))
1815 + {
1816 + ExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message");
1817 + }
1818 + }
1819 +
1820 + er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE);
1821 + // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response
1822 + if (WAIT_OBJECT_0 == er)
1823 + {
1824 + // If the response ID matches what we sent, we actually got a valid reply!
1825 + if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration)
1826 + {
1827 + TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply");
1828 + }
1829 + }
1830 + else if (WAIT_TIMEOUT == er)
1831 + {
1832 + TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message");
1833 + }
1834 + else
1835 + {
1836 + ExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread");
1837 + }
1838 + ++pm->internalWait.dwSendIteration;
1839 + }
1840 + }
1841 + }
1842 +
1843 +LExit:
1844 + if (pm)
1845 + {
1846 + ReleaseHandle(pm->internalWait.hWait);
1847 + }
1848 +
1849 + if (fReturnTrue)
1850 + {
1851 + return TRUE;
1852 + }
1853 + else
1854 + {
1855 + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
1856 + }
1857 +}
1858 +
1859 +static HRESULT CreateMonWindow(
1860 + __in MON_STRUCT *pm,
1861 + __out HWND *pHwnd
1862 + )
1863 +{
1864 + HRESULT hr = S_OK;
1865 + WNDCLASSW wc = { };
1866 +
1867 + wc.lpfnWndProc = MonWndProc;
1868 + wc.hInstance = ::GetModuleHandleW(NULL);
1869 + wc.lpszClassName = MONUTIL_WINDOW_CLASS;
1870 + if (!::RegisterClassW(&wc))
1871 + {
1872 + if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError())
1873 + {
1874 + ExitWithLastError(hr, "Failed to register MonUtil window class.");
1875 + }
1876 + }
1877 +
1878 + *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm);
1879 + ExitOnNullWithLastError(*pHwnd, hr, "Failed to create window.");
1880 +
1881 + // Rumor has it that drive arrival / removal events can be lost in the rare event that some other application higher up in z-order is hanging if we don't make our window topmost
1882 + // SWP_NOACTIVATE is important so the currently active window doesn't lose focus
1883 + SetWindowPos(*pHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_DEFERERASE | SWP_NOACTIVATE);
1884 +
1885 +LExit:
1886 + return hr;
1887 +}
1888 +
1889 +static HRESULT WaitForNetworkChanges(
1890 + __inout HANDLE *phMonitor,
1891 + __in MON_STRUCT *pm
1892 + )
1893 +{
1894 + HRESULT hr = S_OK;
1895 + int nResult = 0;
1896 + DWORD dwBytesReturned = 0;
1897 + WSACOMPLETION wsaCompletion = { };
1898 + WSAQUERYSET qsRestrictions = { };
1899 +
1900 + qsRestrictions.dwSize = sizeof(WSAQUERYSET);
1901 + qsRestrictions.dwNameSpace = NS_NLA;
1902 +
1903 + if (NULL != *phMonitor)
1904 + {
1905 + ::WSALookupServiceEnd(*phMonitor);
1906 + *phMonitor = NULL;
1907 + }
1908 +
1909 + if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor))
1910 + {
1911 + hr = HRESULT_FROM_WIN32(::WSAGetLastError());
1912 + ExitOnFailure(hr, "WSALookupServiceBegin() failed");
1913 + }
1914 +
1915 + wsaCompletion.Type = NSP_NOTIFY_HWND;
1916 + wsaCompletion.Parameters.WindowMessage.hWnd = pm->hwnd;
1917 + wsaCompletion.Parameters.WindowMessage.uMsg = MON_MESSAGE_NETWORK_STATUS_UPDATE;
1918 + nResult = ::WSANSPIoctl(*phMonitor, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &dwBytesReturned, &wsaCompletion);
1919 + if (SOCKET_ERROR != nResult || WSA_IO_PENDING != ::WSAGetLastError())
1920 + {
1921 + hr = HRESULT_FROM_WIN32(::WSAGetLastError());
1922 + if (SUCCEEDED(hr))
1923 + {
1924 + hr = E_FAIL;
1925 + }
1926 + ExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError());
1927 + }
1928 +
1929 +LExit:
1930 + return hr;
1931 +}
1932 +
1933 +static HRESULT UpdateWaitStatus(
1934 + __in HRESULT hrNewStatus,
1935 + __inout MON_WAITER_CONTEXT *pWaiterContext,
1936 + __in DWORD dwRequestIndex,
1937 + __out_opt DWORD *pdwNewRequestIndex
1938 + )
1939 +{
1940 + HRESULT hr = S_OK;
1941 + DWORD dwNewRequestIndex;
1942 + MON_REQUEST *pRequest = pWaiterContext->rgRequests + dwRequestIndex;
1943 +
1944 + if (NULL != pdwNewRequestIndex)
1945 + {
1946 + *pdwNewRequestIndex = dwRequestIndex;
1947 + }
1948 +
1949 + if (SUCCEEDED(pRequest->hrStatus) || SUCCEEDED(hrNewStatus))
1950 + {
1951 + // If it's a network wait, notify as long as it's new status is successful because we *may* have lost some changes
1952 + // before the wait was re-initiated. Otherwise, only notify if there was an interesting status change
1953 + if (SUCCEEDED(pRequest->hrStatus) != SUCCEEDED(hrNewStatus) || (pRequest->fNetwork && SUCCEEDED(hrNewStatus)))
1954 + {
1955 + Notify(hrNewStatus, pWaiterContext, pRequest);
1956 + }
1957 +
1958 + if (SUCCEEDED(pRequest->hrStatus) && FAILED(hrNewStatus))
1959 + {
1960 + // If it's a network wait, notify coordinator thread that a network wait is failing
1961 + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0))
1962 + {
1963 + ExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail");
1964 + }
1965 +
1966 + // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle
1967 + ++pWaiterContext->cRequestsFailing;
1968 + dwNewRequestIndex = pWaiterContext->cRequests - 1;
1969 + MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles));
1970 + MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests));
1971 + // Reset pRequest to the newly swapped item
1972 + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex;
1973 + if (NULL != pdwNewRequestIndex)
1974 + {
1975 + *pdwNewRequestIndex = dwNewRequestIndex;
1976 + }
1977 + }
1978 + else if (FAILED(pRequest->hrStatus) && SUCCEEDED(hrNewStatus))
1979 + {
1980 + Assert(pWaiterContext->cRequestsFailing > 0);
1981 + // If it's a network wait, notify coordinator thread that a network wait is succeeding again
1982 + if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0))
1983 + {
1984 + ExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again");
1985 + }
1986 +
1987 + --pWaiterContext->cRequestsFailing;
1988 + dwNewRequestIndex = 0;
1989 + MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles));
1990 + MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests));
1991 + // Reset pRequest to the newly swapped item
1992 + pRequest = pWaiterContext->rgRequests + dwNewRequestIndex;
1993 + if (NULL != pdwNewRequestIndex)
1994 + {
1995 + *pdwNewRequestIndex = dwNewRequestIndex;
1996 + }
1997 + }
1998 + }
1999 +
2000 + pRequest->hrStatus = hrNewStatus;
2001 +
2002 +LExit:
2003 + return hr;
2004 +}
src/dutil/osutil.cpp new
+188
@@ -0,0 +1,188 @@
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 +
5 +OS_VERSION vOsVersion = OS_VERSION_UNKNOWN;
6 +DWORD vdwOsServicePack = 0;
7 +
8 +/********************************************************************
9 + OsGetVersion
10 +
11 +********************************************************************/
12 +extern "C" void DAPI OsGetVersion(
13 + __out OS_VERSION* pVersion,
14 + __out DWORD* pdwServicePack
15 + )
16 +{
17 + OSVERSIONINFOEXW ovi = { };
18 +
19 + if (OS_VERSION_UNKNOWN == vOsVersion)
20 + {
21 + ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
22 + ::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&ovi)); // only fails if version info size is set incorrectly.
23 +
24 + vdwOsServicePack = static_cast<DWORD>(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor;
25 + if (4 == ovi.dwMajorVersion)
26 + {
27 + vOsVersion = OS_VERSION_WINNT;
28 + }
29 + else if (5 == ovi.dwMajorVersion)
30 + {
31 + if (0 == ovi.dwMinorVersion)
32 + {
33 + vOsVersion = OS_VERSION_WIN2000;
34 + }
35 + else if (1 == ovi.dwMinorVersion)
36 + {
37 + vOsVersion = OS_VERSION_WINXP;
38 + }
39 + else if (2 == ovi.dwMinorVersion)
40 + {
41 + vOsVersion = OS_VERSION_WIN2003;
42 + }
43 + else
44 + {
45 + vOsVersion = OS_VERSION_FUTURE;
46 + }
47 + }
48 + else if (6 == ovi.dwMajorVersion)
49 + {
50 + if (0 == ovi.dwMinorVersion)
51 + {
52 + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008;
53 + }
54 + else if (1 == ovi.dwMinorVersion)
55 + {
56 + vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2;
57 + }
58 + else
59 + {
60 + vOsVersion = OS_VERSION_FUTURE;
61 + }
62 + }
63 + else
64 + {
65 + vOsVersion = OS_VERSION_FUTURE;
66 + }
67 + }
68 +
69 + *pVersion = vOsVersion;
70 + *pdwServicePack = vdwOsServicePack;
71 +}
72 +
73 +extern "C" HRESULT DAPI OsCouldRunPrivileged(
74 + __out BOOL* pfPrivileged
75 + )
76 +{
77 + HRESULT hr = S_OK;
78 + BOOL fUacEnabled = FALSE;
79 + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
80 + PSID AdministratorsGroup = NULL;
81 +
82 + // Do a best effort check to see if UAC is enabled on this machine.
83 + OsIsUacEnabled(&fUacEnabled);
84 +
85 + // If UAC is enabled then the process could run privileged by asking to elevate.
86 + if (fUacEnabled)
87 + {
88 + *pfPrivileged = TRUE;
89 + }
90 + else // no UAC so only privilged if user is in administrators group.
91 + {
92 + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup);
93 + if (*pfPrivileged)
94 + {
95 + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged))
96 + {
97 + *pfPrivileged = FALSE;
98 + }
99 + }
100 + }
101 +
102 + ReleaseSid(AdministratorsGroup);
103 + return hr;
104 +}
105 +
106 +extern "C" HRESULT DAPI OsIsRunningPrivileged(
107 + __out BOOL* pfPrivileged
108 + )
109 +{
110 + HRESULT hr = S_OK;
111 + UINT er = ERROR_SUCCESS;
112 + HANDLE hToken = NULL;
113 + TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault;
114 + DWORD dwSize = 0;
115 + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
116 + PSID AdministratorsGroup = NULL;
117 +
118 + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
119 + {
120 + ExitOnLastError(hr, "Failed to open process token.");
121 + }
122 +
123 + if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize))
124 + {
125 + *pfPrivileged = (TokenElevationTypeFull == elevationType);
126 + ExitFunction1(hr = S_OK);
127 + }
128 +
129 + // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check
130 + er = ::GetLastError();
131 + if (ERROR_INVALID_FUNCTION == er)
132 + {
133 + er = ERROR_SUCCESS;
134 + }
135 + ExitOnWin32Error(er, hr, "Failed to get process token information.");
136 +
137 + // Fallback to this check for some OS's (like XP)
138 + *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup);
139 + if (*pfPrivileged)
140 + {
141 + if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged))
142 + {
143 + *pfPrivileged = FALSE;
144 + }
145 + }
146 +
147 +LExit:
148 + ReleaseSid(AdministratorsGroup);
149 +
150 + if (hToken)
151 + {
152 + ::CloseHandle(hToken);
153 + }
154 +
155 + return hr;
156 +}
157 +
158 +extern "C" HRESULT DAPI OsIsUacEnabled(
159 + __out BOOL* pfUacEnabled
160 + )
161 +{
162 + HRESULT hr = S_OK;
163 + HKEY hk = NULL;
164 + DWORD dwUacEnabled = 0;
165 +
166 + *pfUacEnabled = FALSE; // assume UAC not enabled.
167 +
168 + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk);
169 + if (E_FILENOTFOUND == hr)
170 + {
171 + ExitFunction1(hr = S_OK);
172 + }
173 + ExitOnFailure(hr, "Failed to open system policy key to detect UAC.");
174 +
175 + hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled);
176 + if (E_FILENOTFOUND == hr)
177 + {
178 + ExitFunction1(hr = S_OK);
179 + }
180 + ExitOnFailure(hr, "Failed to read registry value to detect UAC.");
181 +
182 + *pfUacEnabled = (0 != dwUacEnabled);
183 +
184 +LExit:
185 + ReleaseRegKey(hk);
186 +
187 + return hr;
188 +}
src/dutil/path2utl.cpp new
+89
@@ -0,0 +1,89 @@
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 +
5 +
6 +DAPI_(HRESULT) PathCanonicalizePath(
7 + __in_z LPCWSTR wzPath,
8 + __deref_out_z LPWSTR* psczCanonicalized
9 + )
10 +{
11 + HRESULT hr = S_OK;
12 + int cch = MAX_PATH + 1;
13 +
14 + hr = StrAlloc(psczCanonicalized, cch);
15 + ExitOnFailure(hr, "Failed to allocate string for the canonicalized path.");
16 +
17 + if (::PathCanonicalizeW(*psczCanonicalized, wzPath))
18 + {
19 + hr = S_OK;
20 + }
21 + else
22 + {
23 + ExitFunctionWithLastError(hr);
24 + }
25 +
26 +LExit:
27 + return hr;
28 +}
29 +
30 +DAPI_(HRESULT) PathDirectoryContainsPath(
31 + __in_z LPCWSTR wzDirectory,
32 + __in_z LPCWSTR wzPath
33 + )
34 +{
35 + HRESULT hr = S_OK;
36 + LPWSTR sczPath = NULL;
37 + LPWSTR sczDirectory = NULL;
38 + LPWSTR sczOriginalPath = NULL;
39 + LPWSTR sczOriginalDirectory = NULL;
40 +
41 + hr = PathCanonicalizePath(wzPath, &sczOriginalPath);
42 + ExitOnFailure(hr, "Failed to canonicalize the path.");
43 +
44 + hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory);
45 + ExitOnFailure(hr, "Failed to canonicalize the directory.");
46 +
47 + if (!sczOriginalPath || !*sczOriginalPath)
48 + {
49 + ExitFunction1(hr = S_FALSE);
50 + }
51 + if (!sczOriginalDirectory || !*sczOriginalDirectory)
52 + {
53 + ExitFunction1(hr = S_FALSE);
54 + }
55 +
56 + sczPath = sczOriginalPath;
57 + sczDirectory = sczOriginalDirectory;
58 +
59 + for (; *sczDirectory;)
60 + {
61 + if (!*sczPath)
62 + {
63 + ExitFunction1(hr = S_FALSE);
64 + }
65 +
66 + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1))
67 + {
68 + ExitFunction1(hr = S_FALSE);
69 + }
70 +
71 + ++sczDirectory;
72 + ++sczPath;
73 + }
74 +
75 + --sczDirectory;
76 + if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath)
77 + {
78 + hr = S_OK;
79 + }
80 + else
81 + {
82 + hr = S_FALSE;
83 + }
84 +
85 +LExit:
86 + ReleaseStr(sczOriginalPath);
87 + ReleaseStr(sczOriginalDirectory);
88 + return hr;
89 +}
src/dutil/pathutil.cpp new
+1009
@@ -0,0 +1,1009 @@
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 +
5 +#define PATH_GOOD_ENOUGH 64
6 +
7 +
8 +DAPI_(HRESULT) PathCommandLineAppend(
9 + __deref_out_z LPWSTR* psczCommandLine,
10 + __in_z LPCWSTR wzArgument
11 + )
12 +{
13 + HRESULT hr = S_OK;
14 + LPWSTR sczQuotedArg = NULL;
15 + BOOL fRequiresQuoting = FALSE;
16 + DWORD dwMaxEscapedSize = 0;
17 +
18 + // Loop through the argument determining if it needs to be quoted and what the maximum
19 + // size would be if there are escape characters required.
20 + for (LPCWSTR pwz = wzArgument; *pwz; ++pwz)
21 + {
22 + // Arguments with whitespace need quoting.
23 + if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz)
24 + {
25 + fRequiresQuoting = TRUE;
26 + }
27 + else if (L'"' == *pwz) // quotes need quoting and sometimes escaping.
28 + {
29 + fRequiresQuoting = TRUE;
30 + ++dwMaxEscapedSize;
31 + }
32 + else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room.
33 + {
34 + ++dwMaxEscapedSize;
35 + }
36 +
37 + ++dwMaxEscapedSize;
38 + }
39 +
40 + // If we found anything in the argument that requires our argument to be quoted
41 + if (fRequiresQuoting)
42 + {
43 + hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator.
44 + ExitOnFailure(hr, "Failed to allocate argument to be quoted.");
45 +
46 + LPCWSTR pwz = wzArgument;
47 + LPWSTR pwzQuoted = sczQuotedArg;
48 +
49 + *pwzQuoted = L'"';
50 + ++pwzQuoted;
51 + while (*pwz)
52 + {
53 + DWORD dwBackslashes = 0;
54 + while (L'\\' == *pwz)
55 + {
56 + ++dwBackslashes;
57 + ++pwz;
58 + }
59 +
60 + // Escape all backslashes at the end of the string.
61 + if (!*pwz)
62 + {
63 + dwBackslashes *= 2;
64 + }
65 + else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself.
66 + {
67 + dwBackslashes = dwBackslashes * 2 + 1;
68 + }
69 + // the backslashes don't have to be escaped.
70 +
71 + // Add the appropriate number of backslashes
72 + for (DWORD i = 0; i < dwBackslashes; ++i)
73 + {
74 + *pwzQuoted = L'\\';
75 + ++pwzQuoted;
76 + }
77 +
78 + // If there is a character, add it after all the escaped backslashes
79 + if (*pwz)
80 + {
81 + *pwzQuoted = *pwz;
82 + ++pwz;
83 + ++pwzQuoted;
84 + }
85 + }
86 +
87 + *pwzQuoted = L'"';
88 + ++pwzQuoted;
89 + *pwzQuoted = L'\0'; // ensure the arg is null terminated.
90 + }
91 +
92 + // If there is already data in the command line, append a space before appending the
93 + // argument.
94 + if (*psczCommandLine && **psczCommandLine)
95 + {
96 + hr = StrAllocConcat(psczCommandLine, L" ", 0);
97 + ExitOnFailure(hr, "Failed to append space to command line with existing data.");
98 + }
99 +
100 + hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0);
101 + ExitOnFailure(hr, "Failed to copy command line argument.");
102 +
103 +LExit:
104 + ReleaseStr(sczQuotedArg);
105 +
106 + return hr;
107 +}
108 +
109 +
110 +DAPI_(LPWSTR) PathFile(
111 + __in_z LPCWSTR wzPath
112 + )
113 +{
114 + if (!wzPath)
115 + {
116 + return NULL;
117 + }
118 +
119 + LPWSTR wzFile = const_cast<LPWSTR>(wzPath);
120 + for (LPWSTR wz = wzFile; *wz; ++wz)
121 + {
122 + // valid delineators
123 + // \ => Windows path
124 + // / => unix and URL path
125 + // : => relative path from mapped root
126 + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1))
127 + {
128 + wzFile = wz + 1;
129 + }
130 + }
131 +
132 + return wzFile;
133 +}
134 +
135 +
136 +DAPI_(LPCWSTR) PathExtension(
137 + __in_z LPCWSTR wzPath
138 + )
139 +{
140 + if (!wzPath)
141 + {
142 + return NULL;
143 + }
144 +
145 + // Find the last dot in the last thing that could be a file.
146 + LPCWSTR wzExtension = NULL;
147 + for (LPCWSTR wz = wzPath; *wz; ++wz)
148 + {
149 + if (L'\\' == *wz || L'/' == *wz || L':' == *wz)
150 + {
151 + wzExtension = NULL;
152 + }
153 + else if (L'.' == *wz)
154 + {
155 + wzExtension = wz;
156 + }
157 + }
158 +
159 + return wzExtension;
160 +}
161 +
162 +
163 +DAPI_(HRESULT) PathGetDirectory(
164 + __in_z LPCWSTR wzPath,
165 + __out LPWSTR *psczDirectory
166 + )
167 +{
168 + HRESULT hr = S_OK;
169 + DWORD cchDirectory = DWORD_MAX;
170 +
171 + for (LPCWSTR wz = wzPath; *wz; ++wz)
172 + {
173 + // valid delineators:
174 + // \ => Windows path
175 + // / => unix and URL path
176 + // : => relative path from mapped root
177 + if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1))
178 + {
179 + cchDirectory = static_cast<DWORD>(wz - wzPath) + 1;
180 + }
181 + }
182 +
183 + if (DWORD_MAX == cchDirectory)
184 + {
185 + // we were given just a file name, so there's no directory available
186 + return S_FALSE;
187 + }
188 +
189 + if (wzPath[0] == L'\"')
190 + {
191 + ++wzPath;
192 + --cchDirectory;
193 + }
194 +
195 + hr = StrAllocString(psczDirectory, wzPath, cchDirectory);
196 + ExitOnFailure(hr, "Failed to copy directory.");
197 +
198 +LExit:
199 + return hr;
200 +}
201 +
202 +
203 +DAPI_(HRESULT) PathExpand(
204 + __out LPWSTR *psczFullPath,
205 + __in_z LPCWSTR wzRelativePath,
206 + __in DWORD dwResolveFlags
207 + )
208 +{
209 + Assert(wzRelativePath && *wzRelativePath);
210 +
211 + HRESULT hr = S_OK;
212 + DWORD cch = 0;
213 + LPWSTR sczExpandedPath = NULL;
214 + DWORD cchExpandedPath = 0;
215 +
216 + LPWSTR sczFullPath = NULL;
217 +
218 + //
219 + // First, expand any environment variables.
220 + //
221 + if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT)
222 + {
223 + cchExpandedPath = PATH_GOOD_ENOUGH;
224 +
225 + hr = StrAlloc(&sczExpandedPath, cchExpandedPath);
226 + ExitOnFailure(hr, "Failed to allocate space for expanded path.");
227 +
228 + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath);
229 + if (0 == cch)
230 + {
231 + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
232 + }
233 + else if (cchExpandedPath < cch)
234 + {
235 + cchExpandedPath = cch;
236 + hr = StrAlloc(&sczExpandedPath, cchExpandedPath);
237 + ExitOnFailure(hr, "Failed to re-allocate more space for expanded path.");
238 +
239 + cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath);
240 + if (0 == cch)
241 + {
242 + ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
243 + }
244 + else if (cchExpandedPath < cch)
245 + {
246 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
247 + ExitOnRootFailure(hr, "Failed to allocate buffer for expanded path.");
248 + }
249 + }
250 +
251 + if (MAX_PATH < cch)
252 + {
253 + hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet
254 + if (E_INVALIDARG == hr)
255 + {
256 + hr = S_OK;
257 + }
258 + ExitOnFailure(hr, "Failed to prefix long path after expanding environment variables.");
259 +
260 + hr = StrMaxLength(sczExpandedPath, reinterpret_cast<DWORD_PTR *>(&cchExpandedPath));
261 + ExitOnFailure(hr, "Failed to get max length of expanded path.");
262 + }
263 + }
264 +
265 + //
266 + // Second, get the full path.
267 + //
268 + if (dwResolveFlags & PATH_EXPAND_FULLPATH)
269 + {
270 + LPWSTR wzFileName = NULL;
271 + LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath;
272 + DWORD cchFullPath = PATH_GOOD_ENOUGH < cchExpandedPath ? cchExpandedPath : PATH_GOOD_ENOUGH;
273 +
274 + hr = StrAlloc(&sczFullPath, cchFullPath);
275 + ExitOnFailure(hr, "Failed to allocate space for full path.");
276 +
277 + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName);
278 + if (0 == cch)
279 + {
280 + ExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath);
281 + }
282 + else if (cchFullPath < cch)
283 + {
284 + cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed
285 + hr = StrAlloc(&sczFullPath, cchFullPath);
286 + ExitOnFailure(hr, "Failed to re-allocate more space for full path.");
287 +
288 + cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName);
289 + if (0 == cch)
290 + {
291 + ExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath);
292 + }
293 + else if (cchFullPath < cch)
294 + {
295 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
296 + ExitOnRootFailure(hr, "Failed to allocate buffer for full path.");
297 + }
298 + }
299 +
300 + if (MAX_PATH < cch)
301 + {
302 + hr = PathPrefix(&sczFullPath);
303 + ExitOnFailure(hr, "Failed to prefix long path after expanding.");
304 + }
305 + }
306 + else
307 + {
308 + sczFullPath = sczExpandedPath;
309 + sczExpandedPath = NULL;
310 + }
311 +
312 + hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0);
313 + ExitOnFailure(hr, "Failed to copy relative path into full path.");
314 +
315 +LExit:
316 + ReleaseStr(sczFullPath);
317 + ReleaseStr(sczExpandedPath);
318 +
319 + return hr;
320 +}
321 +
322 +
323 +DAPI_(HRESULT) PathPrefix(
324 + __inout LPWSTR *psczFullPath
325 + )
326 +{
327 + Assert(psczFullPath && *psczFullPath);
328 +
329 + HRESULT hr = S_OK;
330 + LPWSTR wzFullPath = *psczFullPath;
331 + DWORD_PTR cbFullPath = 0;
332 +
333 + if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) ||
334 + (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) &&
335 + L':' == wzFullPath[1] &&
336 + L'\\' == wzFullPath[2]) // normal path
337 + {
338 + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4);
339 + ExitOnFailure(hr, "Failed to add prefix to file path.");
340 + }
341 + else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC
342 + {
343 + // ensure that we're not already prefixed
344 + if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3]))
345 + {
346 + hr = StrSize(*psczFullPath, &cbFullPath);
347 + ExitOnFailure(hr, "Failed to get size of full path.");
348 +
349 + memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR));
350 +
351 + hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7);
352 + ExitOnFailure(hr, "Failed to add prefix to UNC path.");
353 + }
354 + }
355 + else
356 + {
357 + hr = E_INVALIDARG;
358 + ExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath);
359 + }
360 +
361 +LExit:
362 + return hr;
363 +}
364 +
365 +
366 +DAPI_(HRESULT) PathFixedBackslashTerminate(
367 + __inout_ecount_z(cchPath) LPWSTR wzPath,
368 + __in DWORD_PTR cchPath
369 + )
370 +{
371 + HRESULT hr = S_OK;
372 + size_t cchLength = 0;
373 +
374 + hr = ::StringCchLengthW(wzPath, cchPath, &cchLength);
375 + ExitOnFailure(hr, "Failed to get length of path.");
376 +
377 + if (cchLength >= cchPath)
378 + {
379 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
380 + }
381 + else if (L'\\' != wzPath[cchLength - 1])
382 + {
383 + wzPath[cchLength] = L'\\';
384 + wzPath[cchLength + 1] = L'\0';
385 + }
386 +
387 +LExit:
388 + return hr;
389 +}
390 +
391 +
392 +DAPI_(HRESULT) PathBackslashTerminate(
393 + __inout LPWSTR* psczPath
394 + )
395 +{
396 + Assert(psczPath && *psczPath);
397 +
398 + HRESULT hr = S_OK;
399 + DWORD_PTR cchPath = 0;
400 + size_t cchLength = 0;
401 +
402 + hr = StrMaxLength(*psczPath, &cchPath);
403 + ExitOnFailure(hr, "Failed to get size of path string.");
404 +
405 + hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength);
406 + ExitOnFailure(hr, "Failed to get length of path.");
407 +
408 + if (L'\\' != (*psczPath)[cchLength - 1])
409 + {
410 + hr = StrAllocConcat(psczPath, L"\\", 1);
411 + ExitOnFailure(hr, "Failed to concat backslash onto string.");
412 + }
413 +
414 +LExit:
415 + return hr;
416 +}
417 +
418 +
419 +DAPI_(HRESULT) PathForCurrentProcess(
420 + __inout LPWSTR *psczFullPath,
421 + __in_opt HMODULE hModule
422 + )
423 +{
424 + HRESULT hr = S_OK;
425 + DWORD cch = MAX_PATH;
426 +
427 + do
428 + {
429 + hr = StrAlloc(psczFullPath, cch);
430 + ExitOnFailure(hr, "Failed to allocate string for module path.");
431 +
432 + DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch);
433 + if (0 == cchRequired)
434 + {
435 + ExitWithLastError(hr, "Failed to get path for executing process.");
436 + }
437 + else if (cchRequired == cch)
438 + {
439 + cch = cchRequired + 1;
440 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
441 + }
442 + else
443 + {
444 + hr = S_OK;
445 + }
446 + } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr);
447 +
448 +LExit:
449 + return hr;
450 +}
451 +
452 +
453 +DAPI_(HRESULT) PathRelativeToModule(
454 + __inout LPWSTR *psczFullPath,
455 + __in_opt LPCWSTR wzFileName,
456 + __in_opt HMODULE hModule
457 + )
458 +{
459 + HRESULT hr = PathForCurrentProcess(psczFullPath, hModule);
460 + ExitOnFailure(hr, "Failed to get current module path.");
461 +
462 + hr = PathGetDirectory(*psczFullPath, psczFullPath);
463 + ExitOnFailure(hr, "Failed to get current module directory.");
464 +
465 + if (wzFileName)
466 + {
467 + hr = PathConcat(*psczFullPath, wzFileName, psczFullPath);
468 + ExitOnFailure(hr, "Failed to append filename.");
469 + }
470 +
471 +LExit:
472 + return hr;
473 +}
474 +
475 +
476 +DAPI_(HRESULT) PathCreateTempFile(
477 + __in_opt LPCWSTR wzDirectory,
478 + __in_opt __format_string LPCWSTR wzFileNameTemplate,
479 + __in DWORD dwUniqueCount,
480 + __in DWORD dwFileAttributes,
481 + __out_opt LPWSTR* psczTempFile,
482 + __out_opt HANDLE* phTempFile
483 + )
484 +{
485 + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count.");
486 +
487 + HRESULT hr = S_OK;
488 +
489 + LPWSTR sczTempPath = NULL;
490 + DWORD cchTempPath = MAX_PATH;
491 +
492 + HANDLE hTempFile = INVALID_HANDLE_VALUE;
493 + LPWSTR scz = NULL;
494 + LPWSTR sczTempFile = NULL;
495 +
496 + if (wzDirectory && *wzDirectory)
497 + {
498 + hr = StrAllocString(&sczTempPath, wzDirectory, 0);
499 + ExitOnFailure(hr, "Failed to copy temp path.");
500 + }
501 + else
502 + {
503 + hr = StrAlloc(&sczTempPath, cchTempPath);
504 + ExitOnFailure(hr, "Failed to allocate memory for the temp path.");
505 +
506 + if (!::GetTempPathW(cchTempPath, sczTempPath))
507 + {
508 + ExitWithLastError(hr, "Failed to get temp path.");
509 + }
510 + }
511 +
512 + if (wzFileNameTemplate && *wzFileNameTemplate)
513 + {
514 + for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i)
515 + {
516 + hr = StrAllocFormatted(&scz, wzFileNameTemplate, i);
517 + ExitOnFailure(hr, "Failed to allocate memory for file template.");
518 +
519 + hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz);
520 + ExitOnFailure(hr, "Failed to allocate temp file name.");
521 +
522 + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL);
523 + if (INVALID_HANDLE_VALUE == hTempFile)
524 + {
525 + // if the file already exists, just try again
526 + hr = HRESULT_FROM_WIN32(::GetLastError());
527 + if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
528 + {
529 + hr = S_OK;
530 + }
531 + ExitOnFailure(hr, "Failed to create file: %ls", sczTempFile);
532 + }
533 + }
534 + }
535 +
536 + // If we were not able to or we did not try to create a temp file, ask
537 + // the system to provide us a temp file using its built-in mechanism.
538 + if (INVALID_HANDLE_VALUE == hTempFile)
539 + {
540 + hr = StrAlloc(&sczTempFile, MAX_PATH);
541 + ExitOnFailure(hr, "Failed to allocate memory for the temp path");
542 +
543 + if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile))
544 + {
545 + ExitWithLastError(hr, "Failed to create new temp file name.");
546 + }
547 +
548 + hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL);
549 + if (INVALID_HANDLE_VALUE == hTempFile)
550 + {
551 + ExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile);
552 + }
553 + }
554 +
555 + // If the caller wanted the temp file name or handle, return them here.
556 + if (psczTempFile)
557 + {
558 + hr = StrAllocString(psczTempFile, sczTempFile, 0);
559 + ExitOnFailure(hr, "Failed to copy temp file string.");
560 + }
561 +
562 + if (phTempFile)
563 + {
564 + *phTempFile = hTempFile;
565 + hTempFile = INVALID_HANDLE_VALUE;
566 + }
567 +
568 +LExit:
569 + if (INVALID_HANDLE_VALUE != hTempFile)
570 + {
571 + ::CloseHandle(hTempFile);
572 + }
573 +
574 + ReleaseStr(scz);
575 + ReleaseStr(sczTempFile);
576 + ReleaseStr(sczTempPath);
577 +
578 + return hr;
579 +}
580 +
581 +
582 +DAPI_(HRESULT) PathCreateTimeBasedTempFile(
583 + __in_z_opt LPCWSTR wzDirectory,
584 + __in_z LPCWSTR wzPrefix,
585 + __in_z_opt LPCWSTR wzPostfix,
586 + __in_z LPCWSTR wzExtension,
587 + __deref_opt_out_z LPWSTR* psczTempFile,
588 + __out_opt HANDLE* phTempFile
589 + )
590 +{
591 + HRESULT hr = S_OK;
592 + BOOL fRetry = FALSE;
593 + WCHAR wzTempPath[MAX_PATH] = { };
594 + LPWSTR sczPrefix = NULL;
595 + LPWSTR sczPrefixFolder = NULL;
596 + SYSTEMTIME time = { };
597 +
598 + LPWSTR sczTempPath = NULL;
599 + HANDLE hTempFile = INVALID_HANDLE_VALUE;
600 + DWORD dwAttempts = 0;
601 +
602 + if (wzDirectory && *wzDirectory)
603 + {
604 + hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix);
605 + ExitOnFailure(hr, "Failed to combine directory and log prefix.");
606 + }
607 + else
608 + {
609 + if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
610 + {
611 + ExitWithLastError(hr, "Failed to get temp folder.");
612 + }
613 +
614 + hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix);
615 + ExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix.");
616 + }
617 +
618 + hr = PathGetDirectory(sczPrefix, &sczPrefixFolder);
619 + if (S_OK == hr)
620 + {
621 + hr = DirEnsureExists(sczPrefixFolder, NULL);
622 + ExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder);
623 + }
624 +
625 + if (!wzPostfix)
626 + {
627 + wzPostfix = L"";
628 + }
629 +
630 + do
631 + {
632 + fRetry = FALSE;
633 + ++dwAttempts;
634 +
635 + ::GetLocalTime(&time);
636 +
637 + // Log format: pre YYYY MM dd hh mm ss post ext
638 + hr = StrAllocFormatted(&sczTempPath, L"%ls_%04u%02u%02u%02u%02u%02u%ls%ls%ls", sczPrefix, time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, wzPostfix, L'.' == *wzExtension ? L"" : L".", wzExtension);
639 + ExitOnFailure(hr, "failed to allocate memory for the temp path");
640 +
641 + hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
642 + if (INVALID_HANDLE_VALUE == hTempFile)
643 + {
644 + // If the file already exists, just try again.
645 + DWORD er = ::GetLastError();
646 + if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er)
647 + {
648 + ::Sleep(100);
649 +
650 + if (10 > dwAttempts)
651 + {
652 + er = ERROR_SUCCESS;
653 + fRetry = TRUE;
654 + }
655 + }
656 +
657 + hr = HRESULT_FROM_WIN32(er);
658 + ExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath);
659 + }
660 + } while (fRetry);
661 +
662 + if (psczTempFile)
663 + {
664 + hr = StrAllocString(psczTempFile, sczTempPath, 0);
665 + ExitOnFailure(hr, "Failed to copy temp path to return.");
666 + }
667 +
668 + if (phTempFile)
669 + {
670 + *phTempFile = hTempFile;
671 + hTempFile = INVALID_HANDLE_VALUE;
672 + }
673 +
674 +LExit:
675 + ReleaseFile(hTempFile);
676 + ReleaseStr(sczTempPath);
677 + ReleaseStr(sczPrefixFolder);
678 + ReleaseStr(sczPrefix);
679 +
680 + return hr;
681 +}
682 +
683 +
684 +DAPI_(HRESULT) PathCreateTempDirectory(
685 + __in_opt LPCWSTR wzDirectory,
686 + __in __format_string LPCWSTR wzDirectoryNameTemplate,
687 + __in DWORD dwUniqueCount,
688 + __out LPWSTR* psczTempDirectory
689 + )
690 +{
691 + AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified.");
692 + AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count.");
693 +
694 + HRESULT hr = S_OK;
695 +
696 + LPWSTR sczTempPath = NULL;
697 + DWORD cchTempPath = MAX_PATH;
698 +
699 + LPWSTR scz = NULL;
700 +
701 + if (wzDirectory && *wzDirectory)
702 + {
703 + hr = StrAllocString(&sczTempPath, wzDirectory, 0);
704 + ExitOnFailure(hr, "Failed to copy temp path.");
705 +
706 + hr = PathBackslashTerminate(&sczTempPath);
707 + ExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory);
708 + }
709 + else
710 + {
711 + hr = StrAlloc(&sczTempPath, cchTempPath);
712 + ExitOnFailure(hr, "Failed to allocate memory for the temp path.");
713 +
714 + if (!::GetTempPathW(cchTempPath, sczTempPath))
715 + {
716 + ExitWithLastError(hr, "Failed to get temp path.");
717 + }
718 + }
719 +
720 + for (DWORD i = 1; i <= dwUniqueCount; ++i)
721 + {
722 + hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i);
723 + ExitOnFailure(hr, "Failed to allocate memory for directory name template.");
724 +
725 + hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz);
726 + ExitOnFailure(hr, "Failed to allocate temp directory name.");
727 +
728 + if (!::CreateDirectoryW(*psczTempDirectory, NULL))
729 + {
730 + DWORD er = ::GetLastError();
731 + if (ERROR_ALREADY_EXISTS == er)
732 + {
733 + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
734 + continue;
735 + }
736 + else if (ERROR_PATH_NOT_FOUND == er)
737 + {
738 + hr = DirEnsureExists(*psczTempDirectory, NULL);
739 + break;
740 + }
741 + else
742 + {
743 + hr = HRESULT_FROM_WIN32(er);
744 + break;
745 + }
746 + }
747 + else
748 + {
749 + hr = S_OK;
750 + break;
751 + }
752 + }
753 + ExitOnFailure(hr, "Failed to create temp directory.");
754 +
755 + hr = PathBackslashTerminate(psczTempDirectory);
756 + ExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated.");
757 +
758 +LExit:
759 + ReleaseStr(scz);
760 + ReleaseStr(sczTempPath);
761 +
762 + return hr;
763 +}
764 +
765 +
766 +DAPI_(HRESULT) PathGetKnownFolder(
767 + __in int csidl,
768 + __out LPWSTR* psczKnownFolder
769 + )
770 +{
771 + HRESULT hr = S_OK;
772 +
773 + hr = StrAlloc(psczKnownFolder, MAX_PATH);
774 + ExitOnFailure(hr, "Failed to allocate memory for known folder.");
775 +
776 + hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder);
777 + ExitOnFailure(hr, "Failed to get known folder path.");
778 +
779 + hr = PathBackslashTerminate(psczKnownFolder);
780 + ExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated.");
781 +
782 +LExit:
783 + return hr;
784 +}
785 +
786 +
787 +DAPI_(BOOL) PathIsAbsolute(
788 + __in_z LPCWSTR wzPath
789 + )
790 +{
791 + DWORD dwLength = lstrlenW(wzPath);
792 + return (1 < dwLength) && (wzPath[0] == L'\\') || (wzPath[1] == L':');
793 +}
794 +
795 +
796 +DAPI_(HRESULT) PathConcat(
797 + __in_opt LPCWSTR wzPath1,
798 + __in_opt LPCWSTR wzPath2,
799 + __deref_out_z LPWSTR* psczCombined
800 + )
801 +{
802 + HRESULT hr = S_OK;
803 +
804 + if (!wzPath2 || !*wzPath2)
805 + {
806 + hr = StrAllocString(psczCombined, wzPath1, 0);
807 + ExitOnFailure(hr, "Failed to copy just path1 to output.");
808 + }
809 + else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2))
810 + {
811 + hr = StrAllocString(psczCombined, wzPath2, 0);
812 + ExitOnFailure(hr, "Failed to copy just path2 to output.");
813 + }
814 + else
815 + {
816 + hr = StrAllocString(psczCombined, wzPath1, 0);
817 + ExitOnFailure(hr, "Failed to copy path1 to output.");
818 +
819 + hr = PathBackslashTerminate(psczCombined);
820 + ExitOnFailure(hr, "Failed to backslashify.");
821 +
822 + hr = StrAllocConcat(psczCombined, wzPath2, 0);
823 + ExitOnFailure(hr, "Failed to append path2 to output.");
824 + }
825 +
826 +LExit:
827 + return hr;
828 +}
829 +
830 +
831 +DAPI_(HRESULT) PathEnsureQuoted(
832 + __inout LPWSTR* ppszPath,
833 + __in BOOL fDirectory
834 + )
835 +{
836 + Assert(ppszPath && *ppszPath);
837 +
838 + HRESULT hr = S_OK;
839 + size_t cchPath = 0;
840 +
841 + hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath);
842 + ExitOnFailure(hr, "Failed to get the length of the path.");
843 +
844 + // Handle simple special cases.
845 + if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0]))
846 + {
847 + hr = StrAllocString(ppszPath, L"\"\"", 2);
848 + ExitOnFailure(hr, "Failed to allocate a quoted empty string.");
849 +
850 + ExitFunction();
851 + }
852 +
853 + if (L'"' != (*ppszPath)[0])
854 + {
855 + hr = StrAllocPrefix(ppszPath, L"\"", 1);
856 + ExitOnFailure(hr, "Failed to allocate an opening quote.");
857 +
858 + // Add a char for the opening quote.
859 + ++cchPath;
860 + }
861 +
862 + if (L'"' != (*ppszPath)[cchPath - 1])
863 + {
864 + hr = StrAllocConcat(ppszPath, L"\"", 1);
865 + ExitOnFailure(hr, "Failed to allocate a closing quote.");
866 +
867 + // Add a char for the closing quote.
868 + ++cchPath;
869 + }
870 +
871 + if (fDirectory)
872 + {
873 + if (L'\\' != (*ppszPath)[cchPath - 2])
874 + {
875 + // Change the last char to a backslash and re-append the closing quote.
876 + (*ppszPath)[cchPath - 1] = L'\\';
877 +
878 + hr = StrAllocConcat(ppszPath, L"\"", 1);
879 + ExitOnFailure(hr, "Failed to allocate another closing quote after the backslash.");
880 + }
881 + }
882 +
883 +LExit:
884 +
885 + return hr;
886 +}
887 +
888 +
889 +DAPI_(HRESULT) PathCompare(
890 + __in_z LPCWSTR wzPath1,
891 + __in_z LPCWSTR wzPath2,
892 + __out int* pnResult
893 + )
894 +{
895 + HRESULT hr = S_OK;
896 + LPWSTR sczPath1 = NULL;
897 + LPWSTR sczPath2 = NULL;
898 +
899 + hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
900 + ExitOnFailure(hr, "Failed to expand path1.");
901 +
902 + hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
903 + ExitOnFailure(hr, "Failed to expand path2.");
904 +
905 + *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1);
906 +
907 +LExit:
908 + ReleaseStr(sczPath2);
909 + ReleaseStr(sczPath1);
910 +
911 + return hr;
912 +}
913 +
914 +
915 +DAPI_(HRESULT) PathCompress(
916 + __in_z LPCWSTR wzPath
917 + )
918 +{
919 + HRESULT hr = S_OK;
920 + HANDLE hPath = INVALID_HANDLE_VALUE;
921 +
922 + hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
923 + if (INVALID_HANDLE_VALUE == hPath)
924 + {
925 + ExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath);
926 + }
927 +
928 + DWORD dwBytesReturned = 0;
929 + USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT;
930 + if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL))
931 + {
932 + // ignore compression attempts on file systems that don't support it
933 + DWORD er = ::GetLastError();
934 + if (ERROR_INVALID_FUNCTION != er)
935 + {
936 + ExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath);
937 + }
938 + }
939 +
940 +LExit:
941 + ReleaseFile(hPath);
942 +
943 + return hr;
944 +}
945 +
946 +DAPI_(HRESULT) PathGetHierarchyArray(
947 + __in_z LPCWSTR wzPath,
948 + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczPathArray,
949 + __inout LPUINT pcPathArray
950 + )
951 +{
952 + HRESULT hr = S_OK;
953 + LPWSTR sczPathCopy = NULL;
954 + LPWSTR sczNewPathCopy = NULL;
955 + DWORD cArraySpacesNeeded = 0;
956 +
957 + for (int i = 0; i < lstrlenW(wzPath); ++i)
958 + {
959 + if (wzPath[i] == L'\\')
960 + {
961 + ++cArraySpacesNeeded;
962 + }
963 + }
964 + if (wzPath[lstrlenW(wzPath) - 1] != L'\\')
965 + {
966 + ++cArraySpacesNeeded;
967 + }
968 +
969 + // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path.
970 + if (wzPath[0] == L'\\' && wzPath[1] == L'\\')
971 + {
972 + cArraySpacesNeeded -= 3;
973 + }
974 +
975 + Assert(cArraySpacesNeeded >= 1);
976 +
977 + hr = MemEnsureArraySize(reinterpret_cast<void **>(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0);
978 + ExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded);
979 + *pcPathArray = cArraySpacesNeeded;
980 +
981 + hr = StrAllocString(&sczPathCopy, wzPath, 0);
982 + ExitOnFailure(hr, "Failed to allocate copy of original path");
983 +
984 + for (DWORD i = 0; i < cArraySpacesNeeded; ++i)
985 + {
986 + hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0);
987 + ExitOnFailure(hr, "Failed to copy path");
988 +
989 + // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path
990 + if (wzPath[lstrlenW(sczPathCopy) - 1] == L'\\')
991 + {
992 + sczPathCopy[lstrlenW(sczPathCopy) - 1] = L'\0';
993 + }
994 +
995 + hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy);
996 + ExitOnFailure(hr, "Failed to get directory portion of path");
997 +
998 + ReleaseStr(sczPathCopy);
999 + sczPathCopy = sczNewPathCopy;
1000 + sczNewPathCopy = NULL;
1001 + }
1002 +
1003 + hr = S_OK;
1004 +
1005 +LExit:
1006 + ReleaseStr(sczPathCopy);
1007 +
1008 + return hr;
1009 +}
src/dutil/perfutil.cpp new
+67
@@ -0,0 +1,67 @@
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 +
5 +static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter
6 +static double vdFrequency = 1;
7 +
8 +
9 +/********************************************************************
10 + PerfInitialize - initializes internal static variables
11 +
12 +********************************************************************/
13 +extern "C" void DAPI PerfInitialize(
14 + )
15 +{
16 + LARGE_INTEGER liFrequency = { };
17 +
18 + //
19 + // check for high perf counter
20 + //
21 + if (!::QueryPerformanceFrequency(&liFrequency))
22 + {
23 + vfHighPerformanceCounter = FALSE;
24 + vdFrequency = 1000; // ticks are measured in milliseconds
25 + }
26 + else
27 + vdFrequency = static_cast<double>(liFrequency.QuadPart);
28 +}
29 +
30 +
31 +/********************************************************************
32 + PerfClickTime - resets the clicker, or returns elapsed time since last call
33 +
34 + NOTE: if pliElapsed is NULL, resets the elapsed time
35 + if pliElapsed is not NULL, returns perf number since last call to PerfClickTime()
36 +********************************************************************/
37 +extern "C" void DAPI PerfClickTime(
38 + __out_opt LARGE_INTEGER* pliElapsed
39 + )
40 +{
41 + static LARGE_INTEGER liStart = { };
42 + LARGE_INTEGER* pli = pliElapsed;
43 +
44 + if (!pli) // if elapsed time time was not requested, reset the start time
45 + pli = &liStart;
46 +
47 + if (vfHighPerformanceCounter)
48 + ::QueryPerformanceCounter(pli);
49 + else
50 + pli->QuadPart = ::GetTickCount();
51 +
52 + if (pliElapsed)
53 + pliElapsed->QuadPart -= liStart.QuadPart;
54 +}
55 +
56 +
57 +/********************************************************************
58 + PerfConvertToSeconds - converts perf number to seconds
59 +
60 +********************************************************************/
61 +extern "C" double DAPI PerfConvertToSeconds(
62 + __in const LARGE_INTEGER* pli
63 + )
64 +{
65 + Assert(0 < vdFrequency);
66 + return pli->QuadPart / vdFrequency;
67 +}
src/dutil/polcutil.cpp new
+111
@@ -0,0 +1,111 @@
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 +
5 +const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\";
6 +
7 +static HRESULT OpenPolicyKey(
8 + __in_z LPCWSTR wzPolicyPath,
9 + __out HKEY* phk
10 + );
11 +
12 +
13 +extern "C" HRESULT DAPI PolcReadNumber(
14 + __in_z LPCWSTR wzPolicyPath,
15 + __in_z LPCWSTR wzPolicyName,
16 + __in DWORD dwDefault,
17 + __out DWORD* pdw
18 + )
19 +{
20 + HRESULT hr = S_OK;
21 + HKEY hk = NULL;
22 +
23 + hr = OpenPolicyKey(wzPolicyPath, &hk);
24 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
25 + {
26 + ExitFunction1(hr = S_FALSE);
27 + }
28 + ExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath);
29 +
30 + hr = RegReadNumber(hk, wzPolicyName, pdw);
31 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
32 + {
33 + ExitFunction1(hr = S_FALSE);
34 + }
35 + ExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName);
36 +
37 +LExit:
38 + ReleaseRegKey(hk);
39 +
40 + if (S_FALSE == hr || FAILED(hr))
41 + {
42 + *pdw = dwDefault;
43 + }
44 +
45 + return hr;
46 +}
47 +
48 +extern "C" HRESULT DAPI PolcReadString(
49 + __in_z LPCWSTR wzPolicyPath,
50 + __in_z LPCWSTR wzPolicyName,
51 + __in_z_opt LPCWSTR wzDefault,
52 + __deref_out_z LPWSTR* pscz
53 + )
54 +{
55 + HRESULT hr = S_OK;
56 + HKEY hk = NULL;
57 +
58 + hr = OpenPolicyKey(wzPolicyPath, &hk);
59 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
60 + {
61 + ExitFunction1(hr = S_FALSE);
62 + }
63 + ExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath);
64 +
65 + hr = RegReadString(hk, wzPolicyName, pscz);
66 + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
67 + {
68 + ExitFunction1(hr = S_FALSE);
69 + }
70 + ExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName);
71 +
72 +LExit:
73 + ReleaseRegKey(hk);
74 +
75 + if (S_FALSE == hr || FAILED(hr))
76 + {
77 + if (NULL == wzDefault)
78 + {
79 + ReleaseNullStr(*pscz);
80 + }
81 + else
82 + {
83 + hr = StrAllocString(pscz, wzDefault, 0);
84 + }
85 + }
86 +
87 + return hr;
88 +}
89 +
90 +
91 +// internal functions
92 +
93 +static HRESULT OpenPolicyKey(
94 + __in_z LPCWSTR wzPolicyPath,
95 + __out HKEY* phk
96 + )
97 +{
98 + HRESULT hr = S_OK;
99 + LPWSTR sczPath = NULL;
100 +
101 + hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath);
102 + ExitOnFailure(hr, "Failed to combine logging path with root path.");
103 +
104 + hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk);
105 + ExitOnFailure(hr, "Failed to open policy registry key.");
106 +
107 +LExit:
108 + ReleaseStr(sczPath);
109 +
110 + return hr;
111 +}
src/dutil/precomp.h new
+95
@@ -0,0 +1,95 @@
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 +#ifndef _WIN32_WINNT
6 +#define _WIN32_WINNT 0x0500
7 +#endif
8 +
9 +#ifndef _WIN32_MSI
10 +#define _WIN32_MSI 200
11 +#endif
12 +
13 +#define JET_VERSION 0x0501
14 +
15 +#include <WinSock2.h>
16 +#include <windows.h>
17 +#include <windowsx.h>
18 +#include <intsafe.h>
19 +#include <strsafe.h>
20 +#include <wininet.h>
21 +#include <msi.h>
22 +#include <msiquery.h>
23 +#include <psapi.h>
24 +#include <shlobj.h>
25 +#include <shlwapi.h>
26 +#include <gdiplus.h>
27 +#include <Tlhelp32.h>
28 +#include <lm.h>
29 +#include <Iads.h>
30 +#include <activeds.h>
31 +#include <richedit.h>
32 +#include <stddef.h>
33 +#include <esent.h>
34 +#include <ahadmin.h>
35 +#include <SRRestorePtAPI.h>
36 +#include <userenv.h>
37 +#include <WinIoCtl.h>
38 +#include <wtsapi32.h>
39 +#include <wuapi.h>
40 +#include <commctrl.h>
41 +#include <dbt.h>
42 +
43 +#include "dutil.h"
44 +#include "aclutil.h"
45 +#include "atomutil.h"
46 +#include "buffutil.h"
47 +#include "butil.h"
48 +#include "cabcutil.h"
49 +#include "cabutil.h"
50 +#include "conutil.h"
51 +#include "cryputil.h"
52 +#include "eseutil.h"
53 +#include "dirutil.h"
54 +#include "dlutil.h"
55 +#include "fileutil.h"
56 +#include "guidutil.h"
57 +#include "gdiputil.h"
58 +#include "dictutil.h"
59 +#include "inetutil.h"
60 +#include "iniutil.h"
61 +#include "jsonutil.h"
62 +#include "locutil.h"
63 +#include "logutil.h"
64 +#include "memutil.h" // NOTE: almost everying is inlined so there is a small .cpp file
65 +//#include "metautil.h" - see metautil.cpp why this *must* be commented out
66 +#include "monutil.h"
67 +#include "osutil.h"
68 +#include "pathutil.h"
69 +#include "perfutil.h"
70 +#include "polcutil.h"
71 +#include "procutil.h"
72 +#include "regutil.h"
73 +#include "resrutil.h"
74 +#include "reswutil.h"
75 +#include "rmutil.h"
76 +#include "rssutil.h"
77 +#include "apuputil.h" // NOTE: this must come after atomutil.h and rssutil.h since it uses them.
78 +#include "shelutil.h"
79 +//#include "sqlutil.h" - see sqlutil.cpp why this *must* be commented out
80 +#include "srputil.h"
81 +#include "strutil.h"
82 +#include "timeutil.h"
83 +#include "timeutil.h"
84 +#include "thmutil.h"
85 +#include "uncutil.h"
86 +#include "uriutil.h"
87 +#include "userutil.h"
88 +#include "varutil.h"
89 +#include "condutil.h" // NOTE: This must come after varutil.h since it uses it.
90 +#include "wiutil.h"
91 +#include "wuautil.h"
92 +#include <comutil.h> // This header is needed for msxml2.h to compile correctly
93 +#include <msxml2.h> // This file is needed to include xmlutil.h
94 +#include "xmlutil.h"
95 +
src/dutil/proc2utl.cpp new
+68
@@ -0,0 +1,68 @@
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 +
5 +/********************************************************************
6 + ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable.
7 +
8 +*******************************************************************/
9 +extern "C" HRESULT DAPI ProcFindAllIdsFromExeName(
10 + __in_z LPCWSTR wzExeName,
11 + __out DWORD** ppdwProcessIds,
12 + __out DWORD* pcProcessIds
13 + )
14 +{
15 + HRESULT hr = S_OK;
16 + DWORD er = ERROR_SUCCESS;
17 + HANDLE hSnap = INVALID_HANDLE_VALUE;
18 + BOOL fContinue = FALSE;
19 + PROCESSENTRY32W peData = { sizeof(peData) };
20 +
21 + hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
22 + if (INVALID_HANDLE_VALUE == hSnap)
23 + {
24 + ExitWithLastError(hr, "Failed to create snapshot of processes on system");
25 + }
26 +
27 + fContinue = ::Process32FirstW(hSnap, &peData);
28 +
29 + while (fContinue)
30 + {
31 + if (0 == lstrcmpiW((LPCWSTR)&(peData.szExeFile), wzExeName))
32 + {
33 + if (!*ppdwProcessIds)
34 + {
35 + *ppdwProcessIds = static_cast<DWORD*>(MemAlloc(sizeof(DWORD), TRUE));
36 + ExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs.");
37 + }
38 + else
39 + {
40 + DWORD* pdwReAllocReturnedPids = NULL;
41 + pdwReAllocReturnedPids = static_cast<DWORD*>(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE));
42 + ExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs.");
43 +
44 + *ppdwProcessIds = pdwReAllocReturnedPids;
45 + }
46 +
47 + (*ppdwProcessIds)[*pcProcessIds] = peData.th32ProcessID;
48 + ++(*pcProcessIds);
49 + }
50 +
51 + fContinue = ::Process32NextW(hSnap, &peData);
52 + }
53 +
54 + er = ::GetLastError();
55 + if (ERROR_NO_MORE_FILES == er)
56 + {
57 + hr = S_OK;
58 + }
59 + else
60 + {
61 + hr = HRESULT_FROM_WIN32(er);
62 + }
63 +
64 +LExit:
65 + ReleaseFile(hSnap);
66 +
67 + return hr;
68 +}
src/dutil/proc3utl.cpp new
+114
@@ -0,0 +1,114 @@
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 +
5 +static HRESULT GetActiveSessionUserToken(
6 + __out HANDLE *phToken
7 + );
8 +
9 +
10 +/********************************************************************
11 + ProcExecuteAsInteractiveUser() - runs process as currently logged in
12 + user.
13 +*******************************************************************/
14 +extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser(
15 + __in_z LPCWSTR wzExecutablePath,
16 + __in_z LPCWSTR wzCommandLine,
17 + __out HANDLE *phProcess
18 + )
19 +{
20 + HRESULT hr = S_OK;
21 + HANDLE hToken = NULL;
22 + LPVOID pEnvironment = NULL;
23 + LPWSTR sczFullCommandLine = NULL;
24 + STARTUPINFOW si = { };
25 + PROCESS_INFORMATION pi = { };
26 +
27 + hr = GetActiveSessionUserToken(&hToken);
28 + ExitOnFailure(hr, "Failed to get active session user token.");
29 +
30 + if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE))
31 + {
32 + ExitWithLastError(hr, "Failed to create environment block for UI process.");
33 + }
34 +
35 + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine);
36 + ExitOnFailure(hr, "Failed to allocate full command-line.");
37 +
38 + si.cb = sizeof(si);
39 + if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi))
40 + {
41 + ExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine);
42 + }
43 +
44 + *phProcess = pi.hProcess;
45 + pi.hProcess = NULL;
46 +
47 +LExit:
48 + ReleaseHandle(pi.hThread);
49 + ReleaseHandle(pi.hProcess);
50 + ReleaseStr(sczFullCommandLine);
51 +
52 + if (pEnvironment)
53 + {
54 + ::DestroyEnvironmentBlock(pEnvironment);
55 + }
56 +
57 + ReleaseHandle(hToken);
58 +
59 + return hr;
60 +}
61 +
62 +
63 +static HRESULT GetActiveSessionUserToken(
64 + __out HANDLE *phToken
65 + )
66 +{
67 + HRESULT hr = S_OK;
68 + PWTS_SESSION_INFO pSessionInfo = NULL;
69 + DWORD cSessions = 0;
70 + DWORD dwSessionId = 0;
71 + BOOL fSessionFound = FALSE;
72 + HANDLE hToken = NULL;
73 +
74 + // Loop through the sessions looking for the active one.
75 + if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions))
76 + {
77 + ExitWithLastError(hr, "Failed to enumerate sessions.");
78 + }
79 +
80 + for (DWORD i = 0; i < cSessions; ++i)
81 + {
82 + if (WTSActive == pSessionInfo[i].State)
83 + {
84 + dwSessionId = pSessionInfo[i].SessionId;
85 + fSessionFound = TRUE;
86 +
87 + break;
88 + }
89 + }
90 +
91 + if (!fSessionFound)
92 + {
93 + ExitFunction1(hr = E_NOTFOUND);
94 + }
95 +
96 + // Get the user token from the active session.
97 + if (!::WTSQueryUserToken(dwSessionId, &hToken))
98 + {
99 + ExitWithLastError(hr, "Failed to get active session user token.");
100 + }
101 +
102 + *phToken = hToken;
103 + hToken = NULL;
104 +
105 +LExit:
106 + ReleaseHandle(hToken);
107 +
108 + if (pSessionInfo)
109 + {
110 + ::WTSFreeMemory(pSessionInfo);
111 + }
112 +
113 + return hr;
114 +}
src/dutil/procutil.cpp new
+488
@@ -0,0 +1,488 @@
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 +
5 +
6 +// private functions
7 +static HRESULT CreatePipes(
8 + __out HANDLE *phOutRead,
9 + __out HANDLE *phOutWrite,
10 + __out HANDLE *phErrWrite,
11 + __out HANDLE *phInRead,
12 + __out HANDLE *phInWrite
13 + );
14 +
15 +static BOOL CALLBACK CloseWindowEnumCallback(
16 + __in HWND hWnd,
17 + __in LPARAM lParam
18 + );
19 +
20 +
21 +extern "C" HRESULT DAPI ProcElevated(
22 + __in HANDLE hProcess,
23 + __out BOOL* pfElevated
24 + )
25 +{
26 + HRESULT hr = S_OK;
27 + HANDLE hToken = NULL;
28 + TOKEN_ELEVATION tokenElevated = { };
29 + DWORD cbToken = 0;
30 +
31 + if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
32 + {
33 + ExitWithLastError(hr, "Failed to open process token.");
34 + }
35 +
36 + if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken))
37 + {
38 + *pfElevated = (0 != tokenElevated.TokenIsElevated);
39 + }
40 + else
41 + {
42 + DWORD er = ::GetLastError();
43 + hr = HRESULT_FROM_WIN32(er);
44 +
45 + // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated.
46 + if (E_INVALIDARG == hr)
47 + {
48 + *pfElevated = FALSE;
49 + hr = S_OK;
50 + }
51 + else
52 + {
53 + ExitOnRootFailure(hr, "Failed to get elevation token from process.");
54 + }
55 + }
56 +
57 +LExit:
58 + ReleaseHandle(hToken);
59 +
60 + return hr;
61 +}
62 +
63 +extern "C" HRESULT DAPI ProcWow64(
64 + __in HANDLE hProcess,
65 + __out BOOL* pfWow64
66 + )
67 +{
68 + HRESULT hr = S_OK;
69 + BOOL fIsWow64 = FALSE;
70 +
71 + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
72 + LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process");
73 +
74 + if (pfnIsWow64Process)
75 + {
76 + if (!pfnIsWow64Process(hProcess, &fIsWow64))
77 + {
78 + ExitWithLastError(hr, "Failed to check WOW64 process.");
79 + }
80 + }
81 +
82 + *pfWow64 = fIsWow64;
83 +
84 +LExit:
85 + return hr;
86 +}
87 +
88 +extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection(
89 + __in PROC_FILESYSTEMREDIRECTION* pfsr
90 + )
91 +{
92 + AssertSz(!pfsr->fDisabled, "File system redirection was already disabled.");
93 + HRESULT hr = S_OK;
94 +
95 + typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *);
96 + LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection");
97 +
98 + if (!pfnWow64DisableWow64FsRedirection)
99 + {
100 + ExitFunction1(hr = E_NOTIMPL);
101 + }
102 +
103 + if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState))
104 + {
105 + ExitWithLastError(hr, "Failed to disable file system redirection.");
106 + }
107 +
108 + pfsr->fDisabled = TRUE;
109 +
110 +LExit:
111 + return hr;
112 +}
113 +
114 +extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection(
115 + __in PROC_FILESYSTEMREDIRECTION* pfsr
116 + )
117 +{
118 + HRESULT hr = S_OK;
119 +
120 + if (pfsr->fDisabled)
121 + {
122 + typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID);
123 + LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection");
124 +
125 + if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState))
126 + {
127 + ExitWithLastError(hr, "Failed to revert file system redirection.");
128 + }
129 +
130 + pfsr->fDisabled = FALSE;
131 + pfsr->pvRevertState = NULL;
132 + }
133 +
134 +LExit:
135 + return hr;
136 +}
137 +
138 +
139 +extern "C" HRESULT DAPI ProcExec(
140 + __in_z LPCWSTR wzExecutablePath,
141 + __in_z_opt LPCWSTR wzCommandLine,
142 + __in int nCmdShow,
143 + __out HANDLE *phProcess
144 + )
145 +{
146 + HRESULT hr = S_OK;
147 + LPWSTR sczFullCommandLine = NULL;
148 + STARTUPINFOW si = { };
149 + PROCESS_INFORMATION pi = { };
150 +
151 + hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L"");
152 + ExitOnFailure(hr, "Failed to allocate full command-line.");
153 +
154 + si.cb = sizeof(si);
155 + si.wShowWindow = static_cast<WORD>(nCmdShow);
156 + if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi))
157 + {
158 + ExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine);
159 + }
160 +
161 + *phProcess = pi.hProcess;
162 + pi.hProcess = NULL;
163 +
164 +LExit:
165 + ReleaseHandle(pi.hThread);
166 + ReleaseHandle(pi.hProcess);
167 + ReleaseStr(sczFullCommandLine);
168 +
169 + return hr;
170 +}
171 +
172 +
173 +/********************************************************************
174 + ProcExecute() - executes a command-line.
175 +
176 +*******************************************************************/
177 +extern "C" HRESULT DAPI ProcExecute(
178 + __in_z LPWSTR wzCommand,
179 + __out HANDLE *phProcess,
180 + __out_opt HANDLE *phChildStdIn,
181 + __out_opt HANDLE *phChildStdOutErr
182 + )
183 +{
184 + HRESULT hr = S_OK;
185 +
186 + PROCESS_INFORMATION pi = { };
187 + STARTUPINFOW si = { };
188 +
189 + HANDLE hOutRead = INVALID_HANDLE_VALUE;
190 + HANDLE hOutWrite = INVALID_HANDLE_VALUE;
191 + HANDLE hErrWrite = INVALID_HANDLE_VALUE;
192 + HANDLE hInRead = INVALID_HANDLE_VALUE;
193 + HANDLE hInWrite = INVALID_HANDLE_VALUE;
194 +
195 + // Create redirect pipes.
196 + hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite);
197 + ExitOnFailure(hr, "failed to create output pipes");
198 +
199 + // Set up startup structure.
200 + si.cb = sizeof(STARTUPINFOW);
201 + si.dwFlags = STARTF_USESTDHANDLES;
202 + si.hStdInput = hInRead;
203 + si.hStdOutput = hOutWrite;
204 + si.hStdError = hErrWrite;
205 +
206 +#pragma prefast(push)
207 +#pragma prefast(disable:25028)
208 + if (::CreateProcessW(NULL,
209 + wzCommand, // command line
210 + NULL, // security info
211 + NULL, // thread info
212 + TRUE, // inherit handles
213 + ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags
214 + NULL, // environment
215 + NULL, // cur dir
216 + &si,
217 + &pi))
218 +#pragma prefast(pop)
219 + {
220 + // Close child process output/input handles so child doesn't hang
221 + // while waiting for input from parent process.
222 + ::CloseHandle(hOutWrite);
223 + hOutWrite = INVALID_HANDLE_VALUE;
224 +
225 + ::CloseHandle(hErrWrite);
226 + hErrWrite = INVALID_HANDLE_VALUE;
227 +
228 + ::CloseHandle(hInRead);
229 + hInRead = INVALID_HANDLE_VALUE;
230 + }
231 + else
232 + {
233 + ExitWithLastError(hr, "Process failed to execute.");
234 + }
235 +
236 + *phProcess = pi.hProcess;
237 + pi.hProcess = 0;
238 +
239 + if (phChildStdIn)
240 + {
241 + *phChildStdIn = hInWrite;
242 + hInWrite = INVALID_HANDLE_VALUE;
243 + }
244 +
245 + if (phChildStdOutErr)
246 + {
247 + *phChildStdOutErr = hOutRead;
248 + hOutRead = INVALID_HANDLE_VALUE;
249 + }
250 +
251 +LExit:
252 + if (pi.hThread)
253 + {
254 + ::CloseHandle(pi.hThread);
255 + }
256 +
257 + if (pi.hProcess)
258 + {
259 + ::CloseHandle(pi.hProcess);
260 + }
261 +
262 + ReleaseFileHandle(hOutRead);
263 + ReleaseFileHandle(hOutWrite);
264 + ReleaseFileHandle(hErrWrite);
265 + ReleaseFileHandle(hInRead);
266 + ReleaseFileHandle(hInWrite);
267 +
268 + return hr;
269 +}
270 +
271 +
272 +/********************************************************************
273 + ProcWaitForCompletion() - waits for process to complete and gets return code.
274 +
275 +*******************************************************************/
276 +extern "C" HRESULT DAPI ProcWaitForCompletion(
277 + __in HANDLE hProcess,
278 + __in DWORD dwTimeout,
279 + __out DWORD *pReturnCode
280 + )
281 +{
282 + HRESULT hr = S_OK;
283 + DWORD er = ERROR_SUCCESS;
284 +
285 + // Wait for everything to finish
286 + er = ::WaitForSingleObject(hProcess, dwTimeout);
287 + if (WAIT_FAILED == er)
288 + {
289 + ExitWithLastError(hr, "Failed to wait for process to complete.");
290 + }
291 + else if (WAIT_TIMEOUT == er)
292 + {
293 + ExitFunction1(hr = HRESULT_FROM_WIN32(er));
294 + }
295 +
296 + if (!::GetExitCodeProcess(hProcess, &er))
297 + {
298 + ExitWithLastError(hr, "Failed to get process return code.");
299 + }
300 +
301 + *pReturnCode = er;
302 +
303 +LExit:
304 + return hr;
305 +}
306 +
307 +/********************************************************************
308 + ProcWaitForIds() - waits for multiple processes to complete.
309 +
310 +*******************************************************************/
311 +extern "C" HRESULT DAPI ProcWaitForIds(
312 + __in_ecount(cProcessIds) const DWORD rgdwProcessIds[],
313 + __in DWORD cProcessIds,
314 + __in DWORD dwMilliseconds
315 + )
316 +{
317 + HRESULT hr = S_OK;
318 + DWORD er = ERROR_SUCCESS;
319 + HANDLE hProcess = NULL;
320 + HANDLE * rghProcesses = NULL;
321 + DWORD cProcesses = 0;
322 +
323 + rghProcesses = static_cast<HANDLE*>(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE));
324 + ExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles.");
325 +
326 + for (DWORD i = 0; i < cProcessIds; ++i)
327 + {
328 + hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]);
329 + if (hProcess != NULL)
330 + {
331 + rghProcesses[cProcesses++] = hProcess;
332 + }
333 + }
334 +
335 + er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds);
336 + if (WAIT_FAILED == er)
337 + {
338 + ExitWithLastError(hr, "Failed to wait for process to complete.");
339 + }
340 + else if (WAIT_TIMEOUT == er)
341 + {
342 + ExitOnWin32Error(er, hr, "Timed out while waiting for process to complete.");
343 + }
344 +
345 +LExit:
346 + if (rghProcesses)
347 + {
348 + for (DWORD i = 0; i < cProcesses; ++i)
349 + {
350 + if (NULL != rghProcesses[i])
351 + {
352 + ::CloseHandle(rghProcesses[i]);
353 + }
354 + }
355 +
356 + MemFree(rghProcesses);
357 + }
358 +
359 + return hr;
360 +}
361 +
362 +/********************************************************************
363 + ProcCloseIds() - sends WM_CLOSE messages to all process ids.
364 +
365 +*******************************************************************/
366 +extern "C" HRESULT DAPI ProcCloseIds(
367 + __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
368 + __in DWORD cProcessIds
369 + )
370 +{
371 + HRESULT hr = S_OK;
372 +
373 + for (DWORD i = 0; i < cProcessIds; ++i)
374 + {
375 + if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i]))
376 + {
377 + ExitWithLastError(hr, "Failed to enumerate windows.");
378 + }
379 + }
380 +
381 +LExit:
382 + return hr;
383 +}
384 +
385 +
386 +static HRESULT CreatePipes(
387 + __out HANDLE *phOutRead,
388 + __out HANDLE *phOutWrite,
389 + __out HANDLE *phErrWrite,
390 + __out HANDLE *phInRead,
391 + __out HANDLE *phInWrite
392 + )
393 +{
394 + HRESULT hr = S_OK;
395 + SECURITY_ATTRIBUTES sa;
396 + HANDLE hOutTemp = INVALID_HANDLE_VALUE;
397 + HANDLE hInTemp = INVALID_HANDLE_VALUE;
398 +
399 + HANDLE hOutRead = INVALID_HANDLE_VALUE;
400 + HANDLE hOutWrite = INVALID_HANDLE_VALUE;
401 + HANDLE hErrWrite = INVALID_HANDLE_VALUE;
402 + HANDLE hInRead = INVALID_HANDLE_VALUE;
403 + HANDLE hInWrite = INVALID_HANDLE_VALUE;
404 +
405 + // Fill out security structure so we can inherit handles
406 + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
407 + sa.nLength = sizeof(SECURITY_ATTRIBUTES);
408 + sa.bInheritHandle = TRUE;
409 + sa.lpSecurityDescriptor = NULL;
410 +
411 + // Create pipes
412 + if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0))
413 + {
414 + ExitWithLastError(hr, "failed to create output pipe");
415 + }
416 +
417 + if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0))
418 + {
419 + ExitWithLastError(hr, "failed to create input pipe");
420 + }
421 +
422 + // Duplicate output pipe so standard error and standard output write to the same pipe.
423 + if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
424 + {
425 + ExitWithLastError(hr, "failed to duplicate write handle");
426 + }
427 +
428 + // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in
429 + // the child process that can't be closed.
430 + if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
431 + {
432 + ExitWithLastError(hr, "failed to duplicate output pipe");
433 + }
434 +
435 + if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
436 + {
437 + ExitWithLastError(hr, "failed to duplicate input pipe");
438 + }
439 +
440 + // now that everything has succeeded, assign to the outputs
441 + *phOutRead = hOutRead;
442 + hOutRead = INVALID_HANDLE_VALUE;
443 +
444 + *phOutWrite = hOutWrite;
445 + hOutWrite = INVALID_HANDLE_VALUE;
446 +
447 + *phErrWrite = hErrWrite;
448 + hErrWrite = INVALID_HANDLE_VALUE;
449 +
450 + *phInRead = hInRead;
451 + hInRead = INVALID_HANDLE_VALUE;
452 +
453 + *phInWrite = hInWrite;
454 + hInWrite = INVALID_HANDLE_VALUE;
455 +
456 +LExit:
457 + ReleaseFileHandle(hOutRead);
458 + ReleaseFileHandle(hOutWrite);
459 + ReleaseFileHandle(hErrWrite);
460 + ReleaseFileHandle(hInRead);
461 + ReleaseFileHandle(hInWrite);
462 + ReleaseFileHandle(hOutTemp);
463 + ReleaseFileHandle(hInTemp);
464 +
465 + return hr;
466 +}
467 +
468 +
469 +/********************************************************************
470 + CloseWindowEnumCallback() - outputs trace and log info
471 +
472 +*******************************************************************/
473 +static BOOL CALLBACK CloseWindowEnumCallback(
474 + __in HWND hWnd,
475 + __in LPARAM lParam
476 + )
477 +{
478 + DWORD dwPid = static_cast<DWORD>(lParam);
479 + DWORD dwProcessId = 0;
480 +
481 + ::GetWindowThreadProcessId(hWnd, &dwProcessId);
482 + if (dwPid == dwProcessId)
483 + {
484 + ::SendMessageW(hWnd, WM_CLOSE, 0, 0);
485 + }
486 +
487 + return TRUE;
488 +}
src/dutil/regutil.cpp new
+926
@@ -0,0 +1,926 @@
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 +
5 +static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW;
6 +static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW;
7 +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL;
8 +static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL;
9 +static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW;
10 +static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW;
11 +static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW;
12 +static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW;
13 +static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW;
14 +static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW;
15 +static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW;
16 +
17 +static HMODULE vhAdvApi32Dll = NULL;
18 +static BOOL vfRegInitialized = FALSE;
19 +
20 +/********************************************************************
21 + RegInitialize - initializes regutil
22 +
23 +*********************************************************************/
24 +extern "C" HRESULT DAPI RegInitialize(
25 + )
26 +{
27 + HRESULT hr = S_OK;
28 +
29 + hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll);
30 + ExitOnFailure(hr, "Failed to load AdvApi32.dll");
31 +
32 + // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW
33 + vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW"));
34 +
35 + if (NULL == vpfnRegDeleteKeyExW)
36 + {
37 + vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary;
38 + }
39 +
40 + vfRegInitialized = TRUE;
41 +
42 +LExit:
43 + return hr;
44 +}
45 +
46 +
47 +/********************************************************************
48 + RegUninitialize - uninitializes regutil
49 +
50 +*********************************************************************/
51 +extern "C" void DAPI RegUninitialize(
52 + )
53 +{
54 + if (vhAdvApi32Dll)
55 + {
56 + ::FreeLibrary(vhAdvApi32Dll);
57 + vhAdvApi32Dll = NULL;
58 + vpfnRegDeleteKeyExWFromLibrary = NULL;
59 + vpfnRegDeleteKeyExW = NULL;
60 + }
61 +
62 + vfRegInitialized = FALSE;
63 +}
64 +
65 +
66 +/********************************************************************
67 + RegFunctionOverride - overrides the registry functions. Typically used
68 + for unit testing.
69 +
70 +*********************************************************************/
71 +extern "C" void DAPI RegFunctionOverride(
72 + __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW,
73 + __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW,
74 + __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW,
75 + __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW,
76 + __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW,
77 + __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
78 + __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
79 + __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
80 + __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW
81 + )
82 +{
83 + vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW;
84 + vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW;
85 + vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary;
86 + vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW;
87 + vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW;
88 + vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW;
89 + vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW;
90 + vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW;
91 + vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW;
92 +}
93 +
94 +
95 +/********************************************************************
96 + RegCreate - creates a registry key.
97 +
98 +*********************************************************************/
99 +extern "C" HRESULT DAPI RegCreate(
100 + __in HKEY hkRoot,
101 + __in_z LPCWSTR wzSubKey,
102 + __in DWORD dwAccess,
103 + __out HKEY* phk
104 + )
105 +{
106 + HRESULT hr = S_OK;
107 + DWORD er = ERROR_SUCCESS;
108 +
109 + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL);
110 + ExitOnWin32Error(er, hr, "Failed to create registry key.");
111 +
112 +LExit:
113 + return hr;
114 +}
115 +
116 +
117 +/********************************************************************
118 + RegCreate - creates a registry key with extra options.
119 +
120 +*********************************************************************/
121 +HRESULT DAPI RegCreateEx(
122 + __in HKEY hkRoot,
123 + __in_z LPCWSTR wzSubKey,
124 + __in DWORD dwAccess,
125 + __in BOOL fVolatile,
126 + __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes,
127 + __out HKEY* phk,
128 + __out_opt BOOL* pfCreated
129 + )
130 +{
131 + HRESULT hr = S_OK;
132 + DWORD er = ERROR_SUCCESS;
133 + DWORD dwDisposition;
134 +
135 + er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition);
136 + ExitOnWin32Error(er, hr, "Failed to create registry key.");
137 +
138 + if (pfCreated)
139 + {
140 + *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition);
141 + }
142 +
143 +LExit:
144 + return hr;
145 +}
146 +
147 +
148 +/********************************************************************
149 + RegOpen - opens a registry key.
150 +
151 +*********************************************************************/
152 +extern "C" HRESULT DAPI RegOpen(
153 + __in HKEY hkRoot,
154 + __in_z LPCWSTR wzSubKey,
155 + __in DWORD dwAccess,
156 + __out HKEY* phk
157 + )
158 +{
159 + HRESULT hr = S_OK;
160 + DWORD er = ERROR_SUCCESS;
161 +
162 + er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk);
163 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
164 + {
165 + ExitFunction1(hr = E_FILENOTFOUND);
166 + }
167 + ExitOnWin32Error(er, hr, "Failed to open registry key.");
168 +
169 +LExit:
170 + return hr;
171 +}
172 +
173 +
174 +/********************************************************************
175 + RegDelete - deletes a registry key (and optionally it's whole tree).
176 +
177 +*********************************************************************/
178 +extern "C" HRESULT DAPI RegDelete(
179 + __in HKEY hkRoot,
180 + __in_z LPCWSTR wzSubKey,
181 + __in REG_KEY_BITNESS kbKeyBitness,
182 + __in BOOL fDeleteTree
183 + )
184 +{
185 + HRESULT hr = S_OK;
186 + DWORD er = ERROR_SUCCESS;
187 + LPWSTR pszEnumeratedSubKey = NULL;
188 + LPWSTR pszRecursiveSubKey = NULL;
189 + HKEY hkKey = NULL;
190 + REGSAM samDesired = 0;
191 +
192 + if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness)
193 + {
194 + hr = E_INVALIDARG;
195 + ExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!");
196 + }
197 +
198 + switch (kbKeyBitness)
199 + {
200 + case REG_KEY_32BIT:
201 + samDesired = KEY_WOW64_32KEY;
202 + break;
203 + case REG_KEY_64BIT:
204 + samDesired = KEY_WOW64_64KEY;
205 + break;
206 + case REG_KEY_DEFAULT:
207 + // Nothing to do
208 + break;
209 + }
210 +
211 + if (fDeleteTree)
212 + {
213 + hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey);
214 + if (E_FILENOTFOUND == hr)
215 + {
216 + ExitFunction1(hr = S_OK);
217 + }
218 + ExitOnFailure(hr, "Failed to open this key for enumerating subkeys", wzSubKey);
219 +
220 + // Yes, keep enumerating the 0th item, because we're deleting it every time
221 + while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey)))
222 + {
223 + ExitOnFailure(hr, "Failed to enumerate key 0");
224 +
225 + hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey);
226 + ExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey);
227 +
228 + hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree);
229 + ExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey);
230 + }
231 +
232 + hr = S_OK;
233 + }
234 +
235 + if (NULL != vpfnRegDeleteKeyExW)
236 + {
237 + er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0);
238 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
239 + {
240 + ExitFunction1(hr = E_FILENOTFOUND);
241 + }
242 + ExitOnWin32Error(er, hr, "Failed to delete registry key (ex).");
243 + }
244 + else
245 + {
246 + er = vpfnRegDeleteKeyW(hkRoot, wzSubKey);
247 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
248 + {
249 + ExitFunction1(hr = E_FILENOTFOUND);
250 + }
251 + ExitOnWin32Error(er, hr, "Failed to delete registry key.");
252 + }
253 +
254 +LExit:
255 + ReleaseRegKey(hkKey);
256 + ReleaseStr(pszEnumeratedSubKey);
257 + ReleaseStr(pszRecursiveSubKey);
258 +
259 + return hr;
260 +}
261 +
262 +
263 +/********************************************************************
264 + RegKeyEnum - enumerates a registry key.
265 +
266 +*********************************************************************/
267 +extern "C" HRESULT DAPI RegKeyEnum(
268 + __in HKEY hk,
269 + __in DWORD dwIndex,
270 + __deref_out_z LPWSTR* psczKey
271 + )
272 +{
273 + HRESULT hr = S_OK;
274 + DWORD er = ERROR_SUCCESS;
275 + DWORD cch = 0;
276 +
277 + if (psczKey && *psczKey)
278 + {
279 + hr = StrMaxLength(*psczKey, reinterpret_cast<DWORD_PTR*>(&cch));
280 + ExitOnFailure(hr, "Failed to determine length of string.");
281 + }
282 +
283 + if (2 > cch)
284 + {
285 + cch = 2;
286 +
287 + hr = StrAlloc(psczKey, cch);
288 + ExitOnFailure(hr, "Failed to allocate string to minimum size.");
289 + }
290 +
291 + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL);
292 + if (ERROR_MORE_DATA == er)
293 + {
294 + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL);
295 + ExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key.");
296 +
297 + ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator.
298 + hr = StrAlloc(psczKey, cch);
299 + ExitOnFailure(hr, "Failed to allocate string bigger for enum registry key.");
300 +
301 + er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL);
302 + }
303 + else if (ERROR_NO_MORE_ITEMS == er)
304 + {
305 + ExitFunction1(hr = E_NOMOREITEMS);
306 + }
307 + ExitOnWin32Error(er, hr, "Failed to enum registry key.");
308 +
309 + // Always ensure the registry key name is null terminated.
310 +#pragma prefast(push)
311 +#pragma prefast(disable:26018)
312 + (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works.
313 +#pragma prefast(pop)
314 +
315 +LExit:
316 + return hr;
317 +}
318 +
319 +
320 +/********************************************************************
321 + RegValueEnum - enumerates a registry value.
322 +
323 +*********************************************************************/
324 +HRESULT DAPI RegValueEnum(
325 + __in HKEY hk,
326 + __in DWORD dwIndex,
327 + __deref_out_z LPWSTR* psczName,
328 + __out_opt DWORD *pdwType
329 + )
330 +{
331 + HRESULT hr = S_OK;
332 + DWORD er = ERROR_SUCCESS;
333 + DWORD cbValueName = 0;
334 +
335 + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL);
336 + ExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key.");
337 +
338 + // Add one for null terminator
339 + ++cbValueName;
340 +
341 + hr = StrAlloc(psczName, cbValueName);
342 + ExitOnFailure(hr, "Failed to allocate array for registry value name");
343 +
344 + er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL);
345 + if (ERROR_NO_MORE_ITEMS == er)
346 + {
347 + ExitFunction1(hr = E_NOMOREITEMS);
348 + }
349 + ExitOnWin32Error(er, hr, "Failed to enumerate registry value");
350 +
351 +LExit:
352 + return hr;
353 +}
354 +
355 +/********************************************************************
356 + RegGetType - reads a registry key value type.
357 + *********************************************************************/
358 +HRESULT DAPI RegGetType(
359 + __in HKEY hk,
360 + __in_z_opt LPCWSTR wzName,
361 + __out DWORD *pdwType
362 + )
363 +{
364 + HRESULT hr = S_OK;
365 + DWORD er = ERROR_SUCCESS;
366 +
367 + er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL);
368 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
369 + {
370 + ExitFunction1(hr = E_FILENOTFOUND);
371 + }
372 + ExitOnWin32Error(er, hr, "Failed to read registry value.");
373 +LExit:
374 +
375 + return hr;
376 +}
377 +
378 +/********************************************************************
379 + RegReadBinary - reads a registry key binary value.
380 + NOTE: caller is responsible for freeing *ppbBuffer
381 +*********************************************************************/
382 +HRESULT DAPI RegReadBinary(
383 + __in HKEY hk,
384 + __in_z_opt LPCWSTR wzName,
385 + __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
386 + __out SIZE_T *pcbBuffer
387 + )
388 +{
389 + HRESULT hr = S_OK;
390 + LPBYTE pbBuffer = NULL;
391 + DWORD er = ERROR_SUCCESS;
392 + DWORD cb = 0;
393 + DWORD dwType = 0;
394 +
395 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb);
396 + ExitOnWin32Error(er, hr, "Failed to get size of registry value.");
397 +
398 + // Zero-length binary values can exist
399 + if (0 < cb)
400 + {
401 + pbBuffer = static_cast<LPBYTE>(MemAlloc(cb, FALSE));
402 + ExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value.");
403 +
404 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb);
405 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
406 + {
407 + ExitFunction1(hr = E_FILENOTFOUND);
408 + }
409 + ExitOnWin32Error(er, hr, "Failed to read registry value.");
410 + }
411 +
412 + if (REG_BINARY == dwType)
413 + {
414 + *ppbBuffer = pbBuffer;
415 + pbBuffer = NULL;
416 + *pcbBuffer = cb;
417 + }
418 + else
419 + {
420 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
421 + ExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType);
422 + }
423 +
424 +LExit:
425 + ReleaseMem(pbBuffer);
426 +
427 + return hr;
428 +}
429 +
430 +
431 +/********************************************************************
432 + RegReadString - reads a registry key value as a string.
433 +
434 +*********************************************************************/
435 +extern "C" HRESULT DAPI RegReadString(
436 + __in HKEY hk,
437 + __in_z_opt LPCWSTR wzName,
438 + __deref_out_z LPWSTR* psczValue
439 + )
440 +{
441 + HRESULT hr = S_OK;
442 + DWORD er = ERROR_SUCCESS;
443 + DWORD cch = 0;
444 + DWORD cb = 0;
445 + DWORD dwType = 0;
446 + LPWSTR sczExpand = NULL;
447 +
448 + if (psczValue && *psczValue)
449 + {
450 + hr = StrMaxLength(*psczValue, reinterpret_cast<DWORD_PTR*>(&cch));
451 + ExitOnFailure(hr, "Failed to determine length of string.");
452 + }
453 +
454 + if (2 > cch)
455 + {
456 + cch = 2;
457 +
458 + hr = StrAlloc(psczValue, cch);
459 + ExitOnFailure(hr, "Failed to allocate string to minimum size.");
460 + }
461 +
462 + cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator.
463 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb);
464 + if (ERROR_MORE_DATA == er)
465 + {
466 + cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator
467 + hr = StrAlloc(psczValue, cch);
468 + ExitOnFailure(hr, "Failed to allocate string bigger for registry value.");
469 +
470 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb);
471 + }
472 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
473 + {
474 + ExitFunction1(hr = E_FILENOTFOUND);
475 + }
476 + ExitOnWin32Error(er, hr, "Failed to read registry key.");
477 +
478 + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
479 + {
480 + // Always ensure the registry value is null terminated.
481 + (*psczValue)[cch - 1] = L'\0';
482 +
483 + if (REG_EXPAND_SZ == dwType)
484 + {
485 + hr = StrAllocString(&sczExpand, *psczValue, 0);
486 + ExitOnFailure(hr, "Failed to copy registry value to expand.");
487 +
488 + hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT);
489 + ExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue);
490 + }
491 + }
492 + else
493 + {
494 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
495 + ExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType);
496 + }
497 +
498 +LExit:
499 + ReleaseStr(sczExpand);
500 +
501 + return hr;
502 +}
503 +
504 +
505 +/********************************************************************
506 + RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array.
507 +
508 +*********************************************************************/
509 +HRESULT DAPI RegReadStringArray(
510 + __in HKEY hk,
511 + __in_z_opt LPCWSTR wzName,
512 + __deref_out_ecount_opt(pcStrings) LPWSTR** prgsczStrings,
513 + __out DWORD *pcStrings
514 + )
515 +{
516 + HRESULT hr = S_OK;
517 + DWORD er = ERROR_SUCCESS;
518 + DWORD dwNullCharacters = 0;
519 + DWORD dwType = 0;
520 + DWORD cb = 0;
521 + DWORD cch = 0;
522 + LPCWSTR wzSource = NULL;
523 + LPWSTR sczValue = NULL;
524 +
525 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb);
526 + if (0 < cb)
527 + {
528 + cch = cb / sizeof(WCHAR);
529 + hr = StrAlloc(&sczValue, cch);
530 + ExitOnFailure(hr, "Failed to allocate string for registry value.");
531 +
532 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb);
533 + }
534 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
535 + {
536 + ExitFunction1(hr = E_FILENOTFOUND);
537 + }
538 + ExitOnWin32Error(er, hr, "Failed to read registry key.");
539 +
540 + if (cb / sizeof(WCHAR) != cch)
541 + {
542 + hr = E_UNEXPECTED;
543 + ExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName);
544 + }
545 +
546 + if (REG_MULTI_SZ != dwType)
547 + {
548 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
549 + ExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName);
550 + }
551 +
552 + // Value exists, but is empty, so no strings to return.
553 + if (2 > cch)
554 + {
555 + *prgsczStrings = NULL;
556 + *pcStrings = 0;
557 + ExitFunction1(hr = S_OK);
558 + }
559 +
560 + // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too.
561 + if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2])
562 + {
563 + hr = E_INVALIDARG;
564 + ExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName);
565 + }
566 +
567 + cch = cb / sizeof(WCHAR);
568 + for (DWORD i = 0; i < cch; ++i)
569 + {
570 + if (L'\0' == sczValue[i])
571 + {
572 + ++dwNullCharacters;
573 + }
574 + }
575 +
576 + // There's one string for every null character encountered (except the extra 1 at the end of the string)
577 + *pcStrings = dwNullCharacters - 1;
578 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0);
579 + ExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value");
580 +
581 +#pragma prefast(push)
582 +#pragma prefast(disable:26010)
583 + wzSource = sczValue;
584 + for (DWORD i = 0; i < *pcStrings; ++i)
585 + {
586 + hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0);
587 + ExitOnFailure(hr, "Failed to allocate copy of string");
588 +
589 + // Skip past this string
590 + wzSource += lstrlenW(wzSource) + 1;
591 + }
592 +#pragma prefast(pop)
593 +
594 +LExit:
595 + ReleaseStr(sczValue);
596 +
597 + return hr;
598 +}
599 +
600 +
601 +/********************************************************************
602 + RegReadVersion - reads a registry key value as a version.
603 +
604 +*********************************************************************/
605 +extern "C" HRESULT DAPI RegReadVersion(
606 + __in HKEY hk,
607 + __in_z_opt LPCWSTR wzName,
608 + __out DWORD64* pdw64Version
609 + )
610 +{
611 + HRESULT hr = S_OK;
612 + DWORD er = ERROR_SUCCESS;
613 + DWORD dwType = 0;
614 + DWORD cb = 0;
615 + LPWSTR sczVersion = NULL;
616 +
617 + cb = sizeof(DWORD64);
618 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*pdw64Version), &cb);
619 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
620 + {
621 + ExitFunction1(hr = E_FILENOTFOUND);
622 + }
623 + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
624 + {
625 + hr = RegReadString(hk, wzName, &sczVersion);
626 + ExitOnFailure(hr, "Failed to read registry version as string.");
627 +
628 + hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version);
629 + ExitOnFailure(hr, "Failed to convert registry string to version.");
630 + }
631 + else if (REG_QWORD == dwType)
632 + {
633 + ExitOnWin32Error(er, hr, "Failed to read registry key.");
634 + }
635 + else // unexpected data type
636 + {
637 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
638 + ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
639 + }
640 +
641 +LExit:
642 + ReleaseStr(sczVersion);
643 +
644 + return hr;
645 +}
646 +
647 +
648 +/********************************************************************
649 + RegReadNumber - reads a DWORD registry key value as a number.
650 +
651 +*********************************************************************/
652 +extern "C" HRESULT DAPI RegReadNumber(
653 + __in HKEY hk,
654 + __in_z_opt LPCWSTR wzName,
655 + __out DWORD* pdwValue
656 + )
657 +{
658 + HRESULT hr = S_OK;
659 + DWORD er = ERROR_SUCCESS;
660 + DWORD dwType = 0;
661 + DWORD cb = sizeof(DWORD);
662 +
663 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pdwValue), &cb);
664 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
665 + {
666 + ExitFunction1(hr = E_FILENOTFOUND);
667 + }
668 + ExitOnWin32Error(er, hr, "Failed to query registry key value.");
669 +
670 + if (REG_DWORD != dwType)
671 + {
672 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
673 + ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
674 + }
675 +
676 +LExit:
677 + return hr;
678 +}
679 +
680 +
681 +/********************************************************************
682 + RegReadQword - reads a QWORD registry key value as a number.
683 +
684 +*********************************************************************/
685 +extern "C" HRESULT DAPI RegReadQword(
686 + __in HKEY hk,
687 + __in_z_opt LPCWSTR wzName,
688 + __out DWORD64* pqwValue
689 + )
690 +{
691 + HRESULT hr = S_OK;
692 + DWORD er = ERROR_SUCCESS;
693 + DWORD dwType = 0;
694 + DWORD cb = sizeof(DWORD64);
695 +
696 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pqwValue), &cb);
697 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
698 + {
699 + ExitFunction1(hr = E_FILENOTFOUND);
700 + }
701 + ExitOnWin32Error(er, hr, "Failed to query registry key value.");
702 +
703 + if (REG_QWORD != dwType)
704 + {
705 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
706 + ExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
707 + }
708 +
709 +LExit:
710 + return hr;
711 +}
712 +
713 +
714 +/********************************************************************
715 + RegWriteBinary - writes a registry key value as a binary.
716 +
717 +*********************************************************************/
718 +HRESULT DAPI RegWriteBinary(
719 + __in HKEY hk,
720 + __in_z_opt LPCWSTR wzName,
721 + __in_bcount(cbBuffer) const BYTE *pbBuffer,
722 + __in DWORD cbBuffer
723 + )
724 +{
725 + HRESULT hr = S_OK;
726 + DWORD er = ERROR_SUCCESS;
727 +
728 + er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer);
729 + ExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName);
730 +
731 +LExit:
732 + return hr;
733 +}
734 +
735 +
736 +/********************************************************************
737 + RegWriteString - writes a registry key value as a string.
738 +
739 + Note: if wzValue is NULL the value will be removed.
740 +*********************************************************************/
741 +extern "C" HRESULT DAPI RegWriteString(
742 + __in HKEY hk,
743 + __in_z_opt LPCWSTR wzName,
744 + __in_z_opt LPCWSTR wzValue
745 + )
746 +{
747 + HRESULT hr = S_OK;
748 + DWORD er = ERROR_SUCCESS;
749 + DWORD cbValue = 0;
750 +
751 + if (wzValue)
752 + {
753 + hr = ::StringCbLengthW(wzValue, DWORD_MAX, reinterpret_cast<size_t*>(&cbValue));
754 + ExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName);
755 +
756 + er = vpfnRegSetValueExW(hk, wzName, 0, REG_SZ, reinterpret_cast<const BYTE *>(wzValue), cbValue);
757 + ExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName);
758 + }
759 + else
760 + {
761 + er = vpfnRegDeleteValueW(hk, wzName);
762 + if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er)
763 + {
764 + er = ERROR_SUCCESS;
765 + }
766 + ExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName);
767 + }
768 +
769 +LExit:
770 + return hr;
771 +}
772 +
773 +
774 +/********************************************************************
775 + RegWriteStringFormatted - writes a registry key value as a formatted string.
776 +
777 +*********************************************************************/
778 +extern "C" HRESULT DAPI RegWriteStringFormatted(
779 + __in HKEY hk,
780 + __in_z_opt LPCWSTR wzName,
781 + __in __format_string LPCWSTR szFormat,
782 + ...
783 + )
784 +{
785 + HRESULT hr = S_OK;
786 + LPWSTR sczValue = NULL;
787 + va_list args;
788 +
789 + va_start(args, szFormat);
790 + hr = StrAllocFormattedArgs(&sczValue, szFormat, args);
791 + va_end(args);
792 + ExitOnFailure(hr, "Failed to allocate %ls value.", wzName);
793 +
794 + hr = RegWriteString(hk, wzName, sczValue);
795 +
796 +LExit:
797 + ReleaseStr(sczValue);
798 +
799 + return hr;
800 +}
801 +
802 +
803 +/********************************************************************
804 + RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value
805 +
806 +*********************************************************************/
807 +HRESULT DAPI RegWriteStringArray(
808 + __in HKEY hk,
809 + __in_z_opt LPCWSTR wzName,
810 + __in_ecount(cValues) LPWSTR *rgwzValues,
811 + __in DWORD cValues
812 + )
813 +{
814 + HRESULT hr = S_OK;
815 + DWORD er = ERROR_SUCCESS;
816 + LPWSTR wzCopyDestination = NULL;
817 + LPCWSTR wzWriteValue = NULL;
818 + LPWSTR sczWriteValue = NULL;
819 + DWORD dwTotalStringSize = 0;
820 + DWORD cbTotalStringSize = 0;
821 + DWORD dwTemp = 0;
822 +
823 + if (0 == cValues)
824 + {
825 + wzWriteValue = L"\0";
826 + }
827 + else
828 + {
829 + // Add space for the null terminator
830 + dwTotalStringSize = 1;
831 +
832 + for (DWORD i = 0; i < cValues; ++i)
833 + {
834 + dwTemp = dwTotalStringSize;
835 + hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize);
836 + ExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ");
837 + }
838 +
839 + hr = StrAlloc(&sczWriteValue, dwTotalStringSize);
840 + ExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ");
841 +
842 + wzCopyDestination = sczWriteValue;
843 + dwTemp = dwTotalStringSize;
844 + for (DWORD i = 0; i < cValues; ++i)
845 + {
846 + hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]);
847 + ExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]);
848 +
849 + dwTemp -= lstrlenW(rgwzValues[i]) + 1;
850 + wzCopyDestination += lstrlenW(rgwzValues[i]) + 1;
851 + }
852 +
853 + wzWriteValue = sczWriteValue;
854 + }
855 +
856 + hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize);
857 + ExitOnFailure(hr, "Failed to get total string size in bytes");
858 +
859 + er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize);
860 + ExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue);
861 +
862 +LExit:
863 + ReleaseStr(sczWriteValue);
864 +
865 + return hr;
866 +}
867 +
868 +/********************************************************************
869 + RegWriteNumber - writes a registry key value as a number.
870 +
871 +*********************************************************************/
872 +extern "C" HRESULT DAPI RegWriteNumber(
873 + __in HKEY hk,
874 + __in_z_opt LPCWSTR wzName,
875 + __in DWORD dwValue
876 + )
877 +{
878 + HRESULT hr = S_OK;
879 + DWORD er = ERROR_SUCCESS;
880 +
881 + er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast<const BYTE *>(&dwValue), sizeof(dwValue));
882 + ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
883 +
884 +LExit:
885 + return hr;
886 +}
887 +
888 +/********************************************************************
889 + RegWriteQword - writes a registry key value as a Qword.
890 +
891 +*********************************************************************/
892 +extern "C" HRESULT DAPI RegWriteQword(
893 + __in HKEY hk,
894 + __in_z_opt LPCWSTR wzName,
895 + __in DWORD64 qwValue
896 + )
897 +{
898 + HRESULT hr = S_OK;
899 + DWORD er = ERROR_SUCCESS;
900 +
901 + er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast<const BYTE *>(&qwValue), sizeof(qwValue));
902 + ExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
903 +
904 +LExit:
905 + return hr;
906 +}
907 +
908 +/********************************************************************
909 + RegQueryKey - queries the key for the number of subkeys and values.
910 +
911 +*********************************************************************/
912 +extern "C" HRESULT DAPI RegQueryKey(
913 + __in HKEY hk,
914 + __out_opt DWORD* pcSubKeys,
915 + __out_opt DWORD* pcValues
916 + )
917 +{
918 + HRESULT hr = S_OK;
919 + DWORD er = ERROR_SUCCESS;
920 +
921 + er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL);
922 + ExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key.");
923 +
924 +LExit:
925 + return hr;
926 +}
src/dutil/resrutil.cpp new
+251
@@ -0,0 +1,251 @@
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 +
5 +#define RES_STRINGS_PER_BLOCK 16
6 +
7 +
8 +BOOL CALLBACK EnumLangIdProc(
9 + __in_opt HMODULE hModule,
10 + __in_z LPCSTR lpType,
11 + __in_z LPCSTR lpName,
12 + __in WORD wLanguage,
13 + __in LONG_PTR lParam
14 + );
15 +
16 +/********************************************************************
17 +ResGetStringLangId - get the language id for a string in the string table.
18 +
19 +********************************************************************/
20 +extern "C" HRESULT DAPI ResGetStringLangId(
21 + __in_opt LPCWSTR wzPath,
22 + __in UINT uID,
23 + __out WORD *pwLangId
24 + )
25 +{
26 + Assert(pwLangId);
27 +
28 + HRESULT hr = S_OK;
29 + HINSTANCE hModule = NULL;
30 + DWORD dwBlockId = (uID / RES_STRINGS_PER_BLOCK) + 1;
31 + WORD wFoundLangId = 0;
32 +
33 + if (wzPath && *wzPath)
34 + {
35 + hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
36 + ExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath);
37 + }
38 +
39 +#pragma prefast(push)
40 +#pragma prefast(disable:25068)
41 + if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast<ENUMRESLANGPROC>(EnumLangIdProc), reinterpret_cast<LONG_PTR>(&wFoundLangId)))
42 +#pragma prefast(pop)
43 + {
44 + ExitWithLastError(hr, "Failed to find string language identifier.");
45 + }
46 +
47 + *pwLangId = wFoundLangId;
48 +
49 +LExit:
50 + if (hModule)
51 + {
52 + ::FreeLibrary(hModule);
53 + }
54 +
55 + return hr;
56 +}
57 +
58 +
59 +/********************************************************************
60 +ResReadString
61 +
62 +NOTE: ppwzString should be freed with StrFree()
63 +********************************************************************/
64 +extern "C" HRESULT DAPI ResReadString(
65 + __in HINSTANCE hinst,
66 + __in UINT uID,
67 + __deref_out_z LPWSTR* ppwzString
68 + )
69 +{
70 + Assert(hinst && ppwzString);
71 +
72 + HRESULT hr = S_OK;
73 + DWORD cch = 64; // first guess
74 + DWORD cchReturned = 0;
75 +
76 + do
77 + {
78 + hr = StrAlloc(ppwzString, cch);
79 + ExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID);
80 +
81 + cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch);
82 + if (0 == cchReturned)
83 + {
84 + ExitWithLastError(hr, "Failed to load string resource id: %d", uID);
85 + }
86 +
87 + // if the returned string count is one character too small, it's likely we have
88 + // more data to read
89 + if (cchReturned + 1 == cch)
90 + {
91 + cch *= 2;
92 + hr = S_FALSE;
93 + }
94 + } while (S_FALSE == hr);
95 + ExitOnFailure(hr, "Failed to load string resource id: %d", uID);
96 +
97 +LExit:
98 + return hr;
99 +}
100 +
101 +
102 +/********************************************************************
103 + ResReadStringAnsi
104 +
105 + NOTE: ppszString should be freed with StrFree()
106 +********************************************************************/
107 +extern "C" HRESULT DAPI ResReadStringAnsi(
108 + __in HINSTANCE hinst,
109 + __in UINT uID,
110 + __deref_out_z LPSTR* ppszString
111 + )
112 +{
113 + Assert(hinst && ppszString);
114 +
115 + HRESULT hr = S_OK;
116 + DWORD cch = 64; // first guess
117 + DWORD cchReturned = 0;
118 +
119 + do
120 + {
121 + hr = StrAnsiAlloc(ppszString, cch);
122 + ExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID);
123 +
124 +#pragma prefast(push)
125 +#pragma prefast(disable:25068)
126 + cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch);
127 +#pragma prefast(pop)
128 + if (0 == cchReturned)
129 + {
130 + ExitWithLastError(hr, "Failed to load string resource id: %d", uID);
131 + }
132 +
133 + // if the returned string count is one character too small, it's likely we have
134 + // more data to read
135 + if (cchReturned + 1 == cch)
136 + {
137 + cch *= 2;
138 + hr = S_FALSE;
139 + }
140 + } while (S_FALSE == hr);
141 + ExitOnFailure(hr, "failed to load string resource id: %d", uID);
142 +
143 +LExit:
144 + return hr;
145 +}
146 +
147 +
148 +/********************************************************************
149 +ResReadData - returns a pointer to the specified resource data
150 +
151 +NOTE: there is no "free" function for this call
152 +********************************************************************/
153 +extern "C" HRESULT DAPI ResReadData(
154 + __in_opt HINSTANCE hinst,
155 + __in_z LPCSTR szDataName,
156 + __deref_out_bcount(*pcb) PVOID *ppv,
157 + __out DWORD *pcb
158 + )
159 +{
160 + Assert(szDataName);
161 + Assert(ppv);
162 +
163 + HRESULT hr = S_OK;
164 + HRSRC hRsrc = NULL;
165 + HGLOBAL hData = NULL;
166 + DWORD cbData = 0;
167 +
168 +#pragma prefast(push)
169 +#pragma prefast(disable:25068)
170 + hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
171 +#pragma prefast(pop)
172 + ExitOnNullWithLastError(hRsrc, hr, "Failed to find resource.");
173 +
174 + hData = ::LoadResource(hinst, hRsrc);
175 + ExitOnNullWithLastError(hData, hr, "Failed to load resource.");
176 +
177 + cbData = ::SizeofResource(hinst, hRsrc);
178 + if (!cbData)
179 + {
180 + ExitWithLastError(hr, "Failed to get size of resource.");
181 + }
182 +
183 + *ppv = ::LockResource(hData);
184 + ExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource.");
185 + *pcb = cbData;
186 +
187 +LExit:
188 + return hr;
189 +}
190 +
191 +
192 +/********************************************************************
193 +ResExportDataToFile - extracts the resource data to the specified target file
194 +
195 +********************************************************************/
196 +extern "C" HRESULT DAPI ResExportDataToFile(
197 + __in_z LPCSTR szDataName,
198 + __in_z LPCWSTR wzTargetFile,
199 + __in DWORD dwCreationDisposition
200 + )
201 +{
202 + HRESULT hr = S_OK;
203 + PVOID pData = NULL;
204 + DWORD cbData = 0;
205 + DWORD cbWritten = 0;
206 + HANDLE hFile = INVALID_HANDLE_VALUE;
207 + BOOL bCreatedFile = FALSE;
208 +
209 + hr = ResReadData(NULL, szDataName, &pData, &cbData);
210 + ExitOnFailure(hr, "Failed to GetData from %s.", szDataName);
211 +
212 + hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
213 + if (INVALID_HANDLE_VALUE == hFile)
214 + {
215 + ExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile);
216 + }
217 + bCreatedFile = TRUE;
218 +
219 + if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL))
220 + {
221 + ExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile);
222 + }
223 +
224 +LExit:
225 + ReleaseFile(hFile);
226 +
227 + if (FAILED(hr))
228 + {
229 + if (bCreatedFile)
230 + {
231 + ::DeleteFileW(wzTargetFile);
232 + }
233 + }
234 +
235 + return hr;
236 +}
237 +
238 +
239 +BOOL CALLBACK EnumLangIdProc(
240 + __in_opt HMODULE /* hModule */,
241 + __in_z LPCSTR /* lpType */,
242 + __in_z LPCSTR /* lpName */,
243 + __in WORD wLanguage,
244 + __in LONG_PTR lParam
245 + )
246 +{
247 + WORD *pwLangId = reinterpret_cast<WORD*>(lParam);
248 +
249 + *pwLangId = wLanguage;
250 + return TRUE;
251 +}
src/dutil/reswutil.cpp new
+368
@@ -0,0 +1,368 @@
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 +
5 +#define RES_STRINGS_PER_BLOCK 16
6 +
7 +// Internal data structure format for a string block in a resource table.
8 +// Note: Strings are always stored as UNICODED.
9 +typedef struct _RES_STRING_BLOCK
10 +{
11 + DWORD dwBlockId;
12 + WORD wLangId;
13 + LPWSTR rgwz[RES_STRINGS_PER_BLOCK];
14 +} RES_STRING_BLOCK;
15 +
16 +
17 +// private functions
18 +static HRESULT StringBlockInitialize(
19 + __in_opt HINSTANCE hModule,
20 + __in DWORD dwBlockId,
21 + __in WORD wLangId,
22 + __in RES_STRING_BLOCK* pStrBlock
23 + );
24 +static void StringBlockUnitialize(
25 + __in RES_STRING_BLOCK* pStrBlock
26 + );
27 +static HRESULT StringBlockChangeString(
28 + __in RES_STRING_BLOCK* pStrBlock,
29 + __in DWORD dwStringId,
30 + __in_z LPCWSTR szData
31 + );
32 +static HRESULT StringBlockConvertToResourceData(
33 + __in const RES_STRING_BLOCK* pStrBlock,
34 + __deref_out_bcount(*pcbData) LPVOID* ppvData,
35 + __out DWORD* pcbData
36 + );
37 +static HRESULT StringBlockConvertFromResourceData(
38 + __in RES_STRING_BLOCK* pStrBlock,
39 + __in_bcount(cbData) LPCVOID pvData,
40 + __in SIZE_T cbData
41 + );
42 +
43 +
44 +/********************************************************************
45 +ResWriteString - sets the string into to the specified file's resource name
46 +
47 +********************************************************************/
48 +extern "C" HRESULT DAPI ResWriteString(
49 + __in_z LPCWSTR wzResourceFile,
50 + __in DWORD dwDataId,
51 + __in_z LPCWSTR wzData,
52 + __in WORD wLangId
53 + )
54 +{
55 + Assert(wzResourceFile);
56 + Assert(wzData);
57 +
58 + HRESULT hr = S_OK;
59 + HINSTANCE hModule = NULL;
60 + HANDLE hUpdate = NULL;
61 + RES_STRING_BLOCK StrBlock = { };
62 + LPVOID pvData = NULL;
63 + DWORD cbData = 0;
64 +
65 + DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1;
66 + DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK);
67 +
68 + hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
69 + ExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile);
70 +
71 + hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock);
72 + ExitOnFailure(hr, "Failed to get string block to update.");
73 +
74 + hr = StringBlockChangeString(&StrBlock, dwStringId, wzData);
75 + ExitOnFailure(hr, "Failed to update string block string.");
76 +
77 + hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData);
78 + ExitOnFailure(hr, "Failed to convert string block to resource data.");
79 +
80 + ::FreeLibrary(hModule);
81 + hModule = NULL;
82 +
83 + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE);
84 + ExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW.");
85 +
86 + if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData))
87 + {
88 + ExitWithLastError(hr, "Failed to ::UpdateResourceA.");
89 + }
90 +
91 + if (!::EndUpdateResource(hUpdate, FALSE))
92 + {
93 + ExitWithLastError(hr, "Failed to ::EndUpdateResourceW.");
94 + }
95 +
96 + hUpdate = NULL;
97 +
98 +LExit:
99 + ReleaseMem(pvData);
100 +
101 + StringBlockUnitialize(&StrBlock);
102 +
103 + if (hUpdate)
104 + {
105 + ::EndUpdateResource(hUpdate, TRUE);
106 + }
107 +
108 + if (hModule)
109 + {
110 + ::FreeLibrary(hModule);
111 + }
112 +
113 + return hr;
114 +}
115 +
116 +
117 +/********************************************************************
118 +ResWriteData - sets the data into to the specified file's resource name
119 +
120 +********************************************************************/
121 +extern "C" HRESULT DAPI ResWriteData(
122 + __in_z LPCWSTR wzResourceFile,
123 + __in_z LPCSTR szDataName,
124 + __in PVOID pData,
125 + __in DWORD cbData
126 + )
127 +{
128 + Assert(wzResourceFile);
129 + Assert(szDataName);
130 + Assert(pData);
131 + Assert(cbData);
132 +
133 + HRESULT hr = S_OK;
134 + HANDLE hUpdate = NULL;
135 +
136 + hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE);
137 + ExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW.");
138 +
139 + if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData))
140 + {
141 + ExitWithLastError(hr, "Failed to ::UpdateResourceA.");
142 + }
143 +
144 + if (!::EndUpdateResource(hUpdate, FALSE))
145 + {
146 + ExitWithLastError(hr, "Failed to ::EndUpdateResourceW.");
147 + }
148 +
149 + hUpdate = NULL;
150 +
151 +LExit:
152 + if (hUpdate)
153 + {
154 + ::EndUpdateResource(hUpdate, TRUE);
155 + }
156 +
157 + return hr;
158 +}
159 +
160 +
161 +/********************************************************************
162 +ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name
163 +
164 +********************************************************************/
165 +extern "C" HRESULT DAPI ResImportDataFromFile(
166 + __in_z LPCWSTR wzTargetFile,
167 + __in_z LPCWSTR wzSourceFile,
168 + __in_z LPCSTR szDataName
169 + )
170 +{
171 + HRESULT hr = S_OK;
172 + HANDLE hFile = INVALID_HANDLE_VALUE;
173 + DWORD cbFile = 0;
174 + HANDLE hMap = NULL;
175 + PVOID pv = NULL;
176 +
177 + hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
178 + if (INVALID_HANDLE_VALUE == hFile)
179 + {
180 + ExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile);
181 + }
182 +
183 + cbFile = ::GetFileSize(hFile, NULL);
184 + if (!cbFile)
185 + {
186 + ExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile);
187 + }
188 +
189 + hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
190 + ExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile);
191 +
192 + pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile);
193 + ExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile);
194 +
195 + hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile);
196 + ExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile);
197 +
198 +LExit:
199 + if (pv)
200 + {
201 + ::UnmapViewOfFile(pv);
202 + }
203 +
204 + if (hMap)
205 + {
206 + ::CloseHandle(hMap);
207 + }
208 +
209 + ReleaseFile(hFile);
210 +
211 + return hr;
212 +}
213 +
214 +
215 +static HRESULT StringBlockInitialize(
216 + __in_opt HINSTANCE hModule,
217 + __in DWORD dwBlockId,
218 + __in WORD wLangId,
219 + __in RES_STRING_BLOCK* pStrBlock
220 + )
221 +{
222 + HRESULT hr = S_OK;
223 + HRSRC hRsrc = NULL;
224 + HGLOBAL hData = NULL;
225 + LPCVOID pvData = NULL; // does not need to be freed
226 + DWORD cbData = 0;
227 +
228 + hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId);
229 + ExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW.");
230 +
231 + hData = ::LoadResource(hModule, hRsrc);
232 + ExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource.");
233 +
234 + cbData = ::SizeofResource(hModule, hRsrc);
235 + if (!cbData)
236 + {
237 + ExitWithLastError(hr, "Failed to ::SizeofResource.");
238 + }
239 +
240 + pvData = ::LockResource(hData);
241 + ExitOnNullWithLastError(pvData, hr, "Failed to lock data resource.");
242 +
243 + pStrBlock->dwBlockId = dwBlockId;
244 + pStrBlock->wLangId = wLangId;
245 +
246 + hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData);
247 + ExitOnFailure(hr, "Failed to convert string block from resource data.");
248 +
249 +LExit:
250 + return hr;
251 +}
252 +
253 +
254 +static void StringBlockUnitialize(
255 + __in RES_STRING_BLOCK* pStrBlock
256 + )
257 +{
258 + if (pStrBlock)
259 + {
260 + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
261 + {
262 + ReleaseNullMem(pStrBlock->rgwz[i]);
263 + }
264 + }
265 +}
266 +
267 +
268 +static HRESULT StringBlockChangeString(
269 + __in RES_STRING_BLOCK* pStrBlock,
270 + __in DWORD dwStringId,
271 + __in_z LPCWSTR szData
272 + )
273 +{
274 + HRESULT hr = S_OK;
275 + LPWSTR pwzData = NULL;
276 + DWORD cchData = lstrlenW(szData);
277 +
278 + pwzData = static_cast<LPWSTR>(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE));
279 + ExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string.");
280 +
281 + hr = ::StringCchCopyW(pwzData, cchData + 1, szData);
282 + ExitOnFailure(hr, "Failed to copy new block string.");
283 +
284 + ReleaseNullMem(pStrBlock->rgwz[dwStringId]);
285 +
286 + pStrBlock->rgwz[dwStringId] = pwzData;
287 + pwzData = NULL;
288 +
289 +LExit:
290 + ReleaseMem(pwzData);
291 +
292 + return hr;
293 +}
294 +
295 +
296 +static HRESULT StringBlockConvertToResourceData(
297 + __in const RES_STRING_BLOCK* pStrBlock,
298 + __deref_out_bcount(*pcbData) LPVOID* ppvData,
299 + __out DWORD* pcbData
300 + )
301 +{
302 + HRESULT hr = S_OK;
303 + DWORD cbData = 0;
304 + LPVOID pvData = NULL;
305 + WCHAR* pwz = NULL;
306 +
307 + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
308 + {
309 + cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1);
310 + }
311 + cbData *= sizeof(WCHAR);
312 +
313 + pvData = MemAlloc(cbData, TRUE);
314 + ExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block.");
315 +
316 + pwz = static_cast<LPWSTR>(pvData);
317 + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
318 + {
319 + DWORD cch = lstrlenW(pStrBlock->rgwz[i]);
320 +
321 + *pwz = static_cast<WCHAR>(cch);
322 + ++pwz;
323 +
324 + for (DWORD j = 0; j < cch; ++j)
325 + {
326 + *pwz = pStrBlock->rgwz[i][j];
327 + ++pwz;
328 + }
329 + }
330 +
331 + *pcbData = cbData;
332 + *ppvData = pvData;
333 + pvData = NULL;
334 +
335 +LExit:
336 + ReleaseMem(pvData);
337 +
338 + return hr;
339 +}
340 +
341 +
342 +static HRESULT StringBlockConvertFromResourceData(
343 + __in RES_STRING_BLOCK* pStrBlock,
344 + __in_bcount(cbData) LPCVOID pvData,
345 + __in SIZE_T cbData
346 + )
347 +{
348 + UNREFERENCED_PARAMETER(cbData);
349 + HRESULT hr = S_OK;
350 + LPCWSTR pwzParse = static_cast<LPCWSTR>(pvData);
351 +
352 + for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
353 + {
354 + DWORD cchParse = static_cast<DWORD>(*pwzParse);
355 + ++pwzParse;
356 +
357 + pStrBlock->rgwz[i] = static_cast<LPWSTR>(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE));
358 + ExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock.");
359 +
360 + hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
361 + ExitOnFailure(hr, "Failed to copy parsed resource data into string block.");
362 +
363 + pwzParse += cchParse;
364 + }
365 +
366 +LExit:
367 + return hr;
368 +}
src/dutil/rexutil.cpp new
+586
@@ -0,0 +1,586 @@
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 +#include "rexutil.h"
5 +
6 +//
7 +// static globals
8 +//
9 +static HMODULE vhCabinetDll = NULL;
10 +static HFDI vhfdi = NULL;
11 +static ERF verf;
12 +
13 +static FAKE_FILE vrgffFileTable[FILETABLESIZE];
14 +static DWORD vcbRes;
15 +static LPCBYTE vpbRes;
16 +static CHAR vszResource[MAX_PATH];
17 +static REX_CALLBACK_WRITE vpfnWrite = NULL;
18 +
19 +static HRESULT vhrLastError = S_OK;
20 +
21 +//
22 +// structs
23 +//
24 +struct REX_CALLBACK_STRUCT
25 +{
26 + BOOL fStopExtracting; // flag set when no more files are needed
27 + LPCWSTR pwzExtract; // file to extract ("*" means extract all)
28 + LPCWSTR pwzExtractDir; // directory to extract files to
29 + LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*")
30 +
31 + // possible user data
32 + REX_CALLBACK_PROGRESS pfnProgress;
33 + LPVOID pvContext;
34 +};
35 +
36 +//
37 +// prototypes
38 +//
39 +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize);
40 +static __callback void DIAMONDAPI RexFree(LPVOID pvData);
41 +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode);
42 +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb);
43 +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb);
44 +static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf);
45 +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype);
46 +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify);
47 +
48 +
49 +/********************************************************************
50 + RexInitialize - initializes internal static variables
51 +
52 +*******************************************************************/
53 +extern "C" HRESULT RexInitialize()
54 +{
55 + Assert(!vhfdi);
56 +
57 + HRESULT hr = S_OK;
58 +
59 + vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf);
60 + if (!vhfdi)
61 + {
62 + hr = E_FAIL;
63 + ExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here
64 + }
65 +
66 + ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable));
67 +
68 +LExit:
69 + if (FAILED(hr))
70 + {
71 + ::FDIDestroy(vhfdi);
72 + vhfdi = NULL;
73 + }
74 +
75 + return hr;
76 +}
77 +
78 +
79 +/********************************************************************
80 + RexUninitialize - initializes internal static variables
81 +
82 +*******************************************************************/
83 +extern "C" void RexUninitialize()
84 +{
85 + if (vhfdi)
86 + {
87 + ::FDIDestroy(vhfdi);
88 + vhfdi = NULL;
89 + }
90 +}
91 +
92 +
93 +/********************************************************************
94 + RexExtract - extracts one or all files from a resource cabinet
95 +
96 + NOTE: wzExtractId can be a single file id or "*" to extract all files
97 + wzExttractDir must be normalized (end in a "\")
98 + wzExtractName is ignored if wzExtractId is "*"
99 +*******************************************************************/
100 +extern "C" HRESULT RexExtract(
101 + __in_z LPCSTR szResource,
102 + __in_z LPCWSTR wzExtractId,
103 + __in_z LPCWSTR wzExtractDir,
104 + __in_z LPCWSTR wzExtractName,
105 + __in REX_CALLBACK_PROGRESS pfnProgress,
106 + __in REX_CALLBACK_WRITE pfnWrite,
107 + __in LPVOID pvContext
108 + )
109 +{
110 + Assert(vhfdi);
111 + HRESULT hr = S_OK;
112 + BOOL fResult;
113 +
114 + HRSRC hResInfo = NULL;
115 + HANDLE hRes = NULL;
116 +
117 + REX_CALLBACK_STRUCT rcs;
118 +
119 + // remember the write callback
120 + vpfnWrite = pfnWrite;
121 +
122 + //
123 + // load the cabinet resource
124 + //
125 + hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
126 + ExitOnNullWithLastError(hResInfo, hr, "Failed to find resource.");
127 + //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10));
128 + //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info");
129 +
130 + hRes = ::LoadResource(NULL, hResInfo);
131 + ExitOnNullWithLastError(hRes, hr, "failed to load resource");
132 +
133 + vcbRes = ::SizeofResource(NULL, hResInfo);
134 + vpbRes = (const BYTE*)::LockResource(hRes);
135 +
136 + // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it
137 +
138 + //
139 + // convert the resource name to multi-byte
140 + //
141 + //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL))
142 + //{
143 + // ExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource);
144 + //}
145 +
146 + hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource);
147 + ExitOnFailure(hr, "Failed to copy resource name to global.");
148 +
149 + //
150 + // iterate through files in cabinet extracting them to the callback function
151 + //
152 + rcs.fStopExtracting = FALSE;
153 + rcs.pwzExtract = wzExtractId;
154 + rcs.pwzExtractDir = wzExtractDir;
155 + rcs.pwzExtractName = wzExtractName;
156 + rcs.pfnProgress = pfnProgress;
157 + rcs.pvContext = pvContext;
158 +
159 + fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast<void*>(&rcs));
160 + if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
161 + {
162 + hr = vhrLastError; // TODO: put verf info in trace message here
163 + }
164 +
165 +LExit:
166 + return hr;
167 +}
168 +
169 +
170 +/****************************************************************************
171 + default extract routines
172 +
173 +****************************************************************************/
174 +static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize)
175 +{
176 + return MemAlloc(dwSize, FALSE);
177 +}
178 +
179 +
180 +static __callback void DIAMONDAPI RexFree(LPVOID pvData)
181 +{
182 + MemFree(pvData);
183 +}
184 +
185 +
186 +static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode)
187 +{
188 + HRESULT hr = S_OK;
189 + HANDLE hFile = INVALID_HANDLE_VALUE;
190 + int i = 0;
191 +
192 + // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail
193 + if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE)))
194 + {
195 + hr = E_OUTOFMEMORY;
196 + ExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual");
197 + }
198 +
199 + // find an empty spot in the fake file table
200 + for (i = 0; i < FILETABLESIZE; ++i)
201 + {
202 + if (!vrgffFileTable[i].fUsed)
203 + {
204 + break;
205 + }
206 + }
207 +
208 + // we should never run out of space in the fake file table
209 + if (FILETABLESIZE <= i)
210 + {
211 + hr = E_OUTOFMEMORY;
212 + ExitOnFailure(hr, "File table exceeded");
213 + }
214 +
215 + if (0 == lstrcmpA(vszResource, pszFile))
216 + {
217 + vrgffFileTable[i].fUsed = TRUE;
218 + vrgffFileTable[i].fftType = MEMORY_FILE;
219 + vrgffFileTable[i].mfFile.vpStart = static_cast<LPCBYTE>(vpbRes);
220 + vrgffFileTable[i].mfFile.uiCurrent = 0;
221 + vrgffFileTable[i].mfFile.uiLength = vcbRes;
222 + }
223 + else // it's a real file
224 + {
225 + hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
226 + if (INVALID_HANDLE_VALUE == hFile)
227 + {
228 + ExitWithLastError(hr, "failed to open file: %s", pszFile);
229 + }
230 +
231 + vrgffFileTable[i].fUsed = TRUE;
232 + vrgffFileTable[i].fftType = NORMAL_FILE;
233 + vrgffFileTable[i].hFile = hFile;
234 + }
235 +
236 +LExit:
237 + if (FAILED(hr))
238 + {
239 + vhrLastError = hr;
240 + }
241 +
242 + return FAILED(hr) ? -1 : i;
243 +}
244 +
245 +
246 +static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb)
247 +{
248 + Assert(vrgffFileTable[hf].fUsed);
249 +
250 + HRESULT hr = S_OK;
251 + DWORD cbRead = 0;
252 + DWORD cbAvailable = 0;
253 +
254 + if (MEMORY_FILE == vrgffFileTable[hf].fftType)
255 + {
256 + // ensure that we don't read past the length of the resource
257 + cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent;
258 + cbRead = cb < cbAvailable? cb : cbAvailable;
259 +
260 + memcpy(pv, static_cast<const void *>(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead);
261 +
262 + vrgffFileTable[hf].mfFile.uiCurrent += cbRead;
263 + }
264 + else // NORMAL_FILE
265 + {
266 + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
267 +
268 + if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL))
269 + {
270 + ExitWithLastError(hr, "failed to read during cabinet extraction");
271 + }
272 + }
273 +
274 +LExit:
275 + if (FAILED(hr))
276 + {
277 + vhrLastError = hr;
278 + }
279 +
280 + return FAILED(hr) ? -1 : cbRead;
281 +}
282 +
283 +
284 +static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb)
285 +{
286 + Assert(vrgffFileTable[hf].fUsed);
287 + Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file
288 +
289 + HRESULT hr = S_OK;
290 + DWORD cbWrite = 0;
291 +
292 + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
293 + if (!::WriteFile(reinterpret_cast<HANDLE>(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL))
294 + {
295 + ExitWithLastError(hr, "failed to write during cabinet extraction");
296 + }
297 +
298 + // call the writer callback if defined
299 + if (vpfnWrite)
300 + {
301 + vpfnWrite(cb);
302 + }
303 +
304 +LExit:
305 + if (FAILED(hr))
306 + {
307 + vhrLastError = hr;
308 + }
309 +
310 + return FAILED(hr) ? -1 : cbWrite;
311 +}
312 +
313 +
314 +static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype)
315 +{
316 + Assert(vrgffFileTable[hf].fUsed);
317 +
318 + HRESULT hr = S_OK;
319 + DWORD dwMoveMethod;
320 + LONG lMove = 0;
321 +
322 + switch (seektype)
323 + {
324 + case 0: // SEEK_SET
325 + dwMoveMethod = FILE_BEGIN;
326 + break;
327 + case 1: /// SEEK_CUR
328 + dwMoveMethod = FILE_CURRENT;
329 + break;
330 + case 2: // SEEK_END
331 + dwMoveMethod = FILE_END;
332 + break;
333 + default :
334 + dwMoveMethod = 0;
335 + hr = E_UNEXPECTED;
336 + ExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype);
337 + }
338 +
339 + if (MEMORY_FILE == vrgffFileTable[hf].fftType)
340 + {
341 + if (FILE_BEGIN == dwMoveMethod)
342 + {
343 + vrgffFileTable[hf].mfFile.uiCurrent = dist;
344 + }
345 + else if (FILE_CURRENT == dwMoveMethod)
346 + {
347 + vrgffFileTable[hf].mfFile.uiCurrent += dist;
348 + }
349 + else // FILE_END
350 + {
351 + vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist;
352 + }
353 +
354 + lMove = vrgffFileTable[hf].mfFile.uiCurrent;
355 + }
356 + else // NORMAL_FILE
357 + {
358 + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
359 +
360 + // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
361 + // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
362 + lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod);
363 + if (0xFFFFFFFF == lMove)
364 + {
365 + ExitWithLastError(hr, "failed to move file pointer %d bytes", dist);
366 + }
367 + }
368 +
369 +LExit:
370 + if (FAILED(hr))
371 + {
372 + vhrLastError = hr;
373 + }
374 +
375 + return FAILED(hr) ? -1 : lMove;
376 +}
377 +
378 +
379 +__callback int FAR DIAMONDAPI RexClose(INT_PTR hf)
380 +{
381 + Assert(vrgffFileTable[hf].fUsed);
382 +
383 + HRESULT hr = S_OK;
384 +
385 + if (MEMORY_FILE == vrgffFileTable[hf].fftType)
386 + {
387 + vrgffFileTable[hf].mfFile.vpStart = NULL;
388 + vrgffFileTable[hf].mfFile.uiCurrent = 0;
389 + vrgffFileTable[hf].mfFile.uiLength = 0;
390 + }
391 + else
392 + {
393 + Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
394 +
395 + if (!::CloseHandle(vrgffFileTable[hf].hFile))
396 + {
397 + ExitWithLastError(hr, "failed to close file during cabinet extraction");
398 + }
399 +
400 + vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE;
401 + }
402 +
403 + vrgffFileTable[hf].fUsed = FALSE;
404 +
405 +LExit:
406 + if (FAILED(hr))
407 + {
408 + vhrLastError = hr;
409 + }
410 +
411 + return FAILED(hr) ? -1 : 0;
412 +}
413 +
414 +
415 +static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify)
416 +{
417 + Assert(pFDINotify->pv);
418 +
419 + HRESULT hr = S_OK;
420 + int ipResult = 0; // result to return on success
421 + HANDLE hFile = INVALID_HANDLE_VALUE;
422 +
423 + REX_CALLBACK_STRUCT* prcs = static_cast<REX_CALLBACK_STRUCT*>(pFDINotify->pv);
424 + LPCSTR sz;
425 + WCHAR wz[MAX_PATH];
426 + FILETIME ft;
427 + int i = 0;
428 +
429 + switch (iNotification)
430 + {
431 + case fdintCOPY_FILE: // beGIN extracting a resource from cabinet
432 + Assert(pFDINotify->psz1);
433 +
434 + if (prcs->fStopExtracting)
435 + {
436 + ExitFunction1(hr = S_FALSE); // no more extracting
437 + }
438 +
439 + // convert params to useful variables
440 + sz = static_cast<LPCSTR>(pFDINotify->psz1);
441 + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
442 + {
443 + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
444 + }
445 +
446 + if (prcs->pfnProgress)
447 + {
448 + hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext);
449 + if (S_OK != hr)
450 + {
451 + ExitFunction();
452 + }
453 + }
454 +
455 + if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz))
456 + {
457 + // get the created date for the resource in the cabinet
458 + if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft))
459 + {
460 + ExitWithLastError(hr, "failed to get time for resource: %ls", wz);
461 + }
462 +
463 + WCHAR wzPath[MAX_PATH];
464 +
465 + hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir);
466 + ExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz);
467 +
468 + if (L'*' == *prcs->pwzExtract)
469 + {
470 + hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
471 + ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
472 + }
473 + else
474 + {
475 + Assert(*prcs->pwzExtractName);
476 +
477 + hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName);
478 + ExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName);
479 + }
480 +
481 + // Quickly chop off the file name part of the path to ensure the path exists
482 + // then put the file name back on the path (by putting the first character
483 + // back over the null terminator).
484 + LPWSTR wzFile = PathFile(wzPath);
485 + WCHAR wzFileFirstChar = *wzFile;
486 + *wzFile = L'\0';
487 +
488 + hr = DirEnsureExists(wzPath, NULL);
489 + ExitOnFailure(hr, "failed to ensure directory: %ls", wzPath);
490 +
491 + hr = S_OK;
492 +
493 + *wzFile = wzFileFirstChar;
494 +
495 + // find an empty spot in the fake file table
496 + for (i = 0; i < FILETABLESIZE; ++i)
497 + {
498 + if (!vrgffFileTable[i].fUsed)
499 + {
500 + break;
501 + }
502 + }
503 +
504 + // we should never run out of space in the fake file table
505 + if (FILETABLESIZE <= i)
506 + {
507 + hr = E_OUTOFMEMORY;
508 + ExitOnFailure(hr, "File table exceeded");
509 + }
510 +
511 + // open the file
512 + hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
513 + if (INVALID_HANDLE_VALUE == hFile)
514 + {
515 + ExitWithLastError(hr, "failed to open file: %ls", wzPath);
516 + }
517 +
518 + vrgffFileTable[i].fUsed = TRUE;
519 + vrgffFileTable[i].fftType = NORMAL_FILE;
520 + vrgffFileTable[i].hFile = hFile;
521 +
522 + ipResult = i;
523 +
524 + ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails)
525 +
526 + if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails)
527 + {
528 + if (::SetEndOfFile(vrgffFileTable[i].hFile))
529 + {
530 + ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer
531 + }
532 + }
533 + }
534 + else // resource wasn't requested, skip it
535 + {
536 + hr = S_OK;
537 + ipResult = 0;
538 + }
539 +
540 + break;
541 + case fdintCLOSE_FILE_INFO: // resource extraction complete
542 + Assert(pFDINotify->hf && pFDINotify->psz1);
543 +
544 + // convert params to useful variables
545 + sz = static_cast<LPCSTR>(pFDINotify->psz1);
546 + if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
547 + {
548 + ExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
549 + }
550 +
551 + RexClose(pFDINotify->hf);
552 +
553 + if (prcs->pfnProgress)
554 + {
555 + hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext);
556 + }
557 +
558 + if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going
559 + {
560 + ipResult = TRUE;
561 + }
562 + else // something went wrong or we only needed to extract one file
563 + {
564 + hr = S_OK;
565 + ipResult = FALSE;
566 + prcs->fStopExtracting = TRUE;
567 + }
568 +
569 + break;
570 + case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through
571 + case fdintNEXT_CABINET: __fallthrough;
572 + case fdintENUMERATE: __fallthrough;
573 + case fdintCABINET_INFO:
574 + break;
575 + default:
576 + AssertSz(FALSE, "RexCallback() - unknown FDI notification command");
577 + };
578 +
579 +LExit:
580 + if (FAILED(hr))
581 + {
582 + vhrLastError = hr;
583 + }
584 +
585 + return (S_OK == hr) ? ipResult : -1;
586 +}
src/dutil/rmutil.cpp new
+473
@@ -0,0 +1,473 @@
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 +#include <restartmanager.h>
5 +
6 +#define ARRAY_GROWTH_SIZE 5
7 +
8 +typedef DWORD (WINAPI *PFNRMJOINSESSION)(
9 + __out DWORD *pSessionHandle,
10 + __in_z const WCHAR strSessionKey[]
11 + );
12 +
13 +typedef DWORD (WINAPI *PFNRMENDSESSION)(
14 + __in DWORD dwSessionHandle
15 + );
16 +
17 +typedef DWORD (WINAPI *PFNRMREGISTERRESOURCES)(
18 + __in DWORD dwSessionHandle,
19 + __in UINT nFiles,
20 + __in_z_opt LPWSTR *rgsFilenames,
21 + __in UINT nApplications,
22 + __in_opt RM_UNIQUE_PROCESS *rgApplications,
23 + __in UINT nServices,
24 + __in_z_opt LPWSTR *rgsServiceNames
25 + );
26 +
27 +typedef struct _RMU_SESSION
28 +{
29 + CRITICAL_SECTION cs;
30 + DWORD dwSessionHandle;
31 + BOOL fStartedSessionHandle;
32 + BOOL fInitialized;
33 +
34 + UINT cFilenames;
35 + LPWSTR *rgsczFilenames;
36 +
37 + UINT cApplications;
38 + RM_UNIQUE_PROCESS *rgApplications;
39 +
40 + UINT cServiceNames;
41 + LPWSTR *rgsczServiceNames;
42 +
43 +} RMU_SESSION;
44 +
45 +static volatile LONG vcRmuInitialized = 0;
46 +static HMODULE vhModule = NULL;
47 +static PFNRMJOINSESSION vpfnRmJoinSession = NULL;
48 +static PFNRMENDSESSION vpfnRmEndSession = NULL;
49 +static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL;
50 +
51 +static HRESULT RmuInitialize();
52 +static void RmuUninitialize();
53 +
54 +static HRESULT RmuApplicationArrayAlloc(
55 + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications,
56 + __inout LPUINT pcApplications,
57 + __in DWORD dwProcessId,
58 + __in FILETIME ProcessStartTime
59 + );
60 +
61 +static HRESULT RmuApplicationArrayFree(
62 + __in RM_UNIQUE_PROCESS *rgApplications
63 + );
64 +
65 +#define ReleaseNullApplicationArray(rg, c) { if (rg) { RmuApplicationArrayFree(rg); c = 0; rg = NULL; } }
66 +
67 +/********************************************************************
68 +RmuJoinSession - Joins an existing Restart Manager session.
69 +
70 +********************************************************************/
71 +extern "C" HRESULT DAPI RmuJoinSession(
72 + __out PRMU_SESSION *ppSession,
73 + __in_z LPCWSTR wzSessionKey
74 + )
75 +{
76 + HRESULT hr = S_OK;
77 + DWORD er = ERROR_SUCCESS;
78 + PRMU_SESSION pSession = NULL;
79 +
80 + *ppSession = NULL;
81 +
82 + pSession = static_cast<PRMU_SESSION>(MemAlloc(sizeof(RMU_SESSION), TRUE));
83 + ExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure.");
84 +
85 + hr = RmuInitialize();
86 + ExitOnFailure(hr, "Failed to initialize Restart Manager.");
87 +
88 + er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey);
89 + ExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey);
90 +
91 + ::InitializeCriticalSection(&pSession->cs);
92 + pSession->fInitialized = TRUE;
93 +
94 + *ppSession = pSession;
95 +
96 +LExit:
97 + if (FAILED(hr))
98 + {
99 + ReleaseNullMem(pSession);
100 + }
101 +
102 + return hr;
103 +}
104 +
105 +/********************************************************************
106 +RmuAddFile - Adds the file path to the Restart Manager session.
107 +
108 +You should call this multiple times as necessary before calling
109 +RmuRegisterResources.
110 +
111 +********************************************************************/
112 +extern "C" HRESULT DAPI RmuAddFile(
113 + __in PRMU_SESSION pSession,
114 + __in_z LPCWSTR wzPath
115 + )
116 +{
117 + HRESULT hr = S_OK;
118 +
119 + ::EnterCriticalSection(&pSession->cs);
120 +
121 + // Create or grow the jagged array.
122 + hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0);
123 + ExitOnFailure(hr, "Failed to add the filename to the array.");
124 +
125 +LExit:
126 + ::LeaveCriticalSection(&pSession->cs);
127 + return hr;
128 +}
129 +
130 +/********************************************************************
131 +RmuAddProcessById - Adds the process ID to the Restart Manager sesion.
132 +
133 +You should call this multiple times as necessary before calling
134 +RmuRegisterResources.
135 +
136 +********************************************************************/
137 +extern "C" HRESULT DAPI RmuAddProcessById(
138 + __in PRMU_SESSION pSession,
139 + __in DWORD dwProcessId
140 + )
141 +{
142 + HRESULT hr = S_OK;
143 + HANDLE hProcess = NULL;
144 + FILETIME CreationTime = {};
145 + FILETIME ExitTime = {};
146 + FILETIME KernelTime = {};
147 + FILETIME UserTime = {};
148 + BOOL fLocked = FALSE;
149 +
150 + HANDLE hToken = NULL;
151 + TOKEN_PRIVILEGES priv = { 0 };
152 + TOKEN_PRIVILEGES* pPrevPriv = NULL;
153 + DWORD cbPrevPriv = 0;
154 + DWORD er = ERROR_SUCCESS;
155 + BOOL fAdjustedPrivileges = FALSE;
156 + BOOL fElevated = FALSE;
157 + ProcElevated(::GetCurrentProcess(), &fElevated);
158 +
159 + // Must be elevated to adjust process privileges
160 + if (fElevated) {
161 + // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context.
162 + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
163 + {
164 + ExitWithLastError(hr, "Failed to get process token.");
165 + }
166 +
167 + priv.PrivilegeCount = 1;
168 + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
169 + if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid))
170 + {
171 + ExitWithLastError(hr, "Failed to get debug privilege LUID.");
172 + }
173 +
174 + cbPrevPriv = sizeof(TOKEN_PRIVILEGES);
175 + pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(MemAlloc(cbPrevPriv, TRUE));
176 + ExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges.");
177 +
178 + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
179 + {
180 + LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE);
181 + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges.");
182 + pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(pv);
183 +
184 + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
185 + {
186 + ExitWithLastError(hr, "Failed to get debug privilege LUID.");
187 + }
188 + }
189 +
190 + fAdjustedPrivileges = TRUE;
191 + }
192 +
193 + hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
194 + if (hProcess)
195 + {
196 + if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime))
197 + {
198 + ExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId);
199 + }
200 +
201 + ::EnterCriticalSection(&pSession->cs);
202 + fLocked = TRUE;
203 + hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime);
204 + ExitOnFailure(hr, "Failed to add the application to the array.");
205 + }
206 + else
207 + {
208 + er = ::GetLastError();
209 + if (ERROR_ACCESS_DENIED == er)
210 + {
211 + // OpenProcess will fail when not elevated and the target process is in another user context. Let the caller log and continue.
212 + hr = E_NOTFOUND;
213 + }
214 + else
215 + {
216 + ExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId);
217 + }
218 + }
219 +
220 +LExit:
221 + if (hProcess)
222 + {
223 + ::CloseHandle(hProcess);
224 + }
225 +
226 + if (fAdjustedPrivileges)
227 + {
228 + ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL);
229 + }
230 +
231 + ReleaseMem(pPrevPriv);
232 + ReleaseHandle(hToken);
233 +
234 + if (fLocked)
235 + {
236 + ::LeaveCriticalSection(&pSession->cs);
237 + }
238 +
239 + return hr;
240 +}
241 +
242 +/********************************************************************
243 +RmuAddProcessesByName - Adds all processes by the given process name
244 + to the Restart Manager Session.
245 +
246 +You should call this multiple times as necessary before calling
247 +RmuRegisterResources.
248 +
249 +********************************************************************/
250 +extern "C" HRESULT DAPI RmuAddProcessesByName(
251 + __in PRMU_SESSION pSession,
252 + __in_z LPCWSTR wzProcessName
253 + )
254 +{
255 + HRESULT hr = S_OK;
256 + DWORD *pdwProcessIds = NULL;
257 + DWORD cProcessIds = 0;
258 + BOOL fNotFound = FALSE;
259 +
260 + hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds);
261 + ExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName);
262 +
263 + for (DWORD i = 0; i < cProcessIds; ++i)
264 + {
265 + hr = RmuAddProcessById(pSession, pdwProcessIds[i]);
266 + if (E_NOTFOUND == hr)
267 + {
268 + // RmuAddProcessById returns E_NOTFOUND when this setup is not elevated and OpenProcess returned access denied (target process running under another user account).
269 + fNotFound = TRUE;
270 + }
271 + else
272 + {
273 + ExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]);
274 + }
275 + }
276 +
277 + // If one or more calls to RmuAddProcessById returned E_NOTFOUND, then return E_NOTFOUND even if other calls succeeded, so that caller can log the issue.
278 + if (fNotFound)
279 + {
280 + hr = E_NOTFOUND;
281 + }
282 +
283 +LExit:
284 + ReleaseMem(pdwProcessIds);
285 +
286 + return hr;
287 +}
288 +
289 +/********************************************************************
290 +RmuAddService - Adds the service name to the Restart Manager session.
291 +
292 +You should call this multiple times as necessary before calling
293 +RmuRegisterResources.
294 +
295 +********************************************************************/
296 +extern "C" HRESULT DAPI RmuAddService(
297 + __in PRMU_SESSION pSession,
298 + __in_z LPCWSTR wzServiceName
299 + )
300 +{
301 + HRESULT hr = S_OK;
302 +
303 + ::EnterCriticalSection(&pSession->cs);
304 +
305 + hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0);
306 + ExitOnFailure(hr, "Failed to add the service name to the array.");
307 +
308 +LExit:
309 + ::LeaveCriticalSection(&pSession->cs);
310 + return hr;
311 +}
312 +
313 +/********************************************************************
314 +RmuRegisterResources - Registers resources for the Restart Manager.
315 +
316 +This should be called rarely because it is expensive to run. Call
317 +functions like RmuAddFile for multiple resources then commit them
318 +as a batch of updates to RmuRegisterResources.
319 +
320 +Duplicate resources appear to be handled by Restart Manager.
321 +Only one WM_QUERYENDSESSION is being sent for each top-level window.
322 +
323 +********************************************************************/
324 +extern "C" HRESULT DAPI RmuRegisterResources(
325 + __in PRMU_SESSION pSession
326 + )
327 +{
328 + HRESULT hr = S_OK;
329 + DWORD er = ERROR_SUCCESS;
330 +
331 + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized.");
332 +
333 + ::EnterCriticalSection(&pSession->cs);
334 +
335 + er = vpfnRmRegisterResources(
336 + pSession->dwSessionHandle,
337 + pSession->cFilenames,
338 + pSession->rgsczFilenames,
339 + pSession->cApplications,
340 + pSession->rgApplications,
341 + pSession->cServiceNames,
342 + pSession->rgsczServiceNames
343 + );
344 + ExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session.");
345 +
346 + // Empty the arrays if registered in case additional resources are added later.
347 + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames);
348 + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications);
349 + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames);
350 +
351 +LExit:
352 + ::LeaveCriticalSection(&pSession->cs);
353 + return hr;
354 +}
355 +
356 +/********************************************************************
357 +RmuEndSession - Ends the session.
358 +
359 +If the session was joined by RmuJoinSession, any remaining resources
360 +are registered before the session is ended (left).
361 +
362 +********************************************************************/
363 +extern "C" HRESULT DAPI RmuEndSession(
364 + __in PRMU_SESSION pSession
365 + )
366 +{
367 + HRESULT hr = S_OK;
368 + DWORD er = ERROR_SUCCESS;
369 +
370 + AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized.");
371 +
372 + // Make sure all resources are registered if we joined the session.
373 + if (!pSession->fStartedSessionHandle)
374 + {
375 + hr = RmuRegisterResources(pSession);
376 + ExitOnFailure(hr, "Failed to register remaining resources.");
377 + }
378 +
379 + er = vpfnRmEndSession(pSession->dwSessionHandle);
380 + ExitOnWin32Error(er, hr, "Failed to end the Restart Manager session.");
381 +
382 +LExit:
383 + if (pSession->fInitialized)
384 + {
385 + ::DeleteCriticalSection(&pSession->cs);
386 + }
387 +
388 + ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames);
389 + ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications);
390 + ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames);
391 + ReleaseNullMem(pSession);
392 +
393 + RmuUninitialize();
394 +
395 + return hr;
396 +}
397 +
398 +static HRESULT RmuInitialize()
399 +{
400 + HRESULT hr = S_OK;
401 + HMODULE hModule = NULL;
402 +
403 + LONG iRef = ::InterlockedIncrement(&vcRmuInitialized);
404 + if (1 == iRef && !vhModule)
405 + {
406 + hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule);
407 + ExitOnFailure(hr, "Failed to load the rstrtmgr.dll module.");
408 +
409 + vpfnRmJoinSession = reinterpret_cast<PFNRMJOINSESSION>(::GetProcAddress(hModule, "RmJoinSession"));
410 + ExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll.");
411 +
412 + vpfnRmRegisterResources = reinterpret_cast<PFNRMREGISTERRESOURCES>(::GetProcAddress(hModule, "RmRegisterResources"));
413 + ExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll.");
414 +
415 + vpfnRmEndSession = reinterpret_cast<PFNRMENDSESSION>(::GetProcAddress(hModule, "RmEndSession"));
416 + ExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll.");
417 +
418 + vhModule = hModule;
419 + }
420 +
421 +LExit:
422 + return hr;
423 +}
424 +
425 +static void RmuUninitialize()
426 +{
427 + LONG iRef = ::InterlockedDecrement(&vcRmuInitialized);
428 + if (0 == iRef && vhModule)
429 + {
430 + vpfnRmJoinSession = NULL;
431 + vpfnRmEndSession = NULL;
432 + vpfnRmRegisterResources = NULL;
433 +
434 + ::FreeLibrary(vhModule);
435 + vhModule = NULL;
436 + }
437 +}
438 +
439 +static HRESULT RmuApplicationArrayAlloc(
440 + __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications,
441 + __inout LPUINT pcApplications,
442 + __in DWORD dwProcessId,
443 + __in FILETIME ProcessStartTime
444 + )
445 +{
446 + HRESULT hr = S_OK;
447 + RM_UNIQUE_PROCESS *pApplication = NULL;
448 +
449 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE);
450 + ExitOnFailure(hr, "Failed to allocate memory for the application array.");
451 +
452 + pApplication = static_cast<RM_UNIQUE_PROCESS*>(&(*prgApplications)[*pcApplications]);
453 + pApplication->dwProcessId = dwProcessId;
454 + pApplication->ProcessStartTime = ProcessStartTime;
455 +
456 + ++(*pcApplications);
457 +
458 +LExit:
459 + return hr;
460 +}
461 +
462 +static HRESULT RmuApplicationArrayFree(
463 + __in RM_UNIQUE_PROCESS *rgApplications
464 + )
465 +{
466 + HRESULT hr = S_OK;
467 +
468 + hr = MemFree(rgApplications);
469 + ExitOnFailure(hr, "Failed to free memory for the application array.");
470 +
471 +LExit:
472 + return hr;
473 +}
src/dutil/rssutil.cpp new
+632
@@ -0,0 +1,632 @@
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 +
5 +static HRESULT ParseRssDocument(
6 + __in IXMLDOMDocument *pixd,
7 + __out RSS_CHANNEL **ppChannel
8 + );
9 +static HRESULT ParseRssChannel(
10 + __in IXMLDOMNode *pixnChannel,
11 + __out RSS_CHANNEL **ppChannel
12 + );
13 +static HRESULT ParseRssItem(
14 + __in IXMLDOMNode *pixnItem,
15 + __in DWORD cItem,
16 + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
17 + );
18 +static HRESULT ParseRssUnknownElement(
19 + __in IXMLDOMNode *pNode,
20 + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement
21 + );
22 +static HRESULT ParseRssUnknownAttribute(
23 + __in IXMLDOMNode *pNode,
24 + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
25 + );
26 +static void FreeRssUnknownElementList(
27 + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement
28 + );
29 +static void FreeRssUnknownAttributeList(
30 + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute
31 + );
32 +
33 +
34 +/********************************************************************
35 + RssInitialize - Initialize RSS utilities.
36 +
37 +*********************************************************************/
38 +extern "C" HRESULT DAPI RssInitialize()
39 +{
40 + return XmlInitialize();
41 +}
42 +
43 +
44 +/********************************************************************
45 + RssUninitialize - Uninitialize RSS utilities.
46 +
47 +*********************************************************************/
48 +extern "C" void DAPI RssUninitialize()
49 +{
50 + XmlUninitialize();
51 +}
52 +
53 +
54 +/********************************************************************
55 + RssParseFromString - parses out an RSS channel from a string.
56 +
57 +*********************************************************************/
58 +extern "C" HRESULT DAPI RssParseFromString(
59 + __in_z LPCWSTR wzRssString,
60 + __out RSS_CHANNEL **ppChannel
61 + )
62 +{
63 + Assert(wzRssString);
64 + Assert(ppChannel);
65 +
66 + HRESULT hr = S_OK;
67 + RSS_CHANNEL *pNewChannel = NULL;
68 + IXMLDOMDocument *pixdRss = NULL;
69 +
70 + hr = XmlLoadDocument(wzRssString, &pixdRss);
71 + ExitOnFailure(hr, "Failed to load RSS string as XML document.");
72 +
73 + hr = ParseRssDocument(pixdRss, &pNewChannel);
74 + ExitOnFailure(hr, "Failed to parse RSS document.");
75 +
76 + *ppChannel = pNewChannel;
77 + pNewChannel = NULL;
78 +
79 +LExit:
80 + ReleaseObject(pixdRss);
81 +
82 + ReleaseRssChannel(pNewChannel);
83 +
84 + return hr;
85 +}
86 +
87 +
88 +/********************************************************************
89 + RssParseFromFile - parses out an RSS channel from a file path.
90 +
91 +*********************************************************************/
92 +extern "C" HRESULT DAPI RssParseFromFile(
93 + __in_z LPCWSTR wzRssFile,
94 + __out RSS_CHANNEL **ppChannel
95 + )
96 +{
97 + Assert(wzRssFile);
98 + Assert(ppChannel);
99 +
100 + HRESULT hr = S_OK;
101 + RSS_CHANNEL *pNewChannel = NULL;
102 + IXMLDOMDocument *pixdRss = NULL;
103 +
104 + hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss);
105 + ExitOnFailure(hr, "Failed to load RSS string as XML document.");
106 +
107 + hr = ParseRssDocument(pixdRss, &pNewChannel);
108 + ExitOnFailure(hr, "Failed to parse RSS document.");
109 +
110 + *ppChannel = pNewChannel;
111 + pNewChannel = NULL;
112 +
113 +LExit:
114 + ReleaseObject(pixdRss);
115 +
116 + ReleaseRssChannel(pNewChannel);
117 +
118 + return hr;
119 +}
120 +
121 +
122 +/********************************************************************
123 + RssFreeChannel - parses out an RSS channel from a string.
124 +
125 +*********************************************************************/
126 +extern "C" void DAPI RssFreeChannel(
127 + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
128 + )
129 +{
130 + if (pChannel)
131 + {
132 + for (DWORD i = 0; i < pChannel->cItems; ++i)
133 + {
134 + ReleaseStr(pChannel->rgItems[i].wzTitle);
135 + ReleaseStr(pChannel->rgItems[i].wzLink);
136 + ReleaseStr(pChannel->rgItems[i].wzDescription);
137 + ReleaseStr(pChannel->rgItems[i].wzGuid);
138 + ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl);
139 + ReleaseStr(pChannel->rgItems[i].wzEnclosureType);
140 +
141 + FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements);
142 + }
143 +
144 + ReleaseStr(pChannel->wzTitle);
145 + ReleaseStr(pChannel->wzLink);
146 + ReleaseStr(pChannel->wzDescription);
147 + FreeRssUnknownElementList(pChannel->pUnknownElements);
148 +
149 + MemFree(pChannel);
150 + }
151 +}
152 +
153 +
154 +/********************************************************************
155 + ParseRssDocument - parses out an RSS channel from a loaded XML DOM document.
156 +
157 +*********************************************************************/
158 +static HRESULT ParseRssDocument(
159 + __in IXMLDOMDocument *pixd,
160 + __out RSS_CHANNEL **ppChannel
161 + )
162 +{
163 + Assert(pixd);
164 + Assert(ppChannel);
165 +
166 + HRESULT hr = S_OK;
167 + IXMLDOMElement *pRssElement = NULL;
168 + IXMLDOMNodeList *pChannelNodes = NULL;
169 + IXMLDOMNode *pNode = NULL;
170 + BSTR bstrNodeName = NULL;
171 +
172 + RSS_CHANNEL *pNewChannel = NULL;
173 +
174 + //
175 + // Get the document element and start processing channels.
176 + //
177 + hr = pixd ->get_documentElement(&pRssElement);
178 + ExitOnFailure(hr, "failed get_documentElement in ParseRssDocument");
179 +
180 + hr = pRssElement->get_childNodes(&pChannelNodes);
181 + ExitOnFailure(hr, "Failed to get child nodes of Rss Document element.");
182 +
183 + while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName)))
184 + {
185 + if (0 == lstrcmpW(bstrNodeName, L"channel"))
186 + {
187 + hr = ParseRssChannel(pNode, &pNewChannel);
188 + ExitOnFailure(hr, "Failed to parse RSS channel.");
189 + }
190 + else if (0 == lstrcmpW(bstrNodeName, L"link"))
191 + {
192 + }
193 +
194 + ReleaseNullBSTR(bstrNodeName);
195 + ReleaseNullObject(pNode);
196 + }
197 +
198 + if (S_FALSE == hr)
199 + {
200 + hr = S_OK;
201 + }
202 +
203 + *ppChannel = pNewChannel;
204 + pNewChannel = NULL;
205 +
206 +LExit:
207 + ReleaseBSTR(bstrNodeName);
208 + ReleaseObject(pNode);
209 + ReleaseObject(pChannelNodes);
210 + ReleaseObject(pRssElement);
211 +
212 + ReleaseRssChannel(pNewChannel);
213 +
214 + return hr;
215 +}
216 +
217 +
218 +/********************************************************************
219 + ParseRssChannel - parses out an RSS channel from a loaded XML DOM element.
220 +
221 +*********************************************************************/
222 +static HRESULT ParseRssChannel(
223 + __in IXMLDOMNode *pixnChannel,
224 + __out RSS_CHANNEL **ppChannel
225 + )
226 +{
227 + Assert(pixnChannel);
228 + Assert(ppChannel);
229 +
230 + HRESULT hr = S_OK;
231 + IXMLDOMNodeList *pNodeList = NULL;
232 +
233 + RSS_CHANNEL *pNewChannel = NULL;
234 + long cItems = 0;
235 +
236 + IXMLDOMNode *pNode = NULL;
237 + BSTR bstrNodeName = NULL;
238 + BSTR bstrNodeValue = NULL;
239 +
240 + //
241 + // First, calculate how many RSS items we're going to have and allocate
242 + // the RSS_CHANNEL structure
243 + //
244 + hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList);
245 + ExitOnFailure(hr, "Failed to select all RSS items in an RSS channel.");
246 +
247 + hr = pNodeList->get_length(&cItems);
248 + ExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel.");
249 +
250 + pNewChannel = static_cast<RSS_CHANNEL*>(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE));
251 + ExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure.");
252 +
253 + pNewChannel->cItems = cItems;
254 +
255 + //
256 + // Process the elements under a channel now.
257 + //
258 + hr = pixnChannel->get_childNodes(&pNodeList);
259 + ExitOnFailure(hr, "Failed to get child nodes of RSS channel element.");
260 +
261 + cItems = 0; // reset the counter and use this to walk through the channel items
262 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
263 + {
264 + if (0 == lstrcmpW(bstrNodeName, L"title"))
265 + {
266 + hr = XmlGetText(pNode, &bstrNodeValue);
267 + ExitOnFailure(hr, "Failed to get RSS channel title.");
268 +
269 + hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0);
270 + ExitOnFailure(hr, "Failed to allocate RSS channel title.");
271 + }
272 + else if (0 == lstrcmpW(bstrNodeName, L"link"))
273 + {
274 + hr = XmlGetText(pNode, &bstrNodeValue);
275 + ExitOnFailure(hr, "Failed to get RSS channel link.");
276 +
277 + hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0);
278 + ExitOnFailure(hr, "Failed to allocate RSS channel link.");
279 + }
280 + else if (0 == lstrcmpW(bstrNodeName, L"description"))
281 + {
282 + hr = XmlGetText(pNode, &bstrNodeValue);
283 + ExitOnFailure(hr, "Failed to get RSS channel description.");
284 +
285 + hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0);
286 + ExitOnFailure(hr, "Failed to allocate RSS channel description.");
287 + }
288 + else if (0 == lstrcmpW(bstrNodeName, L"ttl"))
289 + {
290 + hr = XmlGetText(pNode, &bstrNodeValue);
291 + ExitOnFailure(hr, "Failed to get RSS channel description.");
292 +
293 + pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10);
294 + }
295 + else if (0 == lstrcmpW(bstrNodeName, L"item"))
296 + {
297 + hr = ParseRssItem(pNode, cItems, pNewChannel);
298 + ExitOnFailure(hr, "Failed to parse RSS item.");
299 +
300 + ++cItems;
301 + }
302 + else
303 + {
304 + hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements);
305 + ExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName);
306 + }
307 +
308 + ReleaseNullBSTR(bstrNodeValue);
309 + ReleaseNullBSTR(bstrNodeName);
310 + ReleaseNullObject(pNode);
311 + }
312 +
313 + *ppChannel = pNewChannel;
314 + pNewChannel = NULL;
315 +
316 +LExit:
317 + ReleaseBSTR(bstrNodeName);
318 + ReleaseObject(pNode);
319 + ReleaseObject(pNodeList);
320 +
321 + ReleaseRssChannel(pNewChannel);
322 +
323 + return hr;
324 +}
325 +
326 +
327 +/********************************************************************
328 + ParseRssItem - parses out an RSS item from a loaded XML DOM node.
329 +
330 +*********************************************************************/
331 +static HRESULT ParseRssItem(
332 + __in IXMLDOMNode *pixnItem,
333 + __in DWORD cItem,
334 + __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
335 + )
336 +{
337 + HRESULT hr = S_OK;
338 +
339 + RSS_ITEM *pItem = NULL;
340 + IXMLDOMNodeList *pNodeList = NULL;
341 +
342 + IXMLDOMNode *pNode = NULL;
343 + BSTR bstrNodeName = NULL;
344 + BSTR bstrNodeValue = NULL;
345 +
346 + //
347 + // First make sure we're dealing with a valid item.
348 + //
349 + if (pChannel->cItems <= cItem)
350 + {
351 + hr = E_UNEXPECTED;
352 + ExitOnFailure(hr, "Unexpected number of items parsed.");
353 + }
354 +
355 + pItem = pChannel->rgItems + cItem;
356 +
357 + //
358 + // Process the elements under an item now.
359 + //
360 + hr = pixnItem->get_childNodes(&pNodeList);
361 + ExitOnFailure(hr, "Failed to get child nodes of RSS item element.");
362 + while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
363 + {
364 + if (0 == lstrcmpW(bstrNodeName, L"title"))
365 + {
366 + hr = XmlGetText(pNode, &bstrNodeValue);
367 + ExitOnFailure(hr, "Failed to get RSS channel title.");
368 +
369 + hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0);
370 + ExitOnFailure(hr, "Failed to allocate RSS item title.");
371 + }
372 + else if (0 == lstrcmpW(bstrNodeName, L"link"))
373 + {
374 + hr = XmlGetText(pNode, &bstrNodeValue);
375 + ExitOnFailure(hr, "Failed to get RSS channel link.");
376 +
377 + hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0);
378 + ExitOnFailure(hr, "Failed to allocate RSS item link.");
379 + }
380 + else if (0 == lstrcmpW(bstrNodeName, L"description"))
381 + {
382 + hr = XmlGetText(pNode, &bstrNodeValue);
383 + ExitOnFailure(hr, "Failed to get RSS item description.");
384 +
385 + hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0);
386 + ExitOnFailure(hr, "Failed to allocate RSS item description.");
387 + }
388 + else if (0 == lstrcmpW(bstrNodeName, L"guid"))
389 + {
390 + hr = XmlGetText(pNode, &bstrNodeValue);
391 + ExitOnFailure(hr, "Failed to get RSS item guid.");
392 +
393 + hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0);
394 + ExitOnFailure(hr, "Failed to allocate RSS item guid.");
395 + }
396 + else if (0 == lstrcmpW(bstrNodeName, L"pubDate"))
397 + {
398 + hr = XmlGetText(pNode, &bstrNodeValue);
399 + ExitOnFailure(hr, "Failed to get RSS item guid.");
400 +
401 + hr = TimeFromString(bstrNodeValue, &pItem->ftPublished);
402 + ExitOnFailure(hr, "Failed to convert RSS item time.");
403 + }
404 + else if (0 == lstrcmpW(bstrNodeName, L"enclosure"))
405 + {
406 + hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue);
407 + ExitOnFailure(hr, "Failed to get RSS item enclosure url.");
408 +
409 + hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0);
410 + ExitOnFailure(hr, "Failed to allocate RSS item enclosure url.");
411 + ReleaseNullBSTR(bstrNodeValue);
412 +
413 + hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize);
414 + ExitOnFailure(hr, "Failed to get RSS item enclosure length.");
415 +
416 + hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue);
417 + ExitOnFailure(hr, "Failed to get RSS item enclosure type.");
418 +
419 + hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0);
420 + ExitOnFailure(hr, "Failed to allocate RSS item enclosure type.");
421 + }
422 + else
423 + {
424 + hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements);
425 + ExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName);
426 + }
427 +
428 + ReleaseNullBSTR(bstrNodeValue);
429 + ReleaseNullBSTR(bstrNodeName);
430 + ReleaseNullObject(pNode);
431 + }
432 +
433 +LExit:
434 + ReleaseBSTR(bstrNodeValue);
435 + ReleaseBSTR(bstrNodeName);
436 + ReleaseObject(pNode);
437 + ReleaseObject(pNodeList);
438 +
439 + return hr;
440 +}
441 +
442 +
443 +/********************************************************************
444 + ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node.
445 +
446 +*********************************************************************/
447 +static HRESULT ParseRssUnknownElement(
448 + __in IXMLDOMNode *pNode,
449 + __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement
450 + )
451 +{
452 + Assert(ppUnknownElement);
453 +
454 + HRESULT hr = S_OK;
455 + BSTR bstrNodeNamespace = NULL;
456 + BSTR bstrNodeName = NULL;
457 + BSTR bstrNodeValue = NULL;
458 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
459 + IXMLDOMNode* pixnAttribute = NULL;
460 + RSS_UNKNOWN_ELEMENT* pNewUnknownElement;
461 +
462 + pNewUnknownElement = static_cast<RSS_UNKNOWN_ELEMENT*>(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE));
463 + ExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element.");
464 +
465 + hr = pNode->get_namespaceURI(&bstrNodeNamespace);
466 + if (S_OK == hr)
467 + {
468 + hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0);
469 + ExitOnFailure(hr, "Failed to allocate RSS unknown element namespace.");
470 + }
471 + else if (S_FALSE == hr)
472 + {
473 + hr = S_OK;
474 + }
475 + ExitOnFailure(hr, "Failed to get unknown element namespace.");
476 +
477 + hr = pNode->get_baseName(&bstrNodeName);
478 + ExitOnFailure(hr, "Failed to get unknown element name.");
479 +
480 + hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0);
481 + ExitOnFailure(hr, "Failed to allocate RSS unknown element name.");
482 +
483 + hr = XmlGetText(pNode, &bstrNodeValue);
484 + ExitOnFailure(hr, "Failed to get unknown element value.");
485 +
486 + hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0);
487 + ExitOnFailure(hr, "Failed to allocate RSS unknown element value.");
488 +
489 + hr = pNode->get_attributes(&pixnnmAttributes);
490 + ExitOnFailure(hr, "Failed get attributes on RSS unknown element.");
491 +
492 + while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute)))
493 + {
494 + hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes);
495 + ExitOnFailure(hr, "Failed to parse attribute on RSS unknown element.");
496 +
497 + ReleaseNullObject(pixnAttribute);
498 + }
499 +
500 + if (S_FALSE == hr)
501 + {
502 + hr = S_OK;
503 + }
504 + ExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element.");
505 +
506 + RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement;
507 + while (*ppTail)
508 + {
509 + ppTail = &(*ppTail)->pNext;
510 + }
511 +
512 + *ppTail = pNewUnknownElement;
513 + pNewUnknownElement = NULL;
514 +
515 +LExit:
516 + FreeRssUnknownElementList(pNewUnknownElement);
517 +
518 + ReleaseBSTR(bstrNodeNamespace);
519 + ReleaseBSTR(bstrNodeName);
520 + ReleaseBSTR(bstrNodeValue);
521 + ReleaseObject(pixnnmAttributes);
522 + ReleaseObject(pixnAttribute);
523 +
524 + return hr;
525 +}
526 +
527 +
528 +/********************************************************************
529 + ParseRssUnknownAttribute - parses out attribute from an unknown element
530 +
531 +*********************************************************************/
532 +static HRESULT ParseRssUnknownAttribute(
533 + __in IXMLDOMNode *pNode,
534 + __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
535 + )
536 +{
537 + Assert(ppUnknownAttribute);
538 +
539 + HRESULT hr = S_OK;
540 + BSTR bstrNodeNamespace = NULL;
541 + BSTR bstrNodeName = NULL;
542 + BSTR bstrNodeValue = NULL;
543 + RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute;
544 +
545 + pNewUnknownAttribute = static_cast<RSS_UNKNOWN_ATTRIBUTE*>(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE));
546 + ExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute.");
547 +
548 + hr = pNode->get_namespaceURI(&bstrNodeNamespace);
549 + if (S_OK == hr)
550 + {
551 + hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0);
552 + ExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace.");
553 + }
554 + else if (S_FALSE == hr)
555 + {
556 + hr = S_OK;
557 + }
558 + ExitOnFailure(hr, "Failed to get unknown attribute namespace.");
559 +
560 + hr = pNode->get_baseName(&bstrNodeName);
561 + ExitOnFailure(hr, "Failed to get unknown attribute name.");
562 +
563 + hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0);
564 + ExitOnFailure(hr, "Failed to allocate RSS unknown attribute name.");
565 +
566 + hr = XmlGetText(pNode, &bstrNodeValue);
567 + ExitOnFailure(hr, "Failed to get unknown attribute value.");
568 +
569 + hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0);
570 + ExitOnFailure(hr, "Failed to allocate RSS unknown attribute value.");
571 +
572 + RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute;
573 + while (*ppTail)
574 + {
575 + ppTail = &(*ppTail)->pNext;
576 + }
577 +
578 + *ppTail = pNewUnknownAttribute;
579 + pNewUnknownAttribute = NULL;
580 +
581 +LExit:
582 + FreeRssUnknownAttributeList(pNewUnknownAttribute);
583 +
584 + ReleaseBSTR(bstrNodeNamespace);
585 + ReleaseBSTR(bstrNodeName);
586 + ReleaseBSTR(bstrNodeValue);
587 +
588 + return hr;
589 +}
590 +
591 +
592 +/********************************************************************
593 + FreeRssUnknownElement - releases all of the memory used by a list of unknown elements
594 +
595 +*********************************************************************/
596 +static void FreeRssUnknownElementList(
597 + __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement
598 + )
599 +{
600 + while (pUnknownElement)
601 + {
602 + RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement;
603 + pUnknownElement = pUnknownElement->pNext;
604 +
605 + FreeRssUnknownAttributeList(pFree->pAttributes);
606 + ReleaseStr(pFree->wzNamespace);
607 + ReleaseStr(pFree->wzElement);
608 + ReleaseStr(pFree->wzValue);
609 + MemFree(pFree);
610 + }
611 +}
612 +
613 +
614 +/********************************************************************
615 + FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes
616 +
617 +*********************************************************************/
618 +static void FreeRssUnknownAttributeList(
619 + __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute
620 + )
621 +{
622 + while (pUnknownAttribute)
623 + {
624 + RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute;
625 + pUnknownAttribute = pUnknownAttribute->pNext;
626 +
627 + ReleaseStr(pFree->wzNamespace);
628 + ReleaseStr(pFree->wzAttribute);
629 + ReleaseStr(pFree->wzValue);
630 + MemFree(pFree);
631 + }
632 +}
src/dutil/sceutil.cpp new
+2489
@@ -0,0 +1,2489 @@
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 +
5 +#include "sceutil.h"
6 +
7 +// Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB.
8 +#define MAX_SQLCE_DATABASE_SIZE 4091
9 +
10 +// In case of some older versions of sqlce_oledb.h don't have these definitions, define some types.
11 +#ifndef DBTYPEFOR_DBLENGTH
12 +#ifdef _WIN64
13 +#define SKIP_SCE_COMPILE
14 +#else
15 +#define SCE_32BIT_ONLY
16 +typedef DWORD DBLENGTH;
17 +typedef LONG DBROWOFFSET;
18 +typedef LONG DBROWCOUNT;
19 +typedef DWORD DBCOUNTITEM;
20 +typedef DWORD DBORDINAL;
21 +typedef LONG DB_LORDINAL;
22 +typedef DWORD DBBKMARK;
23 +typedef DWORD DBBYTEOFFSET;
24 +typedef DWORD DBREFCOUNT;
25 +typedef DWORD DB_UPARAMS;
26 +typedef LONG DB_LPARAMS;
27 +typedef DWORD DBHASHVALUE;
28 +typedef DWORD DB_DWRESERVE;
29 +typedef LONG DB_LRESERVE;
30 +typedef DWORD DB_URESERVE;
31 +#endif
32 +
33 +#endif
34 +
35 +#ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit
36 +
37 +// structs
38 +struct SCE_DATABASE_INTERNAL
39 +{
40 + // In case we call DllGetClassObject on a specific file
41 + HMODULE hSqlCeDll;
42 +
43 + volatile LONG dwTransactionRefcount;
44 + IDBInitialize *pIDBInitialize;
45 + IDBCreateSession *pIDBCreateSession;
46 + ITransactionLocal *pITransactionLocal;
47 + IDBProperties *pIDBProperties;
48 + IOpenRowset *pIOpenRowset;
49 + ISessionProperties *pISessionProperties;
50 +
51 + BOOL fChanges; // This database has changed
52 + BOOL fPendingChanges; // Some changes are pending, upon transaction commit
53 + BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back
54 + BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail
55 +
56 + // If the database was opened as read-only, we copied it here - so delete it on close
57 + LPWSTR sczTempDbFile;
58 +};
59 +
60 +struct SCE_ROW
61 +{
62 + SCE_DATABASE_INTERNAL *pDatabaseInternal;
63 +
64 + SCE_TABLE_SCHEMA *pTableSchema;
65 + IRowset *pIRowset;
66 + HROW hRow;
67 + BOOL fInserting;
68 +
69 + DWORD dwBindingIndex;
70 + DBBINDING *rgBinding;
71 + SIZE_T cbOffset;
72 + BYTE *pbData;
73 +};
74 +
75 +struct SCE_QUERY
76 +{
77 + SCE_TABLE_SCHEMA *pTableSchema;
78 + SCE_INDEX_SCHEMA *pIndexSchema;
79 + SCE_DATABASE_INTERNAL *pDatabaseInternal;
80 +
81 + // Accessor build-up members
82 + DWORD dwBindingIndex;
83 + DBBINDING *rgBinding;
84 + SIZE_T cbOffset;
85 + BYTE *pbData;
86 +};
87 +
88 +struct SCE_QUERY_RESULTS
89 +{
90 + SCE_DATABASE_INTERNAL *pDatabaseInternal;
91 + IRowset *pIRowset;
92 + SCE_TABLE_SCHEMA *pTableSchema;
93 +};
94 +
95 +extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW);
96 +extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY);
97 +extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS);
98 +
99 +// The following is the internal Sce-maintained table to tell the identifier and version of the schema
100 +const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] =
101 +{
102 + {
103 + L"AppIdentifier",
104 + DBTYPE_WSTR,
105 + 0,
106 + FALSE,
107 + TRUE,
108 + FALSE,
109 + NULL,
110 + 0,
111 + 0
112 + },
113 + {
114 + L"Version",
115 + DBTYPE_I4,
116 + 0,
117 + FALSE,
118 + FALSE,
119 + FALSE,
120 + NULL,
121 + 0,
122 + 0
123 + }
124 +};
125 +
126 +const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] =
127 +{
128 + L"SceSchemaTablev1",
129 + _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA),
130 + (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA,
131 + 0,
132 + NULL,
133 + NULL,
134 + NULL
135 +};
136 +
137 +// internal function declarations
138 +
139 +// Creates an instance of SQL Server CE object, returning IDBInitialize object
140 +// If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject
141 +// on that file specifically. Otherwise it calls CoCreateInstance
142 +static HRESULT CreateSqlCe(
143 + __in_z_opt LPCWSTR wzSqlCeDllPath,
144 + __out IDBInitialize **ppIDBInitialize,
145 + __out_opt HMODULE *phSqlCeDll
146 + );
147 +static HRESULT RunQuery(
148 + __in BOOL fRange,
149 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle,
150 + __out SCE_QUERY_RESULTS **ppsqrhHandle
151 + );
152 +static HRESULT FillOutColumnDescFromSchema(
153 + __in const SCE_COLUMN_SCHEMA *pSchema,
154 + __out DBCOLUMNDESC pColumnDesc
155 + );
156 +static HRESULT EnsureSchema(
157 + __in SCE_DATABASE *pDatabase,
158 + __in SCE_DATABASE_SCHEMA *pDatabaseSchema
159 + );
160 +static HRESULT OpenSchema(
161 + __in SCE_DATABASE *pDatabase,
162 + __in SCE_DATABASE_SCHEMA *pdsSchema
163 + );
164 +static HRESULT SetColumnValue(
165 + __in const SCE_TABLE_SCHEMA *pTableSchema,
166 + __in DWORD dwColumnIndex,
167 + __in_bcount_opt(cbSize) const BYTE *pbData,
168 + __in SIZE_T cbSize,
169 + __inout DBBINDING *pBinding,
170 + __inout SIZE_T *pcbOffset,
171 + __inout BYTE **ppbBuffer
172 + );
173 +static HRESULT GetColumnValue(
174 + __in SCE_ROW *pRow,
175 + __in DWORD dwColumnIndex,
176 + __out_opt BYTE **ppbData,
177 + __out SIZE_T *pcbSize
178 + );
179 +static HRESULT GetColumnValueFixed(
180 + __in SCE_ROW *pRow,
181 + __in DWORD dwColumnIndex,
182 + __in DWORD cbSize,
183 + __out BYTE *pbData
184 + );
185 +static HRESULT EnsureLocalColumnConstraints(
186 + __in ITableDefinition *pTableDefinition,
187 + __in DBID *pTableID,
188 + __in SCE_TABLE_SCHEMA *pTableSchema
189 + );
190 +static HRESULT EnsureForeignColumnConstraints(
191 + __in ITableDefinition *pTableDefinition,
192 + __in DBID *pTableID,
193 + __in SCE_TABLE_SCHEMA *pTableSchema,
194 + __in SCE_DATABASE_SCHEMA *pDatabaseSchema
195 + );
196 +static HRESULT SetSessionProperties(
197 + __in ISessionProperties *pISessionProperties
198 + );
199 +static HRESULT GetDatabaseSchemaInfo(
200 + __in SCE_DATABASE *pDatabase,
201 + __out LPWSTR *psczSchemaType,
202 + __out DWORD *pdwVersion
203 + );
204 +static HRESULT SetDatabaseSchemaInfo(
205 + __in SCE_DATABASE *pDatabase,
206 + __in LPCWSTR wzSchemaType,
207 + __in DWORD dwVersion
208 + );
209 +static void ReleaseDatabase(
210 + SCE_DATABASE *pDatabase
211 + );
212 +static void ReleaseDatabaseInternal(
213 + SCE_DATABASE_INTERNAL *pDatabaseInternal
214 + );
215 +
216 +// function definitions
217 +extern "C" HRESULT DAPI SceCreateDatabase(
218 + __in_z LPCWSTR sczFile,
219 + __in_z_opt LPCWSTR wzSqlCeDllPath,
220 + __out SCE_DATABASE **ppDatabase
221 + )
222 +{
223 + HRESULT hr = S_OK;
224 + LPWSTR sczDirectory = NULL;
225 + SCE_DATABASE *pNewSceDatabase = NULL;
226 + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL;
227 + IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL;
228 + DBPROPSET rgdbpDataSourcePropSet[2] = { };
229 + DBPROP rgdbpDataSourceProp[2] = { };
230 + DBPROP rgdbpDataSourceSsceProp[1] = { };
231 +
232 + pNewSceDatabase = reinterpret_cast<SCE_DATABASE *>(MemAlloc(sizeof(SCE_DATABASE), TRUE));
233 + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct");
234 +
235 + pNewSceDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE));
236 + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct");
237 +
238 + pNewSceDatabase->sdbHandle = reinterpret_cast<void *>(pNewSceDatabaseInternal);
239 +
240 + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll);
241 + ExitOnFailure(hr, "Failed to get IDBInitialize interface");
242 +
243 + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast<void **>(&pIDBDataSourceAdmin));
244 + ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface");
245 +
246 + hr = PathGetDirectory(sczFile, &sczDirectory);
247 + ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile);
248 +
249 + hr = DirEnsureExists(sczDirectory, NULL);
250 + ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory);
251 +
252 + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
253 + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
254 + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR;
255 + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile);
256 +
257 + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE;
258 + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
259 + rgdbpDataSourceProp[1].vValue.vt = VT_I4;
260 + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE;
261 +
262 + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT
263 + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT;
264 + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
265 + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp);
266 +
267 + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE;
268 + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
269 + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4;
270 + rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE;
271 +
272 + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT;
273 + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp;
274 + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp);
275 +
276 + hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL);
277 + ExitOnFailure(hr, "Failed to create data source");
278 +
279 + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBProperties));
280 + ExitOnFailure(hr, "Failed to get IDBProperties interface");
281 +
282 + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBCreateSession));
283 + ExitOnFailure(hr, "Failed to get IDBCreateSession interface");
284 +
285 + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast<IUnknown **>(&pNewSceDatabaseInternal->pISessionProperties));
286 + ExitOnFailure(hr, "Failed to get ISessionProperties interface");
287 +
288 + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties);
289 + ExitOnFailure(hr, "Failed to set session properties");
290 +
291 + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIOpenRowset));
292 + ExitOnFailure(hr, "Failed to get IOpenRowset interface");
293 +
294 + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pITransactionLocal));
295 + ExitOnFailure(hr, "Failed to get ITransactionLocal interface");
296 +
297 + *ppDatabase = pNewSceDatabase;
298 + pNewSceDatabase = NULL;
299 +
300 +LExit:
301 + ReleaseStr(sczDirectory);
302 + ReleaseObject(pIDBDataSourceAdmin);
303 + ReleaseDatabase(pNewSceDatabase);
304 + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
305 +
306 + return hr;
307 +}
308 +
309 +extern "C" HRESULT DAPI SceOpenDatabase(
310 + __in_z LPCWSTR sczFile,
311 + __in_z_opt LPCWSTR wzSqlCeDllPath,
312 + __in LPCWSTR wzExpectedSchemaType,
313 + __in DWORD dwExpectedVersion,
314 + __out SCE_DATABASE **ppDatabase,
315 + __in BOOL fReadOnly
316 + )
317 +{
318 + HRESULT hr = S_OK;
319 + DWORD dwVersionFound = 0;
320 + WCHAR wzTempDbFile[MAX_PATH];
321 + LPCWSTR wzPathToOpen = NULL;
322 + LPWSTR sczSchemaType = NULL;
323 + SCE_DATABASE *pNewSceDatabase = NULL;
324 + SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL;
325 + DBPROPSET rgdbpDataSourcePropSet[2] = { };
326 + DBPROP rgdbpDataSourceProp[2] = { };
327 + DBPROP rgdbpDataSourceSsceProp[1] = { };
328 +
329 + pNewSceDatabase = reinterpret_cast<SCE_DATABASE *>(MemAlloc(sizeof(SCE_DATABASE), TRUE));
330 + ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct");
331 +
332 + pNewSceDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE));
333 + ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct");
334 +
335 + pNewSceDatabase->sdbHandle = reinterpret_cast<void *>(pNewSceDatabaseInternal);
336 +
337 + hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll);
338 + ExitOnFailure(hr, "Failed to get IDBInitialize interface");
339 +
340 + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBProperties));
341 + ExitOnFailure(hr, "Failed to get IDBProperties interface");
342 +
343 + // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now.
344 + if (fReadOnly)
345 + {
346 + hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile));
347 + ExitOnFailure(hr, "Failed to get temp path");
348 +
349 + hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE);
350 + ExitOnFailure(hr, "Failed to copy file to temp path");
351 +
352 + hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0);
353 + ExitOnFailure(hr, "Failed to copy temp db file path");
354 +
355 + wzPathToOpen = (LPCWSTR)wzTempDbFile;
356 + }
357 + else
358 + {
359 + wzPathToOpen = sczFile;
360 + }
361 +
362 + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
363 + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
364 + rgdbpDataSourceProp[0].vValue.vt = VT_BSTR;
365 + rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen);
366 +
367 + rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE;
368 + rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
369 + rgdbpDataSourceProp[1].vValue.vt = VT_I4;
370 + rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE;
371 +
372 + // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT
373 + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT;
374 + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
375 + rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp);
376 +
377 + rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE;
378 + rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
379 + rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4;
380 + rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE;
381 +
382 + rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT;
383 + rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp;
384 + rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp);
385 +
386 + hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet);
387 + ExitOnFailure(hr, "Failed to set initial properties to open database");
388 +
389 + hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize();
390 + ExitOnFailure(hr, "Failed to open database: %ls", sczFile);
391 +
392 + hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBCreateSession));
393 + ExitOnFailure(hr, "Failed to get IDBCreateSession interface");
394 +
395 + hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast<IUnknown **>(&pNewSceDatabaseInternal->pISessionProperties));
396 + ExitOnFailure(hr, "Failed to get ISessionProperties interface");
397 +
398 + hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties);
399 + ExitOnFailure(hr, "Failed to set session properties");
400 +
401 + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIOpenRowset));
402 + ExitOnFailure(hr, "Failed to get IOpenRowset interface");
403 +
404 + hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pITransactionLocal));
405 + ExitOnFailure(hr, "Failed to get ITransactionLocal interface");
406 +
407 + hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound);
408 + ExitOnFailure(hr, "Failed to find schema version of database");
409 +
410 + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1))
411 + {
412 + hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE);
413 + ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType);
414 + }
415 + else if (dwVersionFound != dwExpectedVersion)
416 + {
417 + hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
418 + ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound);
419 + }
420 +
421 + *ppDatabase = pNewSceDatabase;
422 + pNewSceDatabase = NULL;
423 +
424 +LExit:
425 + ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
426 + ReleaseStr(sczSchemaType);
427 + ReleaseDatabase(pNewSceDatabase);
428 +
429 + return hr;
430 +}
431 +
432 +extern "C" HRESULT DAPI SceEnsureDatabase(
433 + __in_z LPCWSTR sczFile,
434 + __in_z_opt LPCWSTR wzSqlCeDllPath,
435 + __in LPCWSTR wzSchemaType,
436 + __in DWORD dwExpectedVersion,
437 + __in SCE_DATABASE_SCHEMA *pdsSchema,
438 + __out SCE_DATABASE **ppDatabase
439 + )
440 +{
441 + HRESULT hr = S_OK;
442 + SCE_DATABASE *pDatabase = NULL;
443 +
444 + if (FileExistsEx(sczFile, NULL))
445 + {
446 + hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE);
447 + ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile);
448 + }
449 + else
450 + {
451 + hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase);
452 + ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile);
453 +
454 + hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion);
455 + ExitOnFailure(hr, "Failed to set schema version of database");
456 + }
457 +
458 + hr = EnsureSchema(pDatabase, pdsSchema);
459 + ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile);
460 +
461 + // Keep a pointer to the schema in the SCE_DATABASE object for future reference
462 + pDatabase->pdsSchema = pdsSchema;
463 +
464 + *ppDatabase = pDatabase;
465 + pDatabase = NULL;
466 +
467 +LExit:
468 + ReleaseDatabase(pDatabase);
469 +
470 + return hr;
471 +}
472 +
473 +extern "C" HRESULT DAPI SceIsTableEmpty(
474 + __in SCE_DATABASE *pDatabase,
475 + __in DWORD dwTableIndex,
476 + __out BOOL *pfEmpty
477 + )
478 +{
479 + HRESULT hr = S_OK;
480 + SCE_ROW_HANDLE row = NULL;
481 +
482 + hr = SceGetFirstRow(pDatabase, dwTableIndex, &row);
483 + if (E_NOTFOUND == hr)
484 + {
485 + *pfEmpty = TRUE;
486 + ExitFunction1(hr = S_OK);
487 + }
488 + ExitOnFailure(hr, "Failed to get first row while checking if table is empty");
489 +
490 + *pfEmpty = FALSE;
491 +
492 +LExit:
493 + ReleaseSceRow(row);
494 +
495 + return hr;
496 +}
497 +
498 +extern "C" HRESULT DAPI SceGetFirstRow(
499 + __in SCE_DATABASE *pDatabase,
500 + __in DWORD dwTableIndex,
501 + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
502 + )
503 +{
504 + HRESULT hr = S_OK;
505 + DBCOUNTITEM cRowsObtained = 0;
506 + HROW hRow = DB_NULL_HROW;
507 + HROW *phRow = &hRow;
508 + SCE_ROW *pRow = NULL;
509 + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
510 +
511 + hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER);
512 + ExitOnFailure(hr, "Failed to reset IRowset position to beginning");
513 +
514 + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
515 + if (DB_S_ENDOFROWSET == hr)
516 + {
517 + ExitFunction1(hr = E_NOTFOUND);
518 + }
519 + ExitOnFailure(hr, "Failed to get next first row");
520 +
521 + pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
522 + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
523 +
524 + pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
525 + pRow->hRow = hRow;
526 + pRow->pTableSchema = pTable;
527 + pRow->pIRowset = pTable->pIRowset;
528 + pRow->pIRowset->AddRef();
529 +
530 + *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
531 +
532 +LExit:
533 + return hr;
534 +}
535 +
536 +HRESULT DAPI SceGetNextRow(
537 + __in SCE_DATABASE *pDatabase,
538 + __in DWORD dwTableIndex,
539 + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
540 + )
541 +{
542 + HRESULT hr = S_OK;
543 + DBCOUNTITEM cRowsObtained = 0;
544 + HROW hRow = DB_NULL_HROW;
545 + HROW *phRow = &hRow;
546 + SCE_ROW *pRow = NULL;
547 + SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
548 +
549 + hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
550 + if (DB_S_ENDOFROWSET == hr)
551 + {
552 + ExitFunction1(hr = E_NOTFOUND);
553 + }
554 + ExitOnFailure(hr, "Failed to get next first row");
555 +
556 + pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
557 + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
558 +
559 + pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
560 + pRow->hRow = hRow;
561 + pRow->pTableSchema = pTable;
562 + pRow->pIRowset = pTable->pIRowset;
563 + pRow->pIRowset->AddRef();
564 +
565 + *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
566 +
567 +LExit:
568 + return hr;
569 +}
570 +
571 +extern "C" HRESULT DAPI SceBeginTransaction(
572 + __in SCE_DATABASE *pDatabase
573 + )
574 +{
575 + HRESULT hr = S_OK;
576 + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
577 +
578 + if (pDatabaseInternal->fTransactionBadState)
579 + {
580 + // We're in a hosed transaction state - we can't start a new one
581 + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE));
582 + }
583 +
584 + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount);
585 +
586 + if (1 == pDatabaseInternal->dwTransactionRefcount)
587 + {
588 + hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL);
589 + ExitOnFailure(hr, "Failed to start transaction");
590 + }
591 +
592 +LExit:
593 + if (FAILED(hr))
594 + {
595 + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
596 + }
597 +
598 + return hr;
599 +}
600 +
601 +extern "C" HRESULT DAPI SceCommitTransaction(
602 + __in SCE_DATABASE *pDatabase
603 + )
604 +{
605 + HRESULT hr = S_OK;
606 + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
607 + Assert(0 < pDatabaseInternal->dwTransactionRefcount);
608 +
609 + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
610 +
611 + if (0 == pDatabaseInternal->dwTransactionRefcount)
612 + {
613 + if (pDatabaseInternal->fRollbackTransaction)
614 + {
615 + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE);
616 + ExitOnFailure(hr, "Failed to abort transaction");
617 + }
618 + else
619 + {
620 + hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0);
621 + ExitOnFailure(hr, "Failed to commit transaction");
622 + }
623 +
624 + if (pDatabaseInternal->fPendingChanges)
625 + {
626 + pDatabaseInternal->fPendingChanges = FALSE;
627 + pDatabaseInternal->fChanges = TRUE;
628 + }
629 +
630 + pDatabaseInternal->fRollbackTransaction = FALSE;
631 + }
632 +
633 +LExit:
634 + // If we tried to commit and failed, the caller should subsequently call rollback
635 + if (FAILED(hr))
636 + {
637 + ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount);
638 + }
639 +
640 + return hr;
641 +}
642 +
643 +extern "C" HRESULT DAPI SceRollbackTransaction(
644 + __in SCE_DATABASE *pDatabase
645 + )
646 +{
647 + HRESULT hr = S_OK;
648 + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
649 + Assert(0 < pDatabaseInternal->dwTransactionRefcount);
650 +
651 + ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
652 +
653 + if (0 == pDatabaseInternal->dwTransactionRefcount)
654 + {
655 + hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE);
656 + ExitOnFailure(hr, "Failed to abort transaction");
657 + pDatabaseInternal->fPendingChanges = FALSE;
658 +
659 + pDatabaseInternal->fRollbackTransaction = FALSE;
660 + }
661 + else
662 + {
663 + pDatabaseInternal->fRollbackTransaction = TRUE;
664 + }
665 +
666 +LExit:
667 + // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?)
668 + // but mark the database as bad so the user gets an error if they try to start a new transaction.
669 + if (FAILED(hr))
670 + {
671 + TraceError(hr, "Failed to rollback transaction");
672 + pDatabaseInternal->fTransactionBadState = TRUE;
673 + }
674 +
675 + return hr;
676 +}
677 +
678 +extern "C" HRESULT DAPI SceDeleteRow(
679 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
680 + )
681 +{
682 + HRESULT hr = S_OK;
683 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(*pRowHandle);
684 + IRowsetChange *pIRowsetChange = NULL;
685 + DBROWSTATUS rowStatus = DBROWSTATUS_S_OK;
686 +
687 + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pIRowsetChange));
688 + ExitOnFailure(hr, "Failed to get IRowsetChange interface");
689 +
690 + hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus);
691 + ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus);
692 +
693 + ReleaseNullSceRow(*pRowHandle);
694 +
695 +LExit:
696 + ReleaseObject(pIRowsetChange);
697 +
698 + return hr;
699 +}
700 +
701 +extern "C" HRESULT DAPI ScePrepareInsert(
702 + __in SCE_DATABASE *pDatabase,
703 + __in DWORD dwTableIndex,
704 + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
705 + )
706 +{
707 + HRESULT hr = S_OK;
708 + SCE_ROW *pRow = NULL;
709 +
710 + pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
711 + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
712 +
713 + pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
714 + pRow->hRow = DB_NULL_HROW;
715 + pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
716 + pRow->pIRowset = pRow->pTableSchema->pIRowset;
717 + pRow->pIRowset->AddRef();
718 + pRow->fInserting = TRUE;
719 +
720 + *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
721 + pRow = NULL;
722 +
723 +LExit:
724 + ReleaseSceRow(pRow);
725 +
726 + return hr;
727 +}
728 +
729 +extern "C" HRESULT DAPI SceFinishUpdate(
730 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
731 + )
732 +{
733 + HRESULT hr = S_OK;
734 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
735 + IAccessor *pIAccessor = NULL;
736 + IRowsetChange *pIRowsetChange = NULL;
737 + DBBINDSTATUS *rgBindStatus = NULL;
738 + HACCESSOR hAccessor = DB_NULL_HACCESSOR;
739 + HROW hRow = DB_NULL_HROW;
740 +
741 + hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
742 + ExitOnFailure(hr, "Failed to get IAccessor interface");
743 +
744 +// This can be used when stepping through the debugger to see bind failures
745 +#ifdef DEBUG
746 + if (0 < pRow->dwBindingIndex)
747 + {
748 + hr = MemEnsureArraySize(reinterpret_cast<void **>(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0);
749 + ExitOnFailure(hr, "Failed to ensure binding status array size");
750 + }
751 +#endif
752 +
753 + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus);
754 + ExitOnFailure(hr, "Failed to create accessor");
755 +
756 + hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pIRowsetChange));
757 + ExitOnFailure(hr, "Failed to get IRowsetChange interface");
758 +
759 + if (pRow->fInserting)
760 + {
761 + hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow);
762 + ExitOnFailure(hr, "Failed to insert new row");
763 +
764 + pRow->hRow = hRow;
765 + ReleaseNullObject(pRow->pIRowset);
766 + pRow->pIRowset = pRow->pTableSchema->pIRowset;
767 + pRow->pIRowset->AddRef();
768 + }
769 + else
770 + {
771 + hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData);
772 + ExitOnFailure(hr, "Failed to update existing row");
773 + }
774 +
775 + if (0 < pRow->pDatabaseInternal->dwTransactionRefcount)
776 + {
777 + pRow->pDatabaseInternal->fPendingChanges = TRUE;
778 + }
779 + else
780 + {
781 + pRow->pDatabaseInternal->fChanges = TRUE;
782 + }
783 +
784 +LExit:
785 + if (DB_NULL_HACCESSOR != hAccessor)
786 + {
787 + pIAccessor->ReleaseAccessor(hAccessor, NULL);
788 + }
789 + ReleaseMem(rgBindStatus);
790 + ReleaseObject(pIAccessor);
791 + ReleaseObject(pIRowsetChange);
792 +
793 + return hr;
794 +}
795 +
796 +extern "C" HRESULT DAPI SceSetColumnBinary(
797 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
798 + __in DWORD dwColumnIndex,
799 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
800 + __in SIZE_T cbBuffer
801 + )
802 +{
803 + HRESULT hr = S_OK;
804 + size_t cbAllocSize = 0;
805 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
806 +
807 + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
808 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns);
809 +
810 + if (NULL == pRow->rgBinding)
811 + {
812 + pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
813 + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
814 + }
815 +
816 + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
817 + ExitOnFailure(hr, "Failed to set column value as binary");
818 +
819 +LExit:
820 + return hr;
821 +}
822 +
823 +extern "C" HRESULT DAPI SceSetColumnDword(
824 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
825 + __in DWORD dwColumnIndex,
826 + __in const DWORD dwValue
827 + )
828 +{
829 + HRESULT hr = S_OK;
830 + size_t cbAllocSize = 0;
831 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
832 +
833 + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
834 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns);
835 +
836 + if (NULL == pRow->rgBinding)
837 + {
838 + pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
839 + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
840 + }
841 +
842 + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
843 + ExitOnFailure(hr, "Failed to set column value as binary");
844 +
845 +LExit:
846 + return hr;
847 +}
848 +
849 +extern "C" HRESULT DAPI SceSetColumnQword(
850 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
851 + __in DWORD dwColumnIndex,
852 + __in const DWORD64 qwValue
853 + )
854 +{
855 + HRESULT hr = S_OK;
856 + size_t cbAllocSize = 0;
857 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
858 +
859 + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
860 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns);
861 +
862 + if (NULL == pRow->rgBinding)
863 + {
864 + pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
865 + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
866 + }
867 +
868 + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
869 + ExitOnFailure(hr, "Failed to set column value as qword");
870 +
871 +LExit:
872 + return hr;
873 +}
874 +
875 +extern "C" HRESULT DAPI SceSetColumnBool(
876 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
877 + __in DWORD dwColumnIndex,
878 + __in const BOOL fValue
879 + )
880 +{
881 + HRESULT hr = S_OK;
882 + size_t cbAllocSize = 0;
883 + short int sValue = fValue ? 0xFFFF : 0x0000;
884 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
885 +
886 + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
887 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns);
888 +
889 + if (NULL == pRow->rgBinding)
890 + {
891 + pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
892 + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
893 + }
894 +
895 + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
896 + ExitOnFailure(hr, "Failed to set column value as binary");
897 +
898 +LExit:
899 + return hr;
900 +}
901 +
902 +extern "C" HRESULT DAPI SceSetColumnString(
903 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
904 + __in DWORD dwColumnIndex,
905 + __in_z_opt LPCWSTR wzValue
906 + )
907 +{
908 + HRESULT hr = S_OK;
909 + size_t cbAllocSize = 0;
910 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
911 + SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR));
912 +
913 + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
914 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns);
915 +
916 + if (NULL == pRow->rgBinding)
917 + {
918 + pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
919 + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
920 + }
921 +
922 + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
923 + ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue);
924 +
925 +LExit:
926 + return hr;
927 +}
928 +
929 +extern "C" HRESULT DAPI SceSetColumnNull(
930 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
931 + __in DWORD dwColumnIndex
932 + )
933 +{
934 + HRESULT hr = S_OK;
935 + size_t cbAllocSize = 0;
936 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
937 +
938 + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
939 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns);
940 +
941 + if (NULL == pRow->rgBinding)
942 + {
943 + pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
944 + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
945 + }
946 +
947 + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
948 + ExitOnFailure(hr, "Failed to set column value as empty value");
949 +
950 +LExit:
951 + return hr;
952 +}
953 +
954 +extern "C" HRESULT DAPI SceSetColumnSystemTime(
955 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
956 + __in DWORD dwColumnIndex,
957 + __in const SYSTEMTIME *pst
958 + )
959 +{
960 + HRESULT hr = S_OK;
961 + size_t cbAllocSize = 0;
962 + DBTIMESTAMP dbTimeStamp = { };
963 +
964 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
965 +
966 + hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
967 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns);
968 +
969 + if (NULL == pRow->rgBinding)
970 + {
971 + pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
972 + ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
973 + }
974 +
975 + dbTimeStamp.year = pst->wYear;
976 + dbTimeStamp.month = pst->wMonth;
977 + dbTimeStamp.day = pst->wDay;
978 + dbTimeStamp.hour = pst->wHour;
979 + dbTimeStamp.minute = pst->wMinute;
980 + dbTimeStamp.second = pst->wSecond;
981 + // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second,
982 + // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds
983 + // are involved (even when rounded the way SQL CE returns them).
984 +
985 + hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
986 + ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP");
987 +
988 +LExit:
989 + return hr;
990 +}
991 +
992 +extern "C" HRESULT DAPI SceGetColumnBinary(
993 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
994 + __in DWORD dwColumnIndex,
995 + __out_opt BYTE **ppbBuffer,
996 + __inout SIZE_T *pcbBuffer
997 + )
998 +{
999 + HRESULT hr = S_OK;
1000 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1001 +
1002 + hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer);
1003 + if (E_NOTFOUND == hr)
1004 + {
1005 + ExitFunction();
1006 + }
1007 + ExitOnFailure(hr, "Failed to get binary data out of column");
1008 +
1009 +LExit:
1010 + return hr;
1011 +}
1012 +
1013 +extern "C" HRESULT DAPI SceGetColumnDword(
1014 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1015 + __in DWORD dwColumnIndex,
1016 + __out DWORD *pdwValue
1017 + )
1018 +{
1019 + HRESULT hr = S_OK;
1020 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1021 +
1022 + hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast<BYTE *>(pdwValue));
1023 + if (E_NOTFOUND == hr)
1024 + {
1025 + ExitFunction();
1026 + }
1027 + ExitOnFailure(hr, "Failed to get dword data out of column");
1028 +
1029 +LExit:
1030 + return hr;
1031 +}
1032 +
1033 +extern "C" HRESULT DAPI SceGetColumnQword(
1034 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1035 + __in DWORD dwColumnIndex,
1036 + __in DWORD64 *pqwValue
1037 + )
1038 +{
1039 + HRESULT hr = S_OK;
1040 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1041 +
1042 + hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast<BYTE *>(pqwValue));
1043 + if (E_NOTFOUND == hr)
1044 + {
1045 + ExitFunction();
1046 + }
1047 + ExitOnFailure(hr, "Failed to get qword data out of column");
1048 +
1049 +LExit:
1050 + return hr;
1051 +}
1052 +
1053 +extern "C" HRESULT DAPI SceGetColumnBool(
1054 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1055 + __in DWORD dwColumnIndex,
1056 + __out BOOL *pfValue
1057 + )
1058 +{
1059 + HRESULT hr = S_OK;
1060 + short int sValue = 0;
1061 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1062 +
1063 + hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast<BYTE *>(&sValue));
1064 + if (E_NOTFOUND == hr)
1065 + {
1066 + ExitFunction();
1067 + }
1068 + ExitOnFailure(hr, "Failed to get data out of column");
1069 +
1070 + if (sValue == 0x0000)
1071 + {
1072 + *pfValue = FALSE;
1073 + }
1074 + else
1075 + {
1076 + *pfValue = TRUE;
1077 + }
1078 +
1079 +LExit:
1080 + return hr;
1081 +}
1082 +
1083 +extern "C" HRESULT DAPI SceGetColumnString(
1084 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1085 + __in DWORD dwColumnIndex,
1086 + __out_z LPWSTR *psczValue
1087 + )
1088 +{
1089 + HRESULT hr = S_OK;
1090 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1091 + SIZE_T cbSize = 0;
1092 +
1093 + hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast<BYTE **>(psczValue), &cbSize);
1094 + if (E_NOTFOUND == hr)
1095 + {
1096 + ExitFunction();
1097 + }
1098 + ExitOnFailure(hr, "Failed to get string data out of column");
1099 +
1100 +LExit:
1101 + return hr;
1102 +}
1103 +
1104 +extern "C" HRESULT DAPI SceGetColumnSystemTime(
1105 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1106 + __in DWORD dwColumnIndex,
1107 + __out SYSTEMTIME *pst
1108 + )
1109 +{
1110 + HRESULT hr = S_OK;
1111 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1112 + DBTIMESTAMP dbTimeStamp = { };
1113 +
1114 + hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast<BYTE *>(&dbTimeStamp));
1115 + if (E_NOTFOUND == hr)
1116 + {
1117 + ExitFunction();
1118 + }
1119 + ExitOnFailure(hr, "Failed to get string data out of column");
1120 +
1121 + pst->wYear = dbTimeStamp.year;
1122 + pst->wMonth = dbTimeStamp.month;
1123 + pst->wDay = dbTimeStamp.day;
1124 + pst->wHour = dbTimeStamp.hour;
1125 + pst->wMinute = dbTimeStamp.minute;
1126 + pst->wSecond = dbTimeStamp.second;
1127 +
1128 +LExit:
1129 + return hr;
1130 +}
1131 +
1132 +extern "C" void DAPI SceCloseTable(
1133 + __in SCE_TABLE_SCHEMA *pTable
1134 + )
1135 +{
1136 + ReleaseNullObject(pTable->pIRowsetChange);
1137 + ReleaseNullObject(pTable->pIRowset);
1138 +}
1139 +
1140 +extern "C" BOOL DAPI SceDatabaseChanged(
1141 + __in SCE_DATABASE *pDatabase
1142 + )
1143 +{
1144 + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1145 +
1146 + return pDatabaseInternal->fChanges;
1147 +}
1148 +
1149 +void DAPI SceResetDatabaseChanged(
1150 + __in SCE_DATABASE *pDatabase
1151 + )
1152 +{
1153 + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1154 +
1155 + pDatabaseInternal->fChanges = FALSE;
1156 +}
1157 +
1158 +extern "C" HRESULT DAPI SceCloseDatabase(
1159 + __in SCE_DATABASE *pDatabase
1160 + )
1161 +{
1162 + HRESULT hr = S_OK;
1163 +
1164 + ReleaseDatabase(pDatabase);
1165 +
1166 + return hr;
1167 +}
1168 +
1169 +extern "C" HRESULT DAPI SceBeginQuery(
1170 + __in SCE_DATABASE *pDatabase,
1171 + __in DWORD dwTableIndex,
1172 + __in DWORD dwIndex,
1173 + __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle
1174 + )
1175 +{
1176 + HRESULT hr = S_OK;
1177 + size_t cbAllocSize = 0;
1178 + SCE_QUERY *psq = static_cast<SCE_QUERY*>(MemAlloc(sizeof(SCE_QUERY), TRUE));
1179 + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query");
1180 +
1181 + psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
1182 + psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]);
1183 + psq->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1184 +
1185 + hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize);
1186 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns);
1187 +
1188 + psq->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
1189 + ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query");
1190 +
1191 + *psqhHandle = static_cast<SCE_QUERY_HANDLE>(psq);
1192 + psq = NULL;
1193 +
1194 +LExit:
1195 + ReleaseSceQuery(psq);
1196 +
1197 + return hr;
1198 +}
1199 +
1200 +HRESULT DAPI SceSetQueryColumnBinary(
1201 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1202 + __in_bcount(cbBuffer) const BYTE* pbBuffer,
1203 + __in SIZE_T cbBuffer
1204 + )
1205 +{
1206 + HRESULT hr = S_OK;
1207 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1208 +
1209 + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1210 + ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer);
1211 +
1212 + ++(pQuery->dwBindingIndex);
1213 +
1214 +LExit:
1215 + return hr;
1216 +}
1217 +
1218 +HRESULT DAPI SceSetQueryColumnDword(
1219 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1220 + __in const DWORD dwValue
1221 + )
1222 +{
1223 + HRESULT hr = S_OK;
1224 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1225 +
1226 + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1227 + ExitOnFailure(hr, "Failed to set query column value as dword");
1228 +
1229 + ++(pQuery->dwBindingIndex);
1230 +
1231 +LExit:
1232 + return hr;
1233 +}
1234 +
1235 +HRESULT DAPI SceSetQueryColumnQword(
1236 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1237 + __in const DWORD64 qwValue
1238 + )
1239 +{
1240 + HRESULT hr = S_OK;
1241 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1242 +
1243 + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1244 + ExitOnFailure(hr, "Failed to set query column value as qword");
1245 +
1246 + ++(pQuery->dwBindingIndex);
1247 +
1248 +LExit:
1249 + return hr;
1250 +}
1251 +
1252 +HRESULT DAPI SceSetQueryColumnBool(
1253 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1254 + __in const BOOL fValue
1255 + )
1256 +{
1257 + HRESULT hr = S_OK;
1258 + short int sValue = fValue ? 1 : 0;
1259 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1260 +
1261 + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1262 + ExitOnFailure(hr, "Failed to set query column value as boolean");
1263 +
1264 + ++(pQuery->dwBindingIndex);
1265 +
1266 +LExit:
1267 + return hr;
1268 +}
1269 +
1270 +HRESULT DAPI SceSetQueryColumnString(
1271 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1272 + __in_z_opt LPCWSTR wzString
1273 + )
1274 +{
1275 + HRESULT hr = S_OK;
1276 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1277 + SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR));
1278 +
1279 + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1280 + ExitOnFailure(hr, "Failed to set query column value as string");
1281 +
1282 + ++(pQuery->dwBindingIndex);
1283 +
1284 +LExit:
1285 + return hr;
1286 +}
1287 +
1288 +HRESULT DAPI SceSetQueryColumnSystemTime(
1289 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1290 + __in const SYSTEMTIME *pst
1291 + )
1292 +{
1293 + HRESULT hr = S_OK;
1294 + DBTIMESTAMP dbTimeStamp = { };
1295 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1296 +
1297 + dbTimeStamp.year = pst->wYear;
1298 + dbTimeStamp.month = pst->wMonth;
1299 + dbTimeStamp.day = pst->wDay;
1300 + dbTimeStamp.hour = pst->wHour;
1301 + dbTimeStamp.minute = pst->wMinute;
1302 + dbTimeStamp.second = pst->wSecond;
1303 +
1304 + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1305 + ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP");
1306 +
1307 + ++(pQuery->dwBindingIndex);
1308 +
1309 +LExit:
1310 + return hr;
1311 +}
1312 +
1313 +HRESULT DAPI SceSetQueryColumnEmpty(
1314 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle
1315 + )
1316 +{
1317 + HRESULT hr = S_OK;
1318 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1319 +
1320 + hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1321 + ExitOnFailure(hr, "Failed to set query column value as empty value");
1322 +
1323 + ++(pQuery->dwBindingIndex);
1324 +
1325 +LExit:
1326 + return hr;
1327 +}
1328 +
1329 +HRESULT DAPI SceRunQueryExact(
1330 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle,
1331 + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
1332 + )
1333 +{
1334 + HRESULT hr = S_OK;
1335 + SCE_QUERY_RESULTS *pQueryResults = NULL;
1336 +
1337 + hr = RunQuery(FALSE, *psqhHandle, &pQueryResults);
1338 + if (E_NOTFOUND == hr)
1339 + {
1340 + ExitFunction();
1341 + }
1342 + ExitOnFailure(hr, "Failed to run query exact");
1343 +
1344 + hr = SceGetNextResultRow(reinterpret_cast<SCE_QUERY_RESULTS_HANDLE>(pQueryResults), pRowHandle);
1345 + if (E_NOTFOUND == hr)
1346 + {
1347 + ExitFunction();
1348 + }
1349 + ExitOnFailure(hr, "Failed to get next row out of results");
1350 +
1351 +LExit:
1352 + ReleaseNullSceQuery(*psqhHandle);
1353 + ReleaseSceQueryResults(pQueryResults);
1354 +
1355 + return hr;
1356 +}
1357 +
1358 +extern "C" HRESULT DAPI SceRunQueryRange(
1359 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle,
1360 + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle
1361 + )
1362 +{
1363 + HRESULT hr = S_OK;
1364 + SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast<SCE_QUERY_RESULTS **>(psqrhHandle);
1365 +
1366 + hr = RunQuery(TRUE, *psqhHandle, ppQueryResults);
1367 + if (E_NOTFOUND == hr)
1368 + {
1369 + ExitFunction();
1370 + }
1371 + ExitOnFailure(hr, "Failed to run query for range");
1372 +
1373 +LExit:
1374 + ReleaseNullSceQuery(*psqhHandle);
1375 +
1376 + return hr;
1377 +}
1378 +
1379 +extern "C" HRESULT DAPI SceGetNextResultRow(
1380 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle,
1381 + __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
1382 + )
1383 +{
1384 + HRESULT hr = S_OK;
1385 + HROW hRow = DB_NULL_HROW;
1386 + HROW *phRow = &hRow;
1387 + DBCOUNTITEM cRowsObtained = 0;
1388 + SCE_ROW *pRow = NULL;
1389 + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(sqrhHandle);
1390 +
1391 + Assert(pRowHandle && (*pRowHandle == NULL));
1392 +
1393 + hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
1394 + if (DB_S_ENDOFROWSET == hr)
1395 + {
1396 + ExitFunction1(hr = E_NOTFOUND);
1397 + }
1398 + ExitOnFailure(hr, "Failed to get next first row");
1399 +
1400 + pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
1401 + ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
1402 +
1403 + pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pQueryResults->pDatabaseInternal);
1404 + pRow->hRow = hRow;
1405 + pRow->pTableSchema = pQueryResults->pTableSchema;
1406 + pRow->pIRowset = pQueryResults->pIRowset;
1407 + pRow->pIRowset->AddRef();
1408 +
1409 + *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
1410 + pRow = NULL;
1411 + hRow = DB_NULL_HROW;
1412 +
1413 +LExit:
1414 + if (DB_NULL_HROW != hRow)
1415 + {
1416 + pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
1417 + }
1418 + ReleaseSceRow(pRow);
1419 +
1420 + return hr;
1421 +}
1422 +
1423 +extern "C" void DAPI SceFreeRow(
1424 + __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
1425 + )
1426 +{
1427 + SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
1428 +
1429 + if (DB_NULL_HROW != pRow->hRow)
1430 + {
1431 + pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL);
1432 + }
1433 + ReleaseObject(pRow->pIRowset);
1434 + ReleaseMem(pRow->rgBinding);
1435 + ReleaseMem(pRow->pbData);
1436 + ReleaseMem(pRow);
1437 +}
1438 +
1439 +void DAPI SceFreeQuery(
1440 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle
1441 + )
1442 +{
1443 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1444 +
1445 + ReleaseMem(pQuery->rgBinding);
1446 + ReleaseMem(pQuery->pbData);
1447 + ReleaseMem(pQuery);
1448 +}
1449 +
1450 +void DAPI SceFreeQueryResults(
1451 + __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle
1452 + )
1453 +{
1454 + SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(sqrhHandle);
1455 +
1456 + ReleaseObject(pQueryResults->pIRowset);
1457 + ReleaseMem(pQueryResults);
1458 +}
1459 +
1460 +// internal function definitions
1461 +static HRESULT CreateSqlCe(
1462 + __in_z_opt LPCWSTR wzSqlCeDllPath,
1463 + __out IDBInitialize **ppIDBInitialize,
1464 + __out_opt HMODULE *phSqlCeDll
1465 + )
1466 +{
1467 + HRESULT hr = S_OK;
1468 + LPWSTR sczPath = NULL;
1469 + LPWSTR sczDirectory = NULL;
1470 + LPWSTR sczDllFullPath = NULL;
1471 +
1472 + if (NULL == wzSqlCeDllPath)
1473 + {
1474 + hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast<void **>(ppIDBInitialize));
1475 + ExitOnFailure(hr, "Failed to get IDBInitialize interface");
1476 + }
1477 + else
1478 + {
1479 + // First try loading DLL from the path of our EXE
1480 + hr = PathForCurrentProcess(&sczPath, NULL);
1481 + ExitOnFailure(hr, "Failed to get path for current process");
1482 +
1483 + hr = PathGetDirectory(sczPath, &sczDirectory);
1484 + ExitOnFailure(hr, "Failed to get directory of current process");
1485 +
1486 + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath);
1487 + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename");
1488 +
1489 + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath);
1490 +
1491 + // If that failed, fallback to loading from current path
1492 + if (NULL == *phSqlCeDll)
1493 + {
1494 + hr = DirGetCurrent(&sczDirectory);
1495 + ExitOnFailure(hr, "Failed to get current directory");
1496 +
1497 + hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath);
1498 + ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename");
1499 +
1500 + *phSqlCeDll = ::LoadLibraryW(sczDllFullPath);
1501 + ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath);
1502 + }
1503 +
1504 + HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**);
1505 + pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject");
1506 +
1507 + IClassFactory* pFactory = NULL;
1508 + hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory);
1509 + ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath);
1510 + ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL");
1511 +
1512 + hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize);
1513 + pFactory->Release();
1514 + }
1515 +
1516 +LExit:
1517 + ReleaseStr(sczPath);
1518 + ReleaseStr(sczDirectory);
1519 + ReleaseStr(sczDllFullPath);
1520 +
1521 + return hr;
1522 +}
1523 +
1524 +static HRESULT RunQuery(
1525 + __in BOOL fRange,
1526 + __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle,
1527 + __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults
1528 + )
1529 +{
1530 + HRESULT hr = S_OK;
1531 + DBID tableID = { };
1532 + DBID indexID = { };
1533 + IAccessor *pIAccessor = NULL;
1534 + IRowsetIndex *pIRowsetIndex = NULL;
1535 + IRowset *pIRowset = NULL;
1536 + HACCESSOR hAccessor = DB_NULL_HACCESSOR;
1537 + SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(psqhHandle);
1538 + SCE_QUERY_RESULTS *pQueryResults = NULL;
1539 + DBPROPSET rgdbpIndexPropSet[1];
1540 + DBPROP rgdbpIndexProp[1];
1541 +
1542 + rgdbpIndexPropSet[0].cProperties = 1;
1543 + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1544 + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp;
1545 +
1546 + rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex;
1547 + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1548 + rgdbpIndexProp[0].colid = DB_NULLID;
1549 + rgdbpIndexProp[0].vValue.vt = VT_BOOL;
1550 + rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE;
1551 +
1552 + tableID.eKind = DBKIND_NAME;
1553 + tableID.uName.pwszName = const_cast<WCHAR *>(pQuery->pTableSchema->wzName);
1554 +
1555 + indexID.eKind = DBKIND_NAME;
1556 + indexID.uName.pwszName = const_cast<WCHAR *>(pQuery->pIndexSchema->wzName);
1557 +
1558 + hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex);
1559 + ExitOnFailure(hr, "Failed to open IRowsetIndex");
1560 +
1561 + hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast<void **>(&pIRowset));
1562 + ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex");
1563 +
1564 + hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
1565 + ExitOnFailure(hr, "Failed to get IAccessor interface");
1566 +
1567 + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL);
1568 + ExitOnFailure(hr, "Failed to create accessor");
1569 +
1570 + if (!fRange)
1571 + {
1572 + hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ);
1573 + if (DB_E_NOTFOUND == hr)
1574 + {
1575 + ExitFunction1(hr = E_NOTFOUND);
1576 + }
1577 + ExitOnFailure(hr, "Failed to seek to record");
1578 + }
1579 + else
1580 + {
1581 + // If ALL columns in the index were specified, do a full key match
1582 + if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns)
1583 + {
1584 + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH);
1585 + }
1586 + else
1587 + {
1588 + // Otherwise, just match the specified keys.
1589 + // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it
1590 + // So instead, we set the start and end to the same partial key, and then allow inclusive matching
1591 + // This appears to accomplish the same thing
1592 + hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0);
1593 + }
1594 + if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr)
1595 + {
1596 + ExitFunction1(hr = E_NOTFOUND);
1597 + }
1598 + ExitOnFailure(hr, "Failed to set range to find records");
1599 + }
1600 +
1601 + pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE));
1602 + ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct");
1603 +
1604 + pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal;
1605 + pQueryResults->pTableSchema = pQuery->pTableSchema;
1606 + pQueryResults->pIRowset = pIRowset;
1607 + pIRowset = NULL;
1608 +
1609 + *ppQueryResults = pQueryResults;
1610 + pQueryResults = NULL;
1611 +
1612 +LExit:
1613 + if (DB_NULL_HACCESSOR != hAccessor)
1614 + {
1615 + pIAccessor->ReleaseAccessor(hAccessor, NULL);
1616 + }
1617 + ReleaseObject(pIAccessor);
1618 + ReleaseObject(pIRowset);
1619 + ReleaseObject(pIRowsetIndex);
1620 + ReleaseMem(pQueryResults);
1621 + ReleaseSceQueryResults(pQueryResults);
1622 +
1623 + return hr;
1624 +}
1625 +
1626 +static HRESULT FillOutColumnDescFromSchema(
1627 + __in const SCE_COLUMN_SCHEMA *pColumnSchema,
1628 + __out DBCOLUMNDESC *pColumnDesc
1629 + )
1630 +{
1631 + HRESULT hr = S_OK;
1632 + DWORD dwColumnProperties = 0;
1633 + DWORD dwColumnPropertyIndex = 0;
1634 + BOOL fFixedSize = FALSE;
1635 +
1636 + pColumnDesc->dbcid.eKind = DBKIND_NAME;
1637 + pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName;
1638 + pColumnDesc->wType = pColumnSchema->dbtColumnType;
1639 + pColumnDesc->ulColumnSize = pColumnSchema->dwLength;
1640 + if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType))
1641 + {
1642 + fFixedSize = FALSE;
1643 + }
1644 + else
1645 + {
1646 + fFixedSize = TRUE;
1647 + }
1648 +
1649 + dwColumnProperties = 1;
1650 + if (pColumnSchema->fAutoIncrement)
1651 + {
1652 + ++dwColumnProperties;
1653 + }
1654 + if (!pColumnSchema->fNullable)
1655 + {
1656 + ++dwColumnProperties;
1657 + }
1658 +
1659 + if (0 < dwColumnProperties)
1660 + {
1661 + pColumnDesc->cPropertySets = 1;
1662 + pColumnDesc->rgPropertySets = reinterpret_cast<DBPROPSET *>(MemAlloc(sizeof(DBPROPSET), TRUE));
1663 + ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters");
1664 +
1665 + pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties;
1666 + pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN;
1667 + pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast<DBPROP *>(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE));
1668 +
1669 + dwColumnPropertyIndex = 0;
1670 + if (pColumnSchema->fAutoIncrement)
1671 + {
1672 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT;
1673 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1674 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1675 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1676 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE;
1677 + ++dwColumnPropertyIndex;
1678 + }
1679 + if (!pColumnSchema->fNullable)
1680 + {
1681 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE;
1682 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1683 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1684 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1685 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE;
1686 + ++dwColumnPropertyIndex;
1687 + }
1688 +
1689 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH;
1690 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1691 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1692 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1693 + pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE;
1694 + ++dwColumnPropertyIndex;
1695 + }
1696 +
1697 +LExit:
1698 + return hr;
1699 +}
1700 +
1701 +static HRESULT EnsureSchema(
1702 + __in SCE_DATABASE *pDatabase,
1703 + __in SCE_DATABASE_SCHEMA *pdsSchema
1704 + )
1705 +{
1706 + HRESULT hr = S_OK;
1707 + size_t cbAllocSize = 0;
1708 + BOOL fInTransaction = FALSE;
1709 + BOOL fSchemaNeedsSetup = TRUE;
1710 + DBID tableID = { };
1711 + DBID indexID = { };
1712 + DBPROPSET rgdbpIndexPropSet[1];
1713 + DBPROPSET rgdbpRowSetPropSet[1];
1714 + DBPROP rgdbpIndexProp[1];
1715 + DBPROP rgdbpRowSetProp[1];
1716 + DBCOLUMNDESC *rgColumnDescriptions = NULL;
1717 + DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL;
1718 + DWORD cIndexColumnDescriptions = 0;
1719 + DWORD dwTableColumnIndex = 0;
1720 + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1721 + ITableDefinition *pTableDefinition = NULL;
1722 + IIndexDefinition *pIndexDefinition = NULL;
1723 + IRowsetIndex *pIRowsetIndex = NULL;
1724 +
1725 + rgdbpRowSetPropSet[0].cProperties = 1;
1726 + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1727 + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp;
1728 +
1729 + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange;
1730 + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1731 + rgdbpRowSetProp[0].colid = DB_NULLID;
1732 + rgdbpRowSetProp[0].vValue.vt = VT_BOOL;
1733 + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE;
1734 +
1735 + rgdbpIndexPropSet[0].cProperties = 1;
1736 + rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX;
1737 + rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp;
1738 +
1739 + rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS;
1740 + rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1741 + rgdbpIndexProp[0].colid = DB_NULLID;
1742 + rgdbpIndexProp[0].vValue.vt = VT_I4;
1743 + rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL;
1744 +
1745 + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast<void **>(&pTableDefinition));
1746 + ExitOnFailure(hr, "Failed to get ITableDefinition for table creation");
1747 +
1748 + hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast<void **>(&pIndexDefinition));
1749 + ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation");
1750 +
1751 + hr = SceBeginTransaction(pDatabase);
1752 + ExitOnFailure(hr, "Failed to start transaction for ensuring schema");
1753 + fInTransaction = TRUE;
1754 +
1755 + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1756 + {
1757 + tableID.eKind = DBKIND_NAME;
1758 + tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1759 +
1760 + // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist
1761 + rgColumnDescriptions = static_cast<DBCOLUMNDESC *>(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE));
1762 + ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table");
1763 +
1764 + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1765 + {
1766 + hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i);
1767 + ExitOnFailure(hr, "Failed to fill out column description from schema");
1768 + }
1769 +
1770 + // First try to open the table - or if it doesn't exist, create it
1771 + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast<IUnknown **>(&pdsSchema->rgTables[dwTable].pIRowset));
1772 + if (DB_E_NOTABLE == hr)
1773 + {
1774 + // The table doesn't exist, so let's create it
1775 + hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL);
1776 + ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName);
1777 + }
1778 + else
1779 + {
1780 + ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName);
1781 +
1782 + // Close any rowset we opened
1783 + ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset);
1784 +
1785 + // If it does exist, make sure all columns are in the table
1786 + // Only nullable columns can be added to an existing table
1787 + for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1788 + {
1789 + if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable)
1790 + hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL);
1791 + if (DB_E_DUPLICATECOLUMNID == hr)
1792 + {
1793 + hr = S_OK;
1794 + }
1795 + ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName);
1796 + }
1797 + }
1798 +
1799 +#pragma prefast(push)
1800 +#pragma prefast(disable:26010)
1801 + hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable);
1802 +#pragma prefast(pop)
1803 + ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1804 +
1805 + for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1806 + {
1807 + if (NULL != rgColumnDescriptions[i].rgPropertySets)
1808 + {
1809 + ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties);
1810 + ReleaseMem(rgColumnDescriptions[i].rgPropertySets);
1811 + }
1812 + }
1813 +
1814 + ReleaseNullMem(rgColumnDescriptions);
1815 + if (0 < pdsSchema->rgTables[dwTable].cIndexes)
1816 + {
1817 + // Now create indexes for the table
1818 + for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex)
1819 + {
1820 + indexID.eKind = DBKIND_NAME;
1821 + indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName;
1822 +
1823 + // Check if the index exists
1824 + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex);
1825 + if (SUCCEEDED(hr))
1826 + {
1827 + // TODO: If one with the same name exists, check if the schema actually matches
1828 + ReleaseNullObject(pIRowsetIndex);
1829 + continue;
1830 + }
1831 + hr = S_OK;
1832 +
1833 + hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize);
1834 + ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns);
1835 +
1836 + rgIndexColumnDescriptions = reinterpret_cast<DBINDEXCOLUMNDESC *>(MemAlloc(cbAllocSize, TRUE));
1837 + ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions");
1838 + cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns;
1839 +
1840 + for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex)
1841 + {
1842 + dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex];
1843 +
1844 + rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast<DBID *>(MemAlloc(sizeof(DBID), TRUE));
1845 + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME;
1846 + rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast<LPOLESTR>(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName);
1847 + rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC;
1848 + }
1849 +
1850 + hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast<DBORDINAL>(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL);
1851 + if (DB_E_DUPLICATEINDEXID == hr)
1852 + {
1853 + // If the index already exists, no worries
1854 + hr = S_OK;
1855 + }
1856 + ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName);
1857 +
1858 + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i)
1859 + {
1860 + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID);
1861 + }
1862 +
1863 + cIndexColumnDescriptions = 0;
1864 + ReleaseNullMem(rgIndexColumnDescriptions);
1865 + }
1866 + }
1867 + }
1868 +
1869 + // Now once all tables have been created, create foreign key relationships
1870 + if (fSchemaNeedsSetup)
1871 + {
1872 + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1873 + {
1874 + tableID.eKind = DBKIND_NAME;
1875 + tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1876 +
1877 + // Setup any constraints for the table's columns
1878 + hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema);
1879 + ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1880 + }
1881 + }
1882 +
1883 + hr = SceCommitTransaction(pDatabase);
1884 + ExitOnFailure(hr, "Failed to commit transaction for ensuring schema");
1885 + fInTransaction = FALSE;
1886 +
1887 + hr = OpenSchema(pDatabase, pdsSchema);
1888 + ExitOnFailure(hr, "Failed to open schema");
1889 +
1890 +LExit:
1891 + ReleaseObject(pTableDefinition);
1892 + ReleaseObject(pIndexDefinition);
1893 + ReleaseObject(pIRowsetIndex);
1894 +
1895 + if (fInTransaction)
1896 + {
1897 + SceRollbackTransaction(pDatabase);
1898 + }
1899 +
1900 + for (DWORD i = 0; i < cIndexColumnDescriptions; ++i)
1901 + {
1902 + ReleaseMem(rgIndexColumnDescriptions[i].pColumnID);
1903 + }
1904 +
1905 + ReleaseMem(rgIndexColumnDescriptions);
1906 + ReleaseMem(rgColumnDescriptions);
1907 +
1908 + return hr;
1909 +}
1910 +
1911 +static HRESULT OpenSchema(
1912 + __in SCE_DATABASE *pDatabase,
1913 + __in SCE_DATABASE_SCHEMA *pdsSchema
1914 + )
1915 +{
1916 + HRESULT hr = S_OK;
1917 + SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1918 + DBID tableID = { };
1919 + DBPROPSET rgdbpRowSetPropSet[1];
1920 + DBPROP rgdbpRowSetProp[1];
1921 +
1922 + rgdbpRowSetPropSet[0].cProperties = 1;
1923 + rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1924 + rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp;
1925 +
1926 + rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange;
1927 + rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1928 + rgdbpRowSetProp[0].colid = DB_NULLID;
1929 + rgdbpRowSetProp[0].vValue.vt = VT_BOOL;
1930 + rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE;
1931 +
1932 + // Finally, open all tables
1933 + for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1934 + {
1935 + tableID.eKind = DBKIND_NAME;
1936 + tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1937 +
1938 + // And finally, open the table's standard interfaces
1939 + hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast<IUnknown **>(&pdsSchema->rgTables[dwTable].pIRowset));
1940 + ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName);
1941 +
1942 + hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pdsSchema->rgTables[dwTable].pIRowsetChange));
1943 + ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1944 + }
1945 +
1946 +LExit:
1947 + return hr;
1948 +}
1949 +
1950 +static HRESULT SetColumnValue(
1951 + __in const SCE_TABLE_SCHEMA *pTableSchema,
1952 + __in DWORD dwColumnIndex,
1953 + __in_bcount_opt(cbSize) const BYTE *pbData,
1954 + __in SIZE_T cbSize,
1955 + __inout DBBINDING *pBinding,
1956 + __inout SIZE_T *pcbOffset,
1957 + __inout BYTE **ppbBuffer
1958 + )
1959 +{
1960 + HRESULT hr = S_OK;
1961 + size_t cbNewOffset = *pcbOffset;
1962 +
1963 + pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column
1964 + pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
1965 + pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
1966 +
1967 + pBinding->obLength = cbNewOffset;
1968 +
1969 + hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset);
1970 + ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value");
1971 +
1972 + pBinding->obValue = cbNewOffset;
1973 +
1974 + hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset);
1975 + ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize);
1976 +
1977 + pBinding->obStatus = cbNewOffset;
1978 + pBinding->eParamIO = DBPARAMIO_INPUT;
1979 +
1980 + hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset);
1981 + ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value");
1982 +
1983 + pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType;
1984 + pBinding->cbMaxLen = static_cast<DBBYTEOFFSET>(cbSize);
1985 +
1986 + if (NULL == *ppbBuffer)
1987 + {
1988 + *ppbBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbNewOffset, TRUE));
1989 + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string");
1990 + }
1991 + else
1992 + {
1993 + *ppbBuffer = reinterpret_cast<BYTE *>(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE));
1994 + ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string");
1995 + }
1996 +
1997 + *(reinterpret_cast<DBBYTEOFFSET *>(*ppbBuffer + *pcbOffset)) = static_cast<DBBYTEOFFSET>(cbSize);
1998 + *pcbOffset += sizeof(DBBYTEOFFSET);
1999 + memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize);
2000 + *pcbOffset += cbSize;
2001 + if (NULL == pbData)
2002 + {
2003 + *(reinterpret_cast<DBSTATUS *>(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL;
2004 + }
2005 + *pcbOffset += sizeof(DBSTATUS);
2006 +
2007 +LExit:
2008 + return hr;
2009 +}
2010 +
2011 +static HRESULT GetColumnValue(
2012 + __in SCE_ROW *pRow,
2013 + __in DWORD dwColumnIndex,
2014 + __out_opt BYTE **ppbData,
2015 + __out SIZE_T *pcbSize
2016 + )
2017 +{
2018 + HRESULT hr = S_OK;
2019 + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema;
2020 + IAccessor *pIAccessor = NULL;
2021 + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR;
2022 + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR;
2023 + DWORD dwDataSize = 0;
2024 + void *pvRawData = NULL;
2025 + DBBINDING dbBinding = { };
2026 + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK;
2027 +
2028 + dbBinding.iOrdinal = dwColumnIndex + 1;
2029 + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
2030 + dbBinding.dwPart = DBPART_LENGTH;
2031 + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType;
2032 +
2033 + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
2034 + ExitOnFailure(hr, "Failed to get IAccessor interface");
2035 +
2036 + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus);
2037 + ExitOnFailure(hr, "Failed to create accessor");
2038 +
2039 + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast<void *>(&dwDataSize));
2040 + ExitOnFailure(hr, "Failed to get size of data");
2041 +
2042 + // For variable-length columns, zero data returned means NULL
2043 + if (0 == dwDataSize)
2044 + {
2045 + ExitFunction1(hr = E_NOTFOUND);
2046 + }
2047 +
2048 + if (NULL != ppbData)
2049 + {
2050 + dbBinding.dwPart = DBPART_VALUE;
2051 + dbBinding.cbMaxLen = dwDataSize;
2052 +
2053 + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus);
2054 + ExitOnFailure(hr, "Failed to create accessor");
2055 +
2056 + if (DBBINDSTATUS_OK != dbBindStatus)
2057 + {
2058 + hr = E_INVALIDARG;
2059 + ExitOnFailure(hr, "Bad bind status while creating accessor to get value");
2060 + }
2061 +
2062 + if (DBTYPE_WSTR == dbBinding.wType)
2063 + {
2064 + hr = StrAlloc(reinterpret_cast<LPWSTR *>(&pvRawData), dwDataSize / sizeof(WCHAR));
2065 + ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex);
2066 + }
2067 + else
2068 + {
2069 + pvRawData = MemAlloc(dwDataSize, TRUE);
2070 + ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex);
2071 + }
2072 +
2073 + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData);
2074 + ExitOnFailure(hr, "Failed to read data value");
2075 +
2076 + ReleaseMem(*ppbData);
2077 + *ppbData = reinterpret_cast<BYTE *>(pvRawData);
2078 + pvRawData = NULL;
2079 + }
2080 +
2081 + *pcbSize = dwDataSize;
2082 +
2083 +LExit:
2084 + ReleaseMem(pvRawData);
2085 +
2086 + if (DB_NULL_HACCESSOR != hAccessorLength)
2087 + {
2088 + pIAccessor->ReleaseAccessor(hAccessorLength, NULL);
2089 + }
2090 + if (DB_NULL_HACCESSOR != hAccessorValue)
2091 + {
2092 + pIAccessor->ReleaseAccessor(hAccessorValue, NULL);
2093 + }
2094 + ReleaseObject(pIAccessor);
2095 +
2096 + return hr;
2097 +}
2098 +
2099 +static HRESULT GetColumnValueFixed(
2100 + __in SCE_ROW *pRow,
2101 + __in DWORD dwColumnIndex,
2102 + __in DWORD cbSize,
2103 + __out BYTE *pbData
2104 + )
2105 +{
2106 + HRESULT hr = S_OK;
2107 + const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema;
2108 + IAccessor *pIAccessor = NULL;
2109 + HACCESSOR hAccessorLength = DB_NULL_HACCESSOR;
2110 + HACCESSOR hAccessorValue = DB_NULL_HACCESSOR;
2111 + DWORD dwDataSize = 0;
2112 + DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK;
2113 + DBBINDING dbBinding = { };
2114 +
2115 + dbBinding.iOrdinal = dwColumnIndex + 1;
2116 + dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
2117 + dbBinding.dwPart = DBPART_LENGTH;
2118 + dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType;
2119 +
2120 + pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
2121 + ExitOnFailure(hr, "Failed to get IAccessor interface");
2122 +
2123 + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus);
2124 + ExitOnFailure(hr, "Failed to create accessor");
2125 +
2126 + if (DBBINDSTATUS_OK != dbBindStatus)
2127 + {
2128 + hr = E_INVALIDARG;
2129 + ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value");
2130 + }
2131 +
2132 + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast<void *>(&dwDataSize));
2133 + ExitOnFailure(hr, "Failed to get size of data");
2134 +
2135 + if (0 == dwDataSize)
2136 + {
2137 + ExitFunction1(hr = E_NOTFOUND);
2138 + }
2139 +
2140 + dbBinding.dwPart = DBPART_VALUE;
2141 + dbBinding.cbMaxLen = cbSize;
2142 +
2143 + hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus);
2144 + ExitOnFailure(hr, "Failed to create accessor");
2145 +
2146 + if (DBBINDSTATUS_OK != dbBindStatus)
2147 + {
2148 + hr = E_INVALIDARG;
2149 + ExitOnFailure(hr, "Bad bind status while creating accessor to get value");
2150 + }
2151 +
2152 + hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast<void *>(pbData));
2153 + ExitOnFailure(hr, "Failed to read data value");
2154 +
2155 +LExit:
2156 + if (DB_NULL_HACCESSOR != hAccessorLength)
2157 + {
2158 + pIAccessor->ReleaseAccessor(hAccessorLength, NULL);
2159 + }
2160 + if (DB_NULL_HACCESSOR != hAccessorValue)
2161 + {
2162 + pIAccessor->ReleaseAccessor(hAccessorValue, NULL);
2163 + }
2164 + ReleaseObject(pIAccessor);
2165 +
2166 + return hr;
2167 +}
2168 +
2169 +static HRESULT EnsureLocalColumnConstraints(
2170 + __in ITableDefinition *pTableDefinition,
2171 + __in DBID *pTableID,
2172 + __in SCE_TABLE_SCHEMA *pTableSchema
2173 + )
2174 +{
2175 + HRESULT hr = S_OK;
2176 + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL;
2177 + DBCONSTRAINTDESC dbcdConstraint = { };
2178 + DBID dbConstraintID = { };
2179 + DBID dbLocalColumnID = { };
2180 + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL;
2181 +
2182 + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast<void **>(&pTableDefinitionWithConstraints));
2183 + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints");
2184 +
2185 + for (DWORD i = 0; i < pTableSchema->cColumns; ++i)
2186 + {
2187 + pCurrentColumn = pTableSchema->rgColumns + i;
2188 +
2189 + // Add a primary key constraint for this column, if one exists
2190 + if (pCurrentColumn->fPrimaryKey)
2191 + {
2192 + // Setup DBID for new constraint
2193 + dbConstraintID.eKind = DBKIND_NAME;
2194 + dbConstraintID.uName.pwszName = const_cast<LPOLESTR>(L"PrimaryKey");
2195 + dbcdConstraint.pConstraintID = &dbConstraintID;
2196 +
2197 + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY;
2198 +
2199 + dbLocalColumnID.eKind = DBKIND_NAME;
2200 + dbLocalColumnID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzName);
2201 + dbcdConstraint.cColumns = 1;
2202 + dbcdConstraint.rgColumnList = &dbLocalColumnID;
2203 +
2204 + dbcdConstraint.pReferencedTableID = NULL;
2205 + dbcdConstraint.cForeignKeyColumns = 0;
2206 + dbcdConstraint.rgForeignKeyColumnList = NULL;
2207 + dbcdConstraint.pwszConstraintText = NULL;
2208 + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION;
2209 + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION;
2210 + dbcdConstraint.MatchType = DBMATCHTYPE_NONE;
2211 + dbcdConstraint.Deferrability = 0;
2212 + dbcdConstraint.cReserved = 0;
2213 + dbcdConstraint.rgReserved = NULL;
2214 +
2215 + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint);
2216 + if (DB_E_DUPLICATECONSTRAINTID == hr)
2217 + {
2218 + hr = S_OK;
2219 + }
2220 + ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName);
2221 + }
2222 + }
2223 +
2224 +LExit:
2225 + ReleaseObject(pTableDefinitionWithConstraints);
2226 +
2227 + return hr;
2228 +}
2229 +
2230 +static HRESULT EnsureForeignColumnConstraints(
2231 + __in ITableDefinition *pTableDefinition,
2232 + __in DBID *pTableID,
2233 + __in SCE_TABLE_SCHEMA *pTableSchema,
2234 + __in SCE_DATABASE_SCHEMA *pDatabaseSchema
2235 + )
2236 +{
2237 + HRESULT hr = S_OK;
2238 + SCE_COLUMN_SCHEMA *pCurrentColumn = NULL;
2239 + DBCONSTRAINTDESC dbcdConstraint = { };
2240 + DBID dbConstraintID = { };
2241 + DBID dbLocalColumnID = { };
2242 + DBID dbForeignTableID = { };
2243 + DBID dbForeignColumnID = { };
2244 + ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL;
2245 +
2246 + hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast<void **>(&pTableDefinitionWithConstraints));
2247 + ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints");
2248 +
2249 + for (DWORD i = 0; i < pTableSchema->cColumns; ++i)
2250 + {
2251 + pCurrentColumn = pTableSchema->rgColumns + i;
2252 +
2253 + // Add a foreign key constraint for this column, if one exists
2254 + if (NULL != pCurrentColumn->wzRelationName)
2255 + {
2256 + // Setup DBID for new constraint
2257 + dbConstraintID.eKind = DBKIND_NAME;
2258 + dbConstraintID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzRelationName);
2259 + dbcdConstraint.pConstraintID = &dbConstraintID;
2260 +
2261 + dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY;
2262 +
2263 + dbForeignColumnID.eKind = DBKIND_NAME;
2264 + dbForeignColumnID.uName.pwszName = const_cast<LPOLESTR>(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName);
2265 + dbcdConstraint.cColumns = 1;
2266 + dbcdConstraint.rgColumnList = &dbForeignColumnID;
2267 +
2268 + dbForeignTableID.eKind = DBKIND_NAME;
2269 + dbForeignTableID.uName.pwszName = const_cast<LPOLESTR>(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName);
2270 + dbcdConstraint.pReferencedTableID = &dbForeignTableID;
2271 +
2272 + dbLocalColumnID.eKind = DBKIND_NAME;
2273 + dbLocalColumnID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzName);
2274 + dbcdConstraint.cForeignKeyColumns = 1;
2275 + dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID;
2276 +
2277 + dbcdConstraint.pwszConstraintText = NULL;
2278 + dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION;
2279 + dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION;
2280 + dbcdConstraint.MatchType = DBMATCHTYPE_FULL;
2281 + dbcdConstraint.Deferrability = 0;
2282 + dbcdConstraint.cReserved = 0;
2283 + dbcdConstraint.rgReserved = NULL;
2284 +
2285 + hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint);
2286 + if (DB_E_DUPLICATECONSTRAINTID == hr)
2287 + {
2288 + hr = S_OK;
2289 + }
2290 + ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName);
2291 + }
2292 + }
2293 +
2294 +LExit:
2295 + ReleaseObject(pTableDefinitionWithConstraints);
2296 +
2297 + return hr;
2298 +}
2299 +
2300 +static HRESULT SetSessionProperties(
2301 + __in ISessionProperties *pISessionProperties
2302 + )
2303 +{
2304 + HRESULT hr = S_OK;
2305 + DBPROP rgdbpDataSourceProp[1];
2306 + DBPROPSET rgdbpDataSourcePropSet[1];
2307 +
2308 + rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE;
2309 + rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
2310 + rgdbpDataSourceProp[0].vValue.vt = VT_I4;
2311 + rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH;
2312 +
2313 + rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION;
2314 + rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
2315 + rgdbpDataSourcePropSet[0].cProperties = 1;
2316 +
2317 + hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet);
2318 + ExitOnFailure(hr, "Failed to set session properties");
2319 +
2320 +LExit:
2321 + return hr;
2322 +}
2323 +
2324 +static HRESULT GetDatabaseSchemaInfo(
2325 + __in SCE_DATABASE *pDatabase,
2326 + __out LPWSTR *psczSchemaType,
2327 + __out DWORD *pdwVersion
2328 + )
2329 +{
2330 + HRESULT hr = S_OK;
2331 + LPWSTR sczSchemaType = NULL;
2332 + DWORD dwVersionFound = 0;
2333 + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0];
2334 + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable};
2335 + // Database object with our alternate schema
2336 + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema };
2337 + SCE_ROW_HANDLE sceRow = NULL;
2338 +
2339 + hr = OpenSchema(pDatabase, &fullSchema);
2340 + ExitOnFailure(hr, "Failed to ensure internal version schema");
2341 +
2342 + hr = SceGetFirstRow(&database, 0, &sceRow);
2343 + ExitOnFailure(hr, "Failed to get first row in internal version schema table");
2344 +
2345 + hr = SceGetColumnString(sceRow, 0, &sczSchemaType);
2346 + ExitOnFailure(hr, "Failed to get internal schematype");
2347 +
2348 + hr = SceGetColumnDword(sceRow, 1, &dwVersionFound);
2349 + ExitOnFailure(hr, "Failed to get internal version");
2350 +
2351 + *psczSchemaType = sczSchemaType;
2352 + sczSchemaType = NULL;
2353 + *pdwVersion = dwVersionFound;
2354 +
2355 +LExit:
2356 + SceCloseTable(&schemaTable); // ignore failure
2357 + ReleaseStr(sczSchemaType);
2358 + ReleaseSceRow(sceRow);
2359 +
2360 + return hr;
2361 +}
2362 +
2363 +static HRESULT SetDatabaseSchemaInfo(
2364 + __in SCE_DATABASE *pDatabase,
2365 + __in LPCWSTR wzSchemaType,
2366 + __in DWORD dwVersion
2367 + )
2368 +{
2369 + HRESULT hr = S_OK;
2370 + BOOL fInSceTransaction = FALSE;
2371 + SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0];
2372 + SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable};
2373 + // Database object with our alternate schema
2374 + SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema };
2375 + SCE_ROW_HANDLE sceRow = NULL;
2376 +
2377 + hr = EnsureSchema(pDatabase, &fullSchema);
2378 + ExitOnFailure(hr, "Failed to ensure internal version schema");
2379 +
2380 + hr = SceBeginTransaction(&database);
2381 + ExitOnFailure(hr, "Failed to begin transaction");
2382 + fInSceTransaction = TRUE;
2383 +
2384 + hr = SceGetFirstRow(&database, 0, &sceRow);
2385 + if (E_NOTFOUND == hr)
2386 + {
2387 + hr = ScePrepareInsert(&database, 0, &sceRow);
2388 + ExitOnFailure(hr, "Failed to insert only row into internal version schema table");
2389 + }
2390 + else
2391 + {
2392 + ExitOnFailure(hr, "Failed to get first row in internal version schema table");
2393 + }
2394 +
2395 + hr = SceSetColumnString(sceRow, 0, wzSchemaType);
2396 + ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType);
2397 +
2398 + hr = SceSetColumnDword(sceRow, 1, dwVersion);
2399 + ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion);
2400 +
2401 + hr = SceFinishUpdate(sceRow);
2402 + ExitOnFailure(hr, "Failed to insert first row in internal version schema table");
2403 +
2404 + hr = SceCommitTransaction(&database);
2405 + ExitOnFailure(hr, "Failed to commit transaction");
2406 + fInSceTransaction = FALSE;
2407 +
2408 +LExit:
2409 + SceCloseTable(&schemaTable); // ignore failure
2410 + ReleaseSceRow(sceRow);
2411 + if (fInSceTransaction)
2412 + {
2413 + SceRollbackTransaction(&database);
2414 + }
2415 +
2416 + return hr;
2417 +}
2418 +
2419 +static void ReleaseDatabase(
2420 + SCE_DATABASE *pDatabase
2421 + )
2422 +{
2423 + if (NULL != pDatabase)
2424 + {
2425 + if (NULL != pDatabase->pdsSchema)
2426 + {
2427 + for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i)
2428 + {
2429 + SceCloseTable(pDatabase->pdsSchema->rgTables + i);
2430 + }
2431 + }
2432 +
2433 + if (NULL != pDatabase->sdbHandle)
2434 + {
2435 + ReleaseDatabaseInternal(reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle));
2436 + }
2437 + }
2438 + ReleaseMem(pDatabase);
2439 +}
2440 +
2441 +static void ReleaseDatabaseInternal(
2442 + SCE_DATABASE_INTERNAL *pDatabaseInternal
2443 + )
2444 +{
2445 + HRESULT hr = S_OK;
2446 +
2447 + if (NULL != pDatabaseInternal)
2448 + {
2449 + ReleaseObject(pDatabaseInternal->pITransactionLocal);
2450 + ReleaseObject(pDatabaseInternal->pIOpenRowset);
2451 + ReleaseObject(pDatabaseInternal->pISessionProperties);
2452 + ReleaseObject(pDatabaseInternal->pIDBCreateSession);
2453 + ReleaseObject(pDatabaseInternal->pIDBProperties);
2454 +
2455 + if (NULL != pDatabaseInternal->pIDBInitialize)
2456 + {
2457 + hr = pDatabaseInternal->pIDBInitialize->Uninitialize();
2458 + if (FAILED(hr))
2459 + {
2460 + TraceError(hr, "Failed to call uninitialize on IDBInitialize");
2461 + }
2462 + ReleaseObject(pDatabaseInternal->pIDBInitialize);
2463 + }
2464 +
2465 + if (NULL != pDatabaseInternal->hSqlCeDll)
2466 + {
2467 + if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll))
2468 + {
2469 + hr = HRESULT_FROM_WIN32(::GetLastError());
2470 + TraceError(hr, "Failed to free sql ce dll");
2471 + }
2472 + }
2473 + }
2474 +
2475 + // If there was a temp file we copied to (for read-only databases), delete it after close
2476 + if (NULL != pDatabaseInternal->sczTempDbFile)
2477 + {
2478 + hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile);
2479 + if (FAILED(hr))
2480 + {
2481 + TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile);
2482 + }
2483 + ReleaseStr(pDatabaseInternal->sczTempDbFile);
2484 + }
2485 +
2486 + ReleaseMem(pDatabaseInternal);
2487 +}
2488 +
2489 +#endif // end SKIP_SCE_COMPILE
src/dutil/shelutil.cpp new
+327
@@ -0,0 +1,327 @@
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 +
5 +static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW;
6 +
7 +static HRESULT GetDesktopShellView(
8 + __in REFIID riid,
9 + __out void **ppv
10 + );
11 +static HRESULT GetShellDispatchFromView(
12 + __in IShellView *psv,
13 + __in REFIID riid,
14 + __out void **ppv
15 + );
16 +
17 +/********************************************************************
18 + ShelFunctionOverride - overrides the shell functions. Typically used
19 + for unit testing.
20 +
21 +*********************************************************************/
22 +extern "C" void DAPI ShelFunctionOverride(
23 + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW
24 + )
25 +{
26 + vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW;
27 +}
28 +
29 +
30 +/********************************************************************
31 + ShelExec() - executes a target.
32 +
33 +*******************************************************************/
34 +extern "C" HRESULT DAPI ShelExec(
35 + __in_z LPCWSTR wzTargetPath,
36 + __in_z_opt LPCWSTR wzParameters,
37 + __in_z_opt LPCWSTR wzVerb,
38 + __in_z_opt LPCWSTR wzWorkingDirectory,
39 + __in int nShowCmd,
40 + __in_opt HWND hwndParent,
41 + __out_opt HANDLE* phProcess
42 + )
43 +{
44 + HRESULT hr = S_OK;
45 + SHELLEXECUTEINFOW shExecInfo = {};
46 +
47 + shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
48 + shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
49 + shExecInfo.hwnd = hwndParent;
50 + shExecInfo.lpVerb = wzVerb;
51 + shExecInfo.lpFile = wzTargetPath;
52 + shExecInfo.lpParameters = wzParameters;
53 + shExecInfo.lpDirectory = wzWorkingDirectory;
54 + shExecInfo.nShow = nShowCmd;
55 +
56 + if (!vpfnShellExecuteExW(&shExecInfo))
57 + {
58 + ExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er);
59 + }
60 +
61 + if (phProcess)
62 + {
63 + *phProcess = shExecInfo.hProcess;
64 + shExecInfo.hProcess = NULL;
65 + }
66 +
67 +LExit:
68 + ReleaseHandle(shExecInfo.hProcess);
69 +
70 + return hr;
71 +}
72 +
73 +
74 +/********************************************************************
75 + ShelExecUnelevated() - executes a target unelevated.
76 +
77 +*******************************************************************/
78 +extern "C" HRESULT DAPI ShelExecUnelevated(
79 + __in_z LPCWSTR wzTargetPath,
80 + __in_z_opt LPCWSTR wzParameters,
81 + __in_z_opt LPCWSTR wzVerb,
82 + __in_z_opt LPCWSTR wzWorkingDirectory,
83 + __in int nShowCmd
84 + )
85 +{
86 + HRESULT hr = S_OK;
87 + BSTR bstrTargetPath = NULL;
88 + VARIANT vtParameters = { };
89 + VARIANT vtVerb = { };
90 + VARIANT vtWorkingDirectory = { };
91 + VARIANT vtShow = { };
92 + IShellView* psv = NULL;
93 + IShellDispatch2* psd = NULL;
94 +
95 + bstrTargetPath = ::SysAllocString(wzTargetPath);
96 + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR.");
97 +
98 + if (wzParameters && *wzParameters)
99 + {
100 + vtParameters.vt = VT_BSTR;
101 + vtParameters.bstrVal = ::SysAllocString(wzParameters);
102 + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR.");
103 + }
104 +
105 + if (wzVerb && *wzVerb)
106 + {
107 + vtVerb.vt = VT_BSTR;
108 + vtVerb.bstrVal = ::SysAllocString(wzVerb);
109 + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR.");
110 + }
111 +
112 + if (wzWorkingDirectory && *wzWorkingDirectory)
113 + {
114 + vtWorkingDirectory.vt = VT_BSTR;
115 + vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory);
116 + ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR.");
117 + }
118 +
119 + vtShow.vt = VT_INT;
120 + vtShow.intVal = nShowCmd;
121 +
122 + hr = GetDesktopShellView(IID_PPV_ARGS(&psv));
123 + ExitOnFailure(hr, "Failed to get desktop shell view.");
124 +
125 + hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd));
126 + ExitOnFailure(hr, "Failed to get shell dispatch from view.");
127 +
128 + hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow);
129 + if (S_FALSE == hr)
130 + {
131 + hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
132 + }
133 + ExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath);
134 +
135 +LExit:
136 + ReleaseObject(psd);
137 + ReleaseObject(psv);
138 + ReleaseBSTR(vtWorkingDirectory.bstrVal);
139 + ReleaseBSTR(vtVerb.bstrVal);
140 + ReleaseBSTR(vtParameters.bstrVal);
141 + ReleaseBSTR(bstrTargetPath);
142 +
143 + return hr;
144 +}
145 +
146 +
147 +/********************************************************************
148 + ShelGetFolder() - gets a folder by CSIDL.
149 +
150 +*******************************************************************/
151 +extern "C" HRESULT DAPI ShelGetFolder(
152 + __out_z LPWSTR* psczFolderPath,
153 + __in int csidlFolder
154 + )
155 +{
156 + HRESULT hr = S_OK;
157 + WCHAR wzPath[MAX_PATH];
158 +
159 + hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath);
160 + ExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder);
161 +
162 + hr = StrAllocString(psczFolderPath, wzPath, 0);
163 + ExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath);
164 +
165 + hr = PathBackslashTerminate(psczFolderPath);
166 + ExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath);
167 +
168 +LExit:
169 + return hr;
170 +}
171 +
172 +
173 +/********************************************************************
174 + ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID.
175 +
176 + Note: return E_NOTIMPL if called on pre-Vista operating systems.
177 +*******************************************************************/
178 +#ifndef REFKNOWNFOLDERID
179 +#define REFKNOWNFOLDERID REFGUID
180 +#endif
181 +
182 +#ifndef KF_FLAG_CREATE
183 +#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition
184 +#endif
185 +
186 +EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)(
187 + REFKNOWNFOLDERID rfid,
188 + DWORD dwFlags,
189 + HANDLE hToken,
190 + PWSTR *ppszPath
191 + );
192 +
193 +extern "C" HRESULT DAPI ShelGetKnownFolder(
194 + __out_z LPWSTR* psczFolderPath,
195 + __in REFKNOWNFOLDERID rfidFolder
196 + )
197 +{
198 + HRESULT hr = S_OK;
199 + HMODULE hShell32Dll = NULL;
200 + PFN_SHGetKnownFolderPath pfn = NULL;
201 + LPWSTR pwzPath = NULL;
202 +
203 + hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll);
204 + if (E_MODNOTFOUND == hr)
205 + {
206 + TraceError(hr, "Failed to load shell32.dll");
207 + ExitFunction1(hr = E_NOTIMPL);
208 + }
209 + ExitOnFailure(hr, "Failed to load shell32.dll.");
210 +
211 + pfn = reinterpret_cast<PFN_SHGetKnownFolderPath>(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath"));
212 + ExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point.");
213 +
214 + hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath);
215 + ExitOnFailure(hr, "Failed to get known folder path.");
216 +
217 + hr = StrAllocString(psczFolderPath, pwzPath, 0);
218 + ExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath);
219 +
220 + hr = PathBackslashTerminate(psczFolderPath);
221 + ExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath);
222 +
223 +LExit:
224 + if (pwzPath)
225 + {
226 + ::CoTaskMemFree(pwzPath);
227 + }
228 +
229 + if (hShell32Dll)
230 + {
231 + ::FreeLibrary(hShell32Dll);
232 + }
233 +
234 + return hr;
235 +}
236 +
237 +
238 +// Internal functions.
239 +
240 +static HRESULT GetDesktopShellView(
241 + __in REFIID riid,
242 + __out void **ppv
243 + )
244 +{
245 + HRESULT hr = S_OK;
246 + IShellWindows* psw = NULL;
247 + HWND hwnd = NULL;
248 + IDispatch* pdisp = NULL;
249 + VARIANT vEmpty = {}; // VT_EMPTY
250 + IShellBrowser* psb = NULL;
251 + IShellFolder* psf = NULL;
252 + IShellView* psv = NULL;
253 +
254 + // use the shell view for the desktop using the shell windows automation to find the
255 + // desktop web browser and then grabs its view
256 + // returns IShellView, IFolderView and related interfaces
257 + hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
258 + ExitOnFailure(hr, "Failed to get shell view.");
259 +
260 + hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp);
261 + if (S_OK == hr)
262 + {
263 + hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
264 + ExitOnFailure(hr, "Failed to get desktop window.");
265 +
266 + hr = psb->QueryActiveShellView(&psv);
267 + ExitOnFailure(hr, "Failed to get active shell view.");
268 +
269 + hr = psv->QueryInterface(riid, ppv);
270 + ExitOnFailure(hr, "Failed to query for the desktop shell view.");
271 + }
272 + else if (S_FALSE == hr)
273 + {
274 + //Windows XP
275 + hr = SHGetDesktopFolder(&psf);
276 + ExitOnFailure(hr, "Failed to get desktop folder.");
277 +
278 + hr = psf->CreateViewObject(NULL, IID_IShellView, ppv);
279 + ExitOnFailure(hr, "Failed to query for the desktop shell view.");
280 + }
281 + else
282 + {
283 + ExitOnFailure(hr, "Failed to get desktop window.");
284 + }
285 +
286 +LExit:
287 + ReleaseObject(psv);
288 + ReleaseObject(psb);
289 + ReleaseObject(psf);
290 + ReleaseObject(pdisp);
291 + ReleaseObject(psw);
292 +
293 + return hr;
294 +}
295 +
296 +static HRESULT GetShellDispatchFromView(
297 + __in IShellView *psv,
298 + __in REFIID riid,
299 + __out void **ppv
300 + )
301 +{
302 + HRESULT hr = S_OK;
303 + IDispatch *pdispBackground = NULL;
304 + IShellFolderViewDual *psfvd = NULL;
305 + IDispatch *pdisp = NULL;
306 +
307 + // From a shell view object, gets its automation interface and from that get the shell
308 + // application object that implements IShellDispatch2 and related interfaces.
309 + hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
310 + ExitOnFailure(hr, "Failed to get the automation interface for shell.");
311 +
312 + hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
313 + ExitOnFailure(hr, "Failed to get shell folder view dual.");
314 +
315 + hr = psfvd->get_Application(&pdisp);
316 + ExitOnFailure(hr, "Failed to application object.");
317 +
318 + hr = pdisp->QueryInterface(riid, ppv);
319 + ExitOnFailure(hr, "Failed to get IShellDispatch2.");
320 +
321 +LExit:
322 + ReleaseObject(pdisp);
323 + ReleaseObject(psfvd);
324 + ReleaseObject(pdispBackground);
325 +
326 + return hr;
327 +}
src/dutil/sqlutil.cpp new
+868
@@ -0,0 +1,868 @@
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 +
5 +// okay, this may look a little weird, but sqlutil.h cannot be in the
6 +// pre-compiled header because we need to #define these things so the
7 +// correct GUID's get pulled into this object file
8 +#include <initguid.h>
9 +#define DBINITCONSTANTS
10 +#include "sqlutil.h"
11 +
12 +// private prototypes
13 +static HRESULT FileSpecToString(
14 + __in const SQL_FILESPEC* psf,
15 + __out LPWSTR* ppwz
16 + );
17 +
18 +static HRESULT EscapeSqlIdentifier(
19 + __in_z LPCWSTR wzDatabase,
20 + __deref_out_z LPWSTR* ppwz
21 + );
22 +
23 +
24 +/********************************************************************
25 + SqlConnectDatabase - establishes a connection to a database
26 +
27 + NOTE: wzInstance is optional
28 + if fIntegratedAuth is set then wzUser and wzPassword are ignored
29 +********************************************************************/
30 +extern "C" HRESULT DAPI SqlConnectDatabase(
31 + __in_z LPCWSTR wzServer,
32 + __in_z LPCWSTR wzInstance,
33 + __in_z LPCWSTR wzDatabase,
34 + __in BOOL fIntegratedAuth,
35 + __in_z LPCWSTR wzUser,
36 + __in_z LPCWSTR wzPassword,
37 + __out IDBCreateSession** ppidbSession
38 + )
39 +{
40 + Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession);
41 +
42 + HRESULT hr = S_OK;
43 + IDBInitialize* pidbInitialize = NULL;
44 + IDBProperties* pidbProperties = NULL;
45 +
46 + LPWSTR pwzServerInstance = NULL;
47 + DBPROP rgdbpInit[4];
48 + DBPROPSET rgdbpsetInit[1];
49 + ULONG cProperties = 0;
50 +
51 + memset(rgdbpInit, 0, sizeof(rgdbpInit));
52 + memset(rgdbpsetInit, 0, sizeof(rgdbpsetInit));
53 +
54 + //obtain access to the SQLOLEDB provider
55 + hr = ::CoCreateInstance(CLSID_SQLOLEDB, NULL, CLSCTX_INPROC_SERVER,
56 + IID_IDBInitialize, (LPVOID*)&pidbInitialize);
57 + ExitOnFailure(hr, "failed to create IID_IDBInitialize object");
58 +
59 + // if there is an instance
60 + if (wzInstance && *wzInstance)
61 + {
62 + hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance);
63 + }
64 + else
65 + {
66 + hr = StrAllocString(&pwzServerInstance, wzServer, 0);
67 + }
68 + ExitOnFailure(hr, "failed to allocate memory for the server instance");
69 +
70 + // server[\instance]
71 + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE;
72 + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
73 + rgdbpInit[cProperties].colid = DB_NULLID;
74 + ::VariantInit(&rgdbpInit[cProperties].vValue);
75 + rgdbpInit[cProperties].vValue.vt = VT_BSTR;
76 + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance);
77 + ++cProperties;
78 +
79 + // database
80 + rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG;
81 + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
82 + rgdbpInit[cProperties].colid = DB_NULLID;
83 + ::VariantInit(&rgdbpInit[cProperties].vValue);
84 + rgdbpInit[cProperties].vValue.vt = VT_BSTR;
85 + rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase);
86 + ++cProperties;
87 +
88 + if (fIntegratedAuth)
89 + {
90 + // username
91 + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED;
92 + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
93 + rgdbpInit[cProperties].colid = DB_NULLID;
94 + ::VariantInit(&rgdbpInit[cProperties].vValue);
95 + rgdbpInit[cProperties].vValue.vt = VT_BSTR;
96 + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication
97 + ++cProperties;
98 + }
99 + else
100 + {
101 + // username
102 + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID;
103 + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
104 + rgdbpInit[cProperties].colid = DB_NULLID;
105 + ::VariantInit(&rgdbpInit[cProperties].vValue);
106 + rgdbpInit[cProperties].vValue.vt = VT_BSTR;
107 + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser);
108 + ++cProperties;
109 +
110 + // password
111 + rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD;
112 + rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
113 + rgdbpInit[cProperties].colid = DB_NULLID;
114 + ::VariantInit(&rgdbpInit[cProperties].vValue);
115 + rgdbpInit[cProperties].vValue.vt = VT_BSTR;
116 + rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword);
117 + ++cProperties;
118 + }
119 +
120 + // put the properties into a set
121 + rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT;
122 + rgdbpsetInit[0].rgProperties = rgdbpInit;
123 + rgdbpsetInit[0].cProperties = cProperties;
124 +
125 + // create and set the property set
126 + hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties);
127 + ExitOnFailure(hr, "failed to get IID_IDBProperties object");
128 + hr = pidbProperties->SetProperties(1, rgdbpsetInit);
129 + ExitOnFailure(hr, "failed to set properties");
130 +
131 + //initialize connection to datasource
132 + hr = pidbInitialize->Initialize();
133 + ExitOnFailure(hr, "failed to initialize connection to database: %ls", wzDatabase);
134 +
135 + hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession);
136 +
137 +LExit:
138 + for (; 0 < cProperties; cProperties--)
139 + {
140 + ::VariantClear(&rgdbpInit[cProperties - 1].vValue);
141 + }
142 +
143 + ReleaseObject(pidbProperties);
144 + ReleaseObject(pidbInitialize);
145 + ReleaseStr(pwzServerInstance);
146 +
147 + return hr;
148 +}
149 +
150 +
151 +/********************************************************************
152 + SqlStartTransaction - Starts a new transaction that must be ended
153 +
154 +*********************************************************************/
155 +extern "C" HRESULT DAPI SqlStartTransaction(
156 + __in IDBCreateSession* pidbSession,
157 + __out IDBCreateCommand** ppidbCommand,
158 + __out ITransaction** ppit
159 + )
160 +{
161 + Assert(pidbSession && ppit);
162 +
163 + HRESULT hr = S_OK;
164 +
165 + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand);
166 + ExitOnFailure(hr, "unable to create command from session");
167 +
168 + hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit);
169 + ExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal");
170 +
171 + hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL);
172 +
173 +LExit:
174 +
175 + return hr;
176 +}
177 +
178 +/********************************************************************
179 + SqlEndTransaction - Ends the transaction
180 +
181 + NOTE: if fCommit, will commit the transaction, otherwise rolls back
182 +*********************************************************************/
183 +extern "C" HRESULT DAPI SqlEndTransaction(
184 + __in ITransaction* pit,
185 + __in BOOL fCommit
186 + )
187 +{
188 + Assert(pit);
189 +
190 + HRESULT hr = S_OK;
191 +
192 + if (fCommit)
193 + {
194 + hr = pit->Commit(FALSE, XACTTC_SYNC, 0);
195 + ExitOnFailure(hr, "commit of transaction failed");
196 + }
197 + else
198 + {
199 + hr = pit->Abort(NULL, FALSE, FALSE);
200 + ExitOnFailure(hr, "abort of transaction failed");
201 + }
202 +
203 +LExit:
204 +
205 + return hr;
206 +}
207 +
208 +
209 +/********************************************************************
210 + SqlDatabaseExists - determines if database exists
211 +
212 + NOTE: wzInstance is optional
213 + if fIntegratedAuth is set then wzUser and wzPassword are ignored
214 + returns S_OK if database exist
215 + returns S_FALSE if database does not exist
216 + returns E_* on error
217 +********************************************************************/
218 +extern "C" HRESULT DAPI SqlDatabaseExists(
219 + __in_z LPCWSTR wzServer,
220 + __in_z LPCWSTR wzInstance,
221 + __in_z LPCWSTR wzDatabase,
222 + __in BOOL fIntegratedAuth,
223 + __in_z LPCWSTR wzUser,
224 + __in_z LPCWSTR wzPassword,
225 + __out_opt BSTR* pbstrErrorDescription
226 + )
227 +{
228 + Assert(wzServer && wzDatabase && *wzDatabase);
229 +
230 + HRESULT hr = S_OK;
231 + IDBCreateSession* pidbSession = NULL;
232 +
233 + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
234 + ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
235 +
236 + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
237 +
238 +LExit:
239 + ReleaseObject(pidbSession);
240 +
241 + return hr;
242 +}
243 +
244 +
245 +/********************************************************************
246 + SqlSessionDatabaseExists - determines if database exists
247 +
248 + NOTE: pidbSession must be connected to master database
249 + returns S_OK if database exist
250 + returns S_FALSE if database does not exist
251 + returns E_* on error
252 +********************************************************************/
253 +extern "C" HRESULT DAPI SqlSessionDatabaseExists(
254 + __in IDBCreateSession* pidbSession,
255 + __in_z LPCWSTR wzDatabase,
256 + __out_opt BSTR* pbstrErrorDescription
257 + )
258 +{
259 + Assert(pidbSession && wzDatabase && *wzDatabase);
260 +
261 + HRESULT hr = S_OK;
262 +
263 + LPWSTR pwzQuery = NULL;
264 + IRowset* pirs = NULL;
265 +
266 + DBCOUNTITEM cRows = 0;
267 + HROW rghRows[1];
268 + HROW* prow = rghRows;
269 +
270 + //
271 + // query to see if the database exists
272 + //
273 + hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase);
274 + ExitOnFailure(hr, "failed to allocate query string to ensure database exists");
275 +
276 + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription);
277 + ExitOnFailure(hr, "failed to get database list from 'master' database");
278 + Assert(pirs);
279 +
280 + //
281 + // check to see if the database was returned
282 + //
283 + hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow);
284 + ExitOnFailure(hr, "failed to get row with database name");
285 +
286 + // succeeded but no database
287 + if ((DB_S_ENDOFROWSET == hr) || (0 == cRows))
288 + {
289 + hr = S_FALSE;
290 + }
291 +
292 +LExit:
293 + ReleaseObject(pirs);
294 + ReleaseStr(pwzQuery);
295 +
296 + return hr;
297 +}
298 +
299 +
300 +/********************************************************************
301 + SqlDatabaseEnsureExists - creates a database if it does not exist
302 +
303 + NOTE: wzInstance is optional
304 + if fIntegratedAuth is set then wzUser and wzPassword are ignored
305 +********************************************************************/
306 +extern "C" HRESULT DAPI SqlDatabaseEnsureExists(
307 + __in_z LPCWSTR wzServer,
308 + __in_z LPCWSTR wzInstance,
309 + __in_z LPCWSTR wzDatabase,
310 + __in BOOL fIntegratedAuth,
311 + __in_z LPCWSTR wzUser,
312 + __in_z LPCWSTR wzPassword,
313 + __in_opt const SQL_FILESPEC* psfDatabase,
314 + __in_opt const SQL_FILESPEC* psfLog,
315 + __out_opt BSTR* pbstrErrorDescription
316 + )
317 +{
318 + Assert(wzServer && wzDatabase && *wzDatabase);
319 +
320 + HRESULT hr = S_OK;
321 + IDBCreateSession* pidbSession = NULL;
322 +
323 + //
324 + // connect to the master database to create the new database
325 + //
326 + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
327 + ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
328 +
329 + hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
330 + ExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
331 +
332 + Assert(S_OK == hr);
333 +LExit:
334 + ReleaseObject(pidbSession);
335 +
336 + return hr;
337 +}
338 +
339 +
340 +/********************************************************************
341 + SqlSessionDatabaseEnsureExists - creates a database if it does not exist
342 +
343 + NOTE: pidbSession must be connected to the master database
344 +********************************************************************/
345 +extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists(
346 + __in IDBCreateSession* pidbSession,
347 + __in_z LPCWSTR wzDatabase,
348 + __in_opt const SQL_FILESPEC* psfDatabase,
349 + __in_opt const SQL_FILESPEC* psfLog,
350 + __out_opt BSTR* pbstrErrorDescription
351 + )
352 +{
353 + Assert(pidbSession && wzDatabase && *wzDatabase);
354 +
355 + HRESULT hr = S_OK;
356 +
357 + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
358 + ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
359 +
360 + if (S_FALSE == hr)
361 + {
362 + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
363 + ExitOnFailure(hr, "failed to create database: %1", wzDatabase);
364 + }
365 + // else database already exists, return S_FALSE
366 +
367 + Assert(S_OK == hr);
368 +LExit:
369 +
370 + return hr;
371 +}
372 +
373 +
374 +/********************************************************************
375 + SqlCreateDatabase - creates a database on the server
376 +
377 + NOTE: wzInstance is optional
378 + if fIntegratedAuth is set then wzUser and wzPassword are ignored
379 +********************************************************************/
380 +extern "C" HRESULT DAPI SqlCreateDatabase(
381 + __in_z LPCWSTR wzServer,
382 + __in_z LPCWSTR wzInstance,
383 + __in_z LPCWSTR wzDatabase,
384 + __in BOOL fIntegratedAuth,
385 + __in_z LPCWSTR wzUser,
386 + __in_z LPCWSTR wzPassword,
387 + __in_opt const SQL_FILESPEC* psfDatabase,
388 + __in_opt const SQL_FILESPEC* psfLog,
389 + __out_opt BSTR* pbstrErrorDescription
390 + )
391 +{
392 + Assert(wzServer && wzDatabase && *wzDatabase);
393 +
394 + HRESULT hr = S_OK;
395 + IDBCreateSession* pidbSession = NULL;
396 +
397 + //
398 + // connect to the master database to create the new database
399 + //
400 + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
401 + ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
402 +
403 + hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
404 + ExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
405 +
406 + Assert(S_OK == hr);
407 +LExit:
408 + ReleaseObject(pidbSession);
409 +
410 + return hr;
411 +}
412 +
413 +
414 +/********************************************************************
415 + SqlSessionCreateDatabase - creates a database on the server
416 +
417 + NOTE: pidbSession must be connected to the master database
418 +********************************************************************/
419 +extern "C" HRESULT DAPI SqlSessionCreateDatabase(
420 + __in IDBCreateSession* pidbSession,
421 + __in_z LPCWSTR wzDatabase,
422 + __in_opt const SQL_FILESPEC* psfDatabase,
423 + __in_opt const SQL_FILESPEC* psfLog,
424 + __out_opt BSTR* pbstrErrorDescription
425 + )
426 +{
427 + HRESULT hr = S_OK;
428 + LPWSTR pwzDbFile = NULL;
429 + LPWSTR pwzLogFile = NULL;
430 + LPWSTR pwzQuery = NULL;
431 + LPWSTR pwzDatabaseEscaped = NULL;
432 +
433 + if (psfDatabase)
434 + {
435 + hr = FileSpecToString(psfDatabase, &pwzDbFile);
436 + ExitOnFailure(hr, "failed to convert db filespec to string");
437 + }
438 +
439 + if (psfLog)
440 + {
441 + hr = FileSpecToString(psfLog, &pwzLogFile);
442 + ExitOnFailure(hr, "failed to convert log filespec to string");
443 + }
444 +
445 + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
446 + ExitOnFailure(hr, "failed to escape database string");
447 +
448 + hr = StrAllocFormatted(&pwzQuery, L"CREATE DATABASE %s %s%s %s%s", pwzDatabaseEscaped, pwzDbFile ? L"ON " : L"", pwzDbFile ? pwzDbFile : L"", pwzLogFile ? L"LOG ON " : L"", pwzLogFile ? pwzLogFile : L"");
449 + ExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped);
450 +
451 + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
452 + ExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery);
453 +
454 +LExit:
455 + ReleaseStr(pwzQuery);
456 + ReleaseStr(pwzLogFile);
457 + ReleaseStr(pwzDbFile);
458 + ReleaseStr(pwzDatabaseEscaped);
459 +
460 + return hr;
461 +}
462 +
463 +
464 +/********************************************************************
465 + SqlDropDatabase - removes a database from a server if it exists
466 +
467 + NOTE: wzInstance is optional
468 + if fIntegratedAuth is set then wzUser and wzPassword are ignored
469 +********************************************************************/
470 +extern "C" HRESULT DAPI SqlDropDatabase(
471 + __in_z LPCWSTR wzServer,
472 + __in_z LPCWSTR wzInstance,
473 + __in_z LPCWSTR wzDatabase,
474 + __in BOOL fIntegratedAuth,
475 + __in_z LPCWSTR wzUser,
476 + __in_z LPCWSTR wzPassword,
477 + __out_opt BSTR* pbstrErrorDescription
478 + )
479 +{
480 + Assert(wzServer && wzDatabase && *wzDatabase);
481 +
482 + HRESULT hr = S_OK;
483 + IDBCreateSession* pidbSession = NULL;
484 +
485 + //
486 + // connect to the master database to search for wzDatabase
487 + //
488 + hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
489 + ExitOnFailure(hr, "Failed to connect to 'master' database");
490 +
491 + hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription);
492 +
493 +LExit:
494 + ReleaseObject(pidbSession);
495 +
496 + return hr;
497 +}
498 +
499 +
500 +/********************************************************************
501 + SqlSessionDropDatabase - removes a database from a server if it exists
502 +
503 + NOTE: pidbSession must be connected to the master database
504 +********************************************************************/
505 +extern "C" HRESULT DAPI SqlSessionDropDatabase(
506 + __in IDBCreateSession* pidbSession,
507 + __in_z LPCWSTR wzDatabase,
508 + __out_opt BSTR* pbstrErrorDescription
509 + )
510 +{
511 + Assert(pidbSession && wzDatabase && *wzDatabase);
512 +
513 + HRESULT hr = S_OK;
514 + LPWSTR pwzQuery = NULL;
515 + LPWSTR pwzDatabaseEscaped = NULL;
516 +
517 + hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
518 + ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
519 +
520 + hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
521 + ExitOnFailure(hr, "failed to escape database string");
522 +
523 + if (S_OK == hr)
524 + {
525 + hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped);
526 + ExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped);
527 +
528 + hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
529 + ExitOnFailure(hr, "Failed to drop database");
530 + }
531 +
532 +LExit:
533 + ReleaseStr(pwzQuery);
534 + ReleaseStr(pwzDatabaseEscaped);
535 +
536 + return hr;
537 +}
538 +
539 +
540 +/********************************************************************
541 + SqlSessionExecuteQuery - executes a query and returns the results if desired
542 +
543 + NOTE: ppirs and pcRoes and pbstrErrorDescription are optional
544 +********************************************************************/
545 +extern "C" HRESULT DAPI SqlSessionExecuteQuery(
546 + __in IDBCreateSession* pidbSession,
547 + __in __sql_command LPCWSTR wzSql,
548 + __out_opt IRowset** ppirs,
549 + __out_opt DBROWCOUNT* pcRows,
550 + __out_opt BSTR* pbstrErrorDescription
551 + )
552 +{
553 + Assert(pidbSession);
554 +
555 + HRESULT hr = S_OK;
556 + IDBCreateCommand* pidbCommand = NULL;
557 + ICommandText* picmdText = NULL;
558 + ICommand* picmd = NULL;
559 + DBROWCOUNT cRows = 0;
560 +
561 + if (pcRows)
562 + {
563 + *pcRows = NULL;
564 + }
565 +
566 + //
567 + // create the command
568 + //
569 + hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand);
570 + ExitOnFailure(hr, "failed to create database session");
571 + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
572 + ExitOnFailure(hr, "failed to create command to execute session");
573 +
574 + //
575 + // set the sql text into the command
576 + //
577 + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
578 + ExitOnFailure(hr, "failed to get command text object for command");
579 + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
580 + ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
581 +
582 + //
583 + // execute the command
584 + //
585 + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
586 + ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
587 +
588 + if (DB_S_ERRORSOCCURRED == hr)
589 + {
590 + hr = E_FAIL;
591 + }
592 +
593 + if (pcRows)
594 + {
595 + *pcRows = cRows;
596 + }
597 +
598 +LExit:
599 +
600 + if (FAILED(hr) && picmd && pbstrErrorDescription)
601 + {
602 + HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English
603 + if (FAILED(hrGetErrors))
604 + {
605 + ReleaseBSTR(*pbstrErrorDescription);
606 + }
607 + }
608 +
609 + ReleaseObject(picmd);
610 + ReleaseObject(picmdText);
611 + ReleaseObject(pidbCommand);
612 +
613 + return hr;
614 +}
615 +
616 +
617 +/********************************************************************
618 + SqlCommandExecuteQuery - executes a SQL command and returns the results if desired
619 +
620 + NOTE: ppirs and pcRoes are optional
621 +********************************************************************/
622 +extern "C" HRESULT DAPI SqlCommandExecuteQuery(
623 + __in IDBCreateCommand* pidbCommand,
624 + __in __sql_command LPCWSTR wzSql,
625 + __out IRowset** ppirs,
626 + __out DBROWCOUNT* pcRows
627 + )
628 +{
629 + Assert(pidbCommand);
630 +
631 + HRESULT hr = S_OK;
632 + ICommandText* picmdText = NULL;
633 + ICommand* picmd = NULL;
634 + DBROWCOUNT cRows = 0;
635 +
636 + if (pcRows)
637 + {
638 + *pcRows = NULL;
639 + }
640 +
641 + //
642 + // create the command
643 + //
644 + hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
645 + ExitOnFailure(hr, "failed to create command to execute session");
646 +
647 + //
648 + // set the sql text into the command
649 + //
650 + hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
651 + ExitOnFailure(hr, "failed to get command text object for command");
652 + hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
653 + ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
654 +
655 + //
656 + // execute the command
657 + //
658 + hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
659 + ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
660 +
661 + if (DB_S_ERRORSOCCURRED == hr)
662 + {
663 + hr = E_FAIL;
664 + }
665 +
666 + if (pcRows)
667 + {
668 + *pcRows = cRows;
669 + }
670 +
671 +LExit:
672 + ReleaseObject(picmd);
673 + ReleaseObject(picmdText);
674 +
675 + return hr;
676 +}
677 +
678 +
679 +/********************************************************************
680 + SqlGetErrorInfo - gets error information from the last SQL function call
681 +
682 + NOTE: pbstrErrorSource and pbstrErrorDescription are optional
683 +********************************************************************/
684 +extern "C" HRESULT DAPI SqlGetErrorInfo(
685 + __in IUnknown* pObjectWithError,
686 + __in REFIID IID_InterfaceWithError,
687 + __in DWORD dwLocaleId,
688 + __out_opt BSTR* pbstrErrorSource,
689 + __out_opt BSTR* pbstrErrorDescription
690 + )
691 +{
692 + HRESULT hr = S_OK;
693 + Assert(pObjectWithError);
694 +
695 + // interfaces needed to extract error information out
696 + ISupportErrorInfo* pISupportErrorInfo = NULL;
697 + IErrorInfo* pIErrorInfoAll = NULL;
698 + IErrorRecords* pIErrorRecords = NULL;
699 + IErrorInfo* pIErrorInfoRecord = NULL;
700 +
701 + // only ask for error information if the interface supports it.
702 + hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo);
703 + ExitOnFailure(hr, "No error information was found for object.");
704 +
705 + hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError);
706 + ExitOnFailure(hr, "InterfaceWithError is not supported for object with error");
707 +
708 + // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway
709 + hr = ::GetErrorInfo(0, &pIErrorInfoAll);
710 + ExitOnFailure(hr, "failed to get error info");
711 +
712 + if (S_OK == hr && pIErrorInfoAll)
713 + {
714 + // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records
715 + hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
716 + if (SUCCEEDED(hr))
717 + {
718 + ULONG cErrors = 0;
719 + pIErrorRecords->GetRecordCount(&cErrors);
720 +
721 + // get the error information for each record
722 + for (ULONG i = 0; i < cErrors; ++i)
723 + {
724 + hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord);
725 + if (SUCCEEDED(hr))
726 + {
727 + if (pbstrErrorSource)
728 + {
729 + pIErrorInfoRecord->GetSource(pbstrErrorSource);
730 + }
731 + if (pbstrErrorDescription)
732 + {
733 + pIErrorInfoRecord->GetDescription(pbstrErrorDescription);
734 + }
735 +
736 + ReleaseNullObject(pIErrorInfoRecord);
737 +
738 + break; // TODO: return more than one error in the future!
739 + }
740 + }
741 +
742 + ReleaseNullObject(pIErrorRecords);
743 + }
744 + else // we have a simple error record
745 + {
746 + if (pbstrErrorSource)
747 + {
748 + pIErrorInfoAll->GetSource(pbstrErrorSource);
749 + }
750 + if (pbstrErrorDescription)
751 + {
752 + pIErrorInfoAll->GetDescription(pbstrErrorDescription);
753 + }
754 + }
755 + }
756 + else
757 + {
758 + hr = E_NOMOREITEMS;
759 + }
760 +
761 +LExit:
762 + ReleaseObject(pIErrorInfoRecord);
763 + ReleaseObject(pIErrorRecords);
764 + ReleaseObject(pIErrorInfoAll);
765 + ReleaseObject(pISupportErrorInfo);
766 +
767 + return hr;
768 +}
769 +
770 +
771 +//
772 +// private
773 +//
774 +
775 +/********************************************************************
776 + FileSpecToString
777 +
778 +*********************************************************************/
779 +static HRESULT FileSpecToString(
780 + __in const SQL_FILESPEC* psf,
781 + __out LPWSTR* ppwz
782 + )
783 +{
784 + Assert(psf && ppwz);
785 +
786 + HRESULT hr = S_OK;
787 + LPWSTR pwz = NULL;
788 +
789 + hr = StrAllocString(&pwz, L"(", 1024);
790 + ExitOnFailure(hr, "failed to allocate string for database file info");
791 +
792 + ExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info");
793 + ExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info");
794 +
795 + hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName);
796 + ExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName);
797 +
798 + hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename);
799 + ExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename);
800 +
801 + if (0 != psf->wzSize[0])
802 + {
803 + hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize);
804 + ExitOnFailure(hr, "failed to format database file info size: %s", psf->wzSize);
805 + }
806 +
807 + if (0 != psf->wzMaxSize[0])
808 + {
809 + hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize);
810 + ExitOnFailure(hr, "failed to format database file info maxsize: %s", psf->wzMaxSize);
811 + }
812 +
813 + if (0 != psf->wzGrow[0])
814 + {
815 + hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow);
816 + ExitOnFailure(hr, "failed to format database file info growth: %s", psf->wzGrow);
817 + }
818 +
819 + hr = StrAllocFormatted(&pwz, L"%s)", pwz);
820 + ExitOnFailure(hr, "failed to allocate string for file spec");
821 +
822 + *ppwz = pwz;
823 + pwz = NULL; // null here so it doesn't get freed below
824 +
825 +LExit:
826 + ReleaseStr(pwz);
827 + return hr;
828 +}
829 +
830 +static HRESULT EscapeSqlIdentifier(
831 + __in_z LPCWSTR wzIdentifier,
832 + __deref_out_z LPWSTR* ppwz
833 + )
834 +{
835 + Assert(ppwz);
836 +
837 + HRESULT hr = S_OK;
838 + LPWSTR pwz = NULL;
839 +
840 + if (wzIdentifier == NULL)
841 + {
842 + //Just ignore a NULL identifier and clear out the result
843 + ReleaseNullStr(*ppwz);
844 + ExitFunction();
845 + }
846 +
847 + int cchIdentifier = lstrlenW(wzIdentifier);
848 +
849 + //If an empty string or already escaped just copy
850 + if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']'))
851 + {
852 + hr = StrAllocString(&pwz, wzIdentifier, 0);
853 + ExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier);
854 + }
855 + else
856 + {
857 + //escape it
858 + hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier);
859 + ExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier);
860 + }
861 +
862 + *ppwz = pwz;
863 + pwz = NULL; // null here so it doesn't get freed below
864 +
865 +LExit:
866 + ReleaseStr(pwz);
867 + return hr;
868 +}
src/dutil/srputil.cpp new
+237
@@ -0,0 +1,237 @@
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 +
5 +
6 +typedef BOOL (WINAPI *PFN_SETRESTOREPTW)(
7 + __in PRESTOREPOINTINFOW pRestorePtSpec,
8 + __out PSTATEMGRSTATUS pSMgrStatus
9 + );
10 +
11 +static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL;
12 +static HMODULE vhSrClientDll = NULL;
13 +
14 +
15 +static HRESULT InitializeComSecurity();
16 +
17 +
18 +DAPI_(HRESULT) SrpInitialize(
19 + __in BOOL fInitializeComSecurity
20 + )
21 +{
22 + HRESULT hr = S_OK;
23 +
24 + hr = LoadSystemLibrary(L"srclient.dll", &vhSrClientDll);
25 + if (FAILED(hr))
26 + {
27 + ExitFunction1(hr = E_NOTIMPL);
28 + }
29 +
30 + vpfnSRSetRestorePointW = reinterpret_cast<PFN_SETRESTOREPTW>(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW"));
31 + ExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address.");
32 +
33 + // If allowed, initialize COM security to enable NetworkService,
34 + // LocalService and System to make callbacks to the process
35 + // calling System Restore. This is required for any process
36 + // that calls SRSetRestorePoint.
37 + if (fInitializeComSecurity)
38 + {
39 + hr = InitializeComSecurity();
40 + ExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore.");
41 + }
42 +
43 +LExit:
44 + if (FAILED(hr) && vhSrClientDll)
45 + {
46 + SrpUninitialize();
47 + }
48 +
49 + return hr;
50 +}
51 +
52 +DAPI_(void) SrpUninitialize()
53 +{
54 + if (vhSrClientDll)
55 + {
56 + ::FreeLibrary(vhSrClientDll);
57 + vhSrClientDll = NULL;
58 + vpfnSRSetRestorePointW = NULL;
59 + }
60 +}
61 +
62 +DAPI_(HRESULT) SrpCreateRestorePoint(
63 + __in_z LPCWSTR wzApplicationName,
64 + __in SRP_ACTION action
65 + )
66 +{
67 + HRESULT hr = S_OK;
68 + RESTOREPOINTINFOW restorePoint = { };
69 + STATEMGRSTATUS status = { };
70 +
71 + if (!vpfnSRSetRestorePointW)
72 + {
73 + ExitFunction1(hr = E_NOTIMPL);
74 + }
75 +
76 + restorePoint.dwEventType = BEGIN_SYSTEM_CHANGE;
77 + restorePoint.dwRestorePtType = (SRP_ACTION_INSTALL == action) ? APPLICATION_INSTALL : (SRP_ACTION_UNINSTALL == action) ? APPLICATION_UNINSTALL : MODIFY_SETTINGS;
78 + ::StringCbCopyW(restorePoint.szDescription, sizeof(restorePoint.szDescription), wzApplicationName);
79 +
80 + if (!vpfnSRSetRestorePointW(&restorePoint, &status))
81 + {
82 + ExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point.");
83 + }
84 +
85 +LExit:
86 + return hr;
87 +}
88 +
89 +
90 +// internal functions.
91 +
92 +static HRESULT InitializeComSecurity()
93 +{
94 + HRESULT hr = S_OK;
95 + DWORD er = ERROR_SUCCESS;
96 + SECURITY_DESCRIPTOR sd = {0};
97 + EXPLICIT_ACCESS ea[5] = {0};
98 + ACL* pAcl = NULL;
99 + ULONGLONG rgSidBA[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
100 + ULONGLONG rgSidLS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
101 + ULONGLONG rgSidNS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
102 + ULONGLONG rgSidPS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
103 + ULONGLONG rgSidSY[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
104 + DWORD cbSid = 0;
105 +
106 + // Create the security descriptor explicitly as follows because
107 + // CoInitializeSecurity() will not accept the relative security descriptors
108 + // returned by ConvertStringSecurityDescriptorToSecurityDescriptor().
109 + //
110 + // The result is a security descriptor that is equivalent to the following
111 + // security descriptor definition language (SDDL) string:
112 + //
113 + // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)(A;;0x1;;;SY)(A;;0x1;;;BA)
114 + //
115 +
116 + // Initialize the security descriptor.
117 + if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
118 + {
119 + ExitWithLastError(hr, "Failed to initialize security descriptor for system restore.");
120 + }
121 +
122 + // Create an administrator group security identifier (SID).
123 + cbSid = sizeof(rgSidBA);
124 + if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid))
125 + {
126 + ExitWithLastError(hr, "Failed to create administrator SID for system restore.");
127 + }
128 +
129 + // Create a local service security identifier (SID).
130 + cbSid = sizeof(rgSidLS);
131 + if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid))
132 + {
133 + ExitWithLastError(hr, "Failed to create local service SID for system restore.");
134 + }
135 +
136 + // Create a network service security identifier (SID).
137 + cbSid = sizeof(rgSidNS);
138 + if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid))
139 + {
140 + ExitWithLastError(hr, "Failed to create network service SID for system restore.");
141 + }
142 +
143 + // Create a personal account security identifier (SID).
144 + cbSid = sizeof(rgSidPS);
145 + if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid))
146 + {
147 + ExitWithLastError(hr, "Failed to create self SID for system restore.");
148 + }
149 +
150 + // Create a local service security identifier (SID).
151 + cbSid = sizeof(rgSidSY);
152 + if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid))
153 + {
154 + ExitWithLastError(hr, "Failed to create local system SID for system restore.");
155 + }
156 +
157 + // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and
158 + // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required.
159 + ea[0].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
160 + ea[0].grfAccessMode = SET_ACCESS;
161 + ea[0].grfInheritance = NO_INHERITANCE;
162 + ea[0].Trustee.pMultipleTrustee = NULL;
163 + ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
164 + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
165 + ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
166 + ea[0].Trustee.ptstrName = (LPTSTR)rgSidBA;
167 +
168 + ea[1].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
169 + ea[1].grfAccessMode = SET_ACCESS;
170 + ea[1].grfInheritance = NO_INHERITANCE;
171 + ea[1].Trustee.pMultipleTrustee = NULL;
172 + ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
173 + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
174 + ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
175 + ea[1].Trustee.ptstrName = (LPTSTR)rgSidLS;
176 +
177 + ea[2].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
178 + ea[2].grfAccessMode = SET_ACCESS;
179 + ea[2].grfInheritance = NO_INHERITANCE;
180 + ea[2].Trustee.pMultipleTrustee = NULL;
181 + ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
182 + ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
183 + ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
184 + ea[2].Trustee.ptstrName = (LPTSTR)rgSidNS;
185 +
186 + ea[3].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
187 + ea[3].grfAccessMode = SET_ACCESS;
188 + ea[3].grfInheritance = NO_INHERITANCE;
189 + ea[3].Trustee.pMultipleTrustee = NULL;
190 + ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
191 + ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID;
192 + ea[3].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
193 + ea[3].Trustee.ptstrName = (LPTSTR)rgSidPS;
194 +
195 + ea[4].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
196 + ea[4].grfAccessMode = SET_ACCESS;
197 + ea[4].grfInheritance = NO_INHERITANCE;
198 + ea[4].Trustee.pMultipleTrustee = NULL;
199 + ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
200 + ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID;
201 + ea[4].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
202 + ea[4].Trustee.ptstrName = (LPTSTR)rgSidSY;
203 +
204 + // Create an access control list (ACL) using this ACE list.
205 + er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl);
206 + ExitOnWin32Error(er, hr, "Failed to create ACL for system restore.");
207 +
208 + // Set the security descriptor owner to Administrators.
209 + if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE))
210 + {
211 + ExitWithLastError(hr, "Failed to set administrators owner for system restore.");
212 + }
213 +
214 + // Set the security descriptor group to Administrators.
215 + if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE))
216 + {
217 + ExitWithLastError(hr, "Failed to set administrators group access for system restore.");
218 + }
219 +
220 + // Set the discretionary access control list (DACL) to the ACL.
221 + if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE))
222 + {
223 + ExitWithLastError(hr, "Failed to set DACL for system restore.");
224 + }
225 +
226 + // Note that an explicit security descriptor is being passed in.
227 + hr= ::CoInitializeSecurity(&sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, NULL);
228 + ExitOnFailure(hr, "Failed to initialize COM security for system restore.");
229 +
230 +LExit:
231 + if (pAcl)
232 + {
233 + ::LocalFree(pAcl);
234 + }
235 +
236 + return hr;
237 +}
src/dutil/strutil.cpp new
+2730
@@ -0,0 +1,2730 @@
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 +
5 +#define ARRAY_GROWTH_SIZE 5
6 +
7 +// Forward declarations.
8 +static HRESULT AllocHelper(
9 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
10 + __in DWORD_PTR cch,
11 + __in BOOL fZeroOnRealloc
12 + );
13 +static HRESULT AllocStringHelper(
14 + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
15 + __in_z LPCWSTR wzSource,
16 + __in DWORD_PTR cchSource,
17 + __in BOOL fZeroOnRealloc
18 + );
19 +static HRESULT AllocConcatHelper(
20 + __deref_out_z LPWSTR* ppwz,
21 + __in_z LPCWSTR wzSource,
22 + __in DWORD_PTR cchSource,
23 + __in BOOL fZeroOnRealloc
24 + );
25 +static HRESULT AllocFormattedArgsHelper(
26 + __deref_out_z LPWSTR* ppwz,
27 + __in BOOL fZeroOnRealloc,
28 + __in __format_string LPCWSTR wzFormat,
29 + __in va_list args
30 + );
31 +static HRESULT StrAllocStringMapInvariant(
32 + __deref_out_z LPWSTR* pscz,
33 + __in_z LPCWSTR wzSource,
34 + __in int cchSource,
35 + __in DWORD dwMapFlags
36 + );
37 +
38 +/********************************************************************
39 +StrAlloc - allocates or reuses dynamic string memory
40 +
41 +NOTE: caller is responsible for freeing ppwz even if function fails
42 +********************************************************************/
43 +extern "C" HRESULT DAPI StrAlloc(
44 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
45 + __in DWORD_PTR cch
46 + )
47 +{
48 + return AllocHelper(ppwz, cch, FALSE);
49 +}
50 +
51 +/********************************************************************
52 +StrAllocSecure - allocates or reuses dynamic string memory
53 +If the memory needs to reallocated, calls SecureZeroMemory on the
54 +original block of memory after it is moved.
55 +
56 +NOTE: caller is responsible for freeing ppwz even if function fails
57 +********************************************************************/
58 +extern "C" HRESULT DAPI StrAllocSecure(
59 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
60 + __in DWORD_PTR cch
61 + )
62 +{
63 + return AllocHelper(ppwz, cch, TRUE);
64 +}
65 +
66 +/********************************************************************
67 +AllocHelper - allocates or reuses dynamic string memory
68 +If fZeroOnRealloc is true and the memory needs to reallocated,
69 +calls SecureZeroMemory on original block of memory after it is moved.
70 +
71 +NOTE: caller is responsible for freeing ppwz even if function fails
72 +********************************************************************/
73 +static HRESULT AllocHelper(
74 + __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
75 + __in DWORD_PTR cch,
76 + __in BOOL fZeroOnRealloc
77 + )
78 +{
79 + Assert(ppwz && cch);
80 +
81 + HRESULT hr = S_OK;
82 + LPWSTR pwz = NULL;
83 +
84 + if (cch >= MAXDWORD / sizeof(WCHAR))
85 + {
86 + hr = E_OUTOFMEMORY;
87 + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
88 + }
89 +
90 + if (*ppwz)
91 + {
92 + if (fZeroOnRealloc)
93 + {
94 + LPVOID pvNew = NULL;
95 + hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew);
96 + ExitOnFailure(hr, "Failed to reallocate string");
97 + pwz = static_cast<LPWSTR>(pvNew);
98 + }
99 + else
100 + {
101 + pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE));
102 + }
103 + }
104 + else
105 + {
106 + pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
107 + }
108 +
109 + ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
110 +
111 + *ppwz = pwz;
112 +LExit:
113 + return hr;
114 +}
115 +
116 +
117 +/********************************************************************
118 +StrTrimCapacity - Frees any unnecessary memory associated with a string.
119 + Purely used for optimization, generally only when a string
120 + has been changing size, and will no longer grow.
121 +
122 +NOTE: caller is responsible for freeing ppwz even if function fails
123 +********************************************************************/
124 +HRESULT DAPI StrTrimCapacity(
125 + __deref_out_z LPWSTR* ppwz
126 + )
127 +{
128 + Assert(ppwz);
129 +
130 + HRESULT hr = S_OK;
131 + DWORD_PTR cchLen = 0;
132 +
133 + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
134 + ExitOnFailure(hr, "Failed to calculate length of string");
135 +
136 + ++cchLen; // Add 1 for null-terminator
137 +
138 + hr = StrAlloc(ppwz, cchLen);
139 + ExitOnFailure(hr, "Failed to reallocate string");
140 +
141 +LExit:
142 + return hr;
143 +}
144 +
145 +
146 +/********************************************************************
147 +StrTrimWhitespace - allocates or reuses dynamic string memory and copies
148 + in an existing string, excluding whitespace
149 +
150 +NOTE: caller is responsible for freeing ppwz even if function fails
151 +********************************************************************/
152 +HRESULT DAPI StrTrimWhitespace(
153 + __deref_out_z LPWSTR* ppwz,
154 + __in_z LPCWSTR wzSource
155 + )
156 +{
157 + HRESULT hr = S_OK;
158 + int i = 0;
159 + LPWSTR sczResult = NULL;
160 +
161 + // Ignore beginning whitespace
162 + while (L' ' == *wzSource || L'\t' == *wzSource)
163 + {
164 + wzSource++;
165 + }
166 +
167 + i = lstrlenW(wzSource);
168 + // Overwrite ending whitespace with null characters
169 + if (0 < i)
170 + {
171 + // start from the last non-null-terminator character in the array
172 + for (i = i - 1; i > 0; --i)
173 + {
174 + if (L' ' != wzSource[i] && L'\t' != wzSource[i])
175 + {
176 + break;
177 + }
178 + }
179 +
180 + ++i;
181 + }
182 +
183 + hr = StrAllocString(&sczResult, wzSource, i);
184 + ExitOnFailure(hr, "Failed to copy result string");
185 +
186 + // Output result
187 + *ppwz = sczResult;
188 + sczResult = NULL;
189 +
190 +LExit:
191 + ReleaseStr(sczResult);
192 +
193 + return hr;
194 +}
195 +
196 +
197 +/********************************************************************
198 +StrAnsiAlloc - allocates or reuses dynamic ANSI string memory
199 +
200 +NOTE: caller is responsible for freeing ppsz even if function fails
201 +********************************************************************/
202 +extern "C" HRESULT DAPI StrAnsiAlloc(
203 + __deref_out_ecount_part(cch, 0) LPSTR* ppsz,
204 + __in DWORD_PTR cch
205 + )
206 +{
207 + Assert(ppsz && cch);
208 +
209 + HRESULT hr = S_OK;
210 + LPSTR psz = NULL;
211 +
212 + if (cch >= MAXDWORD / sizeof(WCHAR))
213 + {
214 + hr = E_OUTOFMEMORY;
215 + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
216 + }
217 +
218 + if (*ppsz)
219 + {
220 + psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE));
221 + }
222 + else
223 + {
224 + psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
225 + }
226 +
227 + ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
228 +
229 + *ppsz = psz;
230 +LExit:
231 + return hr;
232 +}
233 +
234 +
235 +/********************************************************************
236 +StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string.
237 + Purely used for optimization, generally only when a string
238 + has been changing size, and will no longer grow.
239 +
240 +NOTE: caller is responsible for freeing ppwz even if function fails
241 +********************************************************************/
242 +HRESULT DAPI StrAnsiTrimCapacity(
243 + __deref_out_z LPSTR* ppz
244 + )
245 +{
246 + Assert(ppz);
247 +
248 + HRESULT hr = S_OK;
249 + DWORD_PTR cchLen = 0;
250 +
251 +#pragma prefast(push)
252 +#pragma prefast(disable:25068)
253 + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
254 +#pragma prefast(pop)
255 + ExitOnFailure(hr, "Failed to calculate length of string");
256 +
257 + ++cchLen; // Add 1 for null-terminator
258 +
259 + hr = StrAnsiAlloc(ppz, cchLen);
260 + ExitOnFailure(hr, "Failed to reallocate string");
261 +
262 +LExit:
263 + return hr;
264 +}
265 +
266 +
267 +/********************************************************************
268 +StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies
269 + in an existing string, excluding whitespace
270 +
271 +NOTE: caller is responsible for freeing ppz even if function fails
272 +********************************************************************/
273 +HRESULT DAPI StrAnsiTrimWhitespace(
274 + __deref_out_z LPSTR* ppz,
275 + __in_z LPCSTR szSource
276 + )
277 +{
278 + HRESULT hr = S_OK;
279 + int i = 0;
280 + LPSTR sczResult = NULL;
281 +
282 + // Ignore beginning whitespace
283 + while (' ' == *szSource || '\t' == *szSource)
284 + {
285 + szSource++;
286 + }
287 +
288 + i = lstrlen(szSource);
289 + // Overwrite ending whitespace with null characters
290 + if (0 < i)
291 + {
292 + // start from the last non-null-terminator character in the array
293 + for (i = i - 1; i > 0; --i)
294 + {
295 + if (L' ' != szSource[i] && L'\t' != szSource[i])
296 + {
297 + break;
298 + }
299 + }
300 +
301 + ++i;
302 + }
303 +
304 + hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i);
305 + ExitOnFailure(hr, "Failed to copy result string");
306 +
307 + // Output result
308 + *ppz = sczResult;
309 + sczResult = NULL;
310 +
311 +LExit:
312 + ReleaseStr(sczResult);
313 +
314 + return hr;
315 +}
316 +
317 +/********************************************************************
318 +StrAllocString - allocates or reuses dynamic string memory and copies in an existing string
319 +
320 +NOTE: caller is responsible for freeing ppwz even if function fails
321 +NOTE: cchSource does not have to equal the length of wzSource
322 +NOTE: if cchSource == 0, length of wzSource is used instead
323 +********************************************************************/
324 +extern "C" HRESULT DAPI StrAllocString(
325 + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
326 + __in_z LPCWSTR wzSource,
327 + __in DWORD_PTR cchSource
328 + )
329 +{
330 + return AllocStringHelper(ppwz, wzSource, cchSource, FALSE);
331 +}
332 +
333 +/********************************************************************
334 +StrAllocStringSecure - allocates or reuses dynamic string memory and
335 +copies in an existing string. If the memory needs to reallocated,
336 +calls SecureZeroMemory on original block of memory after it is moved.
337 +
338 +NOTE: caller is responsible for freeing ppwz even if function fails
339 +NOTE: cchSource does not have to equal the length of wzSource
340 +NOTE: if cchSource == 0, length of wzSource is used instead
341 +********************************************************************/
342 +extern "C" HRESULT DAPI StrAllocStringSecure(
343 + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
344 + __in_z LPCWSTR wzSource,
345 + __in DWORD_PTR cchSource
346 + )
347 +{
348 + return AllocStringHelper(ppwz, wzSource, cchSource, TRUE);
349 +}
350 +
351 +/********************************************************************
352 +AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string
353 +If fZeroOnRealloc is true and the memory needs to reallocated,
354 +calls SecureZeroMemory on original block of memory after it is moved.
355 +
356 +NOTE: caller is responsible for freeing ppwz even if function fails
357 +NOTE: cchSource does not have to equal the length of wzSource
358 +NOTE: if cchSource == 0, length of wzSource is used instead
359 +********************************************************************/
360 +static HRESULT AllocStringHelper(
361 + __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
362 + __in_z LPCWSTR wzSource,
363 + __in DWORD_PTR cchSource,
364 + __in BOOL fZeroOnRealloc
365 + )
366 +{
367 + Assert(ppwz && wzSource); // && *wzSource);
368 +
369 + HRESULT hr = S_OK;
370 + DWORD_PTR cch = 0;
371 +
372 + if (*ppwz)
373 + {
374 + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
375 + if (-1 == cch)
376 + {
377 + hr = E_INVALIDARG;
378 + ExitOnFailure(hr, "failed to get size of destination string");
379 + }
380 + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
381 + }
382 +
383 + if (0 == cchSource)
384 + {
385 + cchSource = lstrlenW(wzSource);
386 + }
387 +
388 + DWORD_PTR cchNeeded;
389 + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
390 + ExitOnFailure(hr, "source string is too long");
391 +
392 + if (cch < cchNeeded)
393 + {
394 + cch = cchNeeded;
395 + hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
396 + ExitOnFailure(hr, "failed to allocate string from string.");
397 + }
398 +
399 + // copy everything (the NULL terminator will be included)
400 + hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
401 +
402 +LExit:
403 + return hr;
404 +}
405 +
406 +
407 +/********************************************************************
408 +StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string
409 +
410 +NOTE: caller is responsible for freeing ppsz even if function fails
411 +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
412 +NOTE: if cchSource == 0, length of wzSource is used instead
413 +********************************************************************/
414 +extern "C" HRESULT DAPI StrAnsiAllocString(
415 + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
416 + __in_z LPCWSTR wzSource,
417 + __in DWORD_PTR cchSource,
418 + __in UINT uiCodepage
419 + )
420 +{
421 + Assert(ppsz && wzSource);
422 +
423 + HRESULT hr = S_OK;
424 + LPSTR psz = NULL;
425 + DWORD_PTR cch = 0;
426 + DWORD_PTR cchDest = cchSource; // at least enough
427 +
428 + if (*ppsz)
429 + {
430 + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
431 + if (-1 == cch)
432 + {
433 + hr = E_INVALIDARG;
434 + ExitOnFailure(hr, "failed to get size of destination string");
435 + }
436 + cch /= sizeof(CHAR); //convert the count in bytes to count in characters
437 + }
438 +
439 + if (0 == cchSource)
440 + {
441 + cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL);
442 + if (0 == cchDest)
443 + {
444 + ExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource);
445 + }
446 +
447 + --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below
448 + }
449 + else if (L'\0' == wzSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below
450 + {
451 + cchDest = cchSource - 1;
452 + }
453 +
454 + if (cch < cchDest + 1)
455 + {
456 + cch = cchDest + 1; // add one for the NULL terminator
457 + if (cch >= MAXDWORD / sizeof(WCHAR))
458 + {
459 + hr = E_OUTOFMEMORY;
460 + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
461 + }
462 +
463 + if (*ppsz)
464 + {
465 + psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE));
466 + }
467 + else
468 + {
469 + psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
470 + }
471 + ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
472 +
473 + *ppsz = psz;
474 + }
475 +
476 + if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL))
477 + {
478 + ExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource);
479 + }
480 + (*ppsz)[cchDest] = L'\0';
481 +
482 +LExit:
483 + return hr;
484 +}
485 +
486 +
487 +/********************************************************************
488 +StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string
489 +
490 +NOTE: caller is responsible for freeing ppwz even if function fails
491 +NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
492 +NOTE: if cchSource == 0, length of wzSource is used instead
493 +********************************************************************/
494 +extern "C" HRESULT DAPI StrAllocStringAnsi(
495 + __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
496 + __in_z LPCSTR szSource,
497 + __in DWORD_PTR cchSource,
498 + __in UINT uiCodepage
499 + )
500 +{
501 + Assert(ppwz && szSource);
502 +
503 + HRESULT hr = S_OK;
504 + LPWSTR pwz = NULL;
505 + DWORD_PTR cch = 0;
506 + DWORD_PTR cchDest = cchSource; // at least enough
507 +
508 + if (*ppwz)
509 + {
510 + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
511 + if (-1 == cch)
512 + {
513 + hr = E_INVALIDARG;
514 + ExitOnFailure(hr, "failed to get size of destination string");
515 + }
516 + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
517 + }
518 +
519 + if (0 == cchSource)
520 + {
521 + cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0);
522 + if (0 == cchDest)
523 + {
524 + ExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource);
525 + }
526 +
527 + --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below
528 + }
529 + else if (L'\0' == szSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below
530 + {
531 + cchDest = cchSource - 1;
532 + }
533 +
534 + if (cch < cchDest + 1)
535 + {
536 + cch = cchDest + 1;
537 + if (cch >= MAXDWORD / sizeof(WCHAR))
538 + {
539 + hr = E_OUTOFMEMORY;
540 + ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
541 + }
542 +
543 + if (*ppwz)
544 + {
545 + pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE));
546 + }
547 + else
548 + {
549 + pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
550 + }
551 +
552 + ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
553 +
554 + *ppwz = pwz;
555 + }
556 +
557 + if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch))
558 + {
559 + ExitWithLastError(hr, "failed to convert to unicode: %s", szSource);
560 + }
561 + (*ppwz)[cchDest] = L'\0';
562 +
563 +LExit:
564 + return hr;
565 +}
566 +
567 +
568 +/********************************************************************
569 +StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string
570 +
571 +NOTE: caller is responsible for freeing ppsz even if function fails
572 +NOTE: cchSource does not have to equal the length of wzSource
573 +NOTE: if cchSource == 0, length of wzSource is used instead
574 +********************************************************************/
575 +HRESULT DAPI StrAnsiAllocStringAnsi(
576 + __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
577 + __in_z LPCSTR szSource,
578 + __in DWORD_PTR cchSource
579 + )
580 +{
581 + Assert(ppsz && szSource); // && *szSource);
582 +
583 + HRESULT hr = S_OK;
584 + DWORD_PTR cch = 0;
585 +
586 + if (*ppsz)
587 + {
588 + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
589 + if (-1 == cch)
590 + {
591 + hr = E_INVALIDARG;
592 + ExitOnFailure(hr, "failed to get size of destination string");
593 + }
594 + cch /= sizeof(CHAR); //convert the count in bytes to count in characters
595 + }
596 +
597 + if (0 == cchSource)
598 + {
599 + cchSource = lstrlenA(szSource);
600 + }
601 +
602 + DWORD_PTR cchNeeded;
603 + hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
604 + ExitOnFailure(hr, "source string is too long");
605 +
606 + if (cch < cchNeeded)
607 + {
608 + cch = cchNeeded;
609 + hr = StrAnsiAlloc(ppsz, cch);
610 + ExitOnFailure(hr, "failed to allocate string from string.");
611 + }
612 +
613 + // copy everything (the NULL terminator will be included)
614 +#pragma prefast(push)
615 +#pragma prefast(disable:25068)
616 + hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
617 +#pragma prefast(pop)
618 +
619 +LExit:
620 + return hr;
621 +}
622 +
623 +
624 +/********************************************************************
625 +StrAllocPrefix - allocates or reuses dynamic string memory and
626 + prefixes a string
627 +
628 +NOTE: caller is responsible for freeing ppwz even if function fails
629 +NOTE: cchPrefix does not have to equal the length of wzPrefix
630 +NOTE: if cchPrefix == 0, length of wzPrefix is used instead
631 +********************************************************************/
632 +extern "C" HRESULT DAPI StrAllocPrefix(
633 + __deref_out_z LPWSTR* ppwz,
634 + __in_z LPCWSTR wzPrefix,
635 + __in DWORD_PTR cchPrefix
636 + )
637 +{
638 + Assert(ppwz && wzPrefix);
639 +
640 + HRESULT hr = S_OK;
641 + DWORD_PTR cch = 0;
642 + DWORD_PTR cchLen = 0;
643 +
644 + if (*ppwz)
645 + {
646 + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
647 + if (-1 == cch)
648 + {
649 + hr = E_INVALIDARG;
650 + ExitOnFailure(hr, "failed to get size of destination string");
651 + }
652 + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
653 +
654 + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
655 + ExitOnFailure(hr, "Failed to calculate length of string");
656 + }
657 +
658 + Assert(cchLen <= cch);
659 +
660 + if (0 == cchPrefix)
661 + {
662 + hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchPrefix));
663 + ExitOnFailure(hr, "Failed to calculate length of string");
664 + }
665 +
666 + if (cch - cchLen < cchPrefix + 1)
667 + {
668 + cch = cchPrefix + cchLen + 1;
669 + hr = StrAlloc(ppwz, cch);
670 + ExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix);
671 + }
672 +
673 + if (*ppwz)
674 + {
675 + DWORD_PTR cb = cch * sizeof(WCHAR);
676 + DWORD_PTR cbPrefix = cchPrefix * sizeof(WCHAR);
677 +
678 + memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix);
679 + memcpy(*ppwz, wzPrefix, cbPrefix);
680 + }
681 + else
682 + {
683 + hr = E_UNEXPECTED;
684 + ExitOnFailure(hr, "for some reason our buffer is still null");
685 + }
686 +
687 +LExit:
688 + return hr;
689 +}
690 +
691 +
692 +/********************************************************************
693 +StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string
694 +
695 +NOTE: caller is responsible for freeing ppwz even if function fails
696 +NOTE: cchSource does not have to equal the length of wzSource
697 +NOTE: if cchSource == 0, length of wzSource is used instead
698 +********************************************************************/
699 +extern "C" HRESULT DAPI StrAllocConcat(
700 + __deref_out_z LPWSTR* ppwz,
701 + __in_z LPCWSTR wzSource,
702 + __in DWORD_PTR cchSource
703 + )
704 +{
705 + return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE);
706 +}
707 +
708 +
709 +/********************************************************************
710 +StrAllocConcatSecure - allocates or reuses dynamic string memory and
711 +adds an existing string. If the memory needs to reallocated, calls
712 +SecureZeroMemory on the original block of memory after it is moved.
713 +
714 +NOTE: caller is responsible for freeing ppwz even if function fails
715 +NOTE: cchSource does not have to equal the length of wzSource
716 +NOTE: if cchSource == 0, length of wzSource is used instead
717 +********************************************************************/
718 +extern "C" HRESULT DAPI StrAllocConcatSecure(
719 + __deref_out_z LPWSTR* ppwz,
720 + __in_z LPCWSTR wzSource,
721 + __in DWORD_PTR cchSource
722 + )
723 +{
724 + return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE);
725 +}
726 +
727 +
728 +/********************************************************************
729 +AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string
730 +If fZeroOnRealloc is true and the memory needs to reallocated,
731 +calls SecureZeroMemory on original block of memory after it is moved.
732 +
733 +NOTE: caller is responsible for freeing ppwz even if function fails
734 +NOTE: cchSource does not have to equal the length of wzSource
735 +NOTE: if cchSource == 0, length of wzSource is used instead
736 +********************************************************************/
737 +static HRESULT AllocConcatHelper(
738 + __deref_out_z LPWSTR* ppwz,
739 + __in_z LPCWSTR wzSource,
740 + __in DWORD_PTR cchSource,
741 + __in BOOL fZeroOnRealloc
742 + )
743 +{
744 + Assert(ppwz && wzSource); // && *wzSource);
745 +
746 + HRESULT hr = S_OK;
747 + DWORD_PTR cch = 0;
748 + DWORD_PTR cchLen = 0;
749 +
750 + if (*ppwz)
751 + {
752 + cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
753 + if (-1 == cch)
754 + {
755 + hr = E_INVALIDARG;
756 + ExitOnFailure(hr, "failed to get size of destination string");
757 + }
758 + cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
759 +
760 + hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
761 + ExitOnFailure(hr, "Failed to calculate length of string");
762 + }
763 +
764 + Assert(cchLen <= cch);
765 +
766 + if (0 == cchSource)
767 + {
768 + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
769 + ExitOnFailure(hr, "Failed to calculate length of string");
770 + }
771 +
772 + if (cch - cchLen < cchSource + 1)
773 + {
774 + cch = (cchSource + cchLen + 1) * 2;
775 + hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
776 + ExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource);
777 + }
778 +
779 + if (*ppwz)
780 + {
781 + hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
782 + }
783 + else
784 + {
785 + hr = E_UNEXPECTED;
786 + ExitOnFailure(hr, "for some reason our buffer is still null");
787 + }
788 +
789 +LExit:
790 + return hr;
791 +}
792 +
793 +
794 +/********************************************************************
795 +StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string
796 +
797 +NOTE: caller is responsible for freeing ppz even if function fails
798 +NOTE: cchSource does not have to equal the length of pzSource
799 +NOTE: if cchSource == 0, length of pzSource is used instead
800 +********************************************************************/
801 +extern "C" HRESULT DAPI StrAnsiAllocConcat(
802 + __deref_out_z LPSTR* ppz,
803 + __in_z LPCSTR pzSource,
804 + __in DWORD_PTR cchSource
805 + )
806 +{
807 + Assert(ppz && pzSource); // && *pzSource);
808 +
809 + HRESULT hr = S_OK;
810 + DWORD_PTR cch = 0;
811 + DWORD_PTR cchLen = 0;
812 +
813 + if (*ppz)
814 + {
815 + cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1)
816 + if (-1 == cch)
817 + {
818 + hr = E_INVALIDARG;
819 + ExitOnFailure(hr, "failed to get size of destination string");
820 + }
821 + cch /= sizeof(CHAR); // convert the count in bytes to count in characters
822 +
823 +#pragma prefast(push)
824 +#pragma prefast(disable:25068)
825 + hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
826 +#pragma prefast(pop)
827 + ExitOnFailure(hr, "Failed to calculate length of string");
828 + }
829 +
830 + Assert(cchLen <= cch);
831 +
832 + if (0 == cchSource)
833 + {
834 +#pragma prefast(push)
835 +#pragma prefast(disable:25068)
836 + hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
837 +#pragma prefast(pop)
838 + ExitOnFailure(hr, "Failed to calculate length of string");
839 + }
840 +
841 + if (cch - cchLen < cchSource + 1)
842 + {
843 + cch = (cchSource + cchLen + 1) * 2;
844 + hr = StrAnsiAlloc(ppz, cch);
845 + ExitOnFailure(hr, "failed to allocate string from string: %ls", pzSource);
846 + }
847 +
848 + if (*ppz)
849 + {
850 +#pragma prefast(push)
851 +#pragma prefast(disable:25068)
852 + hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
853 +#pragma prefast(pop)
854 + }
855 + else
856 + {
857 + hr = E_UNEXPECTED;
858 + ExitOnFailure(hr, "for some reason our buffer is still null");
859 + }
860 +
861 +LExit:
862 + return hr;
863 +}
864 +
865 +
866 +/********************************************************************
867 +StrAllocFormatted - allocates or reuses dynamic string memory and formats it
868 +
869 +NOTE: caller is responsible for freeing ppwz even if function fails
870 +********************************************************************/
871 +extern "C" HRESULT __cdecl StrAllocFormatted(
872 + __deref_out_z LPWSTR* ppwz,
873 + __in __format_string LPCWSTR wzFormat,
874 + ...
875 + )
876 +{
877 + Assert(ppwz && wzFormat && *wzFormat);
878 +
879 + HRESULT hr = S_OK;
880 + va_list args;
881 +
882 + va_start(args, wzFormat);
883 + hr = StrAllocFormattedArgs(ppwz, wzFormat, args);
884 + va_end(args);
885 +
886 + return hr;
887 +}
888 +
889 +
890 +/********************************************************************
891 +StrAllocConcatFormatted - allocates or reuses dynamic string memory
892 +and adds a formatted string
893 +
894 +NOTE: caller is responsible for freeing ppwz even if function fails
895 +********************************************************************/
896 +extern "C" HRESULT __cdecl StrAllocConcatFormatted(
897 + __deref_out_z LPWSTR* ppwz,
898 + __in __format_string LPCWSTR wzFormat,
899 + ...
900 + )
901 +{
902 + Assert(ppwz && wzFormat && *wzFormat);
903 +
904 + HRESULT hr = S_OK;
905 + LPWSTR sczFormatted = NULL;
906 + va_list args;
907 +
908 + va_start(args, wzFormat);
909 + hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args);
910 + va_end(args);
911 + ExitOnFailure(hr, "Failed to allocate formatted string");
912 +
913 + hr = StrAllocConcat(ppwz, sczFormatted, 0);
914 +
915 +LExit:
916 + ReleaseStr(sczFormatted);
917 +
918 + return hr;
919 +}
920 +
921 +
922 +/********************************************************************
923 +StrAllocFormattedSecure - allocates or reuses dynamic string memory
924 +and formats it. If the memory needs to reallocated,
925 +calls SecureZeroMemory on original block of memory after it is moved.
926 +
927 +NOTE: caller is responsible for freeing ppwz even if function fails
928 +********************************************************************/
929 +extern "C" HRESULT __cdecl StrAllocFormattedSecure(
930 + __deref_out_z LPWSTR* ppwz,
931 + __in __format_string LPCWSTR wzFormat,
932 + ...
933 + )
934 +{
935 + Assert(ppwz && wzFormat && *wzFormat);
936 +
937 + HRESULT hr = S_OK;
938 + va_list args;
939 +
940 + va_start(args, wzFormat);
941 + hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args);
942 + va_end(args);
943 +
944 + return hr;
945 +}
946 +
947 +
948 +/********************************************************************
949 +StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it
950 +
951 +NOTE: caller is responsible for freeing ppsz even if function fails
952 +********************************************************************/
953 +extern "C" HRESULT DAPI StrAnsiAllocFormatted(
954 + __deref_out_z LPSTR* ppsz,
955 + __in __format_string LPCSTR szFormat,
956 + ...
957 + )
958 +{
959 + Assert(ppsz && szFormat && *szFormat);
960 +
961 + HRESULT hr = S_OK;
962 + va_list args;
963 +
964 + va_start(args, szFormat);
965 + hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args);
966 + va_end(args);
967 +
968 + return hr;
969 +}
970 +
971 +
972 +/********************************************************************
973 +StrAllocFormattedArgs - allocates or reuses dynamic string memory
974 +and formats it with the passed in args
975 +
976 +NOTE: caller is responsible for freeing ppwz even if function fails
977 +********************************************************************/
978 +extern "C" HRESULT DAPI StrAllocFormattedArgs(
979 + __deref_out_z LPWSTR* ppwz,
980 + __in __format_string LPCWSTR wzFormat,
981 + __in va_list args
982 + )
983 +{
984 + return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args);
985 +}
986 +
987 +
988 +/********************************************************************
989 +StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory
990 +and formats it with the passed in args.
991 +
992 +If the memory needs to reallocated, calls SecureZeroMemory on the
993 +original block of memory after it is moved.
994 +
995 +NOTE: caller is responsible for freeing ppwz even if function fails
996 +********************************************************************/
997 +extern "C" HRESULT DAPI StrAllocFormattedArgsSecure(
998 + __deref_out_z LPWSTR* ppwz,
999 + __in __format_string LPCWSTR wzFormat,
1000 + __in va_list args
1001 + )
1002 +{
1003 + return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args);
1004 +}
1005 +
1006 +
1007 +/********************************************************************
1008 +AllocFormattedArgsHelper - allocates or reuses dynamic string memory
1009 +and formats it with the passed in args.
1010 +
1011 +If fZeroOnRealloc is true and the memory needs to reallocated,
1012 +calls SecureZeroMemory on original block of memory after it is moved.
1013 +
1014 +NOTE: caller is responsible for freeing ppwz even if function fails
1015 +********************************************************************/
1016 +static HRESULT AllocFormattedArgsHelper(
1017 + __deref_out_z LPWSTR* ppwz,
1018 + __in BOOL fZeroOnRealloc,
1019 + __in __format_string LPCWSTR wzFormat,
1020 + __in va_list args
1021 + )
1022 +{
1023 + Assert(ppwz && wzFormat && *wzFormat);
1024 +
1025 + HRESULT hr = S_OK;
1026 + SIZE_T cch = 0;
1027 + LPWSTR pwzOriginal = NULL;
1028 + SIZE_T cbOriginal = 0;
1029 + SIZE_T cchOriginal = 0;
1030 +
1031 + if (*ppwz)
1032 + {
1033 + cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
1034 + if (-1 == cbOriginal)
1035 + {
1036 + hr = E_INVALIDARG;
1037 + ExitOnFailure(hr, "failed to get size of destination string");
1038 + }
1039 +
1040 + cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters
1041 + cchOriginal = lstrlenW(*ppwz);
1042 + }
1043 +
1044 + if (0 == cch) // if there is no space in the string buffer
1045 + {
1046 + cch = 256;
1047 +
1048 + hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1049 + ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1050 + }
1051 +
1052 + // format the message (grow until it fits or there is a failure)
1053 + do
1054 + {
1055 + hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args);
1056 + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1057 + {
1058 + if (!pwzOriginal)
1059 + {
1060 + // this allows you to pass the original string as a formatting argument and not crash
1061 + // save the original string and free it after the printf is complete
1062 + pwzOriginal = *ppwz;
1063 + *ppwz = NULL;
1064 +
1065 + // StringCchVPrintfW starts writing to the string...
1066 + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1067 + pwzOriginal[cchOriginal] = 0;
1068 + }
1069 +
1070 + cch *= 2;
1071 +
1072 + hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1073 + ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1074 +
1075 + hr = S_FALSE;
1076 + }
1077 + } while (S_FALSE == hr);
1078 + ExitOnFailure(hr, "failed to format string");
1079 +
1080 +LExit:
1081 + if (pwzOriginal && fZeroOnRealloc)
1082 + {
1083 + SecureZeroMemory(pwzOriginal, cbOriginal);
1084 + }
1085 +
1086 + ReleaseStr(pwzOriginal);
1087 +
1088 + return hr;
1089 +}
1090 +
1091 +
1092 +/********************************************************************
1093 +StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory
1094 +and formats it with the passed in args
1095 +
1096 +NOTE: caller is responsible for freeing ppsz even if function fails
1097 +********************************************************************/
1098 +extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs(
1099 + __deref_out_z LPSTR* ppsz,
1100 + __in __format_string LPCSTR szFormat,
1101 + __in va_list args
1102 + )
1103 +{
1104 + Assert(ppsz && szFormat && *szFormat);
1105 +
1106 + HRESULT hr = S_OK;
1107 + DWORD_PTR cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0;
1108 + LPSTR pszOriginal = NULL;
1109 + DWORD cchOriginal = 0;
1110 +
1111 + if (*ppsz)
1112 + {
1113 + cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
1114 + if (-1 == cch)
1115 + {
1116 + hr = E_INVALIDARG;
1117 + ExitOnFailure(hr, "failed to get size of destination string");
1118 + }
1119 + cch /= sizeof(CHAR); //convert the count in bytes to count in characters
1120 +
1121 + cchOriginal = lstrlenA(*ppsz);
1122 + }
1123 +
1124 + if (0 == cch) // if there is no space in the string buffer
1125 + {
1126 + cch = 256;
1127 + hr = StrAnsiAlloc(ppsz, cch);
1128 + ExitOnFailure(hr, "failed to allocate string to format: %s", szFormat);
1129 + }
1130 +
1131 + // format the message (grow until it fits or there is a failure)
1132 + do
1133 + {
1134 +#pragma prefast(push)
1135 +#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1136 + hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args);
1137 +#pragma prefast(pop)
1138 + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1139 + {
1140 + if (!pszOriginal)
1141 + {
1142 + // this allows you to pass the original string as a formatting argument and not crash
1143 + // save the original string and free it after the printf is complete
1144 + pszOriginal = *ppsz;
1145 + *ppsz = NULL;
1146 + // StringCchVPrintfW starts writing to the string...
1147 + // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1148 + pszOriginal[cchOriginal] = 0;
1149 + }
1150 + cch *= 2;
1151 + hr = StrAnsiAlloc(ppsz, cch);
1152 + ExitOnFailure(hr, "failed to allocate string to format: %ls", szFormat);
1153 + hr = S_FALSE;
1154 + }
1155 + } while (S_FALSE == hr);
1156 + ExitOnFailure(hr, "failed to format string");
1157 +
1158 +LExit:
1159 + ReleaseStr(pszOriginal);
1160 +
1161 + return hr;
1162 +}
1163 +
1164 +
1165 +/********************************************************************
1166 +StrAllocFromError - returns the string for a particular error.
1167 +
1168 +********************************************************************/
1169 +extern "C" HRESULT DAPI StrAllocFromError(
1170 + __inout LPWSTR *ppwzMessage,
1171 + __in HRESULT hrError,
1172 + __in_opt HMODULE hModule,
1173 + ...
1174 + )
1175 +{
1176 + HRESULT hr = S_OK;
1177 + DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM;
1178 + LPVOID pvMessage = NULL;
1179 + DWORD cchMessage = 0;
1180 +
1181 + if (hModule)
1182 + {
1183 + dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
1184 + }
1185 +
1186 + va_list args;
1187 + va_start(args, hModule);
1188 + cchMessage = ::FormatMessageW(dwFlags, static_cast<LPCVOID>(hModule), hrError, 0, reinterpret_cast<LPWSTR>(&pvMessage), 0, &args);
1189 + va_end(args);
1190 +
1191 + if (0 == cchMessage)
1192 + {
1193 + ExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError);
1194 + }
1195 +
1196 + hr = StrAllocString(ppwzMessage, reinterpret_cast<LPCWSTR>(pvMessage), cchMessage);
1197 + ExitOnFailure(hr, "Failed to allocate string for message.");
1198 +
1199 +LExit:
1200 + if (pvMessage)
1201 + {
1202 + ::LocalFree(pvMessage);
1203 + }
1204 +
1205 + return hr;
1206 +}
1207 +
1208 +
1209 +/********************************************************************
1210 +StrMaxLength - returns maximum number of characters that can be stored in dynamic string p
1211 +
1212 +NOTE: assumes Unicode string
1213 +********************************************************************/
1214 +extern "C" HRESULT DAPI StrMaxLength(
1215 + __in LPCVOID p,
1216 + __out DWORD_PTR* pcch
1217 + )
1218 +{
1219 + Assert(pcch);
1220 +
1221 + HRESULT hr = S_OK;
1222 +
1223 + if (p)
1224 + {
1225 + *pcch = MemSize(p); // get size of entire buffer
1226 + if (-1 == *pcch)
1227 + {
1228 + ExitFunction1(hr = E_FAIL);
1229 + }
1230 +
1231 + *pcch /= sizeof(WCHAR); // reduce to count of characters
1232 + }
1233 + else
1234 + {
1235 + *pcch = 0;
1236 + }
1237 + Assert(S_OK == hr);
1238 +
1239 +LExit:
1240 + return hr;
1241 +}
1242 +
1243 +
1244 +/********************************************************************
1245 +StrSize - returns count of bytes in dynamic string p
1246 +
1247 +********************************************************************/
1248 +extern "C" HRESULT DAPI StrSize(
1249 + __in LPCVOID p,
1250 + __out DWORD_PTR* pcb
1251 + )
1252 +{
1253 + Assert(p && pcb);
1254 +
1255 + HRESULT hr = S_OK;
1256 +
1257 + *pcb = MemSize(p);
1258 + if (-1 == *pcb)
1259 + {
1260 + hr = E_FAIL;
1261 + }
1262 +
1263 + return hr;
1264 +}
1265 +
1266 +/********************************************************************
1267 +StrFree - releases dynamic string memory allocated by any StrAlloc*() functions
1268 +
1269 +********************************************************************/
1270 +extern "C" HRESULT DAPI StrFree(
1271 + __in LPVOID p
1272 + )
1273 +{
1274 + Assert(p);
1275 +
1276 + HRESULT hr = MemFree(p);
1277 + ExitOnFailure(hr, "failed to free string");
1278 +
1279 +LExit:
1280 + return hr;
1281 +}
1282 +
1283 +
1284 +/****************************************************************************
1285 +StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1286 +Replaces all instances.
1287 +
1288 +****************************************************************************/
1289 +extern "C" HRESULT DAPI StrReplaceStringAll(
1290 + __inout LPWSTR* ppwzOriginal,
1291 + __in_z LPCWSTR wzOldSubString,
1292 + __in_z LPCWSTR wzNewSubString
1293 + )
1294 +{
1295 + HRESULT hr = S_OK;
1296 + DWORD dwStartIndex = 0;
1297 +
1298 + do
1299 + {
1300 + hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString);
1301 + ExitOnFailure(hr, "Failed to replace substring");
1302 + }
1303 + while (S_OK == hr);
1304 +
1305 + hr = (0 == dwStartIndex) ? S_FALSE : S_OK;
1306 +
1307 +LExit:
1308 + return hr;
1309 +}
1310 +
1311 +
1312 +/****************************************************************************
1313 +StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1314 +Search for old substring starts at dwStartIndex. Does only 1 replace.
1315 +
1316 +****************************************************************************/
1317 +extern "C" HRESULT DAPI StrReplaceString(
1318 + __inout LPWSTR* ppwzOriginal,
1319 + __inout DWORD* pdwStartIndex,
1320 + __in_z LPCWSTR wzOldSubString,
1321 + __in_z LPCWSTR wzNewSubString
1322 + )
1323 +{
1324 + Assert(ppwzOriginal && wzOldSubString && wzNewSubString);
1325 +
1326 + HRESULT hr = S_FALSE;
1327 + LPCWSTR wzSubLocation = NULL;
1328 + LPWSTR pwzBuffer = NULL;
1329 +
1330 + if (!*ppwzOriginal)
1331 + {
1332 + ExitFunction();
1333 + }
1334 +
1335 + wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString);
1336 + if (!wzSubLocation)
1337 + {
1338 + ExitFunction();
1339 + }
1340 +
1341 + hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex);
1342 + ExitOnFailure(hr, "Failed to diff pointers.");
1343 +
1344 + hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal);
1345 + ExitOnFailure(hr, "Failed to duplicate string.");
1346 +
1347 + pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0';
1348 +
1349 + hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0);
1350 + ExitOnFailure(hr, "Failed to append new string.");
1351 +
1352 + hr = StrAllocConcat(&pwzBuffer, wzSubLocation + wcslen(wzOldSubString), 0);
1353 + ExitOnFailure(hr, "Failed to append post string.");
1354 +
1355 + hr = StrFree(*ppwzOriginal);
1356 + ExitOnFailure(hr, "Failed to free original string.");
1357 +
1358 + *ppwzOriginal = pwzBuffer;
1359 + *pdwStartIndex = *pdwStartIndex + static_cast<DWORD>(wcslen(wzNewSubString));
1360 + hr = S_OK;
1361 +
1362 +LExit:
1363 + return hr;
1364 +}
1365 +
1366 +
1367 +static inline BYTE HexCharToByte(
1368 + __in WCHAR wc
1369 + )
1370 +{
1371 + Assert(L'0' <= wc && wc <= L'9' || L'a' <= wc && wc <= L'f' || L'A' <= wc && wc <= L'F'); // make sure wc is a hex character
1372 +
1373 + BYTE b;
1374 + if (L'0' <= wc && wc <= L'9')
1375 + {
1376 + b = (BYTE)(wc - L'0');
1377 + }
1378 + else if ('a' <= wc && wc <= 'f')
1379 + {
1380 + b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1));
1381 + }
1382 + else // must be (L'A' <= wc && wc <= L'F')
1383 + {
1384 + b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1));
1385 + }
1386 +
1387 + Assert(0 <= b && b <= 15);
1388 + return b;
1389 +}
1390 +
1391 +
1392 +/****************************************************************************
1393 +StrHexEncode - converts an array of bytes to a text string
1394 +
1395 +NOTE: wzDest must have space for cbSource * 2 + 1 characters
1396 +****************************************************************************/
1397 +extern "C" HRESULT DAPI StrHexEncode(
1398 + __in_ecount(cbSource) const BYTE* pbSource,
1399 + __in DWORD_PTR cbSource,
1400 + __out_ecount(cchDest) LPWSTR wzDest,
1401 + __in DWORD_PTR cchDest
1402 + )
1403 +{
1404 + Assert(pbSource && wzDest);
1405 +
1406 + HRESULT hr = S_OK;
1407 + DWORD i;
1408 + BYTE b;
1409 +
1410 + if (cchDest < 2 * cbSource + 1)
1411 + {
1412 + ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
1413 + }
1414 +
1415 + for (i = 0; i < cbSource; ++i)
1416 + {
1417 + b = (*pbSource) >> 4;
1418 + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1419 + b = (*pbSource) & 0xF;
1420 + *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1421 +
1422 + ++pbSource;
1423 + }
1424 +
1425 + *wzDest = 0;
1426 +
1427 +LExit:
1428 + return hr;
1429 +}
1430 +
1431 +
1432 +/****************************************************************************
1433 +StrAllocHexEncode - converts an array of bytes to an allocated text string
1434 +
1435 +****************************************************************************/
1436 +HRESULT DAPI StrAllocHexEncode(
1437 + __in_ecount(cbSource) const BYTE* pbSource,
1438 + __in DWORD_PTR cbSource,
1439 + __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest
1440 + )
1441 +{
1442 + HRESULT hr = S_OK;
1443 + DWORD_PTR cchSource = sizeof(WCHAR) * (cbSource + 1);
1444 +
1445 + hr = StrAlloc(ppwzDest, cchSource);
1446 + ExitOnFailure(hr, "Failed to allocate hex string.");
1447 +
1448 + hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource);
1449 + ExitOnFailure(hr, "Failed to encode hex string.");
1450 +
1451 +LExit:
1452 + return hr;
1453 +}
1454 +
1455 +
1456 +/****************************************************************************
1457 +StrHexDecode - converts a string of text to array of bytes
1458 +
1459 +NOTE: wzSource must contain even number of characters
1460 +****************************************************************************/
1461 +extern "C" HRESULT DAPI StrHexDecode(
1462 + __in_z LPCWSTR wzSource,
1463 + __out_bcount(cbDest) BYTE* pbDest,
1464 + __in DWORD_PTR cbDest
1465 + )
1466 +{
1467 + Assert(wzSource && pbDest);
1468 +
1469 + HRESULT hr = S_OK;
1470 + DWORD cchSource = lstrlenW(wzSource);
1471 + DWORD i;
1472 + BYTE b;
1473 +
1474 + Assert(0 == cchSource % 2);
1475 + if (cbDest < cchSource / 2)
1476 + {
1477 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1478 + ExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %u into %u bytes.", wzSource, cchSource, cbDest);
1479 + }
1480 +
1481 + for (i = 0; i < cchSource / 2; ++i)
1482 + {
1483 + b = HexCharToByte(*wzSource++);
1484 + (*pbDest) = b << 4;
1485 +
1486 + b = HexCharToByte(*wzSource++);
1487 + (*pbDest) |= b & 0xF;
1488 +
1489 + ++pbDest;
1490 + }
1491 +
1492 +LExit:
1493 + return hr;
1494 +}
1495 +
1496 +
1497 +/****************************************************************************
1498 +StrAllocHexDecode - allocates a byte array hex-converted from string of text
1499 +
1500 +NOTE: wzSource must contain even number of characters
1501 +****************************************************************************/
1502 +extern "C" HRESULT DAPI StrAllocHexDecode(
1503 + __in_z LPCWSTR wzSource,
1504 + __out_bcount(*pcbDest) BYTE** ppbDest,
1505 + __out_opt DWORD* pcbDest
1506 + )
1507 +{
1508 + Assert(wzSource && *wzSource && ppbDest);
1509 +
1510 + HRESULT hr = S_OK;
1511 + size_t cch = 0;
1512 + BYTE* pb = NULL;
1513 + DWORD cb = 0;
1514 +
1515 + hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch);
1516 + ExitOnFailure(hr, "Failed to calculate length of source string.");
1517 +
1518 + if (cch % 2)
1519 + {
1520 + hr = E_INVALIDARG;
1521 + ExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded.");
1522 + }
1523 +
1524 + cb = static_cast<DWORD>(cch / 2);
1525 + pb = static_cast<BYTE*>(MemAlloc(cb, TRUE));
1526 + ExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode.");
1527 +
1528 + hr = StrHexDecode(wzSource, pb, cb);
1529 + ExitOnFailure(hr, "Failed to decode source string.");
1530 +
1531 + *ppbDest = pb;
1532 + pb = NULL;
1533 +
1534 + if (pcbDest)
1535 + {
1536 + *pcbDest = cb;
1537 + }
1538 +
1539 +LExit:
1540 + ReleaseMem(pb);
1541 +
1542 + return hr;
1543 +}
1544 +
1545 +
1546 +/****************************************************************************
1547 +Base85 encoding/decoding data
1548 +
1549 +****************************************************************************/
1550 +const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~";
1551 +
1552 +const BYTE Base85DecodeTable[256] =
1553 +{
1554 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1555 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1556 + 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1557 + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23,
1558 + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
1559 + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54,
1560 + 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
1561 + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1562 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1563 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1564 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1565 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1566 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1567 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1568 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1569 + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85
1570 +};
1571 +
1572 +const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 };
1573 +
1574 +
1575 +/****************************************************************************
1576 +StrAllocBase85Encode - converts an array of bytes into an XML compatible string
1577 +
1578 +****************************************************************************/
1579 +extern "C" HRESULT DAPI StrAllocBase85Encode(
1580 + __in_bcount_opt(cbSource) const BYTE* pbSource,
1581 + __in DWORD_PTR cbSource,
1582 + __deref_out_z LPWSTR* pwzDest
1583 + )
1584 +{
1585 + HRESULT hr = S_OK;
1586 + DWORD_PTR cchDest = 0;
1587 + LPWSTR wzDest;
1588 + DWORD_PTR iSource = 0;
1589 + DWORD_PTR iDest = 0;
1590 +
1591 + if (!pwzDest || !pbSource)
1592 + {
1593 + return E_INVALIDARG;
1594 + }
1595 +
1596 + // calc actual size of output
1597 + cchDest = cbSource / 4;
1598 + cchDest += cchDest * 4;
1599 + if (cbSource & 3)
1600 + {
1601 + cchDest += (cbSource & 3) + 1;
1602 + }
1603 + ++cchDest; // add room for null terminator
1604 +
1605 + hr = StrAlloc(pwzDest, cchDest);
1606 + ExitOnFailure(hr, "failed to allocate destination string");
1607 +
1608 + wzDest = *pwzDest;
1609 +
1610 + // first, encode full words
1611 + for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5)
1612 + {
1613 + DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24);
1614 + DWORD k = n / 85;
1615 +
1616 + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1617 + wzDest[iDest] = Base85EncodeTable[n - k * 85];
1618 + n = k / 85;
1619 +
1620 + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1621 + wzDest[iDest + 1] = Base85EncodeTable[k - n * 85];
1622 + k = n / 85;
1623 +
1624 + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1625 + wzDest[iDest + 2] = Base85EncodeTable[n - k * 85];
1626 + n = k / 85;
1627 +
1628 + //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1629 + wzDest[iDest + 3] = Base85EncodeTable[k - n * 85];
1630 +
1631 + __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85);
1632 +
1633 + //Assert(0 <= n && n < countof(Base85EncodeTable));
1634 + wzDest[iDest + 4] = Base85EncodeTable[n];
1635 + }
1636 +
1637 + // encode any remaining bytes
1638 + if (iSource < cbSource)
1639 + {
1640 + DWORD n = 0;
1641 + for (DWORD i = 0; iSource + i < cbSource; ++i)
1642 + {
1643 + n += pbSource[iSource + i] << (i << 3);
1644 + }
1645 +
1646 + for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest)
1647 + {
1648 + DWORD k = n / 85;
1649 +
1650 + //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1651 + wzDest[iDest] = Base85EncodeTable[n - k * 85];
1652 +
1653 + n = k;
1654 + }
1655 +
1656 + wzDest[iDest] = Base85EncodeTable[n];
1657 + ++iDest;
1658 + }
1659 + Assert(iSource == cbSource);
1660 + Assert(iDest == cchDest - 1);
1661 +
1662 + wzDest[iDest] = L'\0';
1663 + hr = S_OK;
1664 +
1665 +LExit:
1666 + return hr;
1667 +}
1668 +
1669 +
1670 +/****************************************************************************
1671 +StrAllocBase85Decode - converts a string of text to array of bytes
1672 +
1673 +NOTE: Use MemFree() to release the allocated stream of bytes
1674 +****************************************************************************/
1675 +extern "C" HRESULT DAPI StrAllocBase85Decode(
1676 + __in_z LPCWSTR wzSource,
1677 + __deref_out_bcount(*pcbDest) BYTE** ppbDest,
1678 + __out DWORD_PTR* pcbDest
1679 + )
1680 +{
1681 + HRESULT hr = S_OK;
1682 + DWORD_PTR cchSource = lstrlenW(wzSource);
1683 + DWORD_PTR i, n, k;
1684 +
1685 + BYTE* pbDest;
1686 + DWORD_PTR cbDest;
1687 +
1688 + if (!wzSource || !ppbDest || !pcbDest)
1689 + {
1690 + return E_INVALIDARG;
1691 + }
1692 +
1693 + // evaluate size of output and check it
1694 + k = cchSource / 5;
1695 + cbDest = k << 2;
1696 + k = cchSource - k * 5;
1697 + if (k)
1698 + {
1699 + if (1 == k)
1700 + {
1701 + // decode error -- encoded size cannot equal 1 mod 5
1702 + return E_UNEXPECTED;
1703 + }
1704 +
1705 + cbDest += k - 1;
1706 + }
1707 +
1708 + *ppbDest = static_cast<BYTE*>(MemAlloc(cbDest, FALSE));
1709 + ExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string");
1710 +
1711 + pbDest = *ppbDest;
1712 + *pcbDest = cbDest;
1713 +
1714 + // decode full words first
1715 + while (5 <= cchSource)
1716 + {
1717 + k = Base85DecodeTable[wzSource[0]];
1718 + if (85 == k)
1719 + {
1720 + // illegal symbol
1721 + return E_UNEXPECTED;
1722 + }
1723 + n = k;
1724 +
1725 + k = Base85DecodeTable[wzSource[1]];
1726 + if (85 == k)
1727 + {
1728 + // illegal symbol
1729 + return E_UNEXPECTED;
1730 + }
1731 + n += k * 85;
1732 +
1733 + k = Base85DecodeTable[wzSource[2]];
1734 + if (85 == k)
1735 + {
1736 + // illegal symbol
1737 + return E_UNEXPECTED;
1738 + }
1739 + n += k * (85 * 85);
1740 +
1741 + k = Base85DecodeTable[wzSource[3]];
1742 + if (85 == k)
1743 + {
1744 + // illegal symbol
1745 + return E_UNEXPECTED;
1746 + }
1747 + n += k * (85 * 85 * 85);
1748 +
1749 + k = Base85DecodeTable[wzSource[4]];
1750 + if (85 == k)
1751 + {
1752 + // illegal symbol
1753 + return E_UNEXPECTED;
1754 + }
1755 + k *= (85 * 85 * 85 * 85);
1756 +
1757 + // if (k + n > (1u << 32)) <=> (k > ~n) then decode error
1758 + if (k > ~n)
1759 + {
1760 + // overflow
1761 + return E_UNEXPECTED;
1762 + }
1763 +
1764 + n += k;
1765 +
1766 + pbDest[0] = (BYTE) n;
1767 + pbDest[1] = (BYTE) (n >> 8);
1768 + pbDest[2] = (BYTE) (n >> 16);
1769 + pbDest[3] = (BYTE) (n >> 24);
1770 +
1771 + wzSource += 5;
1772 + pbDest += 4;
1773 + cchSource -= 5;
1774 + }
1775 +
1776 + if (cchSource)
1777 + {
1778 + n = 0;
1779 + for (i = 0; i < cchSource; ++i)
1780 + {
1781 + k = Base85DecodeTable[wzSource[i]];
1782 + if (85 == k)
1783 + {
1784 + // illegal symbol
1785 + return E_UNEXPECTED;
1786 + }
1787 +
1788 + n += k * Base85PowerTable[i];
1789 + }
1790 +
1791 + for (i = 1; i < cchSource; ++i)
1792 + {
1793 + *pbDest++ = (BYTE)n;
1794 + n >>= 8;
1795 + }
1796 +
1797 + if (0 != n)
1798 + {
1799 + // decode error
1800 + return E_UNEXPECTED;
1801 + }
1802 + }
1803 +
1804 + hr = S_OK;
1805 +
1806 +LExit:
1807 + return hr;
1808 +}
1809 +
1810 +
1811 +/****************************************************************************
1812 +MultiSzLen - calculates the length of a MULTISZ string including all nulls
1813 +including the double null terminator at the end of the MULTISZ.
1814 +
1815 +NOTE: returns 0 if the multisz in not properly terminated with two nulls
1816 +****************************************************************************/
1817 +extern "C" HRESULT DAPI MultiSzLen(
1818 + __in __nullnullterminated LPCWSTR pwzMultiSz,
1819 + __out DWORD_PTR* pcch
1820 + )
1821 +{
1822 + Assert(pcch);
1823 +
1824 + HRESULT hr = S_OK;
1825 + LPCWSTR wz = pwzMultiSz;
1826 + DWORD_PTR dwMaxSize = 0;
1827 +
1828 + hr = StrMaxLength(pwzMultiSz, &dwMaxSize);
1829 + ExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length");
1830 +
1831 + *pcch = 0;
1832 + while (*pcch < dwMaxSize)
1833 + {
1834 + if (L'\0' == *wz && L'\0' == *(wz + 1))
1835 + {
1836 + break;
1837 + }
1838 +
1839 + ++wz;
1840 + *pcch = *pcch + 1;
1841 + }
1842 +
1843 + // Add two for the last 2 NULLs (that we looked ahead at)
1844 + *pcch = *pcch + 2;
1845 +
1846 + // If we've walked off the end then the length is 0
1847 + if (*pcch > dwMaxSize)
1848 + {
1849 + *pcch = 0;
1850 + }
1851 +
1852 +LExit:
1853 + return hr;
1854 +}
1855 +
1856 +
1857 +/****************************************************************************
1858 +MultiSzPrepend - prepends a string onto the front of a MUTLISZ
1859 +
1860 +****************************************************************************/
1861 +extern "C" HRESULT DAPI MultiSzPrepend(
1862 + __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz,
1863 + __inout_opt DWORD_PTR *pcchMultiSz,
1864 + __in __nullnullterminated LPCWSTR pwzInsert
1865 + )
1866 +{
1867 + Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
1868 +
1869 + HRESULT hr =S_OK;
1870 + LPWSTR pwzResult = NULL;
1871 + DWORD_PTR cchResult = 0;
1872 + DWORD_PTR cchInsert = 0;
1873 + DWORD_PTR cchMultiSz = 0;
1874 +
1875 + // Get the lengths of the MULTISZ (and prime it if it's not initialized)
1876 + if (pcchMultiSz && 0 != *pcchMultiSz)
1877 + {
1878 + cchMultiSz = *pcchMultiSz;
1879 + }
1880 + else
1881 + {
1882 + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
1883 + ExitOnFailure(hr, "failed to get length of multisz");
1884 + }
1885 +
1886 + cchInsert = lstrlenW(pwzInsert);
1887 +
1888 + cchResult = cchInsert + cchMultiSz + 1;
1889 +
1890 + // Allocate the result buffer
1891 + hr = StrAlloc(&pwzResult, cchResult + 1);
1892 + ExitOnFailure(hr, "failed to allocate result string");
1893 +
1894 + // Prepend
1895 + hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert);
1896 + ExitOnFailure(hr, "failed to copy prepend string: %ls", pwzInsert);
1897 +
1898 + // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in
1899 + if (0 == cchMultiSz)
1900 + {
1901 + pwzResult[cchResult] = L'\0';
1902 + ++cchResult;
1903 + }
1904 + else
1905 + {
1906 + // Copy the rest
1907 + ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR));
1908 +
1909 + // Free the old buffer
1910 + ReleaseNullStr(*ppwzMultiSz);
1911 + }
1912 +
1913 + // Set the result
1914 + *ppwzMultiSz = pwzResult;
1915 +
1916 + if (pcchMultiSz)
1917 + {
1918 + *pcchMultiSz = cchResult;
1919 + }
1920 +
1921 + pwzResult = NULL;
1922 +
1923 +LExit:
1924 + ReleaseNullStr(pwzResult);
1925 +
1926 + return hr;
1927 +}
1928 +
1929 +/****************************************************************************
1930 +MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the
1931 +specified sub string and returns the index of the
1932 +string in the MULTISZ, the address, neither, or both
1933 +
1934 +NOTE: returns S_FALSE if the string is not found
1935 +****************************************************************************/
1936 +extern "C" HRESULT DAPI MultiSzFindSubstring(
1937 + __in __nullnullterminated LPCWSTR pwzMultiSz,
1938 + __in __nullnullterminated LPCWSTR pwzSubstring,
1939 + __out_opt DWORD_PTR* pdwIndex,
1940 + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn
1941 + )
1942 +{
1943 + Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring);
1944 +
1945 + HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty)
1946 + LPCWSTR wz = pwzMultiSz;
1947 + DWORD_PTR dwIndex = 0;
1948 + DWORD_PTR cchMultiSz = 0;
1949 + DWORD_PTR cchProgress = 0;
1950 +
1951 + hr = MultiSzLen(pwzMultiSz, &cchMultiSz);
1952 + ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
1953 +
1954 + // Find the string containing the sub string
1955 + hr = S_OK;
1956 + while (NULL == wcsistr(wz, pwzSubstring))
1957 + {
1958 + // Slide through to the end of the current string
1959 + while (L'\0' != *wz && cchProgress < cchMultiSz)
1960 + {
1961 + ++wz;
1962 + ++cchProgress;
1963 + }
1964 +
1965 + // If we're done, we're done
1966 + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
1967 + {
1968 + hr = S_FALSE;
1969 + break;
1970 + }
1971 +
1972 + // Move on to the next string
1973 + ++wz;
1974 + ++dwIndex;
1975 + }
1976 + Assert(S_OK == hr || S_FALSE == hr);
1977 +
1978 + // If we found it give them what they want
1979 + if (S_OK == hr)
1980 + {
1981 + if (pdwIndex)
1982 + {
1983 + *pdwIndex = dwIndex;
1984 + }
1985 +
1986 + if (ppwzFoundIn)
1987 + {
1988 + *ppwzFoundIn = wz;
1989 + }
1990 + }
1991 +
1992 +LExit:
1993 + return hr;
1994 +}
1995 +
1996 +/****************************************************************************
1997 +MultiSzFindString - finds a string in a MULTISZ and returns the index of
1998 +the string in the MULTISZ, the address or both
1999 +
2000 +NOTE: returns S_FALSE if the string is not found
2001 +****************************************************************************/
2002 +extern "C" HRESULT DAPI MultiSzFindString(
2003 + __in __nullnullterminated LPCWSTR pwzMultiSz,
2004 + __in __nullnullterminated LPCWSTR pwzString,
2005 + __out_opt DWORD_PTR* pdwIndex,
2006 + __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound
2007 + )
2008 +{
2009 + Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound));
2010 +
2011 + HRESULT hr = S_FALSE; // Assume we won't find it
2012 + LPCWSTR wz = pwzMultiSz;
2013 + DWORD_PTR dwIndex = 0;
2014 + DWORD_PTR cchMutliSz = 0;
2015 + DWORD_PTR cchProgress = 0;
2016 +
2017 + hr = MultiSzLen(pwzMultiSz, &cchMutliSz);
2018 + ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2019 +
2020 + // Find the string
2021 + hr = S_OK;
2022 + while (0 != lstrcmpW(wz, pwzString))
2023 + {
2024 + // Slide through to the end of the current string
2025 + while (L'\0' != *wz && cchProgress < cchMutliSz)
2026 + {
2027 + ++wz;
2028 + ++cchProgress;
2029 + }
2030 +
2031 + // If we're done, we're done
2032 + if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz)
2033 + {
2034 + hr = S_FALSE;
2035 + break;
2036 + }
2037 +
2038 + // Move on to the next string
2039 + ++wz;
2040 + ++dwIndex;
2041 + }
2042 + Assert(S_OK == hr || S_FALSE == hr);
2043 +
2044 + // If we found it give them what they want
2045 + if (S_OK == hr)
2046 + {
2047 + if (pdwIndex)
2048 + {
2049 + *pdwIndex = dwIndex;
2050 + }
2051 +
2052 + if (ppwzFound)
2053 + {
2054 + *ppwzFound = wz;
2055 + }
2056 + }
2057 +
2058 +LExit:
2059 + return hr;
2060 +}
2061 +
2062 +/****************************************************************************
2063 +MultiSzRemoveString - removes string from a MULTISZ at the specified
2064 +index
2065 +
2066 +NOTE: does an in place removal without shrinking the memory allocation
2067 +
2068 +NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex
2069 +****************************************************************************/
2070 +extern "C" HRESULT DAPI MultiSzRemoveString(
2071 + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2072 + __in DWORD_PTR dwIndex
2073 + )
2074 +{
2075 + Assert(ppwzMultiSz && *ppwzMultiSz);
2076 +
2077 + HRESULT hr = S_OK;
2078 + LPCWSTR wz = *ppwzMultiSz;
2079 + LPCWSTR wzNext = NULL;
2080 + DWORD_PTR dwCurrentIndex = 0;
2081 + DWORD_PTR cchMultiSz = 0;
2082 + DWORD_PTR cchProgress = 0;
2083 +
2084 + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2085 + ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2086 +
2087 + // Find the index we want to remove
2088 + hr = S_OK;
2089 + while (dwCurrentIndex < dwIndex)
2090 + {
2091 + // Slide through to the end of the current string
2092 + while (L'\0' != *wz && cchProgress < cchMultiSz)
2093 + {
2094 + ++wz;
2095 + ++cchProgress;
2096 + }
2097 +
2098 + // If we're done, we're done
2099 + if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
2100 + {
2101 + hr = S_FALSE;
2102 + break;
2103 + }
2104 +
2105 + // Move on to the next string
2106 + ++wz;
2107 + ++cchProgress;
2108 + ++dwCurrentIndex;
2109 + }
2110 + Assert(S_OK == hr || S_FALSE == hr);
2111 +
2112 + // If we found the index to be removed
2113 + if (S_OK == hr)
2114 + {
2115 + wzNext = wz;
2116 +
2117 + // Slide through to the end of the current string
2118 + while (L'\0' != *wzNext && cchProgress < cchMultiSz)
2119 + {
2120 + ++wzNext;
2121 + ++cchProgress;
2122 + }
2123 +
2124 + // Something weird has happened if we're past the end of the MULTISZ
2125 + if (cchProgress > cchMultiSz)
2126 + {
2127 + hr = E_UNEXPECTED;
2128 + ExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ");
2129 + }
2130 +
2131 + // Move on to the next character
2132 + ++wzNext;
2133 + ++cchProgress;
2134 +
2135 + ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2136 + }
2137 +
2138 +LExit:
2139 + return hr;
2140 +}
2141 +
2142 +/****************************************************************************
2143 +MultiSzInsertString - inserts new string at the specified index
2144 +
2145 +****************************************************************************/
2146 +extern "C" HRESULT DAPI MultiSzInsertString(
2147 + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2148 + __inout_opt DWORD_PTR *pcchMultiSz,
2149 + __in DWORD_PTR dwIndex,
2150 + __in __nullnullterminated LPCWSTR pwzInsert
2151 + )
2152 +{
2153 + Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
2154 +
2155 + HRESULT hr = S_OK;
2156 + LPCWSTR wz = *ppwzMultiSz;
2157 + DWORD_PTR dwCurrentIndex = 0;
2158 + DWORD_PTR cchProgress = 0;
2159 + LPWSTR pwzResult = NULL;
2160 + DWORD_PTR cchResult = 0;
2161 + DWORD_PTR cchString = lstrlenW(pwzInsert);
2162 + DWORD_PTR cchMultiSz = 0;
2163 +
2164 + if (pcchMultiSz && 0 != *pcchMultiSz)
2165 + {
2166 + cchMultiSz = *pcchMultiSz;
2167 + }
2168 + else
2169 + {
2170 + hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2171 + ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2172 + }
2173 +
2174 + // Find the index we want to insert at
2175 + hr = S_OK;
2176 + while (dwCurrentIndex < dwIndex)
2177 + {
2178 + // Slide through to the end of the current string
2179 + while (L'\0' != *wz && cchProgress < cchMultiSz)
2180 + {
2181 + ++wz;
2182 + ++cchProgress;
2183 + }
2184 +
2185 + // If we're done, we're done
2186 + if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz)
2187 + {
2188 + hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
2189 + ExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex);
2190 + }
2191 +
2192 + // Move on to the next string
2193 + ++wz;
2194 + ++cchProgress;
2195 + ++dwCurrentIndex;
2196 + }
2197 +
2198 + //
2199 + // Insert the string
2200 + //
2201 + cchResult = cchMultiSz + cchString + 1;
2202 +
2203 + hr = StrAlloc(&pwzResult, cchResult);
2204 + ExitOnFailure(hr, "failed to allocate result string for MULTISZ insert");
2205 +
2206 + // Copy the part before the insert
2207 + ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR));
2208 +
2209 + // Copy the insert part
2210 + ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR));
2211 +
2212 + // Copy the part after the insert
2213 + ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2214 +
2215 + // Free the old buffer
2216 + ReleaseNullStr(*ppwzMultiSz);
2217 +
2218 + // Set the result
2219 + *ppwzMultiSz = pwzResult;
2220 +
2221 + // If they wanted the resulting length, let 'em have it
2222 + if (pcchMultiSz)
2223 + {
2224 + *pcchMultiSz = cchResult;
2225 + }
2226 +
2227 + pwzResult = NULL;
2228 +
2229 +LExit:
2230 + ReleaseStr(pwzResult);
2231 +
2232 + return hr;
2233 +}
2234 +
2235 +/****************************************************************************
2236 +MultiSzReplaceString - replaces string at the specified index with a new one
2237 +
2238 +****************************************************************************/
2239 +extern "C" HRESULT DAPI MultiSzReplaceString(
2240 + __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2241 + __in DWORD_PTR dwIndex,
2242 + __in __nullnullterminated LPCWSTR pwzString
2243 + )
2244 +{
2245 + Assert(ppwzMultiSz && pwzString && *pwzString);
2246 +
2247 + HRESULT hr = S_OK;
2248 +
2249 + hr = MultiSzRemoveString(ppwzMultiSz, dwIndex);
2250 + ExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex);
2251 +
2252 + hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString);
2253 + ExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex);
2254 +
2255 +LExit:
2256 + return hr;
2257 +}
2258 +
2259 +
2260 +/****************************************************************************
2261 +wcsistr - case insensitive find a substring
2262 +
2263 +****************************************************************************/
2264 +extern "C" LPCWSTR DAPI wcsistr(
2265 + __in_z LPCWSTR wzString,
2266 + __in_z LPCWSTR wzCharSet
2267 + )
2268 +{
2269 + LPCWSTR wzSource = wzString;
2270 + LPCWSTR wzSearch = NULL;
2271 + DWORD_PTR cchSourceIndex = 0;
2272 +
2273 + // Walk through wzString (the source string) one character at a time
2274 + while (*wzSource)
2275 + {
2276 + cchSourceIndex = 0;
2277 + wzSearch = wzCharSet;
2278 +
2279 + // Look ahead in the source string until we get a full match or we hit the end of the source
2280 + while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch))
2281 + {
2282 + ++cchSourceIndex;
2283 + ++wzSearch;
2284 + }
2285 +
2286 + // If we found it, return the point that we found it at
2287 + if (L'\0' == *wzSearch)
2288 + {
2289 + return wzSource;
2290 + }
2291 +
2292 + // Walk ahead one character
2293 + ++wzSource;
2294 + }
2295 +
2296 + return NULL;
2297 +}
2298 +
2299 +/****************************************************************************
2300 +StrStringToInt16 - converts a string to a signed 16-bit integer.
2301 +
2302 +****************************************************************************/
2303 +extern "C" HRESULT DAPI StrStringToInt16(
2304 + __in_z LPCWSTR wzIn,
2305 + __in DWORD cchIn,
2306 + __out SHORT* psOut
2307 + )
2308 +{
2309 + HRESULT hr = S_OK;
2310 + LONGLONG ll = 0;
2311 +
2312 + hr = StrStringToInt64(wzIn, cchIn, &ll);
2313 + ExitOnFailure(hr, "Failed to parse int64.");
2314 +
2315 + if (SHORT_MAX < ll || SHORT_MIN > ll)
2316 + {
2317 + ExitFunction1(hr = DISP_E_OVERFLOW);
2318 + }
2319 + *psOut = (SHORT)ll;
2320 +
2321 +LExit:
2322 + return hr;
2323 +}
2324 +
2325 +/****************************************************************************
2326 +StrStringToUInt16 - converts a string to an unsigned 16-bit integer.
2327 +
2328 +****************************************************************************/
2329 +extern "C" HRESULT DAPI StrStringToUInt16(
2330 + __in_z LPCWSTR wzIn,
2331 + __in DWORD cchIn,
2332 + __out USHORT* pusOut
2333 + )
2334 +{
2335 + HRESULT hr = S_OK;
2336 + ULONGLONG ull = 0;
2337 +
2338 + hr = StrStringToUInt64(wzIn, cchIn, &ull);
2339 + ExitOnFailure(hr, "Failed to parse uint64.");
2340 +
2341 + if (USHORT_MAX < ull)
2342 + {
2343 + ExitFunction1(hr = DISP_E_OVERFLOW);
2344 + }
2345 + *pusOut = (USHORT)ull;
2346 +
2347 +LExit:
2348 + return hr;
2349 +}
2350 +
2351 +/****************************************************************************
2352 +StrStringToInt32 - converts a string to a signed 32-bit integer.
2353 +
2354 +****************************************************************************/
2355 +extern "C" HRESULT DAPI StrStringToInt32(
2356 + __in_z LPCWSTR wzIn,
2357 + __in DWORD cchIn,
2358 + __out INT* piOut
2359 + )
2360 +{
2361 + HRESULT hr = S_OK;
2362 + LONGLONG ll = 0;
2363 +
2364 + hr = StrStringToInt64(wzIn, cchIn, &ll);
2365 + ExitOnFailure(hr, "Failed to parse int64.");
2366 +
2367 + if (INT_MAX < ll || INT_MIN > ll)
2368 + {
2369 + ExitFunction1(hr = DISP_E_OVERFLOW);
2370 + }
2371 + *piOut = (INT)ll;
2372 +
2373 +LExit:
2374 + return hr;
2375 +}
2376 +
2377 +/****************************************************************************
2378 +StrStringToUInt32 - converts a string to an unsigned 32-bit integer.
2379 +
2380 +****************************************************************************/
2381 +extern "C" HRESULT DAPI StrStringToUInt32(
2382 + __in_z LPCWSTR wzIn,
2383 + __in DWORD cchIn,
2384 + __out UINT* puiOut
2385 + )
2386 +{
2387 + HRESULT hr = S_OK;
2388 + ULONGLONG ull = 0;
2389 +
2390 + hr = StrStringToUInt64(wzIn, cchIn, &ull);
2391 + ExitOnFailure(hr, "Failed to parse uint64.");
2392 +
2393 + if (UINT_MAX < ull)
2394 + {
2395 + ExitFunction1(hr = DISP_E_OVERFLOW);
2396 + }
2397 + *puiOut = (UINT)ull;
2398 +
2399 +LExit:
2400 + return hr;
2401 +}
2402 +
2403 +/****************************************************************************
2404 +StrStringToInt64 - converts a string to a signed 64-bit integer.
2405 +
2406 +****************************************************************************/
2407 +extern "C" HRESULT DAPI StrStringToInt64(
2408 + __in_z LPCWSTR wzIn,
2409 + __in DWORD cchIn,
2410 + __out LONGLONG* pllOut
2411 + )
2412 +{
2413 + HRESULT hr = S_OK;
2414 + DWORD i = 0;
2415 + INT iSign = 1;
2416 + INT nDigit = 0;
2417 + LARGE_INTEGER liValue = { };
2418 +
2419 + // get string length if not provided
2420 + if (0 >= cchIn)
2421 + {
2422 + cchIn = lstrlenW(wzIn);
2423 + if (0 >= cchIn)
2424 + {
2425 + ExitFunction1(hr = E_INVALIDARG);
2426 + }
2427 + }
2428 +
2429 + // check sign
2430 + if (L'-' == wzIn[0])
2431 + {
2432 + if (1 >= cchIn)
2433 + {
2434 + ExitFunction1(hr = E_INVALIDARG);
2435 + }
2436 + i = 1;
2437 + iSign = -1;
2438 + }
2439 +
2440 + // read digits
2441 + while (i < cchIn)
2442 + {
2443 + nDigit = wzIn[i] - L'0';
2444 + if (0 > nDigit || 9 < nDigit)
2445 + {
2446 + ExitFunction1(hr = E_INVALIDARG);
2447 + }
2448 + liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign;
2449 +
2450 + if ((liValue.HighPart ^ iSign) & INT_MIN)
2451 + {
2452 + ExitFunction1(hr = DISP_E_OVERFLOW);
2453 + }
2454 + ++i;
2455 + }
2456 +
2457 + *pllOut = liValue.QuadPart;
2458 +
2459 +LExit:
2460 + return hr;
2461 +}
2462 +
2463 +/****************************************************************************
2464 +StrStringToUInt64 - converts a string to an unsigned 64-bit integer.
2465 +
2466 +****************************************************************************/
2467 +extern "C" HRESULT DAPI StrStringToUInt64(
2468 + __in_z LPCWSTR wzIn,
2469 + __in DWORD cchIn,
2470 + __out ULONGLONG* pullOut
2471 + )
2472 +{
2473 + HRESULT hr = S_OK;
2474 + DWORD i = 0;
2475 + DWORD nDigit = 0;
2476 + ULONGLONG ullValue = 0;
2477 + ULONGLONG ull = 0;
2478 +
2479 + // get string length if not provided
2480 + if (0 >= cchIn)
2481 + {
2482 + cchIn = lstrlenW(wzIn);
2483 + if (0 >= cchIn)
2484 + {
2485 + ExitFunction1(hr = E_INVALIDARG);
2486 + }
2487 + }
2488 +
2489 + // read digits
2490 + while (i < cchIn)
2491 + {
2492 + nDigit = wzIn[i] - L'0';
2493 + if (9 < nDigit)
2494 + {
2495 + ExitFunction1(hr = E_INVALIDARG);
2496 + }
2497 + ull = (ULONGLONG)(ullValue * 10 + nDigit);
2498 +
2499 + if (ull < ullValue)
2500 + {
2501 + ExitFunction1(hr = DISP_E_OVERFLOW);
2502 + }
2503 + ullValue = ull;
2504 + ++i;
2505 + }
2506 +
2507 + *pullOut = ullValue;
2508 +
2509 +LExit:
2510 + return hr;
2511 +}
2512 +
2513 +/****************************************************************************
2514 +StrStringToUpper - alters the given string in-place to be entirely uppercase
2515 +
2516 +****************************************************************************/
2517 +void DAPI StrStringToUpper(
2518 + __inout_z LPWSTR wzIn
2519 + )
2520 +{
2521 + ::CharUpperBuffW(wzIn, lstrlenW(wzIn));
2522 +}
2523 +
2524 +/****************************************************************************
2525 +StrStringToLower - alters the given string in-place to be entirely lowercase
2526 +
2527 +****************************************************************************/
2528 +void DAPI StrStringToLower(
2529 + __inout_z LPWSTR wzIn
2530 + )
2531 +{
2532 + ::CharLowerBuffW(wzIn, lstrlenW(wzIn));
2533 +}
2534 +
2535 +/****************************************************************************
2536 +StrAllocStringToUpperInvariant - creates an upper-case copy of a string.
2537 +
2538 +****************************************************************************/
2539 +extern "C" HRESULT DAPI StrAllocStringToUpperInvariant(
2540 + __deref_out_z LPWSTR* pscz,
2541 + __in_z LPCWSTR wzSource,
2542 + __in int cchSource
2543 + )
2544 +{
2545 + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE);
2546 +}
2547 +
2548 +/****************************************************************************
2549 +StrAllocStringToLowerInvariant - creates an lower-case copy of a string.
2550 +
2551 +****************************************************************************/
2552 +extern "C" HRESULT DAPI StrAllocStringToLowerInvariant(
2553 + __deref_out_z LPWSTR* pscz,
2554 + __in_z LPCWSTR wzSource,
2555 + __in int cchSource
2556 + )
2557 +{
2558 + return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE);
2559 +}
2560 +
2561 +/****************************************************************************
2562 +StrArrayAllocString - Allocates a string array.
2563 +
2564 +****************************************************************************/
2565 +extern "C" HRESULT DAPI StrArrayAllocString(
2566 + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2567 + __inout LPUINT pcStrArray,
2568 + __in_z LPCWSTR wzSource,
2569 + __in DWORD_PTR cchSource
2570 + )
2571 +{
2572 + HRESULT hr = S_OK;
2573 + UINT cNewStrArray;
2574 +
2575 + hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray);
2576 + ExitOnFailure(hr, "Failed to increment the string array element count.");
2577 +
2578 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE);
2579 + ExitOnFailure(hr, "Failed to allocate memory for the string array.");
2580 +
2581 + hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource);
2582 + ExitOnFailure(hr, "Failed to allocate and assign the string.");
2583 +
2584 + *pcStrArray = cNewStrArray;
2585 +
2586 +LExit:
2587 + return hr;
2588 +}
2589 +
2590 +/****************************************************************************
2591 +StrArrayFree - Frees a string array.
2592 +
2593 +Use ReleaseNullStrArray to nullify the arguments.
2594 +
2595 +****************************************************************************/
2596 +extern "C" HRESULT DAPI StrArrayFree(
2597 + __in_ecount(cStrArray) LPWSTR *rgsczStrArray,
2598 + __in UINT cStrArray
2599 + )
2600 +{
2601 + HRESULT hr = S_OK;
2602 +
2603 + for (UINT i = 0; i < cStrArray; ++i)
2604 + {
2605 + if (NULL != rgsczStrArray[i])
2606 + {
2607 + hr = StrFree(rgsczStrArray[i]);
2608 + ExitOnFailure(hr, "Failed to free the string at index %u.", i);
2609 + }
2610 + }
2611 +
2612 + hr = MemFree(rgsczStrArray);
2613 + ExitOnFailure(hr, "Failed to free memory for the string array.");
2614 +
2615 +LExit:
2616 + return hr;
2617 +}
2618 +
2619 +/****************************************************************************
2620 +StrSplitAllocArray - Splits a string into an array.
2621 +
2622 +****************************************************************************/
2623 +extern "C" HRESULT DAPI StrSplitAllocArray(
2624 + __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2625 + __inout LPUINT pcStrArray,
2626 + __in_z LPCWSTR wzSource,
2627 + __in_z LPCWSTR wzDelim
2628 + )
2629 +{
2630 + HRESULT hr = S_OK;
2631 + LPWSTR sczCopy = NULL;
2632 + LPWSTR wzContext = NULL;
2633 +
2634 + // Copy wzSource so it is not modified.
2635 + hr = StrAllocString(&sczCopy, wzSource, 0);
2636 + ExitOnFailure(hr, "Failed to copy the source string.");
2637 +
2638 + for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext))
2639 + {
2640 + hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0);
2641 + ExitOnFailure(hr, "Failed to add the string to the string array.");
2642 + }
2643 +
2644 +LExit:
2645 + ReleaseStr(sczCopy);
2646 +
2647 + return hr;
2648 +}
2649 +
2650 +/****************************************************************************
2651 +StrAllocStringMapInvariant - helper function for the ToUpper and ToLower.
2652 +
2653 +Note: Assumes source and destination buffers will be the same.
2654 +****************************************************************************/
2655 +static HRESULT StrAllocStringMapInvariant(
2656 + __deref_out_z LPWSTR* pscz,
2657 + __in_z LPCWSTR wzSource,
2658 + __in int cchSource,
2659 + __in DWORD dwMapFlags
2660 + )
2661 +{
2662 + HRESULT hr = S_OK;
2663 +
2664 + hr = StrAllocString(pscz, wzSource, cchSource);
2665 + ExitOnFailure(hr, "Failed to allocate a copy of the source string.");
2666 +
2667 + if (0 == cchSource)
2668 + {
2669 + // Need the actual string size for LCMapString. This includes the null-terminator
2670 + // but LCMapString doesn't care either way.
2671 + hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast<size_t*>(&cchSource));
2672 + ExitOnFailure(hr, "Failed to get the length of the string.");
2673 + }
2674 +
2675 + // Convert the copy of the string to upper or lower case in-place.
2676 + if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, cchSource, *pscz, cchSource))
2677 + {
2678 + ExitWithLastError(hr, "Failed to convert the string case.");
2679 + }
2680 +
2681 +LExit:
2682 + return hr;
2683 +}
2684 +
2685 +/****************************************************************************
2686 +StrSecureZeroString - zeroes out string to the make sure the contents
2687 +don't remain in memory.
2688 +
2689 +****************************************************************************/
2690 +extern "C" DAPI_(HRESULT) StrSecureZeroString(
2691 + __in LPWSTR pwz
2692 + )
2693 +{
2694 + HRESULT hr = S_OK;
2695 + DWORD_PTR cch;
2696 +
2697 + if (pwz)
2698 + {
2699 + cch = MemSize(pwz);
2700 + if (-1 == cch)
2701 + {
2702 + hr = E_INVALIDARG;
2703 + ExitOnFailure(hr, "Failed to get size of string");
2704 + }
2705 + else
2706 + {
2707 + SecureZeroMemory(pwz, cch);
2708 + }
2709 + }
2710 +
2711 +LExit:
2712 + return hr;
2713 +}
2714 +
2715 +/****************************************************************************
2716 +StrSecureZeroFreeString - zeroes out string to the make sure the contents
2717 +don't remain in memory, then frees the string.
2718 +
2719 +****************************************************************************/
2720 +extern "C" DAPI_(HRESULT) StrSecureZeroFreeString(
2721 + __in LPWSTR pwz
2722 + )
2723 +{
2724 + HRESULT hr = S_OK;
2725 +
2726 + hr = StrSecureZeroString(pwz);
2727 + ReleaseStr(pwz);
2728 +
2729 + return hr;
2730 +}
src/dutil/svcutil.cpp new
+44
@@ -0,0 +1,44 @@
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 +
5 +/********************************************************************
6 +SvcQueryConfig - queries the configuration of a service
7 +
8 +********************************************************************/
9 +extern "C" HRESULT DAPI SvcQueryConfig(
10 + __in SC_HANDLE sch,
11 + __out QUERY_SERVICE_CONFIGW** ppConfig
12 + )
13 +{
14 + HRESULT hr = S_OK;
15 + QUERY_SERVICE_CONFIGW* pConfig = NULL;
16 + DWORD cbConfig = 0;
17 +
18 + if (!::QueryServiceConfigW(sch, NULL, 0, &cbConfig))
19 + {
20 + DWORD er = ::GetLastError();
21 + if (ERROR_INSUFFICIENT_BUFFER == er)
22 + {
23 + pConfig = static_cast<QUERY_SERVICE_CONFIGW*>(MemAlloc(cbConfig, TRUE));
24 + ExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration.");
25 +
26 + if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig))
27 + {
28 + ExitWithLastError(hr, "Failed to read service configuration.");
29 + }
30 + }
31 + else
32 + {
33 + ExitOnWin32Error(er, hr, "Failed to query service configuration.");
34 + }
35 + }
36 +
37 + *ppConfig = pConfig;
38 + pConfig = NULL;
39 +
40 +LExit:
41 + ReleaseMem(pConfig);
42 +
43 + return hr;
44 +}
src/dutil/thmutil.cpp new
+5226
@@ -0,0 +1,5226 @@
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 +
5 +
6 +#ifndef BS_COMMANDLINK
7 +#define BS_COMMANDLINK 0x0000000EL
8 +#endif
9 +
10 +#ifndef BCM_SETNOTE
11 +#define BCM_SETNOTE (BCM_FIRST + 0x0009)
12 +#endif
13 +
14 +#ifndef BCM_SETSHIELD
15 +#define BCM_SETSHIELD (BCM_FIRST + 0x000C)
16 +#endif
17 +
18 +#ifndef LWS_NOPREFIX
19 +#define LWS_NOPREFIX 0x0004
20 +#endif
21 +
22 +const DWORD THEME_INVALID_ID = 0xFFFFFFFF;
23 +const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF;
24 +const DWORD GROW_WINDOW_TEXT = 250;
25 +const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink";
26 +const LPCWSTR THEME_WC_PANEL = L"ThemePanel";
27 +
28 +static Gdiplus::GdiplusStartupInput vgsi;
29 +static Gdiplus::GdiplusStartupOutput vgso = { };
30 +static ULONG_PTR vgdiToken = 0;
31 +static ULONG_PTR vgdiHookToken = 0;
32 +static HMODULE vhHyperlinkRegisteredModule = NULL;
33 +static HMODULE vhPanelRegisteredModule = NULL;
34 +static HMODULE vhModuleRichEd = NULL;
35 +static HCURSOR vhCursorHand = NULL;
36 +
37 +enum INTERNAL_CONTROL_STYLE
38 +{
39 + INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001,
40 + INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002,
41 + INTERNAL_CONTROL_STYLE_DISABLED = 0x0004,
42 + INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008,
43 + INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010,
44 +};
45 +
46 +struct MEMBUFFER_FOR_RICHEDIT
47 +{
48 + BYTE* rgbData;
49 + DWORD cbData;
50 +
51 + DWORD iData;
52 +};
53 +
54 +
55 +// prototypes
56 +static HRESULT RegisterWindowClasses(
57 + __in_opt HMODULE hModule
58 + );
59 +static HRESULT ParseTheme(
60 + __in_opt HMODULE hModule,
61 + __in_opt LPCWSTR wzRelativePath,
62 + __in IXMLDOMDocument* pixd,
63 + __out THEME** ppTheme
64 + );
65 +static HRESULT ParseImage(
66 + __in_opt HMODULE hModule,
67 + __in_z_opt LPCWSTR wzRelativePath,
68 + __in IXMLDOMNode* pElement,
69 + __out HBITMAP* phImage
70 + );
71 +static HRESULT ParseIcon(
72 + __in_opt HMODULE hModule,
73 + __in_z_opt LPCWSTR wzRelativePath,
74 + __in IXMLDOMNode* pElement,
75 + __out HICON* phIcon
76 + );
77 +static HRESULT ParseWindow(
78 + __in_opt HMODULE hModule,
79 + __in_opt LPCWSTR wzRelativePath,
80 + __in IXMLDOMElement* pElement,
81 + __in THEME* pTheme
82 + );
83 +static HRESULT GetFontColor(
84 + __in IXMLDOMNode* pixn,
85 + __in_z LPCWSTR wzAttributeName,
86 + __out COLORREF* pColorRef,
87 + __out DWORD* pdwSystemColor
88 + );
89 +static HRESULT ParseFonts(
90 + __in IXMLDOMElement* pElement,
91 + __in THEME* pTheme
92 + );
93 +static HRESULT ParsePages(
94 + __in_opt HMODULE hModule,
95 + __in_opt LPCWSTR wzRelativePath,
96 + __in IXMLDOMNode* pElement,
97 + __in THEME* pTheme
98 + );
99 +static HRESULT ParseImageLists(
100 + __in_opt HMODULE hModule,
101 + __in_opt LPCWSTR wzRelativePath,
102 + __in IXMLDOMNode* pElement,
103 + __in THEME* pTheme
104 + );
105 +static HRESULT ParseControls(
106 + __in_opt HMODULE hModule,
107 + __in_opt LPCWSTR wzRelativePath,
108 + __in IXMLDOMNode* pElement,
109 + __in THEME* pTheme,
110 + __in_opt THEME_CONTROL* pParentControl,
111 + __in_opt THEME_PAGE* pPage
112 + );
113 +static HRESULT ParseControl(
114 + __in_opt HMODULE hModule,
115 + __in_opt LPCWSTR wzRelativePath,
116 + __in IXMLDOMNode* pixn,
117 + __in THEME* pTheme,
118 + __in THEME_CONTROL* pControl,
119 + __in BOOL fSkipDimensions,
120 + __in_opt THEME_PAGE* pPage
121 + );
122 +static HRESULT ParseActions(
123 + __in IXMLDOMNode* pixn,
124 + __in THEME_CONTROL* pControl
125 + );
126 +static HRESULT ParseColumns(
127 + __in IXMLDOMNode* pixn,
128 + __in THEME_CONTROL* pControl
129 + );
130 +static HRESULT ParseRadioButtons(
131 + __in_opt HMODULE hModule,
132 + __in_opt LPCWSTR wzRelativePath,
133 + __in IXMLDOMNode* pixn,
134 + __in THEME* pTheme,
135 + __in_opt THEME_CONTROL* pParentControl,
136 + __in THEME_PAGE* pPage
137 + );
138 +static HRESULT ParseTabs(
139 + __in IXMLDOMNode* pixn,
140 + __in THEME_CONTROL* pControl
141 + );
142 +static HRESULT ParseText(
143 + __in IXMLDOMNode* pixn,
144 + __in THEME_CONTROL* pControl,
145 + __inout BOOL* pfAnyChildren
146 +);
147 +static HRESULT ParseTooltips(
148 + __in IXMLDOMNode* pixn,
149 + __in THEME_CONTROL* pControl,
150 + __inout BOOL* pfAnyChildren
151 + );
152 +static HRESULT ParseNotes(
153 + __in IXMLDOMNode* pixn,
154 + __in THEME_CONTROL* pControl,
155 + __out BOOL* pfAnyChildren
156 + );
157 +static HRESULT StopBillboard(
158 + __in THEME* pTheme,
159 + __in DWORD dwControl
160 + );
161 +static HRESULT StartBillboard(
162 + __in THEME* pTheme,
163 + __in DWORD dwControl
164 + );
165 +static HRESULT FindImageList(
166 + __in THEME* pTheme,
167 + __in_z LPCWSTR wzImageListName,
168 + __out HIMAGELIST *phImageList
169 + );
170 +static HRESULT LoadControls(
171 + __in THEME* pTheme,
172 + __in_opt THEME_CONTROL* pParentControl,
173 + __in HWND hwndParent,
174 + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
175 + __in DWORD cAssignControlIds
176 + );
177 +static HRESULT ShowControl(
178 + __in THEME* pTheme,
179 + __in THEME_CONTROL* pControl,
180 + __in int nCmdShow,
181 + __in BOOL fSaveEditboxes,
182 + __in THEME_SHOW_PAGE_REASON reason,
183 + __in DWORD dwPageId,
184 + __out_opt HWND* phwndFocus
185 + );
186 +static HRESULT ShowControls(
187 + __in THEME* pTheme,
188 + __in_opt const THEME_CONTROL* pParentControl,
189 + __in int nCmdShow,
190 + __in BOOL fSaveEditboxes,
191 + __in THEME_SHOW_PAGE_REASON reason,
192 + __in DWORD dwPageId
193 + );
194 +static HRESULT DrawButton(
195 + __in THEME* pTheme,
196 + __in DRAWITEMSTRUCT* pdis,
197 + __in const THEME_CONTROL* pControl
198 + );
199 +static void DrawControlText(
200 + __in THEME* pTheme,
201 + __in DRAWITEMSTRUCT* pdis,
202 + __in const THEME_CONTROL* pControl,
203 + __in BOOL fCentered,
204 + __in BOOL fDrawFocusRect
205 + );
206 +static HRESULT DrawHyperlink(
207 + __in THEME* pTheme,
208 + __in DRAWITEMSTRUCT* pdis,
209 + __in const THEME_CONTROL* pControl
210 + );
211 +static HRESULT DrawImage(
212 + __in THEME* pTheme,
213 + __in DRAWITEMSTRUCT* pdis,
214 + __in const THEME_CONTROL* pControl
215 + );
216 +static HRESULT DrawProgressBar(
217 + __in THEME* pTheme,
218 + __in DRAWITEMSTRUCT* pdis,
219 + __in const THEME_CONTROL* pControl
220 + );
221 +static BOOL DrawHoverControl(
222 + __in THEME* pTheme,
223 + __in BOOL fHover
224 + );
225 +static DWORD CALLBACK RichEditStreamFromFileHandleCallback(
226 + __in DWORD_PTR dwCookie,
227 + __in_bcount(cb) LPBYTE pbBuff,
228 + __in LONG cb,
229 + __in LONG *pcb
230 + );
231 +static DWORD CALLBACK RichEditStreamFromMemoryCallback(
232 + __in DWORD_PTR dwCookie,
233 + __in_bcount(cb) LPBYTE pbBuff,
234 + __in LONG cb,
235 + __in LONG *pcb
236 + );
237 +static void FreeFont(
238 + __in THEME_FONT* pFont
239 + );
240 +static void FreePage(
241 + __in THEME_PAGE* pPage
242 + );
243 +static void FreeControl(
244 + __in THEME_CONTROL* pControl
245 + );
246 +static void FreeConditionalText(
247 + __in THEME_CONDITIONAL_TEXT* pConditionalText
248 + );
249 +static void FreeImageList(
250 + __in THEME_IMAGELIST* pImageList
251 + );
252 +static void FreeAction(
253 + __in THEME_ACTION* pAction
254 + );
255 +static void FreeColumn(
256 + __in THEME_COLUMN* pColumn
257 + );
258 +static void FreeTab(
259 + __in THEME_TAB* pTab
260 + );
261 +static void CALLBACK OnBillboardTimer(
262 + __in THEME* pTheme,
263 + __in HWND hwnd,
264 + __in UINT_PTR idEvent
265 + );
266 +static void OnBrowseDirectory(
267 + __in THEME* pTheme,
268 + __in HWND hWnd,
269 + __in const THEME_ACTION* pAction
270 + );
271 +static BOOL OnButtonClicked(
272 + __in THEME* pTheme,
273 + __in HWND hWnd,
274 + __in const THEME_CONTROL* pControl
275 + );
276 +static HRESULT OnRichEditEnLink(
277 + __in LPARAM lParam,
278 + __in HWND hWndRichEdit,
279 + __in HWND hWnd
280 + );
281 +static BOOL ControlIsType(
282 + __in const THEME* pTheme,
283 + __in DWORD dwControl,
284 + __in THEME_CONTROL_TYPE type
285 + );
286 +static const THEME_CONTROL* FindControlFromHWnd(
287 + __in const THEME* pTheme,
288 + __in HWND hWnd,
289 + __in_opt const THEME_CONTROL* pParentControl = NULL
290 + );
291 +static void GetControlDimensions(
292 + __in const RECT* prcParent,
293 + __in const THEME_CONTROL* pControl,
294 + __out int* piWidth,
295 + __out int* piHeight,
296 + __out int* piX,
297 + __out int* piY
298 + );
299 +// Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns
300 +// calculates final width of each column (storing result in each column's nWidth value)
301 +static HRESULT SizeListViewColumns(
302 + __inout THEME_CONTROL* pControl
303 + );
304 +static LRESULT CALLBACK PanelWndProc(
305 + __in HWND hWnd,
306 + __in UINT uMsg,
307 + __in WPARAM wParam,
308 + __in LPARAM lParam
309 + );
310 +static HRESULT LocalizeControls(
311 + __in DWORD cControls,
312 + __in THEME_CONTROL* rgControls,
313 + __in const WIX_LOCALIZATION *pWixLoc
314 + );
315 +static HRESULT LocalizeControl(
316 + __in THEME_CONTROL* pControl,
317 + __in const WIX_LOCALIZATION *pWixLoc
318 + );
319 +static HRESULT LoadControlsString(
320 + __in DWORD cControls,
321 + __in THEME_CONTROL* rgControls,
322 + __in HMODULE hResModule
323 + );
324 +static HRESULT LoadControlString(
325 + __in THEME_CONTROL* pControl,
326 + __in HMODULE hResModule
327 + );
328 +static void ResizeControls(
329 + __in DWORD cControls,
330 + __in THEME_CONTROL* rgControls,
331 + __in const RECT* prcParent
332 + );
333 +static void ResizeControl(
334 + __in THEME_CONTROL* pControl,
335 + __in const RECT* prcParent
336 + );
337 +static void GetControls(
338 + __in THEME* pTheme,
339 + __in_opt THEME_CONTROL* pParentControl,
340 + __out DWORD** ppcControls,
341 + __out THEME_CONTROL*** pprgControls
342 + );
343 +static void GetControls(
344 + __in const THEME* pTheme,
345 + __in_opt const THEME_CONTROL* pParentControl,
346 + __out DWORD& cControls,
347 + __out THEME_CONTROL*& rgControls
348 + );
349 +static void UnloadControls(
350 + __in DWORD cControls,
351 + __in THEME_CONTROL* rgControls
352 + );
353 +
354 +
355 +// Public functions.
356 +
357 +DAPI_(HRESULT) ThemeInitialize(
358 + __in_opt HMODULE hModule
359 + )
360 +{
361 + HRESULT hr = S_OK;
362 + INITCOMMONCONTROLSEX icex = { };
363 +
364 + hr = XmlInitialize();
365 + ExitOnFailure(hr, "Failed to initialize XML.");
366 +
367 + hr = RegisterWindowClasses(hModule);
368 + ExitOnFailure(hr, "Failed to register theme window classes.");
369 +
370 + // Initialize GDI+ and common controls.
371 + vgsi.SuppressBackgroundThread = TRUE;
372 +
373 + hr = GdipInitialize(&vgsi, &vgdiToken, &vgso);
374 + ExitOnFailure(hr, "Failed to initialize GDI+.");
375 +
376 + icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
377 + icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS;
378 + ::InitCommonControlsEx(&icex);
379 +
380 + (*vgso.NotificationHook)(&vgdiHookToken);
381 +
382 +LExit:
383 + return hr;
384 +}
385 +
386 +
387 +DAPI_(void) ThemeUninitialize()
388 +{
389 + if (vhModuleRichEd)
390 + {
391 + ::FreeLibrary(vhModuleRichEd);
392 + vhModuleRichEd = NULL;
393 + }
394 +
395 + if (vhHyperlinkRegisteredModule)
396 + {
397 + ::UnregisterClassW(THEME_WC_HYPERLINK, vhHyperlinkRegisteredModule);
398 + vhHyperlinkRegisteredModule = NULL;
399 + }
400 +
401 + if (vhPanelRegisteredModule)
402 + {
403 + ::UnregisterClassW(THEME_WC_PANEL, vhPanelRegisteredModule);
404 + vhPanelRegisteredModule = NULL;
405 + }
406 +
407 + if (vgdiToken)
408 + {
409 + GdipUninitialize(vgdiToken);
410 + vgdiToken = 0;
411 + }
412 +
413 + XmlUninitialize();
414 +}
415 +
416 +
417 +DAPI_(HRESULT) ThemeLoadFromFile(
418 + __in_z LPCWSTR wzThemeFile,
419 + __out THEME** ppTheme
420 + )
421 +{
422 + HRESULT hr = S_OK;
423 + IXMLDOMDocument* pixd = NULL;
424 + LPWSTR sczRelativePath = NULL;
425 +
426 + hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd);
427 + ExitOnFailure(hr, "Failed to load theme resource as XML document.");
428 +
429 + hr = PathGetDirectory(wzThemeFile, &sczRelativePath);
430 + ExitOnFailure(hr, "Failed to get relative path from theme file.");
431 +
432 + hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme);
433 + ExitOnFailure(hr, "Failed to parse theme.");
434 +
435 +LExit:
436 + ReleaseStr(sczRelativePath);
437 + ReleaseObject(pixd);
438 +
439 + return hr;
440 +}
441 +
442 +
443 +DAPI_(HRESULT) ThemeLoadFromResource(
444 + __in_opt HMODULE hModule,
445 + __in_z LPCSTR szResource,
446 + __out THEME** ppTheme
447 + )
448 +{
449 + HRESULT hr = S_OK;
450 + LPVOID pvResource = NULL;
451 + DWORD cbResource = 0;
452 + LPWSTR sczXml = NULL;
453 + IXMLDOMDocument* pixd = NULL;
454 +
455 + hr = ResReadData(hModule, szResource, &pvResource, &cbResource);
456 + ExitOnFailure(hr, "Failed to read theme from resource.");
457 +
458 + hr = StrAllocStringAnsi(&sczXml, reinterpret_cast<LPCSTR>(pvResource), cbResource, CP_UTF8);
459 + ExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string.");
460 +
461 + hr = XmlLoadDocument(sczXml, &pixd);
462 + ExitOnFailure(hr, "Failed to load theme resource as XML document.");
463 +
464 + hr = ParseTheme(hModule, NULL, pixd, ppTheme);
465 + ExitOnFailure(hr, "Failed to parse theme.");
466 +
467 +LExit:
468 + ReleaseObject(pixd);
469 + ReleaseStr(sczXml);
470 +
471 + return hr;
472 +}
473 +
474 +
475 +DAPI_(void) ThemeFree(
476 + __in THEME* pTheme
477 + )
478 +{
479 + if (pTheme)
480 + {
481 + for (DWORD i = 0; i < pTheme->cFonts; ++i)
482 + {
483 + FreeFont(pTheme->rgFonts + i);
484 + }
485 +
486 + for (DWORD i = 0; i < pTheme->cPages; ++i)
487 + {
488 + FreePage(pTheme->rgPages + i);
489 + }
490 +
491 + for (DWORD i = 0; i < pTheme->cImageLists; ++i)
492 + {
493 + FreeImageList(pTheme->rgImageLists + i);
494 + }
495 +
496 + for (DWORD i = 0; i < pTheme->cControls; ++i)
497 + {
498 + FreeControl(pTheme->rgControls + i);
499 + }
500 +
501 + ReleaseMem(pTheme->rgControls);
502 + ReleaseMem(pTheme->rgPages);
503 + ReleaseMem(pTheme->rgFonts);
504 +
505 + if (pTheme->hImage)
506 + {
507 + ::DeleteBitmap(pTheme->hImage);
508 + }
509 +
510 + ReleaseStr(pTheme->sczCaption);
511 + ReleaseMem(pTheme);
512 + }
513 +}
514 +
515 +DAPI_(HRESULT) ThemeRegisterVariableCallbacks(
516 + __in THEME* pTheme,
517 + __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition,
518 + __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString,
519 + __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable,
520 + __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable,
521 + __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable,
522 + __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable,
523 + __in_opt LPVOID pvContext
524 + )
525 +{
526 + HRESULT hr = S_OK;
527 + ExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first.");
528 +
529 + pTheme->pfnEvaluateCondition = pfnEvaluateCondition;
530 + pTheme->pfnFormatString = pfnFormatString;
531 + pTheme->pfnGetNumericVariable = pfnGetNumericVariable;
532 + pTheme->pfnSetNumericVariable = pfnSetNumericVariable;
533 + pTheme->pfnGetStringVariable = pfnGetStringVariable;
534 + pTheme->pfnSetStringVariable = pfnSetStringVariable;
535 + pTheme->pvVariableContext = pvContext;
536 +
537 +LExit:
538 + return hr;
539 +}
540 +
541 +
542 +DAPI_(HRESULT) ThemeLoadControls(
543 + __in THEME* pTheme,
544 + __in HWND hwndParent,
545 + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
546 + __in DWORD cAssignControlIds
547 + )
548 +{
549 + return LoadControls(pTheme, NULL, hwndParent, rgAssignControlIds, cAssignControlIds);
550 +}
551 +
552 +
553 +DAPI_(void) ThemeUnloadControls(
554 + __in THEME* pTheme
555 + )
556 +{
557 + UnloadControls(pTheme->cControls, pTheme->rgControls);
558 +
559 + pTheme->hwndHover = NULL;
560 + pTheme->hwndParent = NULL;
561 +}
562 +
563 +DAPI_(HRESULT) ThemeLocalize(
564 + __in THEME *pTheme,
565 + __in const WIX_LOCALIZATION *pWixLoc
566 + )
567 +{
568 + HRESULT hr = S_OK;
569 + LPWSTR sczCaption = NULL;
570 +
571 + hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption);
572 + ExitOnFailure(hr, "Failed to localize theme caption.");
573 +
574 + if (pTheme->pfnFormatString)
575 + {
576 + hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext);
577 + if (SUCCEEDED(hr))
578 + {
579 + hr = ThemeUpdateCaption(pTheme, sczCaption);
580 + }
581 + }
582 +
583 + hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc);
584 +
585 +LExit:
586 + ReleaseStr(sczCaption);
587 +
588 + return hr;
589 +}
590 +
591 +/********************************************************************
592 + ThemeLoadStrings - Loads string resources.
593 + Must be called after loading a theme and before calling
594 + ThemeLoadControls.
595 +*******************************************************************/
596 +DAPI_(HRESULT) ThemeLoadStrings(
597 + __in THEME* pTheme,
598 + __in HMODULE hResModule
599 + )
600 +{
601 + HRESULT hr = S_OK;
602 + ExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first.");
603 +
604 + if (UINT_MAX != pTheme->uStringId)
605 + {
606 + hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption);
607 + ExitOnFailure(hr, "Failed to load theme caption.");
608 + }
609 +
610 + hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule);
611 +
612 +LExit:
613 + return hr;
614 +}
615 +
616 +
617 +DAPI_(HRESULT) ThemeLoadRichEditFromFile(
618 + __in THEME* pTheme,
619 + __in DWORD dwControl,
620 + __in_z LPCWSTR wzFileName,
621 + __in HMODULE hModule
622 + )
623 +{
624 + HRESULT hr = S_OK;
625 + LPWSTR sczFile = NULL;
626 + HANDLE hFile = INVALID_HANDLE_VALUE;
627 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
628 +
629 + hr = PathRelativeToModule(&sczFile, wzFileName, hModule);
630 + ExitOnFailure(hr, "Failed to read resource data.");
631 +
632 + hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
633 + if (INVALID_HANDLE_VALUE == hFile)
634 + {
635 + ExitWithLastError(hr, "Failed to open RTF file.");
636 + }
637 + else
638 + {
639 + LONGLONG llRtfSize;
640 + hr = FileSizeByHandle(hFile, &llRtfSize);
641 + if (SUCCEEDED(hr))
642 + {
643 + ::SendMessageW(hWnd, EM_EXLIMITTEXT, 0, static_cast<LPARAM>(llRtfSize));
644 + }
645 +
646 + EDITSTREAM es = { };
647 + es.pfnCallback = RichEditStreamFromFileHandleCallback;
648 + es.dwCookie = reinterpret_cast<DWORD_PTR>(hFile);
649 +
650 + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast<LPARAM>(&es));
651 + hr = es.dwError;
652 + ExitOnFailure(hr, "Failed to update RTF stream.");
653 + }
654 +
655 +LExit:
656 + ReleaseStr(sczFile);
657 + ReleaseFile(hFile);
658 +
659 + return hr;
660 +}
661 +
662 +
663 +DAPI_(HRESULT) ThemeLoadRichEditFromResource(
664 + __in THEME* pTheme,
665 + __in DWORD dwControl,
666 + __in_z LPCSTR szResourceName,
667 + __in HMODULE hModule
668 + )
669 +{
670 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
671 + return ThemeLoadRichEditFromResourceToHWnd(hWnd, szResourceName, hModule);
672 +}
673 +
674 +DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd(
675 + __in HWND hWnd,
676 + __in_z LPCSTR szResourceName,
677 + __in HMODULE hModule
678 + )
679 +{
680 + HRESULT hr = S_OK;
681 + MEMBUFFER_FOR_RICHEDIT buffer = { };
682 + EDITSTREAM es = { };
683 +
684 + hr = ResReadData(hModule, szResourceName, reinterpret_cast<LPVOID*>(&buffer.rgbData), &buffer.cbData);
685 + ExitOnFailure(hr, "Failed to read resource data.");
686 +
687 + es.pfnCallback = RichEditStreamFromMemoryCallback;
688 + es.dwCookie = reinterpret_cast<DWORD_PTR>(&buffer);
689 +
690 + ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast<LPARAM>(&es));
691 + hr = es.dwError;
692 + ExitOnFailure(hr, "Failed to update RTF stream.");
693 +
694 +LExit:
695 + return hr;
696 +}
697 +
698 +
699 +DAPI_(BOOL) ThemeHandleKeyboardMessage(
700 + __in_opt THEME* pTheme,
701 + __in HWND /*hWnd*/,
702 + __in MSG* pMsg
703 + )
704 +{
705 + return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE;
706 +}
707 +
708 +
709 +extern "C" LRESULT CALLBACK ThemeDefWindowProc(
710 + __in_opt THEME* pTheme,
711 + __in HWND hWnd,
712 + __in UINT uMsg,
713 + __in WPARAM wParam,
714 + __in LPARAM lParam
715 + )
716 +{
717 + RECT rcParent = { };
718 + RECT *pRect = NULL;
719 +
720 + if (pTheme)
721 + {
722 + switch (uMsg)
723 + {
724 + case WM_NCHITTEST:
725 + if (pTheme->dwStyle & WS_POPUP)
726 + {
727 + return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control.
728 + }
729 + break;
730 +
731 + case WM_WINDOWPOSCHANGED:
732 + {
733 + //WINDOWPOS* pos = reinterpret_cast<LPWINDOWPOS>(lParam);
734 + //ThemeWindowPositionChanged(pTheme, pos);
735 + }
736 + break;
737 +
738 + case WM_DRAWITEM:
739 + ThemeDrawControl(pTheme, reinterpret_cast<LPDRAWITEMSTRUCT>(lParam));
740 + return TRUE;
741 +
742 + case WM_CTLCOLORBTN: __fallthrough;
743 + case WM_CTLCOLORSTATIC:
744 + {
745 + HBRUSH hBrush = NULL;
746 + if (ThemeSetControlColor(pTheme, reinterpret_cast<HDC>(wParam), reinterpret_cast<HWND>(lParam), &hBrush))
747 + {
748 + return reinterpret_cast<LRESULT>(hBrush);
749 + }
750 + }
751 + break;
752 +
753 + case WM_SETCURSOR:
754 + if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast<HWND>(wParam)))
755 + {
756 + return TRUE;
757 + }
758 + break;
759 +
760 + case WM_PAINT:
761 + if (::GetUpdateRect(hWnd, NULL, FALSE))
762 + {
763 + PAINTSTRUCT ps;
764 + ::BeginPaint(hWnd, &ps);
765 + if (hWnd == pTheme->hwndParent)
766 + {
767 + ThemeDrawBackground(pTheme, &ps);
768 + }
769 + ::EndPaint(hWnd, &ps);
770 + }
771 + return 0;
772 +
773 + case WM_SIZING:
774 + if (pTheme->fAutoResize)
775 + {
776 + pRect = reinterpret_cast<RECT *>(lParam);
777 + if (pRect->right - pRect->left < pTheme->nMinimumWidth)
778 + {
779 + if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT)
780 + {
781 + pRect->left = pRect->right - pTheme->nMinimumWidth;
782 + }
783 + else
784 + {
785 + pRect->right = pRect->left + pTheme->nMinimumWidth;
786 + }
787 + }
788 + if (pRect->bottom - pRect->top < pTheme->nMinimumHeight)
789 + {
790 + if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT)
791 + {
792 + pRect->bottom = pRect->top + pTheme->nMinimumHeight;
793 + }
794 + else
795 + {
796 + pRect->top = pRect->bottom - pTheme->nMinimumHeight;
797 + }
798 + }
799 +
800 + return TRUE;
801 + }
802 + break;
803 +
804 + case WM_SIZE:
805 + if (pTheme->fAutoResize)
806 + {
807 + ::GetClientRect(pTheme->hwndParent, &rcParent);
808 + ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent);
809 + return 0;
810 + }
811 + break;
812 +
813 + case WM_TIMER:
814 + OnBillboardTimer(pTheme, hWnd, wParam);
815 + break;
816 +
817 + case WM_NOTIFY:
818 + if (lParam)
819 + {
820 + LPNMHDR pnmhdr = reinterpret_cast<LPNMHDR>(lParam);
821 + switch (pnmhdr->code)
822 + {
823 + // Tab/Shift+Tab support for rich-edit control.
824 + case EN_MSGFILTER:
825 + {
826 + MSGFILTER* msgFilter = reinterpret_cast<MSGFILTER*>(lParam);
827 + if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam)
828 + {
829 + BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT);
830 + HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift);
831 + ::SetFocus(hwndFocus);
832 + return 1;
833 + }
834 + break;
835 + }
836 +
837 + // Hyperlink clicks from rich-edit control.
838 + case EN_LINK:
839 + return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd));
840 +
841 + // Clicks on a hypertext/syslink control.
842 + case NM_CLICK: __fallthrough;
843 + case NM_RETURN:
844 + if (ControlIsType(pTheme, static_cast<DWORD>(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT))
845 + {
846 + PNMLINK pnmlink = reinterpret_cast<PNMLINK>(lParam);
847 + LITEM litem = pnmlink->item;
848 + ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL);
849 + return 1;
850 + }
851 +
852 + return 0;
853 + }
854 + }
855 + break;
856 +
857 + case WM_COMMAND:
858 + switch (HIWORD(wParam))
859 + {
860 + case BN_CLICKED:
861 + if (lParam)
862 + {
863 + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam);
864 + if (pControl && OnButtonClicked(pTheme, hWnd, pControl))
865 + {
866 + return 0;
867 + }
868 + }
869 + break;
870 + }
871 + break;
872 + }
873 + }
874 +
875 + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
876 +}
877 +
878 +
879 +DAPI_(void) ThemeGetPageIds(
880 + __in const THEME* pTheme,
881 + __in_ecount(cGetPages) LPCWSTR* rgwzFindNames,
882 + __inout_ecount(cGetPages) DWORD* rgdwPageIds,
883 + __in DWORD cGetPages
884 + )
885 +{
886 + for (DWORD i = 0; i < cGetPages; ++i)
887 + {
888 + LPCWSTR wzFindName = rgwzFindNames[i];
889 + for (DWORD j = 0; j < pTheme->cPages; ++j)
890 + {
891 + LPCWSTR wzPageName = pTheme->rgPages[j].sczName;
892 + if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1))
893 + {
894 + rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid).
895 + break;
896 + }
897 + }
898 + }
899 +}
900 +
901 +
902 +DAPI_(THEME_PAGE*) ThemeGetPage(
903 + __in const THEME* pTheme,
904 + __in DWORD dwPage
905 + )
906 +{
907 + DWORD iPage = dwPage - 1;
908 + THEME_PAGE* pPage = NULL;
909 +
910 + if (iPage < pTheme->cPages)
911 + {
912 + pPage = pTheme->rgPages + iPage;
913 + }
914 +
915 + return pPage;
916 +}
917 +
918 +
919 +DAPI_(HRESULT) ThemeShowPage(
920 + __in THEME* pTheme,
921 + __in DWORD dwPage,
922 + __in int nCmdShow
923 + )
924 +{
925 + return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT);
926 +}
927 +
928 +
929 +DAPI_(HRESULT) ThemeShowPageEx(
930 + __in THEME* pTheme,
931 + __in DWORD dwPage,
932 + __in int nCmdShow,
933 + __in THEME_SHOW_PAGE_REASON reason
934 + )
935 +{
936 + HRESULT hr = S_OK;
937 + BOOL fHide = SW_HIDE == nCmdShow;
938 + BOOL fSaveEditboxes = FALSE;
939 + THEME_SAVEDVARIABLE* pSavedVariable = NULL;
940 + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage);
941 +
942 + if (pPage)
943 + {
944 + if (fHide)
945 + {
946 + switch (reason)
947 + {
948 + case THEME_SHOW_PAGE_REASON_DEFAULT:
949 + // Set the variables in the loop below.
950 + fSaveEditboxes = TRUE;
951 + break;
952 + case THEME_SHOW_PAGE_REASON_CANCEL:
953 + if (pPage->cSavedVariables && pTheme->pfnSetStringVariable)
954 + {
955 + // Best effort to cancel any changes to the variables.
956 + for (DWORD v = 0; v < pPage->cSavedVariables; ++v)
957 + {
958 + pSavedVariable = pPage->rgSavedVariables + v;
959 + if (pSavedVariable->wzName)
960 + {
961 + pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, pTheme->pvVariableContext);
962 + }
963 + }
964 + }
965 + break;
966 + }
967 +
968 + if (THEME_SHOW_PAGE_REASON_REFRESH != reason)
969 + {
970 + pPage->cSavedVariables = 0;
971 + if (pPage->rgSavedVariables)
972 + {
973 + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables));
974 + }
975 + }
976 +
977 + pTheme->dwCurrentPageId = 0;
978 + }
979 + else
980 + {
981 + if (THEME_SHOW_PAGE_REASON_REFRESH == reason)
982 + {
983 + fSaveEditboxes = TRUE;
984 + }
985 + else
986 + {
987 + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices);
988 + ExitOnNull(pPage->rgSavedVariables, hr, E_OUTOFMEMORY, "Failed to allocate memory for saved variables.");
989 +
990 + SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables));
991 + pPage->cSavedVariables = pPage->cControlIndices;
992 +
993 + // Save the variables in the loop below.
994 + }
995 +
996 + pTheme->dwCurrentPageId = dwPage;
997 + }
998 + }
999 +
1000 + hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage);
1001 + ExitOnFailure(hr, "Failed to show page controls.");
1002 +
1003 +LExit:
1004 + return hr;
1005 +}
1006 +
1007 +
1008 +DAPI_(BOOL) ThemeControlExists(
1009 + __in const THEME* pTheme,
1010 + __in DWORD dwControl
1011 + )
1012 +{
1013 + BOOL fExists = FALSE;
1014 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1015 + if (hWnd)
1016 + {
1017 + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
1018 + fExists = (pControl && hWnd == pControl->hWnd);
1019 + }
1020 +
1021 + return fExists;
1022 +}
1023 +
1024 +
1025 +DAPI_(void) ThemeControlEnable(
1026 + __in THEME* pTheme,
1027 + __in DWORD dwControl,
1028 + __in BOOL fEnable
1029 + )
1030 +{
1031 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1032 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1033 + if (pControl)
1034 + {
1035 + pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED);
1036 + ::EnableWindow(hWnd, fEnable);
1037 +
1038 + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED)
1039 + {
1040 + ::ShowWindow(hWnd, fEnable ? SW_SHOW : SW_HIDE);
1041 + }
1042 + }
1043 +}
1044 +
1045 +
1046 +DAPI_(BOOL) ThemeControlEnabled(
1047 + __in THEME* pTheme,
1048 + __in DWORD dwControl
1049 + )
1050 +{
1051 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1052 + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
1053 + return pControl && !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED);
1054 +}
1055 +
1056 +
1057 +DAPI_(void) ThemeControlElevates(
1058 + __in THEME* pTheme,
1059 + __in DWORD dwControl,
1060 + __in BOOL fElevates
1061 + )
1062 +{
1063 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1064 + ::SendMessageW(hWnd, BCM_SETSHIELD, 0, fElevates);
1065 +}
1066 +
1067 +
1068 +DAPI_(void) ThemeShowControl(
1069 + __in THEME* pTheme,
1070 + __in DWORD dwControl,
1071 + __in int nCmdShow
1072 + )
1073 +{
1074 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1075 + ::ShowWindow(hWnd, nCmdShow);
1076 +
1077 + // Save the control's visible state.
1078 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1079 + if (pControl)
1080 + {
1081 + pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN);
1082 + }
1083 +}
1084 +
1085 +
1086 +DAPI_(void) ThemeShowControlEx(
1087 + __in THEME* pTheme,
1088 + __in DWORD dwControl,
1089 + __in int nCmdShow
1090 + )
1091 +{
1092 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1093 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1094 + if (pControl)
1095 + {
1096 + ShowControl(pTheme, pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL);
1097 + }
1098 +}
1099 +
1100 +
1101 +DAPI_(BOOL) ThemeControlVisible(
1102 + __in THEME* pTheme,
1103 + __in DWORD dwControl
1104 + )
1105 +{
1106 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1107 + return ::IsWindowVisible(hWnd);
1108 +}
1109 +
1110 +
1111 +DAPI_(BOOL) ThemePostControlMessage(
1112 + __in THEME* pTheme,
1113 + __in DWORD dwControl,
1114 + __in UINT Msg,
1115 + __in WPARAM wParam,
1116 + __in LPARAM lParam
1117 + )
1118 +{
1119 + HRESULT hr = S_OK;
1120 + UINT er = ERROR_SUCCESS;
1121 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1122 +
1123 + if (!::PostMessageW(hWnd, Msg, wParam, lParam))
1124 + {
1125 + er = ::GetLastError();
1126 + hr = HRESULT_FROM_WIN32(er);
1127 + }
1128 +
1129 + return SUCCEEDED(hr);
1130 +}
1131 +
1132 +
1133 +DAPI_(LRESULT) ThemeSendControlMessage(
1134 + __in const THEME* pTheme,
1135 + __in DWORD dwControl,
1136 + __in UINT Msg,
1137 + __in WPARAM wParam,
1138 + __in LPARAM lParam
1139 + )
1140 +{
1141 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1142 + return ::SendMessageW(hWnd, Msg, wParam, lParam);
1143 +}
1144 +
1145 +
1146 +DAPI_(HRESULT) ThemeDrawBackground(
1147 + __in THEME* pTheme,
1148 + __in PAINTSTRUCT* pps
1149 + )
1150 +{
1151 + HRESULT hr = S_FALSE;
1152 +
1153 + if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase)
1154 + {
1155 + HDC hdcMem = ::CreateCompatibleDC(pps->hdc);
1156 + HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pTheme->hImage));
1157 +
1158 + ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, pTheme->nWidth, pTheme->nHeight, SRCCOPY);
1159 +
1160 + ::SelectObject(hdcMem, hDefaultBitmap);
1161 + ::DeleteDC(hdcMem);
1162 +
1163 + hr = S_OK;
1164 + }
1165 +
1166 + return hr;
1167 +}
1168 +
1169 +
1170 +DAPI_(HRESULT) ThemeDrawControl(
1171 + __in THEME* pTheme,
1172 + __in DRAWITEMSTRUCT* pdis
1173 + )
1174 +{
1175 + HRESULT hr = S_OK;
1176 + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, pdis->hwndItem);
1177 +
1178 + AssertSz(pControl, "Expected control window from owner draw window.");
1179 + AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window.");
1180 + AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width.");
1181 + AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height.");
1182 +
1183 + switch (pControl->type)
1184 + {
1185 + case THEME_CONTROL_TYPE_BUTTON:
1186 + hr = DrawButton(pTheme, pdis, pControl);
1187 + ExitOnFailure(hr, "Failed to draw button.");
1188 + break;
1189 +
1190 + case THEME_CONTROL_TYPE_HYPERLINK:
1191 + hr = DrawHyperlink(pTheme, pdis, pControl);
1192 + ExitOnFailure(hr, "Failed to draw hyperlink.");
1193 + break;
1194 +
1195 + case THEME_CONTROL_TYPE_IMAGE:
1196 + hr = DrawImage(pTheme, pdis, pControl);
1197 + ExitOnFailure(hr, "Failed to draw image.");
1198 + break;
1199 +
1200 + case THEME_CONTROL_TYPE_PROGRESSBAR:
1201 + hr = DrawProgressBar(pTheme, pdis, pControl);
1202 + ExitOnFailure(hr, "Failed to draw progress bar.");
1203 + break;
1204 +
1205 + default:
1206 + hr = E_UNEXPECTED;
1207 + ExitOnRootFailure(hr, "Did not specify an owner draw control to draw.");
1208 + }
1209 +
1210 +LExit:
1211 + return hr;
1212 +}
1213 +
1214 +
1215 +DAPI_(BOOL) ThemeHoverControl(
1216 + __in THEME* pTheme,
1217 + __in HWND hwndParent,
1218 + __in HWND hwndControl
1219 + )
1220 +{
1221 + BOOL fHovered = FALSE;
1222 + if (hwndControl != pTheme->hwndHover)
1223 + {
1224 + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent)
1225 + {
1226 + DrawHoverControl(pTheme, FALSE);
1227 + }
1228 +
1229 + pTheme->hwndHover = hwndControl;
1230 +
1231 + if (pTheme->hwndHover && pTheme->hwndHover != hwndParent)
1232 + {
1233 + fHovered = DrawHoverControl(pTheme, TRUE);
1234 + }
1235 + }
1236 +
1237 + return fHovered;
1238 +}
1239 +
1240 +
1241 +DAPI_(BOOL) ThemeIsControlChecked(
1242 + __in THEME* pTheme,
1243 + __in DWORD dwControl
1244 + )
1245 +{
1246 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1247 + return BST_CHECKED == ::SendMessageW(hWnd, BM_GETCHECK, 0, 0);
1248 +}
1249 +
1250 +
1251 +DAPI_(BOOL) ThemeSetControlColor(
1252 + __in THEME* pTheme,
1253 + __in HDC hdc,
1254 + __in HWND hWnd,
1255 + __out HBRUSH* phBackgroundBrush
1256 + )
1257 +{
1258 + THEME_FONT* pFont = NULL;
1259 + BOOL fHasBackground = FALSE;
1260 +
1261 + *phBackgroundBrush = NULL;
1262 +
1263 + if (hWnd == pTheme->hwndParent)
1264 + {
1265 + pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId;
1266 + }
1267 + else
1268 + {
1269 + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
1270 + pFont = (!pControl || THEME_INVALID_ID == pControl->dwFontId) ? NULL : pTheme->rgFonts + pControl->dwFontId;
1271 + }
1272 +
1273 + if (pFont)
1274 + {
1275 + if (pFont->hForeground)
1276 + {
1277 + ::SetTextColor(hdc, pFont->crForeground);
1278 + }
1279 +
1280 + if (pFont->hBackground)
1281 + {
1282 + ::SetBkColor(hdc, pFont->crBackground);
1283 +
1284 + *phBackgroundBrush = pFont->hBackground;
1285 + fHasBackground = TRUE;
1286 + }
1287 + else
1288 + {
1289 + ::SetBkMode(hdc, TRANSPARENT);
1290 + *phBackgroundBrush = static_cast<HBRUSH>(::GetStockObject(NULL_BRUSH));
1291 + fHasBackground = TRUE;
1292 + }
1293 + }
1294 +
1295 + return fHasBackground;
1296 +}
1297 +
1298 +
1299 +DAPI_(HRESULT) ThemeSetProgressControl(
1300 + __in THEME* pTheme,
1301 + __in DWORD dwControl,
1302 + __in DWORD dwProgressPercentage
1303 + )
1304 +{
1305 + HRESULT hr = E_NOTFOUND;
1306 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1307 +
1308 + if (hWnd)
1309 + {
1310 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1311 + if (pControl && THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type)
1312 + {
1313 + DWORD dwCurrentProgress = LOWORD(pControl->dwData);
1314 +
1315 + if (dwCurrentProgress != dwProgressPercentage)
1316 + {
1317 + DWORD dwColor = HIWORD(pControl->dwData);
1318 + pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor);
1319 +
1320 + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)
1321 + {
1322 + if (!::InvalidateRect(hWnd, NULL, FALSE))
1323 + {
1324 + ExitWithLastError(hr, "Failed to invalidate progress bar window.");
1325 + }
1326 + }
1327 + else
1328 + {
1329 + ::SendMessageW(hWnd, PBM_SETPOS, dwProgressPercentage, 0);
1330 + }
1331 +
1332 + hr = S_OK;
1333 + }
1334 + else
1335 + {
1336 + hr = S_FALSE;
1337 + }
1338 + }
1339 + }
1340 +
1341 +LExit:
1342 + return hr;
1343 +}
1344 +
1345 +
1346 +DAPI_(HRESULT) ThemeSetProgressControlColor(
1347 + __in THEME* pTheme,
1348 + __in DWORD dwControl,
1349 + __in DWORD dwColorIndex
1350 + )
1351 +{
1352 + HRESULT hr = S_FALSE;
1353 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1354 + if (hWnd)
1355 + {
1356 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1357 +
1358 + // Only set color on owner draw progress bars.
1359 + if (pControl && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW))
1360 + {
1361 + DWORD dwCurrentColor = HIWORD(pControl->dwData);
1362 +
1363 + if (dwCurrentColor != dwColorIndex)
1364 + {
1365 + DWORD dwCurrentProgress = LOWORD(pControl->dwData);
1366 + pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex);
1367 +
1368 + if (!::InvalidateRect(hWnd, NULL, FALSE))
1369 + {
1370 + ExitWithLastError(hr, "Failed to invalidate progress bar window.");
1371 + }
1372 +
1373 + hr = S_OK;
1374 + }
1375 + }
1376 + }
1377 +
1378 +LExit:
1379 + return hr;
1380 +}
1381 +
1382 +
1383 +DAPI_(HRESULT) ThemeSetTextControl(
1384 + __in const THEME* pTheme,
1385 + __in DWORD dwControl,
1386 + __in_z_opt LPCWSTR wzText
1387 + )
1388 +{
1389 + return ThemeSetTextControlEx(pTheme, dwControl, FALSE, wzText);
1390 +}
1391 +
1392 +
1393 +DAPI_(HRESULT) ThemeSetTextControlEx(
1394 + __in const THEME* pTheme,
1395 + __in DWORD dwControl,
1396 + __in BOOL fUpdate,
1397 + __in_z_opt LPCWSTR wzText
1398 + )
1399 +{
1400 + HRESULT hr = S_OK;
1401 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1402 +
1403 + if (hWnd)
1404 + {
1405 + if (fUpdate)
1406 + {
1407 + ::ShowWindow(hWnd, SW_HIDE);
1408 + }
1409 +
1410 + if (!::SetWindowTextW(hWnd, wzText))
1411 + {
1412 + ExitWithLastError(hr, "Failed to set control text.");
1413 + }
1414 +
1415 + if (fUpdate)
1416 + {
1417 + ::ShowWindow(hWnd, SW_SHOW);
1418 + }
1419 + }
1420 +
1421 +LExit:
1422 + return hr;
1423 +}
1424 +
1425 +
1426 +DAPI_(HRESULT) ThemeGetTextControl(
1427 + __in const THEME* pTheme,
1428 + __in DWORD dwControl,
1429 + __out_z LPWSTR* psczText
1430 + )
1431 +{
1432 + HRESULT hr = S_OK;
1433 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1434 + DWORD cchText = 0;
1435 + DWORD cchTextRead = 0;
1436 +
1437 + // Ensure the string has room for at least one character.
1438 + hr = StrMaxLength(*psczText, reinterpret_cast<DWORD_PTR*>(&cchText));
1439 + ExitOnFailure(hr, "Failed to get text buffer length.");
1440 +
1441 + if (!cchText)
1442 + {
1443 + cchText = GROW_WINDOW_TEXT;
1444 +
1445 + hr = StrAlloc(psczText, cchText);
1446 + ExitOnFailure(hr, "Failed to grow text buffer.");
1447 + }
1448 +
1449 + // Read (and keep growing buffer) until we finally read less than there
1450 + // is room in the buffer.
1451 + for (;;)
1452 + {
1453 + cchTextRead = ::GetWindowTextW(hWnd, *psczText, cchText);
1454 + if (cchTextRead + 1 < cchText)
1455 + {
1456 + break;
1457 + }
1458 + else
1459 + {
1460 + cchText = cchTextRead + GROW_WINDOW_TEXT;
1461 +
1462 + hr = StrAlloc(psczText, cchText);
1463 + ExitOnFailure(hr, "Failed to grow text buffer again.");
1464 + }
1465 + }
1466 +
1467 +LExit:
1468 + return hr;
1469 +}
1470 +
1471 +
1472 +DAPI_(HRESULT) ThemeUpdateCaption(
1473 + __in THEME* pTheme,
1474 + __in_z LPCWSTR wzCaption
1475 + )
1476 +{
1477 + HRESULT hr = S_OK;
1478 +
1479 + hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0);
1480 + ExitOnFailure(hr, "Failed to update theme caption.");
1481 +
1482 +LExit:
1483 + return hr;
1484 +}
1485 +
1486 +
1487 +DAPI_(void) ThemeSetFocus(
1488 + __in THEME* pTheme,
1489 + __in DWORD dwControl
1490 + )
1491 +{
1492 + HWND hwndFocus = ::GetDlgItem(pTheme->hwndParent, dwControl);
1493 + if (hwndFocus && !ThemeControlEnabled(pTheme, dwControl))
1494 + {
1495 + hwndFocus = ::GetNextDlgTabItem(pTheme->hwndParent, hwndFocus, FALSE);
1496 + }
1497 +
1498 + if (hwndFocus)
1499 + {
1500 + ::SetFocus(hwndFocus);
1501 + }
1502 +}
1503 +
1504 +
1505 +DAPI_(void) ThemeShowChild(
1506 + __in THEME* pTheme,
1507 + __in THEME_CONTROL* pParentControl,
1508 + __in DWORD dwIndex
1509 + )
1510 +{
1511 + // show one child, hide the rest
1512 + for (DWORD i = 0; i < pParentControl->cControls; ++i)
1513 + {
1514 + THEME_CONTROL* pControl = pParentControl->rgControls + i;
1515 + ShowControl(pTheme, pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL);
1516 + }
1517 +}
1518 +
1519 +
1520 +// Internal functions.
1521 +
1522 +static HRESULT RegisterWindowClasses(
1523 + __in_opt HMODULE hModule
1524 + )
1525 +{
1526 + HRESULT hr = S_OK;
1527 + WNDCLASSW wcHyperlink = { };
1528 + WNDCLASSW wcPanel = { };
1529 +
1530 + vhCursorHand = ::LoadCursorA(NULL, IDC_HAND);
1531 +
1532 + // Base the theme hyperlink class on a button but give it the "hand" icon.
1533 + if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink))
1534 + {
1535 + ExitWithLastError(hr, "Failed to get button window class.");
1536 + }
1537 +
1538 + wcHyperlink.lpszClassName = THEME_WC_HYPERLINK;
1539 +#pragma prefast(push)
1540 +#pragma prefast(disable:25068)
1541 + wcHyperlink.hCursor = vhCursorHand;
1542 +#pragma prefast(pop)
1543 +
1544 + if (!::RegisterClassW(&wcHyperlink))
1545 + {
1546 + ExitWithLastError(hr, "Failed to get button window class.");
1547 + }
1548 + vhHyperlinkRegisteredModule = hModule;
1549 +
1550 + // Panel is its own do-nothing class.
1551 + wcPanel.lpfnWndProc = PanelWndProc;
1552 + wcPanel.hInstance = hModule;
1553 + wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW);
1554 + wcPanel.lpszClassName = THEME_WC_PANEL;
1555 + if (!::RegisterClassW(&wcPanel))
1556 + {
1557 + ExitWithLastError(hr, "Failed to register window.");
1558 + }
1559 + vhPanelRegisteredModule = hModule;
1560 +
1561 +
1562 +LExit:
1563 + return hr;
1564 +}
1565 +
1566 +static HRESULT ParseTheme(
1567 + __in_opt HMODULE hModule,
1568 + __in_opt LPCWSTR wzRelativePath,
1569 + __in IXMLDOMDocument* pixd,
1570 + __out THEME** ppTheme
1571 + )
1572 +{
1573 + static WORD wThemeId = 0;
1574 +
1575 + HRESULT hr = S_OK;
1576 + THEME* pTheme = NULL;
1577 + IXMLDOMElement *pThemeElement = NULL;
1578 +
1579 + hr = pixd->get_documentElement(&pThemeElement);
1580 + ExitOnFailure(hr, "Failed to get theme element.");
1581 +
1582 + pTheme = static_cast<THEME*>(MemAlloc(sizeof(THEME), TRUE));
1583 + ExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme.");
1584 +
1585 + pTheme->wId = ++wThemeId;
1586 +
1587 + // Parse the optional background resource image.
1588 + hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage);
1589 + ExitOnFailure(hr, "Failed while parsing theme image.");
1590 +
1591 + // Parse the fonts.
1592 + hr = ParseFonts(pThemeElement, pTheme);
1593 + ExitOnFailure(hr, "Failed to parse theme fonts.");
1594 +
1595 + // Parse the window element.
1596 + hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme);
1597 + ExitOnFailure(hr, "Failed to parse theme window element.");
1598 +
1599 + *ppTheme = pTheme;
1600 + pTheme = NULL;
1601 +
1602 +LExit:
1603 + ReleaseObject(pThemeElement);
1604 +
1605 + if (pTheme)
1606 + {
1607 + ThemeFree(pTheme);
1608 + }
1609 +
1610 + return hr;
1611 +}
1612 +
1613 +static HRESULT ParseImage(
1614 + __in_opt HMODULE hModule,
1615 + __in_z_opt LPCWSTR wzRelativePath,
1616 + __in IXMLDOMNode* pElement,
1617 + __out HBITMAP* phImage
1618 + )
1619 +{
1620 + HRESULT hr = S_OK;
1621 + BSTR bstr = NULL;
1622 + LPWSTR sczImageFile = NULL;
1623 + int iResourceId = 0;
1624 + Gdiplus::Bitmap* pBitmap = NULL;
1625 +
1626 + hr = XmlGetAttribute(pElement, L"ImageResource", &bstr);
1627 + ExitOnFailure(hr, "Failed to get image resource attribute.");
1628 +
1629 + if (S_OK == hr)
1630 + {
1631 + iResourceId = wcstol(bstr, NULL, 10);
1632 +
1633 + hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(iResourceId), &pBitmap);
1634 + // Don't fail.
1635 + }
1636 +
1637 + ReleaseNullBSTR(bstr);
1638 +
1639 + // Parse the optional background image from a given file.
1640 + if (!pBitmap)
1641 + {
1642 + hr = XmlGetAttribute(pElement, L"ImageFile", &bstr);
1643 + ExitOnFailure(hr, "Failed to get image file attribute.");
1644 +
1645 + if (S_OK == hr)
1646 + {
1647 + if (wzRelativePath)
1648 + {
1649 + hr = PathConcat(wzRelativePath, bstr, &sczImageFile);
1650 + ExitOnFailure(hr, "Failed to combine image file path.");
1651 + }
1652 + else
1653 + {
1654 + hr = PathRelativeToModule(&sczImageFile, bstr, hModule);
1655 + ExitOnFailure(hr, "Failed to get image filename.");
1656 + }
1657 +
1658 + hr = GdipBitmapFromFile(sczImageFile, &pBitmap);
1659 + // Don't fail.
1660 + }
1661 + }
1662 +
1663 + // If there is an image, convert it into a bitmap handle.
1664 + if (pBitmap)
1665 + {
1666 + Gdiplus::Color black;
1667 + Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage);
1668 + ExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP.");
1669 + }
1670 +
1671 + hr = S_OK;
1672 +
1673 +LExit:
1674 + if (pBitmap)
1675 + {
1676 + delete pBitmap;
1677 + }
1678 +
1679 + ReleaseStr(sczImageFile);
1680 + ReleaseBSTR(bstr);
1681 +
1682 + return hr;
1683 +}
1684 +
1685 +
1686 +static HRESULT ParseIcon(
1687 + __in_opt HMODULE hModule,
1688 + __in_z_opt LPCWSTR wzRelativePath,
1689 + __in IXMLDOMNode* pElement,
1690 + __out HICON* phIcon
1691 + )
1692 +{
1693 + HRESULT hr = S_OK;
1694 + BSTR bstr = NULL;
1695 + LPWSTR sczImageFile = NULL;
1696 + int iResourceId = 0;
1697 +
1698 + hr = XmlGetAttribute(pElement, L"IconResource", &bstr);
1699 + ExitOnFailure(hr, "Failed to get icon resource attribute.");
1700 +
1701 + if (S_OK == hr)
1702 + {
1703 + iResourceId = wcstol(bstr, NULL, 10);
1704 +
1705 + *phIcon = reinterpret_cast<HICON>(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
1706 + ExitOnNullWithLastError(*phIcon, hr, "Failed to load icon.");
1707 + }
1708 + else
1709 + {
1710 + ReleaseNullBSTR(bstr);
1711 +
1712 + hr = XmlGetAttribute(pElement, L"IconFile", &bstr);
1713 + ExitOnFailure(hr, "Failed to get icon file attribute.");
1714 +
1715 + if (S_OK == hr)
1716 + {
1717 + if (wzRelativePath)
1718 + {
1719 + hr = PathConcat(wzRelativePath, bstr, &sczImageFile);
1720 + ExitOnFailure(hr, "Failed to combine image file path.");
1721 + }
1722 + else
1723 + {
1724 + hr = PathRelativeToModule(&sczImageFile, bstr, hModule);
1725 + ExitOnFailure(hr, "Failed to get image filename.");
1726 + }
1727 +
1728 + *phIcon = reinterpret_cast<HICON>(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE));
1729 + ExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile);
1730 + }
1731 + }
1732 +
1733 +LExit:
1734 + ReleaseStr(sczImageFile);
1735 + ReleaseBSTR(bstr);
1736 +
1737 + return hr;
1738 +}
1739 +
1740 +
1741 +static HRESULT ParseWindow(
1742 + __in_opt HMODULE hModule,
1743 + __in_opt LPCWSTR wzRelativePath,
1744 + __in IXMLDOMElement* pElement,
1745 + __in THEME* pTheme
1746 + )
1747 +{
1748 + HRESULT hr = S_OK;
1749 + IXMLDOMNode* pixn = NULL;
1750 + BSTR bstr = NULL;
1751 + LPWSTR sczIconFile = NULL;
1752 +
1753 + hr = XmlSelectSingleNode(pElement, L"Window", &pixn);
1754 + if (S_FALSE == hr)
1755 + {
1756 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1757 + }
1758 + ExitOnFailure(hr, "Failed to find window element.");
1759 +
1760 + hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize);
1761 + if (E_NOTFOUND == hr)
1762 + {
1763 + hr = S_OK;
1764 + }
1765 + ExitOnFailure(hr, "Failed to get window AutoResize attribute.");
1766 +
1767 + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast<DWORD*>(&pTheme->nWidth));
1768 + if (S_FALSE == hr)
1769 + {
1770 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1771 + ExitOnRootFailure(hr, "Failed to find window Width attribute.");
1772 + }
1773 + ExitOnFailure(hr, "Failed to get window Width attribute.");
1774 +
1775 + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&pTheme->nHeight));
1776 + if (S_FALSE == hr)
1777 + {
1778 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1779 + ExitOnRootFailure(hr, "Failed to find window Height attribute.");
1780 + }
1781 + ExitOnFailure(hr, "Failed to get window Height attribute.");
1782 +
1783 + hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", reinterpret_cast<DWORD*>(&pTheme->nMinimumWidth));
1784 + if (S_FALSE == hr)
1785 + {
1786 + hr = S_OK;
1787 + }
1788 + ExitOnFailure(hr, "Failed to get window MinimumWidth attribute.");
1789 +
1790 + hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", reinterpret_cast<DWORD*>(&pTheme->nMinimumHeight));
1791 + if (S_FALSE == hr)
1792 + {
1793 + hr = S_OK;
1794 + }
1795 + ExitOnFailure(hr, "Failed to get window MinimumHeight attribute.");
1796 +
1797 + hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId);
1798 + if (S_FALSE == hr)
1799 + {
1800 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1801 + ExitOnRootFailure(hr, "Failed to find window FontId attribute.");
1802 + }
1803 + ExitOnFailure(hr, "Failed to get window FontId attribute.");
1804 +
1805 + // Get the optional window icon from a resource.
1806 + hr = XmlGetAttribute(pixn, L"IconResource", &bstr);
1807 + ExitOnFailure(hr, "Failed to get window IconResource attribute.");
1808 +
1809 + if (S_OK == hr)
1810 + {
1811 + pTheme->hIcon = ::LoadIconW(hModule, bstr);
1812 + ExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource.");
1813 +
1814 + ReleaseNullBSTR(bstr);
1815 + }
1816 +
1817 + // Get the optional window icon from a file.
1818 + hr = XmlGetAttribute(pixn, L"IconFile", &bstr);
1819 + ExitOnFailure(hr, "Failed to get window IconFile attribute.");
1820 +
1821 + if (S_OK == hr)
1822 + {
1823 + if (wzRelativePath)
1824 + {
1825 + hr = PathConcat(wzRelativePath, bstr, &sczIconFile);
1826 + ExitOnFailure(hr, "Failed to combine icon file path.");
1827 + }
1828 + else
1829 + {
1830 + hr = PathRelativeToModule(&sczIconFile, bstr, hModule);
1831 + ExitOnFailure(hr, "Failed to get icon filename.");
1832 + }
1833 +
1834 + pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
1835 + ExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr);
1836 +
1837 + ReleaseNullBSTR(bstr);
1838 + }
1839 +
1840 + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast<DWORD*>(&pTheme->nSourceX));
1841 + if (S_FALSE == hr)
1842 + {
1843 + pTheme->nSourceX = -1;
1844 + }
1845 + ExitOnFailure(hr, "Failed to get window SourceX attribute.");
1846 +
1847 + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast<DWORD*>(&pTheme->nSourceY));
1848 + if (S_FALSE == hr)
1849 + {
1850 + pTheme->nSourceY = -1;
1851 + }
1852 + ExitOnFailure(hr, "Failed to get window SourceY attribute.");
1853 +
1854 + // Parse the optional window style.
1855 + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle);
1856 + ExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute.");
1857 +
1858 + if (S_FALSE == hr)
1859 + {
1860 + pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU;
1861 + pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED;
1862 + }
1863 +
1864 + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast<DWORD*>(&pTheme->uStringId));
1865 + ExitOnFailure(hr, "Failed to get window StringId attribute.");
1866 +
1867 + if (S_FALSE == hr)
1868 + {
1869 + pTheme->uStringId = UINT_MAX;
1870 +
1871 + hr = XmlGetAttribute(pixn, L"Caption", &bstr);
1872 + ExitOnFailure(hr, "Failed to get window Caption attribute.");
1873 +
1874 + if (S_FALSE == hr)
1875 + {
1876 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1877 + ExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute.");
1878 + }
1879 +
1880 + hr = StrAllocString(&pTheme->sczCaption, bstr, 0);
1881 + ExitOnFailure(hr, "Failed to copy window Caption attribute.");
1882 + }
1883 +
1884 + // Parse any image lists.
1885 + hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme);
1886 + ExitOnFailure(hr, "Failed to parse image lists.");
1887 +
1888 + // Parse the pages.
1889 + hr = ParsePages(hModule, wzRelativePath, pixn, pTheme);
1890 + ExitOnFailure(hr, "Failed to parse theme pages.");
1891 +
1892 + // Parse the non-paged controls.
1893 + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL);
1894 + ExitOnFailure(hr, "Failed to parse theme controls.");
1895 +
1896 +LExit:
1897 + ReleaseStr(sczIconFile);
1898 + ReleaseBSTR(bstr);
1899 + ReleaseObject(pixn);
1900 +
1901 + return hr;
1902 +}
1903 +
1904 +
1905 +static HRESULT ParseFonts(
1906 + __in IXMLDOMElement* pElement,
1907 + __in THEME* pTheme
1908 + )
1909 +{
1910 + HRESULT hr = S_OK;
1911 + IXMLDOMNodeList* pixnl = NULL;
1912 + IXMLDOMNode* pixn = NULL;
1913 + BSTR bstrName = NULL;
1914 + DWORD dwId = 0;
1915 + LOGFONTW lf = { };
1916 + COLORREF crForeground = THEME_INVISIBLE_COLORREF;
1917 + COLORREF crBackground = THEME_INVISIBLE_COLORREF;
1918 + DWORD dwSystemForegroundColor = FALSE;
1919 + DWORD dwSystemBackgroundColor = FALSE;
1920 +
1921 + hr = XmlSelectNodes(pElement, L"Font", &pixnl);
1922 + ExitOnFailure(hr, "Failed to find font elements.");
1923 +
1924 + hr = pixnl->get_length(reinterpret_cast<long*>(&pTheme->cFonts));
1925 + ExitOnFailure(hr, "Failed to count the number of theme fonts.");
1926 +
1927 + if (!pTheme->cFonts)
1928 + {
1929 + ExitFunction1(hr = S_OK);
1930 + }
1931 +
1932 + pTheme->rgFonts = static_cast<THEME_FONT*>(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE));
1933 + ExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts.");
1934 +
1935 + lf.lfQuality = CLEARTYPE_QUALITY;
1936 +
1937 + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
1938 + {
1939 + hr = XmlGetAttributeNumber(pixn, L"Id", &dwId);
1940 + if (S_FALSE == hr)
1941 + {
1942 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1943 + }
1944 + ExitOnFailure(hr, "Failed to find font id.");
1945 +
1946 + if (pTheme->cFonts <= dwId)
1947 + {
1948 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1949 + ExitOnRootFailure(hr, "Invalid theme font id.");
1950 + }
1951 +
1952 + hr = XmlGetText(pixn, &bstrName);
1953 + if (S_FALSE == hr)
1954 + {
1955 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1956 + }
1957 + ExitOnFailure(hr, "Failed to get font name.");
1958 +
1959 + hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), bstrName);
1960 + ExitOnFailure(hr, "Failed to copy font name.");
1961 +
1962 + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&lf.lfHeight));
1963 + if (S_FALSE == hr)
1964 + {
1965 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1966 + }
1967 + ExitOnFailure(hr, "Failed to find font height attribute.");
1968 +
1969 + hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast<DWORD*>(&lf.lfWeight));
1970 + if (S_FALSE == hr)
1971 + {
1972 + lf.lfWeight = FW_DONTCARE;
1973 + hr = S_OK;
1974 + }
1975 + ExitOnFailure(hr, "Failed to find font weight attribute.");
1976 +
1977 + hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast<BOOL*>(&lf.lfUnderline));
1978 + if (E_NOTFOUND == hr)
1979 + {
1980 + lf.lfUnderline = FALSE;
1981 + hr = S_OK;
1982 + }
1983 + ExitOnFailure(hr, "Failed to find font underline attribute.");
1984 +
1985 + hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor);
1986 + ExitOnFailure(hr, "Failed to find font foreground color.");
1987 +
1988 + hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor);
1989 + ExitOnFailure(hr, "Failed to find font background color.");
1990 +
1991 + THEME_FONT* pFont = pTheme->rgFonts + dwId;
1992 + if (pFont->hFont)
1993 + {
1994 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1995 + ExitOnRootFailure(hr, "Theme font id duplicated.");
1996 + }
1997 +
1998 + pFont->hFont = ::CreateFontIndirectW(&lf);
1999 + ExitOnNullWithLastError(pFont->hFont, hr, "Failed to create font %u.", dwId);
2000 +
2001 + pFont->crForeground = crForeground;
2002 + if (THEME_INVISIBLE_COLORREF != pFont->crForeground)
2003 + {
2004 + pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground);
2005 + ExitOnNullWithLastError(pFont->hForeground, hr, "Failed to create text foreground brush.");
2006 + }
2007 +
2008 + pFont->crBackground = crBackground;
2009 + if (THEME_INVISIBLE_COLORREF != pFont->crBackground)
2010 + {
2011 + pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground);
2012 + ExitOnNullWithLastError(pFont->hBackground, hr, "Failed to create text background brush.");
2013 + }
2014 +
2015 + ReleaseNullBSTR(bstrName);
2016 + ReleaseNullObject(pixn);
2017 + }
2018 + ExitOnFailure(hr, "Failed to enumerate all fonts.");
2019 +
2020 + if (S_FALSE == hr)
2021 + {
2022 + hr = S_OK;
2023 + }
2024 +
2025 +LExit:
2026 + ReleaseBSTR(bstrName);
2027 + ReleaseObject(pixn);
2028 + ReleaseObject(pixnl);
2029 +
2030 + return hr;
2031 +}
2032 +
2033 +
2034 +static HRESULT GetFontColor(
2035 + __in IXMLDOMNode* pixn,
2036 + __in_z LPCWSTR wzAttributeName,
2037 + __out COLORREF* pColorRef,
2038 + __out DWORD* pdwSystemColor
2039 + )
2040 +{
2041 + HRESULT hr = S_OK;
2042 + BSTR bstr = NULL;
2043 +
2044 + *pdwSystemColor = 0;
2045 +
2046 + hr = XmlGetAttribute(pixn, wzAttributeName, &bstr);
2047 + if (S_FALSE == hr)
2048 + {
2049 + *pColorRef = THEME_INVISIBLE_COLORREF;
2050 + ExitFunction1(hr = S_OK);
2051 + }
2052 + ExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName);
2053 +
2054 + if (pdwSystemColor)
2055 + {
2056 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1))
2057 + {
2058 + *pdwSystemColor = COLOR_BTNFACE;
2059 + }
2060 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1))
2061 + {
2062 + *pdwSystemColor = COLOR_BTNTEXT;
2063 + }
2064 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1))
2065 + {
2066 + *pdwSystemColor = COLOR_GRAYTEXT;
2067 + }
2068 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1))
2069 + {
2070 + *pdwSystemColor = COLOR_HIGHLIGHT;
2071 + }
2072 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1))
2073 + {
2074 + *pdwSystemColor = COLOR_HIGHLIGHTTEXT;
2075 + }
2076 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1))
2077 + {
2078 + *pdwSystemColor = COLOR_HOTLIGHT;
2079 + }
2080 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1))
2081 + {
2082 + *pdwSystemColor = COLOR_WINDOW;
2083 + }
2084 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1))
2085 + {
2086 + *pdwSystemColor = COLOR_WINDOWTEXT;
2087 + }
2088 + else
2089 + {
2090 + *pColorRef = wcstoul(bstr, NULL, 16);
2091 + }
2092 +
2093 + if (*pdwSystemColor)
2094 + {
2095 + *pColorRef = ::GetSysColor(*pdwSystemColor);
2096 + }
2097 + }
2098 +
2099 +LExit:
2100 + ReleaseBSTR(bstr);
2101 +
2102 + return hr;
2103 +}
2104 +
2105 +static HRESULT ParsePages(
2106 + __in_opt HMODULE hModule,
2107 + __in_opt LPCWSTR wzRelativePath,
2108 + __in IXMLDOMNode* pElement,
2109 + __in THEME* pTheme
2110 + )
2111 +{
2112 + HRESULT hr = S_OK;
2113 + IXMLDOMNodeList* pixnl = NULL;
2114 + IXMLDOMNode* pixn = NULL;
2115 + BSTR bstrType = NULL;
2116 + THEME_PAGE* pPage = NULL;
2117 + DWORD iPage = 0;
2118 +
2119 + hr = XmlSelectNodes(pElement, L"Page", &pixnl);
2120 + ExitOnFailure(hr, "Failed to find page elements.");
2121 +
2122 + hr = pixnl->get_length(reinterpret_cast<long*>(&pTheme->cPages));
2123 + ExitOnFailure(hr, "Failed to count the number of theme pages.");
2124 +
2125 + if (!pTheme->cPages)
2126 + {
2127 + ExitFunction1(hr = S_OK);
2128 + }
2129 +
2130 + pTheme->rgPages = static_cast<THEME_PAGE*>(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE));
2131 + ExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages.");
2132 +
2133 + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType)))
2134 + {
2135 + pPage = pTheme->rgPages + iPage;
2136 +
2137 + pPage->wId = static_cast<WORD>(iPage + 1);
2138 +
2139 + hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName);
2140 + if (E_NOTFOUND == hr)
2141 + {
2142 + hr = S_OK;
2143 + }
2144 + ExitOnFailure(hr, "Failed when querying page Name.");
2145 +
2146 + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage);
2147 + ExitOnFailure(hr, "Failed to parse page controls.");
2148 +
2149 + ++iPage;
2150 +
2151 + ReleaseNullBSTR(bstrType);
2152 + ReleaseNullObject(pixn);
2153 + }
2154 + ExitOnFailure(hr, "Failed to enumerate all pages.");
2155 +
2156 + if (S_FALSE == hr)
2157 + {
2158 + hr = S_OK;
2159 + }
2160 +
2161 +LExit:
2162 + ReleaseBSTR(bstrType);
2163 + ReleaseObject(pixn);
2164 + ReleaseObject(pixnl);
2165 +
2166 + return hr;
2167 +}
2168 +
2169 +
2170 +static HRESULT ParseImageLists(
2171 + __in_opt HMODULE hModule,
2172 + __in_opt LPCWSTR wzRelativePath,
2173 + __in IXMLDOMNode* pElement,
2174 + __in THEME* pTheme
2175 + )
2176 +{
2177 + HRESULT hr = S_OK;
2178 + IXMLDOMNodeList* pixnlImageLists = NULL;
2179 + IXMLDOMNode* pixnImageList = NULL;
2180 + IXMLDOMNodeList* pixnlImages = NULL;
2181 + IXMLDOMNode* pixnImage = NULL;
2182 + DWORD dwImageListIndex = 0;
2183 + DWORD dwImageCount = 0;
2184 + HBITMAP hBitmap = NULL;
2185 + BITMAP bm = { };
2186 + BSTR bstr = NULL;
2187 + DWORD i = 0;
2188 + int iRetVal = 0;
2189 +
2190 + hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists);
2191 + ExitOnFailure(hr, "Failed to find ImageList elements.");
2192 +
2193 + hr = pixnlImageLists->get_length(reinterpret_cast<long*>(&pTheme->cImageLists));
2194 + ExitOnFailure(hr, "Failed to count the number of image lists.");
2195 +
2196 + if (!pTheme->cImageLists)
2197 + {
2198 + ExitFunction1(hr = S_OK);
2199 + }
2200 +
2201 + pTheme->rgImageLists = static_cast<THEME_IMAGELIST*>(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE));
2202 + ExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists.");
2203 +
2204 + while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL)))
2205 + {
2206 + hr = XmlGetAttribute(pixnImageList, L"Name", &bstr);
2207 + if (S_FALSE == hr)
2208 + {
2209 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2210 + }
2211 + ExitOnFailure(hr, "Failed to find ImageList/@Name attribute.");
2212 +
2213 + hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0);
2214 + ExitOnFailure(hr, "Failed to make copy of ImageList name.");
2215 +
2216 + hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages);
2217 + ExitOnFailure(hr, "Failed to select child Image nodes.");
2218 +
2219 + hr = pixnlImages->get_length(reinterpret_cast<long*>(&dwImageCount));
2220 + ExitOnFailure(hr, "Failed to count the number of images in list.");
2221 +
2222 + if (0 < dwImageCount)
2223 + {
2224 + i = 0;
2225 + while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL)))
2226 + {
2227 + if (hBitmap)
2228 + {
2229 + ::DeleteObject(hBitmap);
2230 + hBitmap = NULL;
2231 + }
2232 + hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap);
2233 + ExitOnFailure(hr, "Failed to parse image: %u", i);
2234 +
2235 + if (0 == i)
2236 + {
2237 + ::GetObjectW(hBitmap, sizeof(BITMAP), &bm);
2238 +
2239 + pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0);
2240 + ExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list.");
2241 + }
2242 +
2243 + iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL);
2244 + if (-1 == iRetVal)
2245 + {
2246 + ExitWithLastError(hr, "Failed to add image %u to image list.", i);
2247 + }
2248 +
2249 + ++i;
2250 + }
2251 + }
2252 + ++dwImageListIndex;
2253 + }
2254 +
2255 +LExit:
2256 + if (hBitmap)
2257 + {
2258 + ::DeleteObject(hBitmap);
2259 + }
2260 + ReleaseBSTR(bstr);
2261 + ReleaseObject(pixnlImageLists);
2262 + ReleaseObject(pixnImageList);
2263 + ReleaseObject(pixnlImages);
2264 + ReleaseObject(pixnImage);
2265 +
2266 + return hr;
2267 +}
2268 +
2269 +static void GetControls(
2270 + __in THEME* pTheme,
2271 + __in_opt THEME_CONTROL* pParentControl,
2272 + __out DWORD** ppcControls,
2273 + __out THEME_CONTROL*** pprgControls
2274 + )
2275 +{
2276 + if (pParentControl)
2277 + {
2278 + *ppcControls = &pParentControl->cControls;
2279 + *pprgControls = &pParentControl->rgControls;
2280 + }
2281 + else
2282 + {
2283 + *ppcControls = &pTheme->cControls;
2284 + *pprgControls = &pTheme->rgControls;
2285 + }
2286 +}
2287 +
2288 +static void GetControls(
2289 + __in const THEME* pTheme,
2290 + __in_opt const THEME_CONTROL* pParentControl,
2291 + __out DWORD& cControls,
2292 + __out THEME_CONTROL*& rgControls
2293 + )
2294 +{
2295 + if (pParentControl)
2296 + {
2297 + cControls = pParentControl->cControls;
2298 + rgControls = pParentControl->rgControls;
2299 + }
2300 + else
2301 + {
2302 + cControls = pTheme->cControls;
2303 + rgControls = pTheme->rgControls;
2304 + }
2305 +}
2306 +
2307 +static HRESULT ParseControls(
2308 + __in_opt HMODULE hModule,
2309 + __in_opt LPCWSTR wzRelativePath,
2310 + __in IXMLDOMNode* pElement,
2311 + __in THEME* pTheme,
2312 + __in_opt THEME_CONTROL* pParentControl,
2313 + __in_opt THEME_PAGE* pPage
2314 + )
2315 +{
2316 + HRESULT hr = S_OK;
2317 + IXMLDOMNodeList* pixnl = NULL;
2318 + IXMLDOMNode* pixn = NULL;
2319 + BSTR bstrType = NULL;
2320 + DWORD cNewControls = 0;
2321 + DWORD iControl = 0;
2322 + DWORD iPageControl = 0;
2323 + DWORD* pcControls = NULL;
2324 + THEME_CONTROL** prgControls = NULL;
2325 +
2326 + GetControls(pTheme, pParentControl, &pcControls, &prgControls);
2327 +
2328 + hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage);
2329 + ExitOnFailure(hr, "Failed to parse radio buttons.");
2330 +
2331 + hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl);
2332 + ExitOnFailure(hr, "Failed to find control elements.");
2333 +
2334 + hr = pixnl->get_length(reinterpret_cast<long*>(&cNewControls));
2335 + ExitOnFailure(hr, "Failed to count the number of theme controls.");
2336 +
2337 + if (!cNewControls)
2338 + {
2339 + ExitFunction1(hr = S_OK);
2340 + }
2341 +
2342 + hr = MemReAllocArray(reinterpret_cast<LPVOID*>(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls);
2343 + ExitOnFailure(hr, "Failed to reallocate theme controls.");
2344 +
2345 + cNewControls += *pcControls;
2346 +
2347 + if (pPage)
2348 + {
2349 + iPageControl = pPage->cControlIndices;
2350 + pPage->cControlIndices += cNewControls;
2351 + }
2352 +
2353 + iControl = *pcControls;
2354 + *pcControls = cNewControls;
2355 +
2356 + while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType)))
2357 + {
2358 + THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN;
2359 +
2360 + if (!bstrType)
2361 + {
2362 + hr = E_UNEXPECTED;
2363 + ExitOnFailure(hr, "Null element encountered!");
2364 + }
2365 +
2366 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1))
2367 + {
2368 + type = THEME_CONTROL_TYPE_BILLBOARD;
2369 + }
2370 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1))
2371 + {
2372 + type = THEME_CONTROL_TYPE_BUTTON;
2373 + }
2374 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1))
2375 + {
2376 + type = THEME_CONTROL_TYPE_CHECKBOX;
2377 + }
2378 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1))
2379 + {
2380 + type = THEME_CONTROL_TYPE_COMBOBOX;
2381 + }
2382 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1))
2383 + {
2384 + type = THEME_CONTROL_TYPE_COMMANDLINK;
2385 + }
2386 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1))
2387 + {
2388 + type = THEME_CONTROL_TYPE_EDITBOX;
2389 + }
2390 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1))
2391 + {
2392 + type = THEME_CONTROL_TYPE_HYPERLINK;
2393 + }
2394 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1))
2395 + {
2396 + type = THEME_CONTROL_TYPE_HYPERTEXT;
2397 + }
2398 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1))
2399 + {
2400 + type = THEME_CONTROL_TYPE_IMAGE;
2401 + }
2402 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1))
2403 + {
2404 + type = THEME_CONTROL_TYPE_LABEL;
2405 + }
2406 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1))
2407 + {
2408 + type = THEME_CONTROL_TYPE_LISTVIEW;
2409 + }
2410 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1))
2411 + {
2412 + type = THEME_CONTROL_TYPE_PANEL;
2413 + }
2414 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1))
2415 + {
2416 + type = THEME_CONTROL_TYPE_PROGRESSBAR;
2417 + }
2418 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1))
2419 + {
2420 + type = THEME_CONTROL_TYPE_RICHEDIT;
2421 + }
2422 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1))
2423 + {
2424 + type = THEME_CONTROL_TYPE_STATIC;
2425 + }
2426 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1))
2427 + {
2428 + type = THEME_CONTROL_TYPE_TAB;
2429 + }
2430 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1))
2431 + {
2432 + type = THEME_CONTROL_TYPE_TREEVIEW;
2433 + }
2434 +
2435 + if (THEME_CONTROL_TYPE_UNKNOWN != type)
2436 + {
2437 + THEME_CONTROL* pControl = *prgControls + iControl;
2438 + pControl->type = type;
2439 +
2440 + // billboard children are always the size of the billboard
2441 + BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type;
2442 +
2443 + hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage);
2444 + ExitOnFailure(hr, "Failed to parse control.");
2445 +
2446 + if (fBillboardSizing)
2447 + {
2448 + pControl->nX = 0;
2449 + pControl->nY = 0;
2450 + pControl->nWidth = -0;
2451 + pControl->nHeight = 0;
2452 + }
2453 +
2454 + if (pPage)
2455 + {
2456 + pControl->wPageId = pPage->wId;
2457 + ++iPageControl;
2458 + }
2459 +
2460 + ++iControl;
2461 + }
2462 +
2463 + ReleaseNullBSTR(bstrType);
2464 + ReleaseNullObject(pixn);
2465 + }
2466 + ExitOnFailure(hr, "Failed to enumerate all controls.");
2467 +
2468 + if (S_FALSE == hr)
2469 + {
2470 + hr = S_OK;
2471 + }
2472 +
2473 + AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls.");
2474 +
2475 +LExit:
2476 + ReleaseBSTR(bstrType);
2477 + ReleaseObject(pixn);
2478 + ReleaseObject(pixnl);
2479 +
2480 + return hr;
2481 +}
2482 +
2483 +
2484 +static HRESULT ParseControl(
2485 + __in_opt HMODULE hModule,
2486 + __in_opt LPCWSTR wzRelativePath,
2487 + __in IXMLDOMNode* pixn,
2488 + __in THEME* pTheme,
2489 + __in THEME_CONTROL* pControl,
2490 + __in BOOL fSkipDimensions,
2491 + __in_opt THEME_PAGE* pPage
2492 + )
2493 +{
2494 + HRESULT hr = S_OK;
2495 + DWORD dwValue = 0;
2496 + BOOL fValue = FALSE;
2497 + BSTR bstrText = NULL;
2498 + BOOL fAnyTextChildren = FALSE;
2499 + BOOL fAnyNoteChildren = FALSE;
2500 +
2501 + hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName);
2502 + if (E_NOTFOUND == hr)
2503 + {
2504 + hr = S_OK;
2505 + }
2506 + ExitOnFailure(hr, "Failed when querying control Name attribute.");
2507 +
2508 + hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition);
2509 + if (E_NOTFOUND == hr)
2510 + {
2511 + hr = S_OK;
2512 + }
2513 + ExitOnFailure(hr, "Failed when querying control EnableCondition attribute.");
2514 +
2515 + hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition);
2516 + if (E_NOTFOUND == hr)
2517 + {
2518 + hr = S_OK;
2519 + }
2520 + ExitOnFailure(hr, "Failed when querying control VisibleCondition attribute.");
2521 +
2522 + if (!fSkipDimensions)
2523 + {
2524 + hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast<DWORD*>(&pControl->nX));
2525 + if (S_FALSE == hr)
2526 + {
2527 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2528 + }
2529 + ExitOnFailure(hr, "Failed to find control X attribute.");
2530 +
2531 + hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast<DWORD*>(&pControl->nY));
2532 + if (S_FALSE == hr)
2533 + {
2534 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2535 + }
2536 + ExitOnFailure(hr, "Failed to find control Y attribute.");
2537 +
2538 + hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&pControl->nHeight));
2539 + if (S_FALSE == hr)
2540 + {
2541 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2542 + }
2543 + ExitOnFailure(hr, "Failed to find control Height attribute.");
2544 +
2545 + hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast<DWORD*>(&pControl->nWidth));
2546 + if (S_FALSE == hr)
2547 + {
2548 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2549 + }
2550 + ExitOnFailure(hr, "Failed to find control Width attribute.");
2551 + }
2552 +
2553 + // Parse the optional background resource image.
2554 + hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage);
2555 + ExitOnFailure(hr, "Failed while parsing control image.");
2556 +
2557 + hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast<DWORD*>(&pControl->nSourceX));
2558 + if (S_FALSE == hr)
2559 + {
2560 + pControl->nSourceX = -1;
2561 + }
2562 + ExitOnFailure(hr, "Failed when querying control SourceX attribute.");
2563 +
2564 + hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast<DWORD*>(&pControl->nSourceY));
2565 + if (S_FALSE == hr)
2566 + {
2567 + pControl->nSourceY = -1;
2568 + }
2569 + ExitOnFailure(hr, "Failed when querying control SourceY attribute.");
2570 +
2571 + hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId);
2572 + if (S_FALSE == hr)
2573 + {
2574 + pControl->dwFontId = THEME_INVALID_ID;
2575 + }
2576 + ExitOnFailure(hr, "Failed when querying control FontId attribute.");
2577 +
2578 + // Parse the optional window style.
2579 + hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle);
2580 + ExitOnFailure(hr, "Failed when querying control HexStyle attribute.");
2581 +
2582 + // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above.
2583 + hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue);
2584 + if (E_NOTFOUND == hr)
2585 + {
2586 + hr = S_OK;
2587 + }
2588 + else
2589 + {
2590 + ExitOnFailure(hr, "Failed when querying control TabStop attribute.");
2591 +
2592 + if (fValue)
2593 + {
2594 + pControl->dwStyle |= WS_TABSTOP;
2595 + }
2596 + }
2597 +
2598 + hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue);
2599 + if (E_NOTFOUND == hr)
2600 + {
2601 + hr = S_OK;
2602 + }
2603 + else
2604 + {
2605 + ExitOnFailure(hr, "Failed when querying control Visible attribute.");
2606 +
2607 + if (fValue)
2608 + {
2609 + pControl->dwStyle |= WS_VISIBLE;
2610 + }
2611 + }
2612 +
2613 + hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue);
2614 + if (E_NOTFOUND == hr)
2615 + {
2616 + hr = S_OK;
2617 + }
2618 + else
2619 + {
2620 + ExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute.");
2621 +
2622 + if (fValue)
2623 + {
2624 + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED;
2625 + }
2626 + }
2627 +
2628 + hr = XmlGetYesNoAttribute(pixn, L"DisableAutomaticBehavior", &pControl->fDisableVariableFunctionality);
2629 + if (E_NOTFOUND == hr)
2630 + {
2631 + hr = S_OK;
2632 + }
2633 + else
2634 + {
2635 + ExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute.");
2636 + }
2637 +
2638 + hr = ParseActions(pixn, pControl);
2639 + ExitOnFailure(hr, "Failed to parse action nodes of the control.");
2640 +
2641 + hr = ParseText(pixn, pControl, &fAnyTextChildren);
2642 + ExitOnFailure(hr, "Failed to parse text nodes of the control.");
2643 +
2644 + hr = ParseTooltips(pixn, pControl, &fAnyTextChildren);
2645 + ExitOnFailure(hr, "Failed to parse control Tooltip.");
2646 +
2647 + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
2648 + {
2649 + hr = ParseNotes(pixn, pControl, &fAnyNoteChildren);
2650 + ExitOnFailure(hr, "Failed to parse note text nodes of the control.");
2651 + }
2652 +
2653 + if (fAnyTextChildren || fAnyNoteChildren)
2654 + {
2655 + pControl->uStringId = UINT_MAX;
2656 + }
2657 + else
2658 + {
2659 + hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast<DWORD*>(&pControl->uStringId));
2660 + ExitOnFailure(hr, "Failed when querying control StringId attribute.");
2661 +
2662 + if (S_FALSE == hr)
2663 + {
2664 + pControl->uStringId = UINT_MAX;
2665 +
2666 + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type || THEME_CONTROL_TYPE_PANEL == pControl->type)
2667 + {
2668 + // Billboards and panels have child elements and we don't want to pick up child element text in the parents.
2669 + hr = S_OK;
2670 + }
2671 + else
2672 + {
2673 + hr = XmlGetText(pixn, &bstrText);
2674 + ExitOnFailure(hr, "Failed to get control inner text.");
2675 +
2676 + if (S_OK == hr)
2677 + {
2678 + hr = StrAllocString(&pControl->sczText, bstrText, 0);
2679 + ExitOnFailure(hr, "Failed to copy control text.");
2680 +
2681 + ReleaseNullBSTR(bstrText);
2682 + }
2683 + else if (S_FALSE == hr)
2684 + {
2685 + hr = S_OK;
2686 + }
2687 + }
2688 + }
2689 + }
2690 +
2691 + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
2692 + {
2693 + hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops);
2694 + if (E_NOTFOUND == hr)
2695 + {
2696 + hr = S_OK;
2697 + }
2698 + ExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute.");
2699 +
2700 + pControl->wBillboardInterval = 5000;
2701 + hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue);
2702 + if (S_OK == hr && dwValue)
2703 + {
2704 + pControl->wBillboardInterval = static_cast<WORD>(dwValue & 0xFFFF);
2705 + }
2706 + ExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute.");
2707 +
2708 + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage);
2709 + ExitOnFailure(hr, "Failed to parse billboard children.");
2710 + }
2711 + else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
2712 + {
2713 + hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon);
2714 + ExitOnFailure(hr, "Failed while parsing control icon.");
2715 + }
2716 + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type)
2717 + {
2718 + hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue);
2719 + if (E_NOTFOUND == hr)
2720 + {
2721 + hr = S_OK;
2722 + }
2723 + else
2724 + {
2725 + ExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute.");
2726 +
2727 + if (fValue)
2728 + {
2729 + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE;
2730 + }
2731 + }
2732 + }
2733 + else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type)
2734 + {
2735 + hr = XmlGetAttributeNumber(pixn, L"HoverFontId", &pControl->dwFontHoverId);
2736 + if (S_FALSE == hr)
2737 + {
2738 + pControl->dwFontHoverId = THEME_INVALID_ID;
2739 + }
2740 + ExitOnFailure(hr, "Failed when querying control HoverFontId attribute.");
2741 +
2742 + hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId);
2743 + if (S_FALSE == hr)
2744 + {
2745 + pControl->dwFontSelectedId = THEME_INVALID_ID;
2746 + }
2747 + ExitOnFailure(hr, "Failed when querying control SelectedFontId attribute.");
2748 + }
2749 + else if (THEME_CONTROL_TYPE_LABEL == pControl->type)
2750 + {
2751 + hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue);
2752 + if (E_NOTFOUND == hr)
2753 + {
2754 + hr = S_OK;
2755 + }
2756 + else if (fValue)
2757 + {
2758 + pControl->dwStyle |= SS_CENTER;
2759 + }
2760 + ExitOnFailure(hr, "Failed when querying Label/@Center attribute.");
2761 +
2762 + hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue);
2763 + if (E_NOTFOUND == hr)
2764 + {
2765 + hr = S_OK;
2766 + }
2767 + else if (fValue)
2768 + {
2769 + pControl->dwStyle |= SS_NOPREFIX;
2770 + }
2771 + ExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute.");
2772 + }
2773 + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
2774 + {
2775 + // Parse the optional extended window style.
2776 + hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle);
2777 + ExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute.");
2778 +
2779 + hr = XmlGetAttribute(pixn, L"ImageList", &bstrText);
2780 + if (S_FALSE != hr)
2781 + {
2782 + ExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute.");
2783 +
2784 + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]);
2785 + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText);
2786 + }
2787 +
2788 + hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText);
2789 + if (S_FALSE != hr)
2790 + {
2791 + ExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute.");
2792 +
2793 + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]);
2794 + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText);
2795 + }
2796 +
2797 + hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText);
2798 + if (S_FALSE != hr)
2799 + {
2800 + ExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute.");
2801 +
2802 + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]);
2803 + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText);
2804 + }
2805 +
2806 + hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText);
2807 + if (S_FALSE != hr)
2808 + {
2809 + ExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute.");
2810 +
2811 + hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]);
2812 + ExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText);
2813 + }
2814 +
2815 + hr = ParseColumns(pixn, pControl);
2816 + ExitOnFailure(hr, "Failed to parse columns.");
2817 + }
2818 + else if (THEME_CONTROL_TYPE_PANEL == pControl->type)
2819 + {
2820 + hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage);
2821 + ExitOnFailure(hr, "Failed to parse panel children.");
2822 + }
2823 + else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type)
2824 + {
2825 + hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue);
2826 + if (E_NOTFOUND == hr)
2827 + {
2828 + hr = S_OK;
2829 + }
2830 + ExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute.");
2831 + }
2832 + else if (THEME_CONTROL_TYPE_TAB == pControl->type)
2833 + {
2834 + hr = ParseTabs(pixn, pControl);
2835 + ExitOnFailure(hr, "Failed to parse tabs");
2836 + }
2837 + else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type)
2838 + {
2839 + pControl->dwStyle |= TVS_DISABLEDRAGDROP;
2840 +
2841 + hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue);
2842 + if (E_NOTFOUND == hr)
2843 + {
2844 + hr = S_OK;
2845 + }
2846 + else if (fValue)
2847 + {
2848 + pControl->dwStyle &= ~TVS_DISABLEDRAGDROP;
2849 + }
2850 + ExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute.");
2851 +
2852 + hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue);
2853 + if (E_NOTFOUND == hr)
2854 + {
2855 + hr = S_OK;
2856 + }
2857 + else if (fValue)
2858 + {
2859 + pControl->dwStyle |= TVS_FULLROWSELECT;
2860 + }
2861 + ExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute.");
2862 +
2863 + hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue);
2864 + if (E_NOTFOUND == hr)
2865 + {
2866 + hr = S_OK;
2867 + }
2868 + else if (fValue)
2869 + {
2870 + pControl->dwStyle |= TVS_HASBUTTONS;
2871 + }
2872 + ExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute.");
2873 +
2874 + hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue);
2875 + if (E_NOTFOUND == hr)
2876 + {
2877 + hr = S_OK;
2878 + }
2879 + else if (fValue)
2880 + {
2881 + pControl->dwStyle |= TVS_SHOWSELALWAYS;
2882 + }
2883 + ExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute.");
2884 +
2885 + hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue);
2886 + if (E_NOTFOUND == hr)
2887 + {
2888 + hr = S_OK;
2889 + }
2890 + else if (fValue)
2891 + {
2892 + pControl->dwStyle |= TVS_LINESATROOT;
2893 + }
2894 + ExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute.");
2895 +
2896 + hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue);
2897 + if (E_NOTFOUND == hr)
2898 + {
2899 + hr = S_OK;
2900 + }
2901 + else if (fValue)
2902 + {
2903 + pControl->dwStyle |= TVS_HASLINES;
2904 + }
2905 + ExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute.");
2906 + }
2907 +
2908 +LExit:
2909 + ReleaseBSTR(bstrText);
2910 +
2911 + return hr;
2912 +}
2913 +
2914 +
2915 +static HRESULT ParseActions(
2916 + __in IXMLDOMNode* pixn,
2917 + __in THEME_CONTROL* pControl
2918 + )
2919 +{
2920 + HRESULT hr = S_OK;
2921 + DWORD i = 0;
2922 + IXMLDOMNodeList* pixnl = NULL;
2923 + IXMLDOMNode* pixnChild = NULL;
2924 + BSTR bstrType = NULL;
2925 +
2926 + hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl);
2927 + ExitOnFailure(hr, "Failed to select child action nodes.");
2928 +
2929 + hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cActions));
2930 + ExitOnFailure(hr, "Failed to count the number of action nodes.");
2931 +
2932 + if (0 < pControl->cActions)
2933 + {
2934 + MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions);
2935 + ExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs.");
2936 +
2937 + i = 0;
2938 + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType)))
2939 + {
2940 + if (!bstrType)
2941 + {
2942 + hr = E_UNEXPECTED;
2943 + ExitOnFailure(hr, "Null element encountered!");
2944 + }
2945 +
2946 + THEME_ACTION* pAction = pControl->rgActions + i;
2947 +
2948 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1))
2949 + {
2950 + pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY;
2951 +
2952 + hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName);
2953 + ExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute.");
2954 + }
2955 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1))
2956 + {
2957 + pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE;
2958 +
2959 + hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName);
2960 + ExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute.");
2961 +
2962 + hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel);
2963 + if (E_NOTFOUND != hr)
2964 + {
2965 + ExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute.");
2966 + }
2967 + }
2968 + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1))
2969 + {
2970 + pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW;
2971 + }
2972 + else
2973 + {
2974 + hr = E_UNEXPECTED;
2975 + ExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType);
2976 + }
2977 +
2978 + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition);
2979 + if (E_NOTFOUND != hr)
2980 + {
2981 + ExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType);
2982 + }
2983 +
2984 + if (!pAction->sczCondition)
2985 + {
2986 + if (pControl->pDefaultAction)
2987 + {
2988 + hr = E_INVALIDDATA;
2989 + ExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName);
2990 + }
2991 +
2992 + pControl->pDefaultAction = pAction;
2993 + }
2994 +
2995 + ++i;
2996 + ReleaseNullBSTR(bstrType);
2997 + ReleaseObject(pixnChild);
2998 + }
2999 + }
3000 +
3001 +LExit:
3002 + ReleaseObject(pixnl);
3003 + ReleaseObject(pixnChild);
3004 + ReleaseBSTR(bstrType);
3005 +
3006 + return hr;
3007 +}
3008 +
3009 +
3010 +static HRESULT ParseColumns(
3011 + __in IXMLDOMNode* pixn,
3012 + __in THEME_CONTROL* pControl
3013 + )
3014 +{
3015 + HRESULT hr = S_OK;
3016 + DWORD i = 0;
3017 + IXMLDOMNodeList* pixnl = NULL;
3018 + IXMLDOMNode* pixnChild = NULL;
3019 + BSTR bstrText = NULL;
3020 +
3021 + hr = XmlSelectNodes(pixn, L"Column", &pixnl);
3022 + ExitOnFailure(hr, "Failed to select child column nodes.");
3023 +
3024 + hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cColumns));
3025 + ExitOnFailure(hr, "Failed to count the number of control columns.");
3026 +
3027 + if (0 < pControl->cColumns)
3028 + {
3029 + hr = MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns);
3030 + ExitOnFailure(hr, "Failed to allocate column structs.");
3031 +
3032 + i = 0;
3033 + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3034 + {
3035 + hr = XmlGetText(pixnChild, &bstrText);
3036 + ExitOnFailure(hr, "Failed to get inner text of column element.");
3037 +
3038 + hr = XmlGetAttributeNumber(pixnChild, L"Width", reinterpret_cast<DWORD*>(&pControl->ptcColumns[i].nBaseWidth));
3039 + if (S_FALSE == hr)
3040 + {
3041 + pControl->ptcColumns[i].nBaseWidth = 100;
3042 + }
3043 + ExitOnFailure(hr, "Failed to get column width attribute.");
3044 +
3045 + hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast<BOOL*>(&pControl->ptcColumns[i].fExpands));
3046 + if (E_NOTFOUND == hr)
3047 + {
3048 + hr = S_OK;
3049 + }
3050 + ExitOnFailure(hr, "Failed to get expands attribute.");
3051 +
3052 + hr = StrAllocString(&(pControl->ptcColumns[i].pszName), bstrText, 0);
3053 + ExitOnFailure(hr, "Failed to copy column name.");
3054 +
3055 + ++i;
3056 + ReleaseNullBSTR(bstrText);
3057 + }
3058 + }
3059 +
3060 +LExit:
3061 + ReleaseObject(pixnl);
3062 + ReleaseObject(pixnChild);
3063 + ReleaseBSTR(bstrText);
3064 +
3065 + return hr;
3066 +}
3067 +
3068 +
3069 +static HRESULT ParseRadioButtons(
3070 + __in_opt HMODULE hModule,
3071 + __in_opt LPCWSTR wzRelativePath,
3072 + __in IXMLDOMNode* pixn,
3073 + __in THEME* pTheme,
3074 + __in_opt THEME_CONTROL* pParentControl,
3075 + __in THEME_PAGE* pPage
3076 + )
3077 +{
3078 + HRESULT hr = S_OK;
3079 + DWORD cRadioButtons = 0;
3080 + DWORD iControl = 0;
3081 + DWORD iPageControl = 0;
3082 + IXMLDOMNodeList* pixnlRadioButtons = NULL;
3083 + IXMLDOMNodeList* pixnl = NULL;
3084 + IXMLDOMNode* pixnRadioButtons = NULL;
3085 + IXMLDOMNode* pixnChild = NULL;
3086 + LPWSTR sczName = NULL;
3087 + THEME_CONTROL* pControl = NULL;
3088 + BOOL fFirst = FALSE;
3089 + DWORD* pcControls = NULL;
3090 + THEME_CONTROL** prgControls = NULL;
3091 +
3092 + GetControls(pTheme, pParentControl, &pcControls, &prgControls);
3093 +
3094 + hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons);
3095 + ExitOnFailure(hr, "Failed to select RadioButtons nodes.");
3096 +
3097 + while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL)))
3098 + {
3099 + hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName);
3100 + if (E_NOTFOUND == hr)
3101 + {
3102 + hr = S_OK;
3103 + }
3104 + ExitOnFailure(hr, "Failed when querying RadioButtons Name.");
3105 +
3106 + hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl);
3107 + ExitOnFailure(hr, "Failed to select RadioButton nodes.");
3108 +
3109 + hr = pixnl->get_length(reinterpret_cast<long*>(&cRadioButtons));
3110 + ExitOnFailure(hr, "Failed to count the number of RadioButton nodes.");
3111 +
3112 + if (cRadioButtons)
3113 + {
3114 + if (pPage)
3115 + {
3116 + iPageControl = pPage->cControlIndices;
3117 + pPage->cControlIndices += cRadioButtons;
3118 + }
3119 +
3120 + hr = MemReAllocArray(reinterpret_cast<LPVOID*>(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons);
3121 + ExitOnFailure(hr, "Failed to reallocate theme controls.");
3122 +
3123 + iControl = *pcControls;
3124 + *pcControls += cRadioButtons;
3125 +
3126 + fFirst = TRUE;
3127 +
3128 + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3129 + {
3130 + pControl = *prgControls + iControl;
3131 + pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON;
3132 +
3133 + hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage);
3134 + ExitOnFailure(hr, "Failed to parse control.");
3135 +
3136 + if (fFirst)
3137 + {
3138 + pControl->dwStyle |= WS_GROUP;
3139 + fFirst = FALSE;
3140 + }
3141 +
3142 + hr = StrAllocString(&pControl->sczVariable, sczName, 0);
3143 + ExitOnFailure(hr, "Failed to copy radio button variable.");
3144 +
3145 + if (pPage)
3146 + {
3147 + pControl->wPageId = pPage->wId;
3148 + ++iPageControl;
3149 + }
3150 +
3151 + ++iControl;
3152 + }
3153 +
3154 + if (!fFirst)
3155 + {
3156 + pControl->fLastRadioButton = TRUE;
3157 + }
3158 + }
3159 + }
3160 +
3161 +LExit:
3162 + ReleaseStr(sczName);
3163 + ReleaseObject(pixnl);
3164 + ReleaseObject(pixnChild);
3165 + ReleaseObject(pixnlRadioButtons);
3166 + ReleaseObject(pixnRadioButtons);
3167 +
3168 + return hr;
3169 +}
3170 +
3171 +
3172 +static HRESULT ParseTabs(
3173 + __in IXMLDOMNode* pixn,
3174 + __in THEME_CONTROL* pControl
3175 + )
3176 +{
3177 + HRESULT hr = S_OK;
3178 + DWORD i = 0;
3179 + IXMLDOMNodeList* pixnl = NULL;
3180 + IXMLDOMNode* pixnChild = NULL;
3181 + BSTR bstrText = NULL;
3182 +
3183 + hr = XmlSelectNodes(pixn, L"Tab", &pixnl);
3184 + ExitOnFailure(hr, "Failed to select child tab nodes.");
3185 +
3186 + hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cTabs));
3187 + ExitOnFailure(hr, "Failed to count the number of tabs.");
3188 +
3189 + if (0 < pControl->cTabs)
3190 + {
3191 + hr = MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs);
3192 + ExitOnFailure(hr, "Failed to allocate tab structs.");
3193 +
3194 + i = 0;
3195 + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3196 + {
3197 + hr = XmlGetText(pixnChild, &bstrText);
3198 + ExitOnFailure(hr, "Failed to get inner text of tab element.");
3199 +
3200 + hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0);
3201 + ExitOnFailure(hr, "Failed to copy tab name.");
3202 +
3203 + ++i;
3204 + ReleaseNullBSTR(bstrText);
3205 + }
3206 + }
3207 +
3208 +LExit:
3209 + ReleaseObject(pixnl);
3210 + ReleaseObject(pixnChild);
3211 + ReleaseBSTR(bstrText);
3212 +
3213 + return hr;
3214 +}
3215 +
3216 +
3217 +static HRESULT ParseText(
3218 + __in IXMLDOMNode* pixn,
3219 + __in THEME_CONTROL* pControl,
3220 + __inout BOOL* pfAnyChildren
3221 + )
3222 +{
3223 + HRESULT hr = S_OK;
3224 + DWORD i = 0;
3225 + IXMLDOMNodeList* pixnl = NULL;
3226 + IXMLDOMNode* pixnChild = NULL;
3227 + BSTR bstrText = NULL;
3228 +
3229 + hr = XmlSelectNodes(pixn, L"Text", &pixnl);
3230 + ExitOnFailure(hr, "Failed to select child Text nodes.");
3231 +
3232 + hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cConditionalText));
3233 + ExitOnFailure(hr, "Failed to count the number of Text nodes.");
3234 +
3235 + *pfAnyChildren |= 0 < pControl->cConditionalText;
3236 +
3237 + if (0 < pControl->cConditionalText)
3238 + {
3239 + MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText);
3240 + ExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs.");
3241 +
3242 + i = 0;
3243 + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3244 + {
3245 + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i;
3246 +
3247 + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition);
3248 + if (E_NOTFOUND == hr)
3249 + {
3250 + hr = S_OK;
3251 + }
3252 + ExitOnFailure(hr, "Failed when querying Text/@Condition attribute.");
3253 +
3254 + hr = XmlGetText(pixnChild, &bstrText);
3255 + ExitOnFailure(hr, "Failed to get inner text of Text element.");
3256 +
3257 + if (S_OK == hr)
3258 + {
3259 + if (pConditionalText->sczCondition)
3260 + {
3261 + hr = StrAllocString(&pConditionalText->sczText, bstrText, 0);
3262 + ExitOnFailure(hr, "Failed to copy text to conditional text.");
3263 +
3264 + ++i;
3265 + }
3266 + else
3267 + {
3268 + if (pControl->sczText)
3269 + {
3270 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
3271 + ExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName);
3272 + }
3273 +
3274 + hr = StrAllocString(&pControl->sczText, bstrText, 0);
3275 + ExitOnFailure(hr, "Failed to copy text to control.");
3276 +
3277 + // Unconditional text entries aren't stored in the conditional text list.
3278 + --pControl->cConditionalText;
3279 + }
3280 + }
3281 +
3282 + ReleaseNullBSTR(bstrText);
3283 + }
3284 + }
3285 +
3286 +LExit:
3287 + ReleaseObject(pixnl);
3288 + ReleaseObject(pixnChild);
3289 + ReleaseBSTR(bstrText);
3290 +
3291 + return hr;
3292 +}
3293 +
3294 +
3295 +static HRESULT ParseTooltips(
3296 + __in IXMLDOMNode* pixn,
3297 + __in THEME_CONTROL* pControl,
3298 + __inout BOOL* pfAnyChildren
3299 +)
3300 +{
3301 + HRESULT hr = S_OK;
3302 + IXMLDOMNode* pixnChild = NULL;
3303 + BSTR bstrText = NULL;
3304 +
3305 + hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild);
3306 + ExitOnFailure(hr, "Failed to select child Tooltip node.");
3307 +
3308 + if (S_OK == hr)
3309 + {
3310 + *pfAnyChildren |= TRUE;
3311 +
3312 + hr = XmlGetText(pixnChild, &bstrText);
3313 + ExitOnFailure(hr, "Failed to get inner text of Tooltip element.");
3314 +
3315 + if (S_OK == hr)
3316 + {
3317 + hr = StrAllocString(&pControl->sczTooltip, bstrText, 0);
3318 + ExitOnFailure(hr, "Failed to copy tooltip text to control.");
3319 + }
3320 + }
3321 +
3322 +LExit:
3323 + ReleaseObject(pixnChild);
3324 + ReleaseBSTR(bstrText);
3325 +
3326 + return hr;
3327 +}
3328 +
3329 +
3330 +static HRESULT ParseNotes(
3331 + __in IXMLDOMNode* pixn,
3332 + __in THEME_CONTROL* pControl,
3333 + __out BOOL* pfAnyChildren
3334 + )
3335 +{
3336 + HRESULT hr = S_OK;
3337 + DWORD i = 0;
3338 + IXMLDOMNodeList* pixnl = NULL;
3339 + IXMLDOMNode* pixnChild = NULL;
3340 + BSTR bstrText = NULL;
3341 +
3342 + hr = XmlSelectNodes(pixn, L"Note", &pixnl);
3343 + ExitOnFailure(hr, "Failed to select child Note nodes.");
3344 +
3345 + hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cConditionalNotes));
3346 + ExitOnFailure(hr, "Failed to count the number of Note nodes.");
3347 +
3348 + if (pfAnyChildren)
3349 + {
3350 + *pfAnyChildren = 0 < pControl->cConditionalNotes;
3351 + }
3352 +
3353 + if (0 < pControl->cConditionalNotes)
3354 + {
3355 + MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes);
3356 + ExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs.");
3357 +
3358 + i = 0;
3359 + while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3360 + {
3361 + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + i;
3362 +
3363 + hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition);
3364 + if (E_NOTFOUND == hr)
3365 + {
3366 + hr = S_OK;
3367 + }
3368 + ExitOnFailure(hr, "Failed when querying Note/@Condition attribute.");
3369 +
3370 + hr = XmlGetText(pixnChild, &bstrText);
3371 + ExitOnFailure(hr, "Failed to get inner text of Note element.");
3372 +
3373 + if (S_OK == hr)
3374 + {
3375 + if (pConditionalNote->sczCondition)
3376 + {
3377 + hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0);
3378 + ExitOnFailure(hr, "Failed to copy text to conditional note text.");
3379 +
3380 + ++i;
3381 + }
3382 + else
3383 + {
3384 + if (pControl->sczNote)
3385 + {
3386 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
3387 + ExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName);
3388 + }
3389 +
3390 + hr = StrAllocString(&pControl->sczNote, bstrText, 0);
3391 + ExitOnFailure(hr, "Failed to copy text to command link control.");
3392 +
3393 + // Unconditional note entries aren't stored in the conditional notes list.
3394 + --pControl->cConditionalNotes;
3395 + }
3396 + }
3397 +
3398 + ReleaseNullBSTR(bstrText);
3399 + }
3400 + }
3401 +
3402 +LExit:
3403 + ReleaseObject(pixnl);
3404 + ReleaseObject(pixnChild);
3405 + ReleaseBSTR(bstrText);
3406 +
3407 + return hr;
3408 +}
3409 +
3410 +
3411 +static HRESULT StartBillboard(
3412 + __in THEME* pTheme,
3413 + __in DWORD dwControl
3414 + )
3415 +{
3416 + HRESULT hr = E_NOTFOUND;
3417 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
3418 +
3419 + if (hWnd)
3420 + {
3421 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
3422 + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
3423 + {
3424 + // kick off
3425 + pControl->dwData = 0;
3426 + OnBillboardTimer(pTheme, pTheme->hwndParent, dwControl);
3427 +
3428 + if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL))
3429 + {
3430 + ExitWithLastError(hr, "Failed to start billboard.");
3431 + }
3432 +
3433 + hr = S_OK;
3434 + }
3435 + }
3436 +
3437 +LExit:
3438 + return hr;
3439 +}
3440 +
3441 +
3442 +static HRESULT StopBillboard(
3443 + __in THEME* pTheme,
3444 + __in DWORD dwControl
3445 + )
3446 +{
3447 + HRESULT hr = E_NOTFOUND;
3448 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
3449 +
3450 + if (hWnd)
3451 + {
3452 + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
3453 + if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
3454 + {
3455 + ThemeControlEnable(pTheme, dwControl, FALSE);
3456 +
3457 + if (::KillTimer(pTheme->hwndParent, pControl->wId))
3458 + {
3459 + hr = S_OK;
3460 + }
3461 + }
3462 + }
3463 +
3464 + return hr;
3465 +}
3466 +
3467 +
3468 +static HRESULT FindImageList(
3469 + __in THEME* pTheme,
3470 + __in_z LPCWSTR wzImageListName,
3471 + __out HIMAGELIST *phImageList
3472 + )
3473 +{
3474 + HRESULT hr = S_OK;
3475 +
3476 + for (DWORD i = 0; i < pTheme->cImageLists; ++i)
3477 + {
3478 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1))
3479 + {
3480 + *phImageList = pTheme->rgImageLists[i].hImageList;
3481 + ExitFunction1(hr = S_OK);
3482 + }
3483 + }
3484 +
3485 + hr = E_NOTFOUND;
3486 +
3487 +LExit:
3488 + return hr;
3489 +}
3490 +
3491 +
3492 +static HRESULT DrawButton(
3493 + __in THEME* pTheme,
3494 + __in DRAWITEMSTRUCT* pdis,
3495 + __in const THEME_CONTROL* pControl
3496 + )
3497 +{
3498 + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3499 + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY;
3500 +
3501 + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3502 + HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
3503 +
3504 + DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE);
3505 + // "clicked" gets priority
3506 + if (ODS_SELECTED & pdis->itemState)
3507 + {
3508 + nSourceY += pControl->nHeight * 2;
3509 + }
3510 + // then hover
3511 + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
3512 + {
3513 + nSourceY += pControl->nHeight;
3514 + }
3515 + // then focused
3516 + else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState)
3517 + {
3518 + nSourceY += pControl->nHeight * 3;
3519 + }
3520 +
3521 + ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, pControl->nWidth, pControl->nHeight, SRCCOPY);
3522 +
3523 + ::SelectObject(hdcMem, hDefaultBitmap);
3524 + ::DeleteDC(hdcMem);
3525 +
3526 + DrawControlText(pTheme, pdis, pControl, TRUE, FALSE);
3527 +
3528 + return S_OK;
3529 +}
3530 +
3531 +
3532 +static HRESULT DrawHyperlink(
3533 + __in THEME* pTheme,
3534 + __in DRAWITEMSTRUCT* pdis,
3535 + __in const THEME_CONTROL* pControl
3536 + )
3537 +{
3538 + DrawControlText(pTheme, pdis, pControl, FALSE, TRUE);
3539 + return S_OK;
3540 +}
3541 +
3542 +
3543 +static void DrawControlText(
3544 + __in THEME* pTheme,
3545 + __in DRAWITEMSTRUCT* pdis,
3546 + __in const THEME_CONTROL* pControl,
3547 + __in BOOL fCentered,
3548 + __in BOOL fDrawFocusRect
3549 + )
3550 +{
3551 + WCHAR wzText[256] = { };
3552 + DWORD cchText = 0;
3553 + THEME_FONT* pFont = NULL;
3554 + HFONT hfPrev = NULL;
3555 +
3556 + if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText))))
3557 + {
3558 + // nothing to do
3559 + return;
3560 + }
3561 +
3562 + if (ODS_SELECTED & pdis->itemState)
3563 + {
3564 + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId);
3565 + }
3566 + else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
3567 + {
3568 + pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId);
3569 + }
3570 + else
3571 + {
3572 + pFont = pTheme->rgFonts + pControl->dwFontId;
3573 + }
3574 +
3575 + hfPrev = SelectFont(pdis->hDC, pFont->hFont);
3576 +
3577 + ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL);
3578 +
3579 + if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState))
3580 + {
3581 + ::DrawFocusRect(pdis->hDC, &pdis->rcItem);
3582 + }
3583 +
3584 + SelectFont(pdis->hDC, hfPrev);
3585 +}
3586 +
3587 +
3588 +static HRESULT DrawImage(
3589 + __in THEME* pTheme,
3590 + __in DRAWITEMSTRUCT* pdis,
3591 + __in const THEME_CONTROL* pControl
3592 + )
3593 +{
3594 + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3595 + DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left;
3596 + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3597 + int nSourceY = pControl->hImage ? 0 : pControl->nSourceY;
3598 +
3599 + BLENDFUNCTION bf = { };
3600 + bf.BlendOp = AC_SRC_OVER;
3601 + bf.SourceConstantAlpha = 255;
3602 + bf.AlphaFormat = AC_SRC_ALPHA;
3603 +
3604 + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3605 + HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
3606 +
3607 + // Try to draw the image with transparency and if that fails (usually because the image has no
3608 + // alpha channel) then draw the image as is.
3609 + if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwWidth, dwHeight, bf))
3610 + {
3611 + ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwWidth, dwHeight, SRCCOPY);
3612 + }
3613 +
3614 + ::SelectObject(hdcMem, hDefaultBitmap);
3615 + ::DeleteDC(hdcMem);
3616 + return S_OK;
3617 +}
3618 +
3619 +
3620 +static HRESULT DrawProgressBar(
3621 + __in THEME* pTheme,
3622 + __in DRAWITEMSTRUCT* pdis,
3623 + __in const THEME_CONTROL* pControl
3624 + )
3625 +{
3626 + DWORD dwProgressColor = HIWORD(pControl->dwData);
3627 + DWORD dwProgressPercentage = LOWORD(pControl->dwData);
3628 + DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3629 + DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100;
3630 + int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3631 + int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * pControl->nHeight);
3632 +
3633 + HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3634 + HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
3635 +
3636 + // Draw the left side of the progress bar.
3637 + ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwHeight, SRCCOPY);
3638 +
3639 + // Draw the filled side of the progress bar, if there is any.
3640 + if (0 < dwCenter)
3641 + {
3642 + ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwHeight, SRCCOPY);
3643 + }
3644 +
3645 + // Draw the unfilled side of the progress bar, if there is any.
3646 + if (dwCenter < static_cast<DWORD>(pdis->rcItem.right - 2))
3647 + {
3648 + ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwHeight, SRCCOPY);
3649 + }
3650 +
3651 + // Draw the right side of the progress bar.
3652 + ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwHeight, SRCCOPY);
3653 +
3654 + ::SelectObject(hdcMem, hDefaultBitmap);
3655 + ::DeleteDC(hdcMem);
3656 + return S_OK;
3657 +}
3658 +
3659 +
3660 +static BOOL DrawHoverControl(
3661 + __in THEME* pTheme,
3662 + __in BOOL fHover
3663 + )
3664 +{
3665 + BOOL fChangedHover = FALSE;
3666 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, pTheme->hwndHover));
3667 +
3668 + // Only hyperlinks and owner-drawn buttons have hover states.
3669 + if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type ||
3670 + (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW))))
3671 + {
3672 + if (fHover)
3673 + {
3674 + pControl->dwData |= THEME_CONTROL_DATA_HOVER;
3675 + }
3676 + else
3677 + {
3678 + pControl->dwData &= ~THEME_CONTROL_DATA_HOVER;
3679 + }
3680 +
3681 + ::InvalidateRect(pControl->hWnd, NULL, FALSE);
3682 + fChangedHover = TRUE;
3683 + }
3684 +
3685 + return fChangedHover;
3686 +}
3687 +
3688 +
3689 +static void FreePage(
3690 + __in THEME_PAGE* pPage
3691 + )
3692 +{
3693 + if (pPage)
3694 + {
3695 + ReleaseStr(pPage->sczName);
3696 +
3697 + if (pPage->cSavedVariables)
3698 + {
3699 + for (DWORD i = 0; i < pPage->cSavedVariables; ++i)
3700 + {
3701 + ReleaseStr(pPage->rgSavedVariables[i].sczValue);
3702 + }
3703 + }
3704 +
3705 + ReleaseMem(pPage->rgSavedVariables);
3706 + }
3707 +}
3708 +
3709 +
3710 +static void FreeImageList(
3711 + __in THEME_IMAGELIST* pImageList
3712 + )
3713 +{
3714 + if (pImageList)
3715 + {
3716 + ReleaseStr(pImageList->sczName);
3717 + ImageList_Destroy(pImageList->hImageList);
3718 + }
3719 +}
3720 +
3721 +static void FreeControl(
3722 + __in THEME_CONTROL* pControl
3723 + )
3724 +{
3725 + if (pControl)
3726 + {
3727 + if (::IsWindow(pControl->hWnd))
3728 + {
3729 + ::CloseWindow(pControl->hWnd);
3730 + pControl->hWnd = NULL;
3731 + }
3732 +
3733 + ReleaseStr(pControl->sczName);
3734 + ReleaseStr(pControl->sczText);
3735 + ReleaseStr(pControl->sczTooltip);
3736 + ReleaseStr(pControl->sczNote);
3737 + ReleaseStr(pControl->sczEnableCondition);
3738 + ReleaseStr(pControl->sczVisibleCondition);
3739 + ReleaseStr(pControl->sczValue);
3740 + ReleaseStr(pControl->sczVariable);
3741 +
3742 + if (pControl->hImage)
3743 + {
3744 + ::DeleteBitmap(pControl->hImage);
3745 + }
3746 +
3747 + for (DWORD i = 0; i < pControl->cControls; ++i)
3748 + {
3749 + FreeControl(pControl->rgControls + i);
3750 + }
3751 +
3752 + for (DWORD i = 0; i < pControl->cActions; ++i)
3753 + {
3754 + FreeAction(&(pControl->rgActions[i]));
3755 + }
3756 +
3757 + for (DWORD i = 0; i < pControl->cColumns; ++i)
3758 + {
3759 + FreeColumn(&(pControl->ptcColumns[i]));
3760 + }
3761 +
3762 + for (DWORD i = 0; i < pControl->cConditionalText; ++i)
3763 + {
3764 + FreeConditionalText(&(pControl->rgConditionalText[i]));
3765 + }
3766 +
3767 + for (DWORD i = 0; i < pControl->cConditionalNotes; ++i)
3768 + {
3769 + FreeConditionalText(&(pControl->rgConditionalNotes[i]));
3770 + }
3771 +
3772 + for (DWORD i = 0; i < pControl->cTabs; ++i)
3773 + {
3774 + FreeTab(&(pControl->pttTabs[i]));
3775 + }
3776 +
3777 + ReleaseMem(pControl->rgActions)
3778 + ReleaseMem(pControl->ptcColumns);
3779 + ReleaseMem(pControl->rgConditionalText);
3780 + ReleaseMem(pControl->rgConditionalNotes);
3781 + ReleaseMem(pControl->pttTabs);
3782 + }
3783 +}
3784 +
3785 +
3786 +static void FreeAction(
3787 + __in THEME_ACTION* pAction
3788 + )
3789 +{
3790 + switch (pAction->type)
3791 + {
3792 + case THEME_ACTION_TYPE_BROWSE_DIRECTORY:
3793 + ReleaseStr(pAction->BrowseDirectory.sczVariableName);
3794 + break;
3795 + case THEME_ACTION_TYPE_CHANGE_PAGE:
3796 + ReleaseStr(pAction->ChangePage.sczPageName);
3797 + break;
3798 + }
3799 +
3800 + ReleaseStr(pAction->sczCondition);
3801 +}
3802 +
3803 +
3804 +static void FreeColumn(
3805 + __in THEME_COLUMN* pColumn
3806 + )
3807 +{
3808 + ReleaseStr(pColumn->pszName);
3809 +}
3810 +
3811 +
3812 +static void FreeConditionalText(
3813 + __in THEME_CONDITIONAL_TEXT* pConditionalText
3814 + )
3815 +{
3816 + ReleaseStr(pConditionalText->sczCondition);
3817 + ReleaseStr(pConditionalText->sczText);
3818 +}
3819 +
3820 +
3821 +static void FreeTab(
3822 + __in THEME_TAB* pTab
3823 + )
3824 +{
3825 + ReleaseStr(pTab->pszName);
3826 +}
3827 +
3828 +
3829 +static void FreeFont(
3830 + __in THEME_FONT* pFont
3831 + )
3832 +{
3833 + if (pFont)
3834 + {
3835 + if (pFont->hBackground)
3836 + {
3837 + ::DeleteObject(pFont->hBackground);
3838 + pFont->hBackground = NULL;
3839 + }
3840 +
3841 + if (pFont->hForeground)
3842 + {
3843 + ::DeleteObject(pFont->hForeground);
3844 + pFont->hForeground = NULL;
3845 + }
3846 +
3847 + if (pFont->hFont)
3848 + {
3849 + ::DeleteObject(pFont->hFont);
3850 + pFont->hFont = NULL;
3851 + }
3852 + }
3853 +}
3854 +
3855 +
3856 +static DWORD CALLBACK RichEditStreamFromFileHandleCallback(
3857 + __in DWORD_PTR dwCookie,
3858 + __in_bcount(cb) LPBYTE pbBuff,
3859 + __in LONG cb,
3860 + __in LONG* pcb
3861 + )
3862 +{
3863 + HRESULT hr = S_OK;
3864 + HANDLE hFile = reinterpret_cast<HANDLE>(dwCookie);
3865 +
3866 + if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast<DWORD*>(pcb), NULL))
3867 + {
3868 + ExitWithLastError(hr, "Failed to read file");
3869 + }
3870 +
3871 +LExit:
3872 + return hr;
3873 +}
3874 +
3875 +
3876 +static DWORD CALLBACK RichEditStreamFromMemoryCallback(
3877 + __in DWORD_PTR dwCookie,
3878 + __in_bcount(cb) LPBYTE pbBuff,
3879 + __in LONG cb,
3880 + __in LONG* pcb
3881 + )
3882 +{
3883 + HRESULT hr = S_OK;
3884 + MEMBUFFER_FOR_RICHEDIT* pBuffer = reinterpret_cast<MEMBUFFER_FOR_RICHEDIT*>(dwCookie);
3885 + DWORD cbCopy = 0;
3886 +
3887 + if (pBuffer->iData < pBuffer->cbData)
3888 + {
3889 + cbCopy = min(static_cast<DWORD>(cb), pBuffer->cbData - pBuffer->iData);
3890 + memcpy(pbBuff, pBuffer->rgbData + pBuffer->iData, cbCopy);
3891 +
3892 + pBuffer->iData += cbCopy;
3893 + Assert(pBuffer->iData <= pBuffer->cbData);
3894 + }
3895 +
3896 + *pcb = cbCopy;
3897 + return hr;
3898 +}
3899 +
3900 +
3901 +static void CALLBACK OnBillboardTimer(
3902 + __in THEME* pTheme,
3903 + __in HWND hwnd,
3904 + __in UINT_PTR idEvent
3905 + )
3906 +{
3907 + HWND hwndControl = ::GetDlgItem(hwnd, static_cast<int>(idEvent));
3908 + if (hwndControl)
3909 + {
3910 + THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hwndControl));
3911 + AssertSz(pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages.");
3912 +
3913 + if (pControl)
3914 + {
3915 + if (pControl->dwData < pControl->cControls)
3916 + {
3917 + ThemeShowChild(pTheme, pControl, pControl->dwData);
3918 + }
3919 + else if (pControl->fBillboardLoops)
3920 + {
3921 + pControl->dwData = 0;
3922 + ThemeShowChild(pTheme, pControl, pControl->dwData);
3923 + }
3924 + else // no more looping
3925 + {
3926 + ::KillTimer(hwnd, idEvent);
3927 + }
3928 +
3929 + ++pControl->dwData;
3930 + }
3931 + }
3932 +}
3933 +
3934 +static void OnBrowseDirectory(
3935 + __in THEME* pTheme,
3936 + __in HWND hWnd,
3937 + __in const THEME_ACTION* pAction
3938 + )
3939 +{
3940 + HRESULT hr = S_OK;
3941 + WCHAR wzPath[MAX_PATH] = { };
3942 + BROWSEINFOW browseInfo = { };
3943 + PIDLIST_ABSOLUTE pidl = NULL;
3944 +
3945 + browseInfo.hwndOwner = hWnd;
3946 + browseInfo.pszDisplayName = wzPath;
3947 + browseInfo.lpszTitle = pTheme->sczCaption;
3948 + browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
3949 + pidl = ::SHBrowseForFolderW(&browseInfo);
3950 + if (pidl && ::SHGetPathFromIDListW(pidl, wzPath))
3951 + {
3952 + // Since editbox changes aren't immediately saved off, we have to treat them differently.
3953 + THEME_CONTROL* pTargetControl = NULL;
3954 +
3955 + for (DWORD i = 0; i < pTheme->cControls; ++i)
3956 + {
3957 + THEME_CONTROL* pControl = pTheme->rgControls + i;
3958 +
3959 + if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) &&
3960 + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1))
3961 + {
3962 + pTargetControl = pControl;
3963 + break;
3964 + }
3965 + }
3966 +
3967 + if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality)
3968 + {
3969 + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath);
3970 + ExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName);
3971 + }
3972 + else if (pTheme->pfnSetStringVariable)
3973 + {
3974 + hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, pTheme->pvVariableContext);
3975 + ExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName);
3976 + }
3977 + else if (pTargetControl)
3978 + {
3979 + hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath);
3980 + ExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName);
3981 + }
3982 +
3983 + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH);
3984 + }
3985 +
3986 +LExit:
3987 + if (pidl)
3988 + {
3989 + ::CoTaskMemFree(pidl);
3990 + }
3991 +}
3992 +
3993 +static BOOL OnButtonClicked(
3994 + __in THEME* pTheme,
3995 + __in HWND hWnd,
3996 + __in const THEME_CONTROL* pControl
3997 + )
3998 +{
3999 + HRESULT hr = S_OK;
4000 + BOOL fHandled = FALSE;
4001 +
4002 + if (THEME_CONTROL_TYPE_BUTTON == pControl->type || THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
4003 + {
4004 + if (pControl->cActions)
4005 + {
4006 + fHandled = TRUE;
4007 + THEME_ACTION* pChosenAction = pControl->pDefaultAction;
4008 +
4009 + if (pTheme->pfnEvaluateCondition)
4010 + {
4011 + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined.
4012 + // This is the current implementation and can change at any time.
4013 + for (DWORD j = 0; j < pControl->cActions; ++j)
4014 + {
4015 + THEME_ACTION* pAction = pControl->rgActions + j;
4016 +
4017 + if (pAction->sczCondition)
4018 + {
4019 + BOOL fCondition = FALSE;
4020 +
4021 + hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext);
4022 + ExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition);
4023 +
4024 + if (fCondition)
4025 + {
4026 + pChosenAction = pAction;
4027 + break;
4028 + }
4029 + }
4030 + }
4031 + }
4032 +
4033 + if (pChosenAction)
4034 + {
4035 + switch (pChosenAction->type)
4036 + {
4037 + case THEME_ACTION_TYPE_BROWSE_DIRECTORY:
4038 + OnBrowseDirectory(pTheme, hWnd, pChosenAction);
4039 + break;
4040 +
4041 + case THEME_ACTION_TYPE_CLOSE_WINDOW:
4042 + ::SendMessageW(hWnd, WM_CLOSE, 0, 0);
4043 + break;
4044 +
4045 + case THEME_ACTION_TYPE_CHANGE_PAGE:
4046 + DWORD dwPageId = 0;
4047 + LPCWSTR pPageNames = pChosenAction->ChangePage.sczPageName;
4048 + ThemeGetPageIds(pTheme, &pPageNames, &dwPageId, 1);
4049 +
4050 + if (!dwPageId)
4051 + {
4052 + ExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName);
4053 + }
4054 +
4055 + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT);
4056 + ThemeShowPage(pTheme, dwPageId, SW_SHOW);
4057 + break;
4058 + }
4059 + }
4060 + }
4061 + }
4062 + else if (!pControl->fDisableVariableFunctionality && (pTheme->pfnSetNumericVariable || pTheme->pfnSetStringVariable))
4063 + {
4064 + BOOL fRefresh = FALSE;
4065 +
4066 + switch (pControl->type)
4067 + {
4068 + case THEME_CONTROL_TYPE_CHECKBOX:
4069 + if (pTheme->pfnSetNumericVariable && pControl->sczName && *pControl->sczName)
4070 + {
4071 + BOOL fChecked = ThemeIsControlChecked(pTheme, pControl->wId);
4072 + pTheme->pfnSetNumericVariable(pControl->sczName, fChecked ? 1 : 0, pTheme->pvVariableContext);
4073 + fRefresh = TRUE;
4074 + }
4075 + break;
4076 + case THEME_CONTROL_TYPE_RADIOBUTTON:
4077 + if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId))
4078 + {
4079 + pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, pTheme->pvVariableContext);
4080 + fRefresh = TRUE;
4081 + }
4082 + break;
4083 + }
4084 +
4085 + if (fRefresh)
4086 + {
4087 + ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH);
4088 + fHandled = TRUE;
4089 + }
4090 + }
4091 +
4092 +LExit:
4093 + return fHandled;
4094 +}
4095 +
4096 +static HRESULT OnRichEditEnLink(
4097 + __in LPARAM lParam,
4098 + __in HWND hWndRichEdit,
4099 + __in HWND hWnd
4100 + )
4101 +{
4102 + HRESULT hr = S_OK;
4103 + LPWSTR sczLink = NULL;
4104 + ENLINK* link = reinterpret_cast<ENLINK*>(lParam);
4105 +
4106 + switch (link->msg)
4107 + {
4108 + case WM_LBUTTONDOWN:
4109 + {
4110 + hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2);
4111 + ExitOnFailure(hr, "Failed to allocate string for link.");
4112 +
4113 + TEXTRANGEW tr;
4114 + tr.chrg.cpMin = link->chrg.cpMin;
4115 + tr.chrg.cpMax = link->chrg.cpMax;
4116 + tr.lpstrText = sczLink;
4117 +
4118 + if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&tr)))
4119 + {
4120 + hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL);
4121 + ExitOnFailure(hr, "Failed to launch link: %ls", sczLink);
4122 + }
4123 +
4124 + break;
4125 + }
4126 +
4127 + case WM_SETCURSOR:
4128 + ::SetCursor(vhCursorHand);
4129 + break;
4130 + }
4131 +
4132 +LExit:
4133 + ReleaseStr(sczLink);
4134 +
4135 + return hr;
4136 +}
4137 +
4138 +static BOOL ControlIsType(
4139 + __in const THEME* pTheme,
4140 + __in DWORD dwControl,
4141 + __in const THEME_CONTROL_TYPE type
4142 + )
4143 +{
4144 + BOOL fIsType = FALSE;
4145 + HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
4146 + if (hWnd)
4147 + {
4148 + const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
4149 + fIsType = (pControl && type == pControl->type);
4150 + }
4151 +
4152 + return fIsType;
4153 +}
4154 +
4155 +static const THEME_CONTROL* FindControlFromHWnd(
4156 + __in const THEME* pTheme,
4157 + __in HWND hWnd,
4158 + __in_opt const THEME_CONTROL* pParentControl
4159 + )
4160 +{
4161 + DWORD cControls = 0;
4162 + THEME_CONTROL* rgControls = NULL;
4163 +
4164 + GetControls(pTheme, pParentControl, cControls, rgControls);
4165 +
4166 + // As we can't use GWLP_USERDATA (SysLink controls on Windows XP uses it too)...
4167 + for (DWORD i = 0; i < cControls; ++i)
4168 + {
4169 + if (hWnd == rgControls[i].hWnd)
4170 + {
4171 + return rgControls + i;
4172 + }
4173 + else if (0 < rgControls[i].cControls)
4174 + {
4175 + const THEME_CONTROL* pChildControl = FindControlFromHWnd(pTheme, hWnd, rgControls + i);
4176 + if (pChildControl)
4177 + {
4178 + return pChildControl;
4179 + }
4180 + }
4181 + }
4182 +
4183 + return NULL;
4184 +}
4185 +
4186 +static void GetControlDimensions(
4187 + __in const RECT* prcParent,
4188 + __in const THEME_CONTROL* pControl,
4189 + __out int* piWidth,
4190 + __out int* piHeight,
4191 + __out int* piX,
4192 + __out int* piY
4193 + )
4194 +{
4195 + *piWidth = pControl->nWidth < 1 ? pControl->nX < 0 ? prcParent->right + pControl->nWidth : prcParent->right + pControl->nWidth - pControl->nX : pControl->nWidth;
4196 + *piHeight = pControl->nHeight < 1 ? pControl->nY < 0 ? prcParent->bottom + pControl->nHeight : prcParent->bottom + pControl->nHeight - pControl->nY : pControl->nHeight;
4197 + *piX = pControl->nX < 0 ? prcParent->right + pControl->nX - *piWidth : pControl->nX;
4198 + *piY = pControl->nY < 0 ? prcParent->bottom + pControl->nY - *piHeight : pControl->nY;
4199 +}
4200 +
4201 +static HRESULT SizeListViewColumns(
4202 + __inout THEME_CONTROL* pControl
4203 + )
4204 +{
4205 + HRESULT hr = S_OK;
4206 + RECT rcParent = { };
4207 + int cNumExpandingColumns = 0;
4208 + int iExtraAvailableSize;
4209 +
4210 + if (!::GetWindowRect(pControl->hWnd, &rcParent))
4211 + {
4212 + ExitWithLastError(hr, "Failed to get window rect of listview control.");
4213 + }
4214 +
4215 + iExtraAvailableSize = rcParent.right - rcParent.left;
4216 +
4217 + for (DWORD i = 0; i < pControl->cColumns; ++i)
4218 + {
4219 + if (pControl->ptcColumns[i].fExpands)
4220 + {
4221 + ++cNumExpandingColumns;
4222 + }
4223 +
4224 + iExtraAvailableSize -= pControl->ptcColumns[i].nBaseWidth;
4225 + }
4226 +
4227 + // Leave room for a vertical scroll bar just in case.
4228 + iExtraAvailableSize -= ::GetSystemMetrics(SM_CXVSCROLL);
4229 +
4230 + for (DWORD i = 0; i < pControl->cColumns; ++i)
4231 + {
4232 + if (pControl->ptcColumns[i].fExpands)
4233 + {
4234 + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth + (iExtraAvailableSize / cNumExpandingColumns);
4235 + // In case there is any remainder, use it up the first chance we get.
4236 + pControl->ptcColumns[i].nWidth += iExtraAvailableSize % cNumExpandingColumns;
4237 + iExtraAvailableSize -= iExtraAvailableSize % cNumExpandingColumns;
4238 + }
4239 + else
4240 + {
4241 + pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth;
4242 + }
4243 + }
4244 +
4245 +LExit:
4246 + return hr;
4247 +}
4248 +
4249 +
4250 +static HRESULT ShowControl(
4251 + __in THEME* pTheme,
4252 + __in THEME_CONTROL* pControl,
4253 + __in int nCmdShow,
4254 + __in BOOL fSaveEditboxes,
4255 + __in THEME_SHOW_PAGE_REASON reason,
4256 + __in DWORD dwPageId,
4257 + __out_opt HWND* phwndFocus
4258 + )
4259 +{
4260 + HRESULT hr = S_OK;
4261 + DWORD iPageControl = 0;
4262 + HWND hwndFocus = NULL;
4263 + LPWSTR sczText = NULL;
4264 + THEME_SAVEDVARIABLE* pSavedVariable = NULL;
4265 + BOOL fHide = SW_HIDE == nCmdShow;
4266 + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPageId);
4267 +
4268 + // Save the editbox value if necessary (other control types save their values immediately).
4269 + if (pTheme->pfnSetStringVariable && !pControl->fDisableVariableFunctionality &&
4270 + fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName)
4271 + {
4272 + hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText);
4273 + ExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName);
4274 +
4275 + hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, pTheme->pvVariableContext);
4276 + ExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText);
4277 + }
4278 +
4279 + HWND hWnd = pControl->hWnd;
4280 +
4281 + if (fHide && pControl->wPageId)
4282 + {
4283 + ::ShowWindow(hWnd, SW_HIDE);
4284 +
4285 + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
4286 + {
4287 + StopBillboard(pTheme, pControl->wId);
4288 + }
4289 +
4290 + ExitFunction();
4291 + }
4292 +
4293 + BOOL fEnabled = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED);
4294 + BOOL fVisible = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDDEN);
4295 +
4296 + if (!pControl->fDisableVariableFunctionality)
4297 + {
4298 + if (pTheme->pfnEvaluateCondition)
4299 + {
4300 + // If the control has a VisibleCondition, check if it's true.
4301 + if (pControl->sczVisibleCondition)
4302 + {
4303 + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext);
4304 + ExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition);
4305 + }
4306 +
4307 + // If the control has an EnableCondition, check if it's true.
4308 + if (pControl->sczEnableCondition)
4309 + {
4310 + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext);
4311 + ExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition);
4312 + }
4313 + }
4314 +
4315 + // Try to format each control's text based on context, except for editboxes since their text comes from the user.
4316 + if (pTheme->pfnFormatString && ((pControl->sczText && *pControl->sczText) || pControl->cConditionalText) && THEME_CONTROL_TYPE_EDITBOX != pControl->type)
4317 + {
4318 + LPWSTR wzText = pControl->sczText;
4319 + LPWSTR wzNote = pControl->sczNote;
4320 +
4321 + if (pTheme->pfnEvaluateCondition)
4322 + {
4323 + // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined.
4324 + // This is the current implementation and can change at any time.
4325 + for (DWORD j = 0; j < pControl->cConditionalText; ++j)
4326 + {
4327 + THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + j;
4328 + wzText = pConditionalText->sczText;
4329 +
4330 + if (pConditionalText->sczCondition)
4331 + {
4332 + BOOL fCondition = FALSE;
4333 +
4334 + hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext);
4335 + ExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition);
4336 +
4337 + if (fCondition)
4338 + {
4339 + wzText = pConditionalText->sczText;
4340 + break;
4341 + }
4342 + }
4343 + }
4344 +
4345 + for (DWORD j = 0; j < pControl->cConditionalNotes; ++j)
4346 + {
4347 + THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + j;
4348 + wzNote = pConditionalNote->sczText;
4349 +
4350 + if (pConditionalNote->sczCondition)
4351 + {
4352 + BOOL fCondition = FALSE;
4353 +
4354 + hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext);
4355 + ExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition);
4356 +
4357 + if (fCondition)
4358 + {
4359 + wzNote = pConditionalNote->sczText;
4360 + break;
4361 + }
4362 + }
4363 + }
4364 + }
4365 +
4366 + if (wzText && *wzText)
4367 + {
4368 + hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext);
4369 + ExitOnFailure(hr, "Failed to format string: %ls", wzText);
4370 + }
4371 + else
4372 + {
4373 + ReleaseNullStr(sczText);
4374 + }
4375 +
4376 + ThemeSetTextControl(pTheme, pControl->wId, sczText);
4377 +
4378 + if (wzNote && *wzNote)
4379 + {
4380 + hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext);
4381 + ExitOnFailure(hr, "Failed to format note: %ls", wzNote);
4382 + }
4383 + else
4384 + {
4385 + ReleaseNullStr(sczText);
4386 + }
4387 +
4388 + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast<WPARAM>(sczText));
4389 + }
4390 +
4391 + // If this is a named control, do variable magic.
4392 + if (pControl->sczName && *pControl->sczName)
4393 + {
4394 + // If this is a checkbox control,
4395 + // try to set its default state to the state of a matching named variable.
4396 + if (pTheme->pfnGetNumericVariable && THEME_CONTROL_TYPE_CHECKBOX == pControl->type)
4397 + {
4398 + LONGLONG llValue = 0;
4399 + hr = pTheme->pfnGetNumericVariable(pControl->sczName, &llValue, pTheme->pvVariableContext);
4400 + if (E_NOTFOUND == hr)
4401 + {
4402 + hr = S_OK;
4403 + }
4404 + ExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName);
4405 +
4406 + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId)
4407 + {
4408 + pSavedVariable = pPage->rgSavedVariables + iPageControl;
4409 + pSavedVariable->wzName = pControl->sczName;
4410 +
4411 + if (SUCCEEDED(hr))
4412 + {
4413 + hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue);
4414 + ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName);
4415 + }
4416 +
4417 + ++iPageControl;
4418 + }
4419 +
4420 + ThemeSendControlMessage(pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0);
4421 + }
4422 +
4423 + // If this is an editbox control,
4424 + // try to set its default state to the state of a matching named variable.
4425 + if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_EDITBOX == pControl->type)
4426 + {
4427 + hr = pTheme->pfnGetStringVariable(pControl->sczName, &sczText, pTheme->pvVariableContext);
4428 + if (E_NOTFOUND == hr)
4429 + {
4430 + ReleaseNullStr(sczText);
4431 + }
4432 + else
4433 + {
4434 + ExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczName);
4435 + }
4436 +
4437 + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId)
4438 + {
4439 + pSavedVariable = pPage->rgSavedVariables + iPageControl;
4440 + pSavedVariable->wzName = pControl->sczName;
4441 +
4442 + if (SUCCEEDED(hr))
4443 + {
4444 + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0);
4445 + ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName);
4446 + }
4447 +
4448 + ++iPageControl;
4449 + }
4450 +
4451 + ThemeSetTextControl(pTheme, pControl->wId, sczText);
4452 + }
4453 + }
4454 +
4455 + // If this is a radio button associated with a variable,
4456 + // try to set its default state to the state of the variable.
4457 + if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type && pControl->sczVariable && *pControl->sczVariable)
4458 + {
4459 + hr = pTheme->pfnGetStringVariable(pControl->sczVariable, &sczText, pTheme->pvVariableContext);
4460 + if (E_NOTFOUND == hr)
4461 + {
4462 + ReleaseNullStr(sczText);
4463 + }
4464 + else
4465 + {
4466 + ExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable);
4467 + }
4468 +
4469 + if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton)
4470 + {
4471 + pSavedVariable = pPage->rgSavedVariables + iPageControl;
4472 + pSavedVariable->wzName = pControl->sczVariable;
4473 +
4474 + if (SUCCEEDED(hr))
4475 + {
4476 + hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0);
4477 + ExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable);
4478 + }
4479 +
4480 + ++iPageControl;
4481 + }
4482 +
4483 + Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1));
4484 + }
4485 + }
4486 +
4487 + if (!fVisible || (!fEnabled && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED)))
4488 + {
4489 + ::ShowWindow(hWnd, SW_HIDE);
4490 + }
4491 + else
4492 + {
4493 + ::EnableWindow(hWnd, !fHide && fEnabled);
4494 +
4495 + if (!hwndFocus && pControl->wPageId && (pControl->dwStyle & WS_TABSTOP))
4496 + {
4497 + hwndFocus = hWnd;
4498 + }
4499 +
4500 + ::ShowWindow(hWnd, nCmdShow);
4501 + }
4502 +
4503 + if (0 < pControl->cControls)
4504 + {
4505 + ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId);
4506 + }
4507 +
4508 + if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId)
4509 + {
4510 + if (fEnabled)
4511 + {
4512 + StartBillboard(pTheme, pControl->wId);
4513 + }
4514 + else
4515 + {
4516 + StopBillboard(pTheme, pControl->wId);
4517 + }
4518 + }
4519 +
4520 + if (phwndFocus)
4521 + {
4522 + *phwndFocus = hwndFocus;
4523 + }
4524 +
4525 +LExit:
4526 + ReleaseStr(sczText);
4527 +
4528 + return hr;
4529 +}
4530 +
4531 +static HRESULT ShowControls(
4532 + __in THEME* pTheme,
4533 + __in_opt const THEME_CONTROL* pParentControl,
4534 + __in int nCmdShow,
4535 + __in BOOL fSaveEditboxes,
4536 + __in THEME_SHOW_PAGE_REASON reason,
4537 + __in DWORD dwPageId
4538 + )
4539 +{
4540 + HRESULT hr = S_OK;
4541 + HWND hwndFocus = NULL;
4542 + DWORD cControls = 0;
4543 + THEME_CONTROL* rgControls = NULL;
4544 +
4545 + GetControls(pTheme, pParentControl, cControls, rgControls);
4546 +
4547 + for (DWORD i = 0; i < cControls; ++i)
4548 + {
4549 + THEME_CONTROL* pControl = rgControls + i;
4550 +
4551 + // Only look at non-page controls and the specified page's controls.
4552 + if (!pControl->wPageId || pControl->wPageId == dwPageId)
4553 + {
4554 + hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus);
4555 + ExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i);
4556 + }
4557 + }
4558 +
4559 + if (hwndFocus)
4560 + {
4561 + ::SetFocus(hwndFocus);
4562 + }
4563 +
4564 +LExit:
4565 + return hr;
4566 +}
4567 +
4568 +
4569 +static LRESULT CALLBACK PanelWndProc(
4570 + __in HWND hWnd,
4571 + __in UINT uMsg,
4572 + __in WPARAM wParam,
4573 + __in LPARAM lParam
4574 + )
4575 +{
4576 + LRESULT lres = 0;
4577 + THEME* pTheme = reinterpret_cast<THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
4578 +
4579 + switch (uMsg)
4580 + {
4581 + case WM_NCCREATE:
4582 + {
4583 + LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
4584 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
4585 + }
4586 + break;
4587 +
4588 + case WM_NCDESTROY:
4589 + lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
4590 + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
4591 + return lres;
4592 +
4593 + case WM_NCHITTEST:
4594 + return HTCLIENT;
4595 + break;
4596 +
4597 + case WM_DRAWITEM:
4598 + ThemeDrawControl(pTheme, reinterpret_cast<LPDRAWITEMSTRUCT>(lParam));
4599 + return TRUE;
4600 + }
4601 +
4602 + return ThemeDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam);
4603 +}
4604 +
4605 +
4606 +static HRESULT LoadControls(
4607 + __in THEME* pTheme,
4608 + __in_opt THEME_CONTROL* pParentControl,
4609 + __in HWND hwndParent,
4610 + __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
4611 + __in DWORD cAssignControlIds
4612 + )
4613 +{
4614 + HRESULT hr = S_OK;
4615 + RECT rcParent = { };
4616 + LPWSTR sczText = NULL;
4617 + BOOL fStartNewGroup = FALSE;
4618 + DWORD cControls = 0;
4619 + THEME_CONTROL* rgControls = NULL;
4620 +
4621 + if (!pParentControl)
4622 + {
4623 + AssertSz(!pTheme->hwndParent, "Theme already loaded controls because it has a parent window.");
4624 + pTheme->hwndParent = hwndParent;
4625 + }
4626 +
4627 + GetControls(pTheme, pParentControl, cControls, rgControls);
4628 + ::GetClientRect(pParentControl ? pParentControl->hWnd : pTheme->hwndParent, &rcParent);
4629 +
4630 + for (DWORD i = 0; i < cControls; ++i)
4631 + {
4632 + THEME_CONTROL* pControl = rgControls + i;
4633 + THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL;
4634 + LPCWSTR wzWindowClass = NULL;
4635 + DWORD dwWindowBits = WS_CHILD;
4636 + DWORD dwWindowExBits = 0;
4637 +
4638 + if (fStartNewGroup)
4639 + {
4640 + dwWindowBits |= WS_GROUP;
4641 + fStartNewGroup = FALSE;
4642 + }
4643 +
4644 + switch (pControl->type)
4645 + {
4646 + case THEME_CONTROL_TYPE_BILLBOARD:
4647 + __fallthrough;
4648 + case THEME_CONTROL_TYPE_PANEL:
4649 + wzWindowClass = THEME_WC_PANEL;
4650 + dwWindowBits |= WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
4651 + dwWindowExBits |= WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT;
4652 +#ifdef DEBUG
4653 + StrAllocFormatted(&pControl->sczText, L"Panel '%ls', id: %d", pControl->sczName, pControl->wId);
4654 +#endif
4655 + break;
4656 +
4657 + case THEME_CONTROL_TYPE_CHECKBOX:
4658 + dwWindowBits |= BS_AUTOCHECKBOX | BS_MULTILINE; // checkboxes are basically buttons with an extra bit tossed in.
4659 + __fallthrough;
4660 + case THEME_CONTROL_TYPE_BUTTON:
4661 + wzWindowClass = WC_BUTTONW;
4662 + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
4663 + {
4664 + dwWindowBits |= BS_OWNERDRAW;
4665 + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
4666 + }
4667 + break;
4668 +
4669 + case THEME_CONTROL_TYPE_COMBOBOX:
4670 + wzWindowClass = WC_COMBOBOXW;
4671 + dwWindowBits |= CBS_DROPDOWNLIST | CBS_HASSTRINGS;
4672 + break;
4673 +
4674 + case THEME_CONTROL_TYPE_COMMANDLINK:
4675 + wzWindowClass = WC_BUTTONW;
4676 + dwWindowBits |= BS_COMMANDLINK;
4677 + break;
4678 +
4679 + case THEME_CONTROL_TYPE_EDITBOX:
4680 + wzWindowClass = WC_EDITW;
4681 + dwWindowBits |= ES_LEFT | ES_AUTOHSCROLL;
4682 + dwWindowExBits = WS_EX_CLIENTEDGE;
4683 + break;
4684 +
4685 + case THEME_CONTROL_TYPE_HYPERLINK: // hyperlinks are basically just owner drawn buttons.
4686 + wzWindowClass = THEME_WC_HYPERLINK;
4687 + dwWindowBits |= BS_OWNERDRAW | BTNS_NOPREFIX;
4688 + break;
4689 +
4690 + case THEME_CONTROL_TYPE_HYPERTEXT:
4691 + wzWindowClass = WC_LINK;
4692 + dwWindowBits |= LWS_NOPREFIX;
4693 + break;
4694 +
4695 + case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps).
4696 + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
4697 + {
4698 + wzWindowClass = WC_STATICW;
4699 + dwWindowBits |= SS_OWNERDRAW;
4700 + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
4701 + }
4702 + else
4703 + {
4704 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
4705 + ExitOnRootFailure(hr, "Invalid image or image list coordinates.");
4706 + }
4707 + break;
4708 +
4709 + case THEME_CONTROL_TYPE_LABEL:
4710 + wzWindowClass = WC_STATICW;
4711 + break;
4712 +
4713 + case THEME_CONTROL_TYPE_LISTVIEW:
4714 + // If thmutil is handling the image list for this listview, tell Windows not to free it when the control is destroyed.
4715 + if (pControl->rghImageList[0] || pControl->rghImageList[1] || pControl->rghImageList[2] || pControl->rghImageList[3])
4716 + {
4717 + pControl->dwStyle |= LVS_SHAREIMAGELISTS;
4718 + }
4719 + wzWindowClass = WC_LISTVIEWW;
4720 + break;
4721 +
4722 + case THEME_CONTROL_TYPE_PROGRESSBAR:
4723 + if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
4724 + {
4725 + wzWindowClass = WC_STATICW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control.
4726 + dwWindowBits |= SS_OWNERDRAW;
4727 + pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
4728 + }
4729 + else
4730 + {
4731 + wzWindowClass = PROGRESS_CLASSW;
4732 + }
4733 + break;
4734 +
4735 + case THEME_CONTROL_TYPE_RADIOBUTTON:
4736 + dwWindowBits |= BS_AUTORADIOBUTTON | BS_MULTILINE;
4737 + wzWindowClass = WC_BUTTONW;
4738 +
4739 + if (pControl->fLastRadioButton)
4740 + {
4741 + fStartNewGroup = TRUE;
4742 + }
4743 + break;
4744 +
4745 + case THEME_CONTROL_TYPE_RICHEDIT:
4746 + if (!vhModuleRichEd)
4747 + {
4748 + hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd);
4749 + ExitOnFailure(hr, "Failed to load Rich Edit control library.");
4750 + }
4751 + wzWindowClass = RICHEDIT_CLASSW;
4752 + dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY;
4753 + break;
4754 +
4755 + case THEME_CONTROL_TYPE_STATIC:
4756 + wzWindowClass = WC_STATICW;
4757 + dwWindowBits |= SS_ETCHEDHORZ;
4758 + break;
4759 +
4760 + case THEME_CONTROL_TYPE_TAB:
4761 + wzWindowClass = WC_TABCONTROLW;
4762 + break;
4763 +
4764 + case THEME_CONTROL_TYPE_TREEVIEW:
4765 + wzWindowClass = WC_TREEVIEWW;
4766 + break;
4767 + }
4768 + ExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type);
4769 +
4770 + // Default control ids to the theme id and its index in the control array, unless there
4771 + // is a specific id to assign to a named control.
4772 + WORD wControlId = MAKEWORD(i, pTheme->wId);
4773 + for (DWORD iAssignControl = 0; pControl->sczName && iAssignControl < cAssignControlIds; ++iAssignControl)
4774 + {
4775 + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pControl->sczName, -1, rgAssignControlIds[iAssignControl].wzName, -1))
4776 + {
4777 + wControlId = rgAssignControlIds[iAssignControl].wId;
4778 + break;
4779 + }
4780 + }
4781 +
4782 + pControl->wId = wControlId;
4783 +
4784 + int w, h, x, y;
4785 + GetControlDimensions(&rcParent, pControl, &w, &h, &x, &y);
4786 +
4787 + BOOL fVisible = pControl->dwStyle & WS_VISIBLE;
4788 + BOOL fDisabled = pControl->dwStyle & WS_DISABLED;
4789 +
4790 + // If the control is supposed to be initially visible and it has a VisibleCondition, check if it's true.
4791 + if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality)
4792 + {
4793 + hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext);
4794 + ExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition);
4795 +
4796 + if (!fVisible)
4797 + {
4798 + pControl->dwStyle &= ~WS_VISIBLE;
4799 + }
4800 + }
4801 +
4802 + // Disable controls that aren't visible so their shortcut keys don't trigger.
4803 + if (!fVisible)
4804 + {
4805 + dwWindowBits |= WS_DISABLED;
4806 + fDisabled = TRUE;
4807 + }
4808 +
4809 + // If the control is supposed to be initially enabled and it has an EnableCondition, check if it's true.
4810 + if (!fDisabled && pControl->sczEnableCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality)
4811 + {
4812 + BOOL fEnable = TRUE;
4813 +
4814 + hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext);
4815 + ExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition);
4816 +
4817 + fDisabled = !fEnable;
4818 + dwWindowBits |= fDisabled ? WS_DISABLED : 0;
4819 + }
4820 +
4821 + // Honor the HideWhenDisabled option.
4822 + if ((pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) && fVisible && fDisabled)
4823 + {
4824 + fVisible = FALSE;
4825 + pControl->dwStyle &= ~WS_VISIBLE;
4826 + }
4827 +
4828 + pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast<HMENU>(wControlId), NULL, pTheme);
4829 + ExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window.");
4830 +
4831 + if (pControl->sczTooltip)
4832 + {
4833 + if (!pTheme->hwndTooltip)
4834 + {
4835 + pTheme->hwndTooltip = ::CreateWindowExW(WS_EX_TOOLWINDOW, TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, NULL, NULL, NULL);
4836 + }
4837 +
4838 + if (pTheme->hwndTooltip)
4839 + {
4840 + TOOLINFOW toolinfo = {};
4841 + toolinfo.cbSize = sizeof(toolinfo);
4842 + toolinfo.hwnd = hwndParent;
4843 + toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
4844 + toolinfo.uId = reinterpret_cast<UINT_PTR>(pControl->hWnd);
4845 + toolinfo.lpszText = pControl->sczTooltip;
4846 + ::SendMessageW(pTheme->hwndTooltip, TTM_ADDTOOLW, 0, reinterpret_cast<LPARAM>(&toolinfo));
4847 + }
4848 + }
4849 +
4850 + if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
4851 + {
4852 + if (pControl->sczNote)
4853 + {
4854 + ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast<WPARAM>(pControl->sczNote));
4855 + }
4856 +
4857 + if (pControl->hImage)
4858 + {
4859 + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast<LPARAM>(pControl->hImage));
4860 + }
4861 + else if (pControl->hIcon)
4862 + {
4863 + ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_ICON, reinterpret_cast<LPARAM>(pControl->hIcon));
4864 + }
4865 + }
4866 + else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type)
4867 + {
4868 + if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE)
4869 + {
4870 + hr = ::SHAutoComplete(pControl->hWnd, SHACF_FILESYS_ONLY);
4871 + }
4872 + }
4873 + else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
4874 + {
4875 + ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle);
4876 +
4877 + hr = SizeListViewColumns(pControl);
4878 + ExitOnFailure(hr, "Failed to get size of list view columns.");
4879 +
4880 + for (DWORD j = 0; j < pControl->cColumns; ++j)
4881 + {
4882 + LVCOLUMNW lvc = { };
4883 + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
4884 + lvc.cx = pControl->ptcColumns[j].nWidth;
4885 + lvc.iSubItem = j;
4886 + lvc.pszText = pControl->ptcColumns[j].pszName;
4887 + lvc.fmt = LVCFMT_LEFT;
4888 + lvc.cchTextMax = 4;
4889 +
4890 + if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc)))
4891 + {
4892 + ExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j);
4893 + }
4894 +
4895 + // Return value tells us the old image list, we don't care.
4896 + if (pControl->rghImageList[0])
4897 + {
4898 + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_NORMAL), reinterpret_cast<LPARAM>(pControl->rghImageList[0]));
4899 + }
4900 + else if (pControl->rghImageList[1])
4901 + {
4902 + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_SMALL), reinterpret_cast<LPARAM>(pControl->rghImageList[1]));
4903 + }
4904 + else if (pControl->rghImageList[2])
4905 + {
4906 + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_STATE), reinterpret_cast<LPARAM>(pControl->rghImageList[2]));
4907 + }
4908 + else if (pControl->rghImageList[3])
4909 + {
4910 + ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_GROUPHEADER), reinterpret_cast<LPARAM>(pControl->rghImageList[3]));
4911 + }
4912 + }
4913 + }
4914 + else if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type)
4915 + {
4916 + ::SendMessageW(pControl->hWnd, EM_AUTOURLDETECT, static_cast<WPARAM>(TRUE), 0);
4917 + ::SendMessageW(pControl->hWnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_LINK);
4918 + }
4919 + else if (THEME_CONTROL_TYPE_TAB == pControl->type)
4920 + {
4921 + ULONG_PTR hbrBackground = 0;
4922 + if (THEME_INVALID_ID != pControl->dwFontId)
4923 + {
4924 + hbrBackground = reinterpret_cast<ULONG_PTR>(pTheme->rgFonts[pControl->dwFontId].hBackground);
4925 + }
4926 + else
4927 + {
4928 + hbrBackground = ::GetClassLongPtr(pTheme->hwndParent, GCLP_HBRBACKGROUND);
4929 + }
4930 + ::SetClassLongPtr(pControl->hWnd, GCLP_HBRBACKGROUND, hbrBackground);
4931 +
4932 + for (DWORD j = 0; j < pControl->cTabs; ++j)
4933 + {
4934 + TCITEMW tci = { };
4935 + tci.mask = TCIF_TEXT | TCIF_IMAGE;
4936 + tci.iImage = -1;
4937 + tci.pszText = pControl->pttTabs[j].pszName;
4938 +
4939 + if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci)))
4940 + {
4941 + ExitWithLastError(hr, "Failed to insert tab %u into tab control.", j);
4942 + }
4943 + }
4944 + }
4945 +
4946 + if (pControlFont)
4947 + {
4948 + ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFont->hFont, FALSE);
4949 + }
4950 +
4951 + // Initialize the text on all "application" (non-page) controls, best effort only.
4952 + if (pTheme->pfnFormatString && !pControl->wPageId && pControl->sczText && *pControl->sczText)
4953 + {
4954 + HRESULT hrFormat = pTheme->pfnFormatString(pControl->sczText, &sczText, pTheme->pvVariableContext);
4955 + if (SUCCEEDED(hrFormat))
4956 + {
4957 + ThemeSetTextControl(pTheme, pControl->wId, sczText);
4958 + }
4959 + }
4960 +
4961 + if (pControl->cControls)
4962 + {
4963 + hr = LoadControls(pTheme, pControl, pControl->hWnd, rgAssignControlIds, cAssignControlIds);
4964 + ExitOnFailure(hr, "Failed to load child controls.");
4965 + }
4966 + }
4967 +
4968 +LExit:
4969 + ReleaseStr(sczText);
4970 +
4971 + return hr;
4972 +}
4973 +
4974 +static HRESULT LocalizeControls(
4975 + __in DWORD cControls,
4976 + __in THEME_CONTROL* rgControls,
4977 + __in const WIX_LOCALIZATION *pWixLoc
4978 + )
4979 +{
4980 + HRESULT hr = S_OK;
4981 +
4982 + for (DWORD i = 0; i < cControls; ++i)
4983 + {
4984 + THEME_CONTROL* pControl = rgControls + i;
4985 + hr = LocalizeControl(pControl, pWixLoc);
4986 + ExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName);
4987 + }
4988 +
4989 +LExit:
4990 + return hr;
4991 +}
4992 +
4993 +static HRESULT LocalizeControl(
4994 + __in THEME_CONTROL* pControl,
4995 + __in const WIX_LOCALIZATION *pWixLoc
4996 + )
4997 +{
4998 + HRESULT hr = S_OK;
4999 + LOC_CONTROL* pLocControl = NULL;

This file is too large to show in full.

src/dutil/timeutil.cpp new
+370
@@ -0,0 +1,370 @@
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 +
5 +const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" };
6 +const LPCWSTR MONTH_OF_YEAR[] = { L"None", L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" };
7 +enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone };
8 +enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone };
9 +
10 +// prototypes
11 +static HRESULT DayFromString(
12 + __in_z LPCWSTR wzDay,
13 + __out WORD* pwDayOfWeek
14 + );
15 +static HRESULT MonthFromString(
16 + __in_z LPCWSTR wzMonth,
17 + __out WORD* pwMonthOfYear
18 + );
19 +
20 +
21 +/********************************************************************
22 + TimeFromString - converts string to FILETIME
23 +
24 +*******************************************************************/
25 +extern "C" HRESULT DAPI TimeFromString(
26 + __in_z LPCWSTR wzTime,
27 + __out FILETIME* pFileTime
28 + )
29 +{
30 + Assert(wzTime && pFileTime);
31 +
32 + HRESULT hr = S_OK;
33 + LPWSTR pwzTime = NULL;
34 +
35 + SYSTEMTIME sysTime = { };
36 + TIME_PARSER timeParser = DayOfWeek;
37 +
38 + LPCWSTR pwzStart = NULL;
39 + LPWSTR pwzEnd = NULL;
40 +
41 + hr = StrAllocString(&pwzTime, wzTime, 0);
42 + ExitOnFailure(hr, "Failed to copy time.");
43 +
44 + pwzStart = pwzEnd = pwzTime;
45 + while (pwzEnd && *pwzEnd)
46 + {
47 + if (L',' == *pwzEnd || L' ' == *pwzEnd || L':' == *pwzEnd)
48 + {
49 + *pwzEnd = L'\0'; // null terminate
50 + ++pwzEnd;
51 +
52 + while (L' ' == *pwzEnd)
53 + {
54 + ++pwzEnd; // and skip past the blank space
55 + }
56 +
57 + switch (timeParser)
58 + {
59 + case DayOfWeek:
60 + hr = DayFromString(pwzStart, &sysTime.wDayOfWeek);
61 + ExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart);
62 + break;
63 +
64 + case DayOfMonth:
65 + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10);
66 + break;
67 +
68 + case MonthOfYear:
69 + hr = MonthFromString(pwzStart, &sysTime.wMonth);
70 + ExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart);
71 + break;
72 +
73 + case Year:
74 + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10);
75 + break;
76 +
77 + case Hours:
78 + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10);
79 + break;
80 +
81 + case Minutes:
82 + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10);
83 + break;
84 +
85 + case Seconds:
86 + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10);
87 + break;
88 +
89 + case TimeZone:
90 + // TODO: do something with this in the future, but this should only hit outside of the while loop.
91 + break;
92 +
93 + default:
94 + break;
95 + }
96 +
97 + pwzStart = pwzEnd;
98 + timeParser = (TIME_PARSER)((int)timeParser + 1);
99 + }
100 +
101 + ++pwzEnd;
102 + }
103 +
104 +
105 + if (!::SystemTimeToFileTime(&sysTime, pFileTime))
106 + {
107 + ExitWithLastError(hr, "Failed to convert system time to file time.");
108 + }
109 +
110 +LExit:
111 + ReleaseStr(pwzTime);
112 +
113 + return hr;
114 +}
115 +
116 +/********************************************************************
117 + TimeFromString3339 - converts string formated in accorance with RFC3339 to FILETIME
118 + http://tools.ietf.org/html/rfc3339
119 +*******************************************************************/
120 +extern "C" HRESULT DAPI TimeFromString3339(
121 + __in_z LPCWSTR wzTime,
122 + __out FILETIME* pFileTime
123 + )
124 +{
125 + Assert(wzTime && pFileTime);
126 +
127 + HRESULT hr = S_OK;
128 + LPWSTR pwzTime = NULL;
129 +
130 + SYSTEMTIME sysTime = { };
131 + TIME_PARSERRFC3339 timeParser = RFC3339_Year;
132 +
133 + LPCWSTR pwzStart = NULL;
134 + LPWSTR pwzEnd = NULL;
135 +
136 + hr = StrAllocString(&pwzTime, wzTime, 0);
137 + ExitOnFailure(hr, "Failed to copy time.");
138 +
139 + pwzStart = pwzEnd = pwzTime;
140 + while (pwzEnd && *pwzEnd)
141 + {
142 + if (L'T' == *pwzEnd || L':' == *pwzEnd || L'-' == *pwzEnd)
143 + {
144 + *pwzEnd = L'\0'; // null terminate
145 + ++pwzEnd;
146 +
147 + switch (timeParser)
148 + {
149 + case RFC3339_Year:
150 + sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10);
151 + break;
152 +
153 + case RFC3339_Month:
154 + sysTime.wMonth = (WORD)wcstoul(pwzStart, NULL, 10);
155 + break;
156 +
157 + case RFC3339_Day:
158 + sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10);
159 + break;
160 +
161 + case RFC3339_Hours:
162 + sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10);
163 + break;
164 +
165 + case RFC3339_Minutes:
166 + sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10);
167 + break;
168 +
169 + case RFC3339_Seconds:
170 + sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10);
171 + break;
172 +
173 + case RFC3339_TimeZone:
174 + // TODO: do something with this in the future, but this should only hit outside of the while loop.
175 + break;
176 +
177 + default:
178 + break;
179 + }
180 +
181 + pwzStart = pwzEnd;
182 + timeParser = (TIME_PARSERRFC3339)((int)timeParser + 1);
183 + }
184 +
185 + ++pwzEnd;
186 + }
187 +
188 +
189 + if (!::SystemTimeToFileTime(&sysTime, pFileTime))
190 + {
191 + ExitWithLastError(hr, "Failed to convert system time to file time.");
192 + }
193 +
194 +LExit:
195 + ReleaseStr(pwzTime);
196 +
197 + return hr;
198 +}
199 +/****************************************************************************
200 +TimeCurrentTime - gets the current time in string format
201 +
202 +****************************************************************************/
203 +extern "C" HRESULT DAPI TimeCurrentTime(
204 + __deref_out_z LPWSTR* ppwz,
205 + __in BOOL fGMT
206 + )
207 +{
208 + SYSTEMTIME st;
209 +
210 + if (fGMT)
211 + {
212 + ::GetSystemTime(&st);
213 + }
214 + else
215 + {
216 + SYSTEMTIME stGMT;
217 + TIME_ZONE_INFORMATION tzi;
218 +
219 + ::GetTimeZoneInformation(&tzi);
220 + ::GetSystemTime(&stGMT);
221 + ::SystemTimeToTzSpecificLocalTime(&tzi, &stGMT, &st);
222 + }
223 +
224 + return StrAllocFormatted(ppwz, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
225 +}
226 +
227 +
228 +/****************************************************************************
229 +TimeCurrentDateTime - gets the current date and time in string format,
230 + per format described in RFC 3339
231 +****************************************************************************/
232 +extern "C" HRESULT DAPI TimeCurrentDateTime(
233 + __deref_out_z LPWSTR* ppwz,
234 + __in BOOL fGMT
235 + )
236 +{
237 + SYSTEMTIME st;
238 +
239 + ::GetSystemTime(&st);
240 +
241 + return TimeSystemDateTime(ppwz, &st, fGMT);
242 +}
243 +
244 +
245 +/****************************************************************************
246 +TimeSystemDateTime - converts the provided system time struct to string format,
247 + per format described in RFC 3339
248 +****************************************************************************/
249 +extern "C" HRESULT DAPI TimeSystemDateTime(
250 + __deref_out_z LPWSTR* ppwz,
251 + __in const SYSTEMTIME *pst,
252 + __in BOOL fGMT
253 + )
254 +{
255 + DWORD dwAbsBias = 0;
256 +
257 + if (fGMT)
258 + {
259 + return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02huZ", pst->wYear, pst->wMonth, pst->wDay, pst->wHour, pst->wMinute, pst->wSecond);
260 + }
261 + else
262 + {
263 + SYSTEMTIME st;
264 + TIME_ZONE_INFORMATION tzi;
265 +
266 + ::GetTimeZoneInformation(&tzi);
267 + ::SystemTimeToTzSpecificLocalTime(&tzi, pst, &st);
268 + dwAbsBias = abs(tzi.Bias);
269 +
270 + return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02u:%02u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, 0 >= tzi.Bias ? L'+' : L'-', dwAbsBias / 60, dwAbsBias % 60);
271 + }
272 +}
273 +
274 +
275 +/****************************************************************************
276 +TimeSystemToDateTimeString - converts the provided system time struct to
277 + string format representing date and time for the specified locale
278 +****************************************************************************/
279 +HRESULT DAPI TimeSystemToDateTimeString(
280 + __deref_out_z LPWSTR* ppwz,
281 + __in const SYSTEMTIME* pst,
282 + __in LCID locale
283 + )
284 +{
285 + HRESULT hr = S_OK;
286 + const WCHAR * DATE_FORMAT = L"MMM dd',' yyyy',' ";
287 + const WCHAR * TIME_FORMAT = L"hh':'mm':'ss tt";
288 + int iLenDate = 0;
289 + int iLenTime = 0;
290 +
291 + iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0);
292 + if (0 >= iLenDate)
293 + {
294 + ExitWithLastError(hr, "Failed to get date format with NULL");
295 + }
296 +
297 + iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0);
298 + if (0 >= iLenTime)
299 + {
300 + ExitWithLastError(hr, "Failed to get time format with NULL");
301 + }
302 +
303 + // Between both lengths we account for 2 null terminators, and only need one, so we subtract one
304 + hr = StrAlloc(ppwz, iLenDate + iLenTime - 1);
305 + ExitOnFailure(hr, "Failed to allocate string");
306 +
307 + if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate))
308 + {
309 + ExitWithLastError(hr, "Failed to get date format with buffer");
310 + }
311 + // Space to separate them
312 + (*ppwz)[iLenDate - 1] = ' ';
313 +
314 + if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime))
315 + {
316 + ExitWithLastError(hr, "Failed to get time format with buffer");
317 + }
318 +
319 +LExit:
320 + return hr;
321 +}
322 +
323 +/********************************************************************
324 + DayFromString - converts string to day
325 +
326 +*******************************************************************/
327 +static HRESULT DayFromString(
328 + __in_z LPCWSTR wzDay,
329 + __out WORD* pwDayOfWeek
330 + )
331 +{
332 + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name
333 +
334 + for (WORD i = 0; i < countof(DAY_OF_WEEK); ++i)
335 + {
336 + if (0 == lstrcmpW(wzDay, DAY_OF_WEEK[i]))
337 + {
338 + *pwDayOfWeek = i;
339 + hr = S_OK;
340 + break;
341 + }
342 + }
343 +
344 + return hr;
345 +}
346 +
347 +
348 +/********************************************************************
349 + MonthFromString - converts string to month
350 +
351 +*******************************************************************/
352 +static HRESULT MonthFromString(
353 + __in_z LPCWSTR wzMonth,
354 + __out WORD* pwMonthOfYear
355 + )
356 +{
357 + HRESULT hr = E_INVALIDARG; // assume we won't find a matching name
358 +
359 + for (WORD i = 0; i < countof(MONTH_OF_YEAR); ++i)
360 + {
361 + if (0 == lstrcmpW(wzMonth, MONTH_OF_YEAR[i]))
362 + {
363 + *pwMonthOfYear = i;
364 + hr = S_OK;
365 + break;
366 + }
367 + }
368 +
369 + return hr;
370 +}
src/dutil/uncutil.cpp new
+54
@@ -0,0 +1,54 @@
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 +
5 +DAPI_(HRESULT) UncConvertFromMountedDrive(
6 + __inout LPWSTR *psczUNCPath,
7 + __in LPCWSTR sczMountedDrivePath
8 + )
9 +{
10 + HRESULT hr = S_OK;
11 + DWORD dwLength = 0;
12 + DWORD er = ERROR_SUCCESS;
13 + LPWSTR sczDrive = NULL;
14 +
15 + // Only copy drive letter and colon
16 + hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2);
17 + ExitOnFailure(hr, "Failed to copy drive");
18 +
19 + // ERROR_NOT_CONNECTED means it's not a mapped drive
20 + er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength);
21 + if (ERROR_MORE_DATA == er)
22 + {
23 + er = ERROR_SUCCESS;
24 +
25 + hr = StrAlloc(psczUNCPath, dwLength);
26 + ExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength);
27 +
28 + er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength);
29 + if (ERROR_CONNECTION_UNAVAIL == er)
30 + {
31 + // This means the drive is remembered but not currently connected, this can mean the location is accessible via UNC path but not via mounted drive path
32 + er = ERROR_SUCCESS;
33 + }
34 + ExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive);
35 +
36 + // Skip drive letter and colon
37 + hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0);
38 + ExitOnFailure(hr, "Failed to copy rest of database path");
39 + }
40 + else
41 + {
42 + if (ERROR_SUCCESS == er)
43 + {
44 + er = ERROR_NO_DATA;
45 + }
46 +
47 + ExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive);
48 + }
49 +
50 +LExit:
51 + ReleaseStr(sczDrive);
52 +
53 + return hr;
54 +}
src/dutil/uriutil.cpp new
+538
@@ -0,0 +1,538 @@
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 +
5 +
6 +//
7 +// UriCanonicalize - canonicalizes a URI.
8 +//
9 +extern "C" HRESULT DAPI UriCanonicalize(
10 + __inout_z LPWSTR* psczUri
11 + )
12 +{
13 + HRESULT hr = S_OK;
14 + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { };
15 + DWORD cch = countof(wz);
16 +
17 + if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE))
18 + {
19 + ExitWithLastError(hr, "Failed to canonicalize URI.");
20 + }
21 +
22 + hr = StrAllocString(psczUri, wz, cch);
23 + ExitOnFailure(hr, "Failed copy canonicalized URI.");
24 +
25 +LExit:
26 + return hr;
27 +}
28 +
29 +
30 +//
31 +// UriCrack - cracks a URI into constituent parts.
32 +//
33 +extern "C" HRESULT DAPI UriCrack(
34 + __in_z LPCWSTR wzUri,
35 + __out_opt INTERNET_SCHEME* pScheme,
36 + __deref_opt_out_z LPWSTR* psczHostName,
37 + __out_opt INTERNET_PORT* pPort,
38 + __deref_opt_out_z LPWSTR* psczUser,
39 + __deref_opt_out_z LPWSTR* psczPassword,
40 + __deref_opt_out_z LPWSTR* psczPath,
41 + __deref_opt_out_z LPWSTR* psczQueryString
42 + )
43 +{
44 + HRESULT hr = S_OK;
45 + URL_COMPONENTSW components = { };
46 + WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1];
47 + WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];
48 + WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1];
49 + WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1];
50 + WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1];
51 +
52 + components.dwStructSize = sizeof(URL_COMPONENTSW);
53 +
54 + if (psczHostName)
55 + {
56 + components.lpszHostName = wzHostName;
57 + components.dwHostNameLength = countof(wzHostName);
58 + }
59 +
60 + if (psczUser)
61 + {
62 + components.lpszUserName = wzUserName;
63 + components.dwUserNameLength = countof(wzUserName);
64 + }
65 +
66 + if (psczPassword)
67 + {
68 + components.lpszPassword = wzPassword;
69 + components.dwPasswordLength = countof(wzPassword);
70 + }
71 +
72 + if (psczPath)
73 + {
74 + components.lpszUrlPath = wzPath;
75 + components.dwUrlPathLength = countof(wzPath);
76 + }
77 +
78 + if (psczQueryString)
79 + {
80 + components.lpszExtraInfo = wzQueryString;
81 + components.dwExtraInfoLength = countof(wzQueryString);
82 + }
83 +
84 + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components))
85 + {
86 + ExitWithLastError(hr, "Failed to crack URI.");
87 + }
88 +
89 + if (pScheme)
90 + {
91 + *pScheme = components.nScheme;
92 + }
93 +
94 + if (psczHostName)
95 + {
96 + hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength);
97 + ExitOnFailure(hr, "Failed to copy host name.");
98 + }
99 +
100 + if (pPort)
101 + {
102 + *pPort = components.nPort;
103 + }
104 +
105 + if (psczUser)
106 + {
107 + hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength);
108 + ExitOnFailure(hr, "Failed to copy user name.");
109 + }
110 +
111 + if (psczPassword)
112 + {
113 + hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength);
114 + ExitOnFailure(hr, "Failed to copy password.");
115 + }
116 +
117 + if (psczPath)
118 + {
119 + hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength);
120 + ExitOnFailure(hr, "Failed to copy path.");
121 + }
122 +
123 + if (psczQueryString)
124 + {
125 + hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength);
126 + ExitOnFailure(hr, "Failed to copy query string.");
127 + }
128 +
129 +LExit:
130 + return hr;
131 +}
132 +
133 +
134 +//
135 +// UriCrackEx - cracks a URI into URI_INFO.
136 +//
137 +extern "C" HRESULT DAPI UriCrackEx(
138 + __in_z LPCWSTR wzUri,
139 + __in URI_INFO* pUriInfo
140 + )
141 +{
142 + HRESULT hr = S_OK;
143 +
144 + hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString);
145 + ExitOnFailure(hr, "Failed to crack URI.");
146 +
147 +LExit:
148 + return hr;
149 +}
150 +
151 +
152 +//
153 +// UriInfoUninitialize - frees the memory in a URI_INFO struct.
154 +//
155 +extern "C" void DAPI UriInfoUninitialize(
156 + __in URI_INFO* pUriInfo
157 + )
158 +{
159 + ReleaseStr(pUriInfo->sczHostName);
160 + ReleaseStr(pUriInfo->sczUser);
161 + ReleaseStr(pUriInfo->sczPassword);
162 + ReleaseStr(pUriInfo->sczPath);
163 + ReleaseStr(pUriInfo->sczQueryString);
164 + memset(pUriInfo, 0, sizeof(URI_INFO));
165 +}
166 +
167 +
168 +//
169 +// UriCreate - creates a URI from constituent parts.
170 +//
171 +extern "C" HRESULT DAPI UriCreate(
172 + __inout_z LPWSTR* psczUri,
173 + __in INTERNET_SCHEME scheme,
174 + __in_z_opt LPWSTR wzHostName,
175 + __in INTERNET_PORT port,
176 + __in_z_opt LPWSTR wzUser,
177 + __in_z_opt LPWSTR wzPassword,
178 + __in_z_opt LPWSTR wzPath,
179 + __in_z_opt LPWSTR wzQueryString
180 + )
181 +{
182 + HRESULT hr = S_OK;
183 + WCHAR wz[INTERNET_MAX_URL_LENGTH] = { };
184 + DWORD cch = countof(wz);
185 + URL_COMPONENTSW components = { };
186 +
187 + components.dwStructSize = sizeof(URL_COMPONENTSW);
188 + components.nScheme = scheme;
189 + components.lpszHostName = wzHostName;
190 + components.nPort = port;
191 + components.lpszUserName = wzUser;
192 + components.lpszPassword = wzPassword;
193 + components.lpszUrlPath = wzPath;
194 + components.lpszExtraInfo = wzQueryString;
195 +
196 + if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch))
197 + {
198 + ExitWithLastError(hr, "Failed to create URI.");
199 + }
200 +
201 + hr = StrAllocString(psczUri, wz, cch);
202 + ExitOnFailure(hr, "Failed copy created URI.");
203 +
204 +LExit:
205 + return hr;
206 +}
207 +
208 +
209 +//
210 +// UriGetServerAndResource - gets the server and resource as independent strings from a URI.
211 +//
212 +// NOTE: This function is useful for the InternetConnect/HttpRequest APIs.
213 +//
214 +extern "C" HRESULT DAPI UriGetServerAndResource(
215 + __in_z LPCWSTR wzUri,
216 + __out_z LPWSTR* psczServer,
217 + __out_z LPWSTR* psczResource
218 + )
219 +{
220 + HRESULT hr = S_OK;
221 + INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN;
222 + LPWSTR sczHostName = NULL;
223 + INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER;
224 + LPWSTR sczUser = NULL;
225 + LPWSTR sczPassword = NULL;
226 + LPWSTR sczPath = NULL;
227 + LPWSTR sczQueryString = NULL;
228 +
229 + hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString);
230 + ExitOnFailure(hr, "Failed to crack URI.");
231 +
232 + hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL);
233 + ExitOnFailure(hr, "Failed to allocate server URI.");
234 +
235 + hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString);
236 + ExitOnFailure(hr, "Failed to allocate resource URI.");
237 +
238 +LExit:
239 + ReleaseStr(sczQueryString);
240 + ReleaseStr(sczPath);
241 + ReleaseStr(sczPassword);
242 + ReleaseStr(sczUser);
243 + ReleaseStr(sczHostName);
244 +
245 + return hr;
246 +}
247 +
248 +
249 +//
250 +// UriFile - returns the file part of the URI.
251 +//
252 +extern "C" HRESULT DAPI UriFile(
253 + __deref_out_z LPWSTR* psczFile,
254 + __in_z LPCWSTR wzUri
255 + )
256 +{
257 + HRESULT hr = S_OK;
258 + WCHAR wz[MAX_PATH + 1];
259 + DWORD cch = countof(wz);
260 + URL_COMPONENTSW uc = { };
261 +
262 + uc.dwStructSize = sizeof(uc);
263 + uc.lpszUrlPath = wz;
264 + uc.dwUrlPathLength = cch;
265 +
266 + if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc))
267 + {
268 + ExitWithLastError(hr, "Failed to crack URI.");
269 + }
270 +
271 + // Copy only the file name. Fortunately, PathFile() understands that
272 + // forward slashes can be directory separators like backslashes.
273 + hr = StrAllocString(psczFile, PathFile(wz), 0);
274 + ExitOnFailure(hr, "Failed to copy file name");
275 +
276 +LExit:
277 + return hr;
278 +}
279 +
280 +
281 +/*******************************************************************
282 + UriProtocol - determines the protocol of a URI.
283 +
284 +********************************************************************/
285 +extern "C" HRESULT DAPI UriProtocol(
286 + __in_z LPCWSTR wzUri,
287 + __out URI_PROTOCOL* pProtocol
288 + )
289 +{
290 + Assert(wzUri && *wzUri);
291 + Assert(pProtocol);
292 +
293 + HRESULT hr = S_OK;
294 +
295 + if ((L'h' == wzUri[0] || L'H' == wzUri[0]) &&
296 + (L't' == wzUri[1] || L'T' == wzUri[1]) &&
297 + (L't' == wzUri[2] || L'T' == wzUri[2]) &&
298 + (L'p' == wzUri[3] || L'P' == wzUri[3]) &&
299 + (L's' == wzUri[4] || L'S' == wzUri[4]) &&
300 + L':' == wzUri[5] &&
301 + L'/' == wzUri[6] &&
302 + L'/' == wzUri[7])
303 + {
304 + *pProtocol = URI_PROTOCOL_HTTPS;
305 + }
306 + else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) &&
307 + (L't' == wzUri[1] || L'T' == wzUri[1]) &&
308 + (L't' == wzUri[2] || L'T' == wzUri[2]) &&
309 + (L'p' == wzUri[3] || L'P' == wzUri[3]) &&
310 + L':' == wzUri[4] &&
311 + L'/' == wzUri[5] &&
312 + L'/' == wzUri[6])
313 + {
314 + *pProtocol = URI_PROTOCOL_HTTP;
315 + }
316 + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) &&
317 + (L't' == wzUri[1] || L'T' == wzUri[1]) &&
318 + (L'p' == wzUri[2] || L'P' == wzUri[2]) &&
319 + L':' == wzUri[3] &&
320 + L'/' == wzUri[4] &&
321 + L'/' == wzUri[5])
322 + {
323 + *pProtocol = URI_PROTOCOL_FTP;
324 + }
325 + else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) &&
326 + (L'i' == wzUri[1] || L'I' == wzUri[1]) &&
327 + (L'l' == wzUri[2] || L'L' == wzUri[2]) &&
328 + (L'e' == wzUri[3] || L'E' == wzUri[3]) &&
329 + L':' == wzUri[4] &&
330 + L'/' == wzUri[5] &&
331 + L'/' == wzUri[6])
332 + {
333 + *pProtocol = URI_PROTOCOL_FILE;
334 + }
335 + else
336 + {
337 + *pProtocol = URI_PROTOCOL_UNKNOWN;
338 + }
339 +
340 + return hr;
341 +}
342 +
343 +
344 +/*******************************************************************
345 + UriRoot - returns the root of the path specified in the URI.
346 +
347 + examples:
348 + file:///C:\path\path -> C:\
349 + file://server/share/path/path -> \\server\share
350 + http://www.example.com/path/path -> http://www.example.com/
351 + ftp://ftp.example.com/path/path -> ftp://www.example.com/
352 +
353 + NOTE: This function should only be used on cannonicalized URIs.
354 + It does not cannonicalize itself.
355 +********************************************************************/
356 +extern "C" HRESULT DAPI UriRoot(
357 + __in_z LPCWSTR wzUri,
358 + __out LPWSTR* ppwzRoot,
359 + __out_opt URI_PROTOCOL* pProtocol
360 + )
361 +{
362 + Assert(wzUri && *wzUri);
363 + Assert(ppwzRoot);
364 +
365 + HRESULT hr = S_OK;
366 + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN;
367 + LPCWSTR pwcSlash = NULL;
368 +
369 + hr = UriProtocol(wzUri, &protocol);
370 + ExitOnFailure(hr, "Invalid URI.");
371 +
372 + switch (protocol)
373 + {
374 + case URI_PROTOCOL_FILE:
375 + if (L'/' == wzUri[7]) // file path
376 + {
377 + if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9])
378 + {
379 + hr = StrAlloc(ppwzRoot, 4);
380 + ExitOnFailure(hr, "Failed to allocate string for root of URI.");
381 + *ppwzRoot[0] = wzUri[8];
382 + *ppwzRoot[1] = L':';
383 + *ppwzRoot[2] = L'\\';
384 + *ppwzRoot[3] = L'\0';
385 + }
386 + else
387 + {
388 + hr = E_INVALIDARG;
389 + ExitOnFailure(hr, "Invalid file path in URI.");
390 + }
391 + }
392 + else // UNC share
393 + {
394 + pwcSlash = wcschr(wzUri + 8, L'/');
395 + if (!pwcSlash)
396 + {
397 + hr = E_INVALIDARG;
398 + ExitOnFailure(hr, "Invalid server name in URI.");
399 + }
400 + else
401 + {
402 + hr = StrAllocString(ppwzRoot, L"\\\\", 64);
403 + ExitOnFailure(hr, "Failed to allocate string for root of URI.");
404 +
405 + pwcSlash = wcschr(pwcSlash + 1, L'/');
406 + if (pwcSlash)
407 + {
408 + hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8);
409 + ExitOnFailure(hr, "Failed to add server/share to root of URI.");
410 + }
411 + else
412 + {
413 + hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0);
414 + ExitOnFailure(hr, "Failed to add server/share to root of URI.");
415 + }
416 +
417 + // replace all slashes with backslashes to be truly UNC.
418 + for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc)
419 + {
420 + if (L'/' == *pwc)
421 + {
422 + *pwc = L'\\';
423 + }
424 + }
425 + }
426 + }
427 + break;
428 +
429 + case URI_PROTOCOL_FTP:
430 + pwcSlash = wcschr(wzUri + 6, L'/');
431 + if (pwcSlash)
432 + {
433 + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri);
434 + ExitOnFailure(hr, "Failed allocate root from URI.");
435 + }
436 + else
437 + {
438 + hr = StrAllocString(ppwzRoot, wzUri, 0);
439 + ExitOnFailure(hr, "Failed allocate root from URI.");
440 + }
441 + break;
442 +
443 + case URI_PROTOCOL_HTTP:
444 + pwcSlash = wcschr(wzUri + 7, L'/');
445 + if (pwcSlash)
446 + {
447 + hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri);
448 + ExitOnFailure(hr, "Failed allocate root from URI.");
449 + }
450 + else
451 + {
452 + hr = StrAllocString(ppwzRoot, wzUri, 0);
453 + ExitOnFailure(hr, "Failed allocate root from URI.");
454 + }
455 + break;
456 +
457 + default:
458 + hr = E_INVALIDARG;
459 + ExitOnFailure(hr, "Unknown URI protocol.");
460 + }
461 +
462 + if (pProtocol)
463 + {
464 + *pProtocol = protocol;
465 + }
466 +
467 +LExit:
468 + return hr;
469 +}
470 +
471 +
472 +extern "C" HRESULT DAPI UriResolve(
473 + __in_z LPCWSTR wzUri,
474 + __in_opt LPCWSTR wzBaseUri,
475 + __out LPWSTR* ppwzResolvedUri,
476 + __out_opt const URI_PROTOCOL* pResolvedProtocol
477 + )
478 +{
479 + UNREFERENCED_PARAMETER(wzUri);
480 + UNREFERENCED_PARAMETER(wzBaseUri);
481 + UNREFERENCED_PARAMETER(ppwzResolvedUri);
482 + UNREFERENCED_PARAMETER(pResolvedProtocol);
483 +
484 + HRESULT hr = E_NOTIMPL;
485 +#if 0
486 + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN;
487 +
488 + hr = UriProtocol(wzUri, &protocol);
489 + ExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri);
490 +
491 + ExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided");
492 +
493 + if (URI_PROTOCOL_UNKNOWN == protocol)
494 + {
495 + ExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL");
496 +
497 + if (L'/' == *wzUri || L'\\' == *wzUri)
498 + {
499 + hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol);
500 + ExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri);
501 +
502 + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0);
503 + ExitOnFailure(hr, "Failed to concat file to base URI.");
504 + }
505 + else
506 + {
507 + hr = UriProtocol(wzBaseUri, &protocol);
508 + ExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri);
509 +
510 + LPCWSTR pwcFile = const_cast<LPCWSTR> (UriFile(wzBaseUri));
511 + if (!pwcFile)
512 + {
513 + hr = E_INVALIDARG;
514 + ExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri);
515 + }
516 +
517 + hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri);
518 + ExitOnFailure(hr, "Failed to allocate string for resolved URI.");
519 +
520 + hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0);
521 + ExitOnFailure(hr, "Failed to concat file to resolved URI.");
522 + }
523 + }
524 + else
525 + {
526 + hr = StrAllocString(ppwzResolvedUri, wzUri, 0);
527 + ExitOnFailure(hr, "Failed to copy resolved URI.");
528 + }
529 +
530 + if (pResolvedProtocol)
531 + {
532 + *pResolvedProtocol = protocol;
533 + }
534 +
535 +LExit:
536 +#endif
537 + return hr;
538 +}
src/dutil/userutil.cpp new
+285
@@ -0,0 +1,285 @@
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 +
5 +static BOOL CheckIsMemberHelper(
6 + __in_z LPCWSTR pwzGroupUserDomain,
7 + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData,
8 + __in DWORD cguiGroupData
9 + );
10 +
11 +/*******************************************************************
12 + UserBuildDomainUserName - builds a DOMAIN\USERNAME string
13 +
14 +********************************************************************/
15 +extern "C" HRESULT DAPI UserBuildDomainUserName(
16 + __out_ecount_z(cchDest) LPWSTR wzDest,
17 + __in int cchDest,
18 + __in_z LPCWSTR pwzName,
19 + __in_z LPCWSTR pwzDomain
20 + )
21 +{
22 + HRESULT hr = S_OK;
23 + DWORD cchLeft = cchDest;
24 + WCHAR* pwz = wzDest;
25 + DWORD cchWz = cchDest;
26 + DWORD cch;
27 +
28 + cch = lstrlenW(pwzDomain);
29 + if (cch >= cchLeft)
30 + {
31 + hr = ERROR_MORE_DATA;
32 + ExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain);
33 + }
34 + else if (cch > 0)
35 + {
36 + // handle the domain case
37 +
38 + hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0'
39 + ExitOnFailure(hr, "Failed to copy Domain onto string.");
40 +
41 + cchLeft -= cch;
42 + pwz += cch;
43 + cchWz -= cch;
44 +
45 + if (1 >= cchLeft)
46 + {
47 + hr = ERROR_MORE_DATA;
48 + ExitOnFailure(hr, "Insufficient buffer size while building domain user name");
49 + }
50 +
51 + hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0'
52 + ExitOnFailure(hr, "Failed to copy backslash onto string.");
53 +
54 + --cchLeft;
55 + ++pwz;
56 + --cchWz;
57 + }
58 +
59 + cch = lstrlenW(pwzName);
60 + if (cch >= cchLeft)
61 + {
62 + hr = ERROR_MORE_DATA;
63 + ExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName);
64 + }
65 +
66 + hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0'
67 + ExitOnFailure(hr, "Failed to copy User name onto string.");
68 +
69 +LExit:
70 + return hr;
71 +}
72 +
73 +
74 +/*******************************************************************
75 + Checks whether a user is a member of a group - outputs the result via lpfMember
76 +********************************************************************/
77 +extern "C" HRESULT DAPI UserCheckIsMember(
78 + __in_z LPCWSTR pwzName,
79 + __in_z LPCWSTR pwzDomain,
80 + __in_z LPCWSTR pwzGroupName,
81 + __in_z LPCWSTR pwzGroupDomain,
82 + __out LPBOOL lpfMember
83 + )
84 +{
85 + HRESULT hr = S_OK;
86 + UINT er = ERROR_SUCCESS;
87 +
88 + DWORD dwRead = 0;
89 + DWORD dwTotal = 0;
90 + LPCWSTR wz = NULL;
91 + GROUP_USERS_INFO_0 *pguiGroupData = NULL;
92 + WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME
93 + WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME
94 + BSTR bstrUser = NULL;
95 + BSTR bstrGroup = NULL;
96 +
97 + IADsGroup *pGroup = NULL;
98 + VARIANT_BOOL vtBoolResult = VARIANT_FALSE;
99 +
100 + hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain);
101 + ExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName);
102 +
103 + hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain);
104 + ExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName);
105 +
106 + if (pwzDomain && *pwzDomain)
107 + {
108 + wz = pwzDomain;
109 + }
110 +
111 + er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal);
112 + // Ignore these errors, and just go to the fallback checks
113 + if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er)
114 + {
115 + Trace(REPORT_VERBOSE, "failed to get groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er));
116 + er = ERROR_SUCCESS;
117 + }
118 + ExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName);
119 +
120 + if (dwRead != dwTotal)
121 + {
122 + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
123 + ExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName);
124 + }
125 +
126 + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead))
127 + {
128 + *lpfMember = TRUE;
129 + ExitFunction1(hr = S_OK);
130 + }
131 +
132 + if (NULL != pguiGroupData)
133 + {
134 + ::NetApiBufferFree(pguiGroupData);
135 + pguiGroupData = NULL;
136 + }
137 +
138 + // If we fail with the global groups, try again with the local groups
139 + er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal);
140 + // Ignore these errors, and just go to the fallback checks
141 + if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er)
142 + {
143 + Trace(REPORT_VERBOSE, "failed to get local groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er));
144 + er = ERROR_SUCCESS;
145 + }
146 + ExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName);
147 +
148 + if (dwRead != dwTotal)
149 + {
150 + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
151 + ExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName);
152 + }
153 +
154 + if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead))
155 + {
156 + *lpfMember = TRUE;
157 + ExitFunction1(hr = S_OK);
158 + }
159 +
160 + // If the above methods failed, let's try active directory
161 + hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser);
162 + ExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain);
163 +
164 + hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup);
165 + ExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain);
166 +
167 + if (lstrlenW(pwzGroupDomain) > 0)
168 + {
169 + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
170 + ExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast<WCHAR*>(bstrGroup) );
171 +
172 + hr = pGroup->IsMember(bstrUser, &vtBoolResult);
173 + ExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
174 + }
175 +
176 + if (vtBoolResult)
177 + {
178 + *lpfMember = TRUE;
179 + ExitFunction1(hr = S_OK);
180 + }
181 +
182 + hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
183 + ExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast<WCHAR*>(bstrGroup) );
184 +
185 + hr = pGroup->IsMember(bstrUser, &vtBoolResult);
186 + ExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
187 +
188 + if (vtBoolResult)
189 + {
190 + *lpfMember = TRUE;
191 + ExitFunction1(hr = S_OK);
192 + }
193 +
194 +LExit:
195 + ReleaseObject(pGroup);
196 + ReleaseBSTR(bstrUser);
197 + ReleaseBSTR(bstrGroup);
198 +
199 + if (NULL != pguiGroupData)
200 + {
201 + ::NetApiBufferFree(pguiGroupData);
202 + }
203 +
204 + return hr;
205 +}
206 +
207 +
208 +/*******************************************************************
209 + Takes a domain and name, and allocates a BSTR which represents
210 + DOMAIN\NAME's active directory path. The BSTR this function returns
211 + should be released manually using the ReleaseBSTR() macro.
212 +********************************************************************/
213 +extern "C" HRESULT DAPI UserCreateADsPath(
214 + __in_z LPCWSTR wzObjectDomain,
215 + __in_z LPCWSTR wzObjectName,
216 + __out BSTR *pbstrAdsPath
217 + )
218 +{
219 + Assert(wzObjectDomain && wzObjectName && *wzObjectName);
220 +
221 + HRESULT hr = S_OK;
222 + LPWSTR pwzAdsPath = NULL;
223 +
224 + hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0);
225 + ExitOnFailure(hr, "failed to allocate AdsPath string");
226 +
227 + if (*wzObjectDomain)
228 + {
229 + hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName);
230 + ExitOnFailure(hr, "failed to allocate AdsPath string");
231 + }
232 + else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/"))
233 + {
234 + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0);
235 + ExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName);
236 + }
237 + else
238 + {
239 + hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0);
240 + ExitOnFailure(hr, "failed to concat LocalHost/");
241 +
242 + hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0);
243 + ExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName);
244 + }
245 +
246 + *pbstrAdsPath = ::SysAllocString(pwzAdsPath);
247 + if (NULL == *pbstrAdsPath)
248 + {
249 + hr = E_OUTOFMEMORY;
250 + }
251 +
252 +LExit:
253 + ReleaseStr(pwzAdsPath);
254 +
255 + return hr;
256 +}
257 +
258 +
259 +/*******************************************************************
260 + Helper function to check if pwzGroupUserDomain (in form of "domain\username" is
261 + a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the
262 + output from both NetUserGetGroups() and NetUserGetLocalGroups()
263 +********************************************************************/
264 +static BOOL CheckIsMemberHelper(
265 + __in_z LPCWSTR pwzGroupUserDomain,
266 + __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData,
267 + __in DWORD cguiGroupData
268 + )
269 +{
270 + if (NULL == pguiGroupData)
271 + {
272 + return FALSE;
273 + }
274 +
275 + for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter)
276 + {
277 + // If the user is a member of the group, set the output flag to true
278 + if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name))
279 + {
280 + return TRUE;
281 + }
282 + }
283 +
284 + return FALSE;
285 +}
src/dutil/varutil.cpp new
+274
@@ -0,0 +1,274 @@
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 +
5 +struct VARIABLE_ENUM_STRUCT
6 +{
7 +};
8 +
9 +struct VARIABLES_STRUCT
10 +{
11 +};
12 +
13 +const int VARIABLE_ENUM_HANDLE_BYTES = sizeof(VARIABLE_ENUM_STRUCT);
14 +const int VARIABLES_HANDLE_BYTES = sizeof(VARIABLES_STRUCT);
15 +
16 +// function definitions
17 +
18 +/********************************************************************
19 +VarCreate - creates a variables group.
20 +********************************************************************/
21 +extern "C" HRESULT DAPI VarCreate(
22 + __out_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE* ppVariables
23 + )
24 +{
25 + UNREFERENCED_PARAMETER(ppVariables);
26 + return E_NOTIMPL;
27 +}
28 +
29 +/********************************************************************
30 +VarDestroy - destroys a variables group, accepting an optional callback
31 + to help free the variable contexts.
32 +********************************************************************/
33 +extern "C" void DAPI VarDestroy(
34 + __in_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE pVariables,
35 + __in_opt PFN_FREEVARIABLECONTEXT vpfFreeVariableContext
36 + )
37 +{
38 + UNREFERENCED_PARAMETER(pVariables);
39 + UNREFERENCED_PARAMETER(vpfFreeVariableContext);
40 +}
41 +
42 +/********************************************************************
43 +VarFreeValue - frees a variable value.
44 +********************************************************************/
45 +extern "C" void DAPI VarFreeValue(
46 + __in VARIABLE_VALUE* pValue
47 + )
48 +{
49 + UNREFERENCED_PARAMETER(pValue);
50 +}
51 +
52 +/********************************************************************
53 +VarEscapeString - escapes special characters in wzIn so that it can
54 + be used in conditions or variable values.
55 +********************************************************************/
56 +extern "C" HRESULT DAPI VarEscapeString(
57 + __in_z LPCWSTR wzIn,
58 + __out_z LPWSTR* psczOut
59 + )
60 +{
61 + UNREFERENCED_PARAMETER(wzIn);
62 + UNREFERENCED_PARAMETER(psczOut);
63 + return E_NOTIMPL;
64 +}
65 +
66 +/********************************************************************
67 +VarFormatString - similar to MsiFormatRecord.
68 +********************************************************************/
69 +extern "C" HRESULT DAPI VarFormatString(
70 + __in C_VARIABLES_HANDLE pVariables,
71 + __in_z LPCWSTR wzIn,
72 + __out_z_opt LPWSTR* psczOut,
73 + __out_opt DWORD* pcchOut
74 + )
75 +{
76 + UNREFERENCED_PARAMETER(pVariables);
77 + UNREFERENCED_PARAMETER(wzIn);
78 + UNREFERENCED_PARAMETER(psczOut);
79 + UNREFERENCED_PARAMETER(pcchOut);
80 + return E_NOTIMPL;
81 +}
82 +
83 +/********************************************************************
84 +VarGetFormatted - gets the formatted value of a single variable.
85 +********************************************************************/
86 +extern "C" HRESULT DAPI VarGetFormatted(
87 + __in C_VARIABLES_HANDLE pVariables,
88 + __in_z LPCWSTR wzVariable,
89 + __out_z LPWSTR* psczValue
90 + )
91 +{
92 + UNREFERENCED_PARAMETER(pVariables);
93 + UNREFERENCED_PARAMETER(wzVariable);
94 + UNREFERENCED_PARAMETER(psczValue);
95 + return E_NOTIMPL;
96 +}
97 +
98 +/********************************************************************
99 +VarGetNumeric - gets the numeric value of a variable. If the type of
100 + the variable is not numeric, it will attempt to
101 + convert the value into a number.
102 +********************************************************************/
103 +extern "C" HRESULT DAPI VarGetNumeric(
104 + __in C_VARIABLES_HANDLE pVariables,
105 + __in_z LPCWSTR wzVariable,
106 + __out LONGLONG* pllValue
107 + )
108 +{
109 + UNREFERENCED_PARAMETER(pVariables);
110 + UNREFERENCED_PARAMETER(wzVariable);
111 + UNREFERENCED_PARAMETER(pllValue);
112 + return E_NOTIMPL;
113 +}
114 +
115 +/********************************************************************
116 +VarGetString - gets the unformatted string value of a variable. If
117 + the type of the variable is not string, it will
118 + convert the value to a string.
119 +********************************************************************/
120 +extern "C" HRESULT DAPI VarGetString(
121 + __in C_VARIABLES_HANDLE pVariables,
122 + __in_z LPCWSTR wzVariable,
123 + __out_z LPWSTR* psczValue
124 + )
125 +{
126 + UNREFERENCED_PARAMETER(pVariables);
127 + UNREFERENCED_PARAMETER(wzVariable);
128 + UNREFERENCED_PARAMETER(psczValue);
129 + return E_NOTIMPL;
130 +}
131 +
132 +/********************************************************************
133 +VarGetVersion - gets the version value of a variable. If the type of
134 + the variable is not version, it will attempt to
135 + convert the value into a version.
136 +********************************************************************/
137 +extern "C" HRESULT DAPI VarGetVersion(
138 + __in C_VARIABLES_HANDLE pVariables,
139 + __in_z LPCWSTR wzVariable,
140 + __in DWORD64* pqwValue
141 + )
142 +{
143 + UNREFERENCED_PARAMETER(pVariables);
144 + UNREFERENCED_PARAMETER(wzVariable);
145 + UNREFERENCED_PARAMETER(pqwValue);
146 + return E_NOTIMPL;
147 +}
148 +
149 +/********************************************************************
150 +VarGetValue - gets the value of a variable along with its metadata.
151 +********************************************************************/
152 +extern "C" HRESULT DAPI VarGetValue(
153 + __in C_VARIABLES_HANDLE pVariables,
154 + __in_z LPCWSTR wzVariable,
155 + __out VARIABLE_VALUE** ppValue
156 + )
157 +{
158 + UNREFERENCED_PARAMETER(pVariables);
159 + UNREFERENCED_PARAMETER(wzVariable);
160 + UNREFERENCED_PARAMETER(ppValue);
161 + return E_NOTIMPL;
162 +}
163 +
164 +/********************************************************************
165 +VarSetNumeric - sets the value of the variable to a number, the type
166 + of the variable to numeric, and adds the variable to
167 + the group if necessary.
168 +********************************************************************/
169 +extern "C" HRESULT DAPI VarSetNumeric(
170 + __in VARIABLES_HANDLE pVariables,
171 + __in_z LPCWSTR wzVariable,
172 + __in LONGLONG llValue
173 + )
174 +{
175 + UNREFERENCED_PARAMETER(pVariables);
176 + UNREFERENCED_PARAMETER(wzVariable);
177 + UNREFERENCED_PARAMETER(llValue);
178 + return E_NOTIMPL;
179 +}
180 +
181 +/********************************************************************
182 +VarSetString - sets the value of the variable to a string, the type
183 + of the variable to string, and adds the variable to
184 + the group if necessary.
185 +********************************************************************/
186 +extern "C" HRESULT DAPI VarSetString(
187 + __in VARIABLES_HANDLE pVariables,
188 + __in_z LPCWSTR wzVariable,
189 + __in_z_opt LPCWSTR wzValue
190 + )
191 +{
192 + UNREFERENCED_PARAMETER(pVariables);
193 + UNREFERENCED_PARAMETER(wzVariable);
194 + UNREFERENCED_PARAMETER(wzValue);
195 + return E_NOTIMPL;
196 +}
197 +
198 +/********************************************************************
199 +VarSetVersion - sets the value of the variable to a version, the type
200 + of the variable to version, and adds the variable to
201 + the group if necessary.
202 +********************************************************************/
203 +extern "C" HRESULT DAPI VarSetVersion(
204 + __in VARIABLES_HANDLE pVariables,
205 + __in_z LPCWSTR wzVariable,
206 + __in DWORD64 qwValue
207 + )
208 +{
209 + UNREFERENCED_PARAMETER(pVariables);
210 + UNREFERENCED_PARAMETER(wzVariable);
211 + UNREFERENCED_PARAMETER(qwValue);
212 + return E_NOTIMPL;
213 +}
214 +
215 +/********************************************************************
216 +VarSetValue - sets the value of the variable along with its metadata.
217 + Also adds the variable to the group if necessary.
218 +********************************************************************/
219 +extern "C" HRESULT DAPI VarSetValue(
220 + __in VARIABLES_HANDLE pVariables,
221 + __in_z LPCWSTR wzVariable,
222 + __in VARIABLE_VALUE* pValue
223 + )
224 +{
225 + UNREFERENCED_PARAMETER(pVariables);
226 + UNREFERENCED_PARAMETER(wzVariable);
227 + UNREFERENCED_PARAMETER(pValue);
228 + return E_NOTIMPL;
229 +}
230 +
231 +/********************************************************************
232 +VarStartEnum - starts the enumeration of the variable group. There
233 + is no guarantee for the order of the variable enumeration.
234 +
235 +NOTE: caller is responsible for calling VarFinishEnum even if function fails
236 +********************************************************************/
237 +extern "C" HRESULT DAPI VarStartEnum(
238 + __in VARIABLES_HANDLE pVariables,
239 + __out_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE* ppEnum,
240 + __out VARIABLE_VALUE** ppValue
241 + )
242 +{
243 + UNREFERENCED_PARAMETER(pVariables);
244 + UNREFERENCED_PARAMETER(ppEnum);
245 + UNREFERENCED_PARAMETER(ppValue);
246 + return E_NOTIMPL;
247 +}
248 +
249 +/********************************************************************
250 +VarNextVariable - continues the enumeration of the variable group. It
251 + will fail if any variables were added or removed
252 + during the enumeration.
253 +
254 +NOTE: caller is responsible for calling VarFinishEnum even if function fails
255 +********************************************************************/
256 +extern "C" HRESULT DAPI VarNextVariable(
257 + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum,
258 + __out VARIABLE_VALUE** ppValue
259 + )
260 +{
261 + UNREFERENCED_PARAMETER(pEnum);
262 + UNREFERENCED_PARAMETER(ppValue);
263 + return E_NOTIMPL;
264 +}
265 +
266 +/********************************************************************
267 +VarFinishEnum - cleans up resources used for the enumeration.
268 +********************************************************************/
269 +extern "C" void DAPI VarFinishEnum(
270 + __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum
271 + )
272 +{
273 + UNREFERENCED_PARAMETER(pEnum);
274 +}
src/dutil/wiutil.cpp new
+1494
@@ -0,0 +1,1494 @@
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 +
5 +
6 +// constants
7 +
8 +const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF;
9 +const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64;
10 +
11 +
12 +// structs
13 +
14 +
15 +static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW;
16 +static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW;
17 +static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW;
18 +static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW;
19 +static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW;
20 +static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW;
21 +static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW;
22 +static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW;
23 +static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI;
24 +static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW;
25 +static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW;
26 +static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW;
27 +
28 +// MSI 3.0+
29 +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL;
30 +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL;
31 +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL;
32 +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL;
33 +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL;
34 +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL;
35 +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL;
36 +
37 +static HMODULE vhMsiDll = NULL;
38 +static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL;
39 +static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL;
40 +static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL;
41 +static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL;
42 +static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL;
43 +static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL;
44 +static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL;
45 +static BOOL vfWiuInitialized = FALSE;
46 +
47 +// globals
48 +static DWORD vdwMsiDllMajorMinor = 0;
49 +static DWORD vdwMsiDllBuildRevision = 0;
50 +
51 +
52 +// internal function declarations
53 +
54 +static DWORD CheckForRestartErrorCode(
55 + __in DWORD dwErrorCode,
56 + __out WIU_RESTART* pRestart
57 + );
58 +static INT CALLBACK InstallEngineCallback(
59 + __in LPVOID pvContext,
60 + __in UINT uiMessage,
61 + __in_z_opt LPCWSTR wzMessage
62 + );
63 +static INT CALLBACK InstallEngineRecordCallback(
64 + __in LPVOID pvContext,
65 + __in UINT uiMessage,
66 + __in_opt MSIHANDLE hRecord
67 + );
68 +static INT HandleInstallMessage(
69 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
70 + __in INSTALLMESSAGE mt,
71 + __in UINT uiFlags,
72 + __in_z LPCWSTR wzMessage,
73 + __in_opt MSIHANDLE hRecord
74 + );
75 +static INT HandleInstallProgress(
76 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
77 + __in_z_opt LPCWSTR wzMessage,
78 + __in_opt MSIHANDLE hRecord
79 + );
80 +static INT SendMsiMessage(
81 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
82 + __in INSTALLMESSAGE mt,
83 + __in UINT uiFlags,
84 + __in_z LPCWSTR wzMessage,
85 + __in_opt MSIHANDLE hRecord
86 + );
87 +static INT SendErrorMessage(
88 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
89 + __in UINT uiFlags,
90 + __in_z LPCWSTR wzMessage,
91 + __in_opt MSIHANDLE hRecord
92 + );
93 +static INT SendFilesInUseMessage(
94 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
95 + __in_opt MSIHANDLE hRecord,
96 + __in BOOL fRestartManagerRequest
97 + );
98 +static INT SendProgressUpdate(
99 + __in WIU_MSI_EXECUTE_CONTEXT* pContext
100 + );
101 +static void ResetProgress(
102 + __in WIU_MSI_EXECUTE_CONTEXT* pContext
103 + );
104 +static DWORD CalculatePhaseProgress(
105 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
106 + __in DWORD dwProgressIndex,
107 + __in DWORD dwWeightPercentage
108 + );
109 +void InitializeMessageData(
110 + __in MSIHANDLE hRecord,
111 + __out LPWSTR** prgsczData,
112 + __out DWORD* pcData
113 + );
114 +void UninitializeMessageData(
115 + __in LPWSTR* rgsczData,
116 + __in DWORD cData
117 + );
118 +
119 +
120 +/********************************************************************
121 + WiuInitialize - initializes wiutil
122 +
123 +*********************************************************************/
124 +extern "C" HRESULT DAPI WiuInitialize(
125 + )
126 +{
127 + HRESULT hr = S_OK;
128 + LPWSTR sczMsiDllPath = NULL;
129 +
130 + hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath);
131 + ExitOnFailure(hr, "Failed to load Msi.DLL");
132 +
133 + // Ignore failures
134 + FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision);
135 +
136 + vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast<PFN_MSIDETERMINEPATCHSEQUENCEW>(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW"));
137 + if (NULL == vpfnMsiDeterminePatchSequenceW)
138 + {
139 + vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary;
140 + }
141 +
142 + vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast<PFN_MSIDETERMINEAPPLICABLEPATCHESW>(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW"));
143 + if (NULL == vpfnMsiDetermineApplicablePatchesW)
144 + {
145 + vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary;
146 + }
147 +
148 + vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast<PFN_MSIENUMPRODUCTSEXW>(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW"));
149 + if (NULL == vpfnMsiEnumProductsExW)
150 + {
151 + vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary;
152 + }
153 +
154 + vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast<PFN_MSIGETPATCHINFOEXW>(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW"));
155 + if (NULL == vpfnMsiGetPatchInfoExW)
156 + {
157 + vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary;
158 + }
159 +
160 + vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast<PFN_MSIGETPRODUCTINFOEXW>(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW"));
161 + if (NULL == vpfnMsiGetProductInfoExW)
162 + {
163 + vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary;
164 + }
165 +
166 + vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast<PFN_MSISETEXTERNALUIRECORD>(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord"));
167 + if (NULL == vpfnMsiSetExternalUIRecord)
168 + {
169 + vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary;
170 + }
171 +
172 + //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL;
173 + vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast<PFN_MSISOURCELISTADDSOURCEEXW>(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW"));
174 + if (NULL == vpfnMsiSourceListAddSourceExW)
175 + {
176 + vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary;
177 + }
178 +
179 + vfWiuInitialized = TRUE;
180 +
181 +LExit:
182 + ReleaseStr(sczMsiDllPath);
183 + return hr;
184 +}
185 +
186 +
187 +/********************************************************************
188 + WiuUninitialize - uninitializes wiutil
189 +
190 +*********************************************************************/
191 +extern "C" void DAPI WiuUninitialize(
192 + )
193 +{
194 + if (vhMsiDll)
195 + {
196 + ::FreeLibrary(vhMsiDll);
197 + vhMsiDll = NULL;
198 + vpfnMsiSetExternalUIRecordFromLibrary = NULL;
199 + vpfnMsiGetProductInfoExWFromLibrary = NULL;
200 + vpfnMsiGetPatchInfoExWFromLibrary = NULL;
201 + vpfnMsiEnumProductsExWFromLibrary = NULL;
202 + vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL;
203 + vpfnMsiDeterminePatchSequenceWFromLibrary = NULL;
204 + vpfnMsiSourceListAddSourceExWFromLibrary = NULL;
205 + }
206 +
207 + vfWiuInitialized = FALSE;
208 +}
209 +
210 +
211 +/********************************************************************
212 + WiuFunctionOverride - overrides the Windows installer functions. Typically used
213 + for unit testing.
214 +
215 +*********************************************************************/
216 +extern "C" void DAPI WiuFunctionOverride(
217 + __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW,
218 + __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW,
219 + __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW,
220 + __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW,
221 + __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW,
222 + __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW,
223 + __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW,
224 + __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW,
225 + __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI,
226 + __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW,
227 + __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW,
228 + __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord,
229 + __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW
230 + )
231 +{
232 + vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW;
233 + vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW;
234 + vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW;
235 + vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW;
236 + vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW;
237 + vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW;
238 + vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW;
239 + vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI;
240 + vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW;
241 + vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW;
242 + vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary;
243 + vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary;
244 + vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary;
245 +}
246 +
247 +
248 +extern "C" HRESULT DAPI WiuGetComponentPath(
249 + __in_z LPCWSTR wzProductCode,
250 + __in_z LPCWSTR wzComponentId,
251 + __out INSTALLSTATE* pInstallState,
252 + __out_z LPWSTR* psczValue
253 + )
254 +{
255 + HRESULT hr = S_OK;
256 + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
257 + DWORD cchCompare;
258 +
259 + hr = StrAlloc(psczValue, cch);
260 + ExitOnFailure(hr, "Failed to allocate string for component path.");
261 +
262 + cchCompare = cch;
263 + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch);
264 + if (INSTALLSTATE_MOREDATA == *pInstallState)
265 + {
266 + ++cch;
267 + hr = StrAlloc(psczValue, cch);
268 + ExitOnFailure(hr, "Failed to reallocate string for component path.");
269 +
270 + cchCompare = cch;
271 + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch);
272 + }
273 +
274 + if (INSTALLSTATE_INVALIDARG == *pInstallState)
275 + {
276 + hr = E_INVALIDARG;
277 + ExitOnRootFailure(hr, "Invalid argument when getting component path.");
278 + }
279 + else if (INSTALLSTATE_UNKNOWN == *pInstallState)
280 + {
281 + ExitFunction();
282 + }
283 +
284 + // If the actual path length is greater than or equal to the original buffer
285 + // allocate a larger buffer and get the path again, just in case we are
286 + // missing any part of the path.
287 + if (cchCompare <= cch)
288 + {
289 + ++cch;
290 + hr = StrAlloc(psczValue, cch);
291 + ExitOnFailure(hr, "Failed to reallocate string for component path.");
292 +
293 + *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch);
294 + }
295 +
296 +LExit:
297 + return hr;
298 +}
299 +
300 +
301 +extern "C" HRESULT DAPI WiuLocateComponent(
302 + __in_z LPCWSTR wzComponentId,
303 + __out INSTALLSTATE* pInstallState,
304 + __out_z LPWSTR* psczValue
305 + )
306 +{
307 + HRESULT hr = S_OK;
308 + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
309 + DWORD cchCompare;
310 +
311 + hr = StrAlloc(psczValue, cch);
312 + ExitOnFailure(hr, "Failed to allocate string for component path.");
313 +
314 + cchCompare = cch;
315 + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch);
316 + if (INSTALLSTATE_MOREDATA == *pInstallState)
317 + {
318 + ++cch;
319 + hr = StrAlloc(psczValue, cch);
320 + ExitOnFailure(hr, "Failed to reallocate string for component path.");
321 +
322 + cchCompare = cch;
323 + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch);
324 + }
325 +
326 + if (INSTALLSTATE_INVALIDARG == *pInstallState)
327 + {
328 + hr = E_INVALIDARG;
329 + ExitOnRootFailure(hr, "Invalid argument when locating component.");
330 + }
331 + else if (INSTALLSTATE_UNKNOWN == *pInstallState)
332 + {
333 + ExitFunction();
334 + }
335 +
336 + // If the actual path length is greater than or equal to the original buffer
337 + // allocate a larger buffer and get the path again, just in case we are
338 + // missing any part of the path.
339 + if (cchCompare <= cch)
340 + {
341 + ++cch;
342 + hr = StrAlloc(psczValue, cch);
343 + ExitOnFailure(hr, "Failed to reallocate string for component path.");
344 +
345 + *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch);
346 + }
347 +
348 +LExit:
349 + return hr;
350 +}
351 +
352 +
353 +extern "C" HRESULT DAPI WiuQueryFeatureState(
354 + __in_z LPCWSTR wzProduct,
355 + __in_z LPCWSTR wzFeature,
356 + __out INSTALLSTATE* pInstallState
357 + )
358 +{
359 + HRESULT hr = S_OK;
360 +
361 + *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature);
362 + if (INSTALLSTATE_INVALIDARG == *pInstallState)
363 + {
364 + hr = E_INVALIDARG;
365 + ExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct);
366 + }
367 +
368 +LExit:
369 + return hr;
370 +}
371 +
372 +
373 +extern "C" HRESULT DAPI WiuGetProductInfo(
374 + __in_z LPCWSTR wzProductCode,
375 + __in_z LPCWSTR wzProperty,
376 + __out LPWSTR* psczValue
377 + )
378 +{
379 + HRESULT hr = S_OK;
380 + UINT er = ERROR_SUCCESS;
381 + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
382 +
383 + hr = StrAlloc(psczValue, cch);
384 + ExitOnFailure(hr, "Failed to allocate string for product info.");
385 +
386 + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch);
387 + if (ERROR_MORE_DATA == er)
388 + {
389 + ++cch;
390 + hr = StrAlloc(psczValue, cch);
391 + ExitOnFailure(hr, "Failed to reallocate string for product info.");
392 +
393 + er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch);
394 + }
395 + ExitOnWin32Error(er, hr, "Failed to get product info.");
396 +
397 +LExit:
398 + return hr;
399 +}
400 +
401 +
402 +extern "C" HRESULT DAPI WiuGetProductInfoEx(
403 + __in_z LPCWSTR wzProductCode,
404 + __in_z_opt LPCWSTR wzUserSid,
405 + __in MSIINSTALLCONTEXT dwContext,
406 + __in_z LPCWSTR wzProperty,
407 + __out LPWSTR* psczValue
408 + )
409 +{
410 + HRESULT hr = S_OK;
411 + UINT er = ERROR_SUCCESS;
412 + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
413 +
414 + if (!vpfnMsiGetProductInfoExW)
415 + {
416 + hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue);
417 + ExitOnFailure(hr, "Failed to get product info when extended info was not available.");
418 +
419 + ExitFunction();
420 + }
421 +
422 + hr = StrAlloc(psczValue, cch);
423 + ExitOnFailure(hr, "Failed to allocate string for extended product info.");
424 +
425 + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
426 + if (ERROR_MORE_DATA == er)
427 + {
428 + ++cch;
429 + hr = StrAlloc(psczValue, cch);
430 + ExitOnFailure(hr, "Failed to reallocate string for extended product info.");
431 +
432 + er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
433 + }
434 + ExitOnWin32Error(er, hr, "Failed to get extended product info.");
435 +
436 +LExit:
437 + return hr;
438 +}
439 +
440 +
441 +extern "C" HRESULT DAPI WiuGetProductProperty(
442 + __in MSIHANDLE hProduct,
443 + __in_z LPCWSTR wzProperty,
444 + __out LPWSTR* psczValue
445 + )
446 +{
447 + HRESULT hr = S_OK;
448 + UINT er = ERROR_SUCCESS;
449 + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
450 +
451 + hr = StrAlloc(psczValue, cch);
452 + ExitOnFailure(hr, "Failed to allocate string for product property.");
453 +
454 + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch);
455 + if (ERROR_MORE_DATA == er)
456 + {
457 + ++cch;
458 + hr = StrAlloc(psczValue, cch);
459 + ExitOnFailure(hr, "Failed to reallocate string for product property.");
460 +
461 + er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch);
462 + }
463 + ExitOnWin32Error(er, hr, "Failed to get product property.");
464 +
465 +LExit:
466 + return hr;
467 +}
468 +
469 +
470 +extern "C" HRESULT DAPI WiuGetPatchInfoEx(
471 + __in_z LPCWSTR wzPatchCode,
472 + __in_z LPCWSTR wzProductCode,
473 + __in_z_opt LPCWSTR wzUserSid,
474 + __in MSIINSTALLCONTEXT dwContext,
475 + __in_z LPCWSTR wzProperty,
476 + __out LPWSTR* psczValue
477 + )
478 +{
479 + HRESULT hr = S_OK;
480 + UINT er = ERROR_SUCCESS;
481 + DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
482 +
483 + if (!vpfnMsiGetPatchInfoExW)
484 + {
485 + ExitFunction1(hr = E_NOTIMPL);
486 + }
487 +
488 + hr = StrAlloc(psczValue, cch);
489 + ExitOnFailure(hr, "Failed to allocate string for extended patch info.");
490 +
491 + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
492 + if (ERROR_MORE_DATA == er)
493 + {
494 + ++cch;
495 + hr = StrAlloc(psczValue, cch);
496 + ExitOnFailure(hr, "Failed to reallocate string for extended patch info.");
497 +
498 + er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
499 + }
500 + ExitOnWin32Error(er, hr, "Failed to get extended patch info.");
501 +
502 +LExit:
503 + return hr;
504 +}
505 +
506 +
507 +extern "C" HRESULT DAPI WiuDeterminePatchSequence(
508 + __in_z LPCWSTR wzProductCode,
509 + __in_z_opt LPCWSTR wzUserSid,
510 + __in MSIINSTALLCONTEXT context,
511 + __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
512 + __in DWORD cPatchInfo
513 + )
514 +{
515 + HRESULT hr = S_OK;
516 + DWORD er = ERROR_SUCCESS;
517 +
518 + if (!vpfnMsiDeterminePatchSequenceW)
519 + {
520 + ExitFunction1(hr = E_NOTIMPL);
521 + }
522 +
523 + er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo);
524 + ExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code.");
525 +
526 +LExit:
527 + return hr;
528 +}
529 +
530 +
531 +extern "C" HRESULT DAPI WiuDetermineApplicablePatches(
532 + __in_z LPCWSTR wzProductPackagePath,
533 + __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
534 + __in DWORD cPatchInfo
535 + )
536 +{
537 + HRESULT hr = S_OK;
538 + DWORD er = ERROR_SUCCESS;
539 +
540 + if (!vpfnMsiDetermineApplicablePatchesW)
541 + {
542 + ExitFunction1(hr = E_NOTIMPL);
543 + }
544 +
545 + er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo);
546 + ExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package.");
547 +
548 +LExit:
549 + return hr;
550 +}
551 +
552 +
553 +extern "C" HRESULT DAPI WiuEnumProducts(
554 + __in DWORD iProductIndex,
555 + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
556 + )
557 +{
558 + HRESULT hr = S_OK;
559 + DWORD er = ERROR_SUCCESS;
560 +
561 + er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode);
562 + if (ERROR_NO_MORE_ITEMS == er)
563 + {
564 + ExitFunction1(hr = HRESULT_FROM_WIN32(er));
565 + }
566 + ExitOnWin32Error(er, hr, "Failed to enumerate products.");
567 +
568 +LExit:
569 + return hr;
570 +}
571 +
572 +
573 +extern "C" HRESULT DAPI WiuEnumProductsEx(
574 + __in_z_opt LPCWSTR wzProductCode,
575 + __in_z_opt LPCWSTR wzUserSid,
576 + __in DWORD dwContext,
577 + __in DWORD dwIndex,
578 + __out_opt WCHAR wzInstalledProductCode[39],
579 + __out_opt MSIINSTALLCONTEXT *pdwInstalledContext,
580 + __out_opt LPWSTR wzSid,
581 + __inout_opt LPDWORD pcchSid
582 + )
583 +{
584 + HRESULT hr = S_OK;
585 + DWORD er = ERROR_SUCCESS;
586 +
587 + if (!vpfnMsiEnumProductsExW)
588 + {
589 + ExitFunction1(hr = E_NOTIMPL);
590 + }
591 +
592 + er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid);
593 + if (ERROR_NO_MORE_ITEMS == er)
594 + {
595 + ExitFunction1(hr = HRESULT_FROM_WIN32(er));
596 + }
597 + ExitOnWin32Error(er, hr, "Failed to enumerate products.");
598 +
599 +LExit:
600 + return hr;
601 +}
602 +
603 +
604 +extern "C" HRESULT DAPI WiuEnumRelatedProducts(
605 + __in_z LPCWSTR wzUpgradeCode,
606 + __in DWORD iProductIndex,
607 + __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
608 + )
609 +{
610 + HRESULT hr = S_OK;
611 + DWORD er = ERROR_SUCCESS;
612 +
613 + er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode);
614 + if (ERROR_NO_MORE_ITEMS == er)
615 + {
616 + ExitFunction1(hr = HRESULT_FROM_WIN32(er));
617 + }
618 + ExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode);
619 +
620 +LExit:
621 + return hr;
622 +}
623 +
624 +/********************************************************************
625 + WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code.
626 +
627 + Parameters:
628 + wzUpgradeCode - The upgrade code that will be used to find the related products.
629 + prgsczProductCodes - Pointer to the array that will contain the product codes.
630 + pcRelatedProducts - Returns the count of the number of related products found.
631 + fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found.
632 +********************************************************************/
633 +extern "C" HRESULT DAPI WiuEnumRelatedProductCodes(
634 + __in_z LPCWSTR wzUpgradeCode,
635 + __deref_out_ecount_opt(pcRelatedProducts) LPWSTR** prgsczProductCodes,
636 + __out DWORD* pcRelatedProducts,
637 + __in BOOL fReturnHighestVersionOnly
638 + )
639 +{
640 + HRESULT hr = S_OK;
641 + WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { };
642 + LPWSTR sczInstalledVersion = NULL;
643 + DWORD64 qwCurrentVersion = 0;
644 + DWORD64 qwHighestVersion = 0;
645 +
646 + // make sure we start at zero
647 + *pcRelatedProducts = 0;
648 +
649 + for (DWORD i = 0; ; ++i)
650 + {
651 + hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode);
652 +
653 + if (E_NOMOREITEMS == hr)
654 + {
655 + hr = S_OK;
656 + break;
657 + }
658 + ExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode);
659 +
660 + if (fReturnHighestVersionOnly)
661 + {
662 + // get the version
663 + hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion);
664 + ExitOnFailure(hr, "Failed to get version for product code: %ls", wzCurrentProductCode);
665 +
666 + hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwCurrentVersion);
667 + ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for product code: %ls", sczInstalledVersion, wzCurrentProductCode);
668 +
669 + // if this is the first product found then it is the highest version (for now)
670 + if (0 == *pcRelatedProducts)
671 + {
672 + qwHighestVersion = qwCurrentVersion;
673 + }
674 + else
675 + {
676 + // if this is the highest version encountered so far then overwrite
677 + // the first item in the array (there will never be more than one item)
678 + if (qwCurrentVersion > qwHighestVersion)
679 + {
680 + qwHighestVersion = qwCurrentVersion;
681 +
682 + hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0);
683 + ExitOnFailure(hr, "Failed to update array with higher versioned product code.");
684 + }
685 +
686 + // continue here as we don't want anything else added to the list
687 + continue;
688 + }
689 + }
690 +
691 + hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0);
692 + ExitOnFailure(hr, "Failed to add product code to array.");
693 + }
694 +
695 +LExit:
696 + ReleaseStr(sczInstalledVersion);
697 + return hr;
698 +}
699 +
700 +
701 +extern "C" HRESULT DAPI WiuEnableLog(
702 + __in DWORD dwLogMode,
703 + __in_z LPCWSTR wzLogFile,
704 + __in DWORD dwLogAttributes
705 + )
706 +{
707 + HRESULT hr = S_OK;
708 + DWORD er = ERROR_SUCCESS;
709 +
710 + er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes);
711 + ExitOnWin32Error(er, hr, "Failed to enable MSI internal logging.");
712 +
713 +LExit:
714 + return hr;
715 +}
716 +
717 +
718 +extern "C" HRESULT DAPI WiuInitializeExternalUI(
719 + __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
720 + __in INSTALLUILEVEL internalUILevel,
721 + __in HWND hwndParent,
722 + __in LPVOID pvContext,
723 + __in BOOL fRollback,
724 + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
725 + )
726 +{
727 + HRESULT hr = S_OK;
728 + DWORD er = ERROR_SUCCESS;
729 +
730 + DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE |
731 + INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING |
732 + INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE |
733 + INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA|
734 + INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE;
735 +
736 + if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor)
737 + {
738 + dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE;
739 + }
740 +
741 + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT));
742 + pExecuteContext->fRollback = fRollback;
743 + pExecuteContext->pfnMessageHandler = pfnMessageHandler;
744 + pExecuteContext->pvContext = pvContext;
745 +
746 + // Wire the internal and external UI handler.
747 + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent);
748 + pExecuteContext->hwndPreviousParentWindow = hwndParent;
749 +
750 + // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external
751 + // UI handler if necesary.
752 + if (vpfnMsiSetExternalUIRecord)
753 + {
754 + er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord);
755 + ExitOnWin32Error(er, hr, "Failed to wire up external UI record handler.");
756 + pExecuteContext->fSetPreviousExternalUIRecord = TRUE;
757 + }
758 + else
759 + {
760 + pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext);
761 + pExecuteContext->fSetPreviousExternalUI = TRUE;
762 + }
763 +
764 +LExit:
765 + return hr;
766 +}
767 +
768 +
769 +extern "C" void DAPI WiuUninitializeExternalUI(
770 + __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
771 + )
772 +{
773 + if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel)
774 + {
775 + pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow);
776 + }
777 +
778 + if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler
779 + {
780 + vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL);
781 + }
782 +
783 + if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler
784 + {
785 + vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL);
786 + }
787 +
788 + memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT));
789 +}
790 +
791 +
792 +extern "C" HRESULT DAPI WiuConfigureProductEx(
793 + __in_z LPCWSTR wzProduct,
794 + __in int iInstallLevel,
795 + __in INSTALLSTATE eInstallState,
796 + __in_z LPCWSTR wzCommandLine,
797 + __out WIU_RESTART* pRestart
798 + )
799 +{
800 + HRESULT hr = S_OK;
801 + DWORD er = ERROR_SUCCESS;
802 +
803 + er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine);
804 + er = CheckForRestartErrorCode(er, pRestart);
805 + ExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct);
806 +
807 +LExit:
808 + return hr;
809 +}
810 +
811 +
812 +extern "C" HRESULT DAPI WiuInstallProduct(
813 + __in_z LPCWSTR wzPackagePath,
814 + __in_z LPCWSTR wzCommandLine,
815 + __out WIU_RESTART* pRestart
816 + )
817 +{
818 + HRESULT hr = S_OK;
819 + DWORD er = ERROR_SUCCESS;
820 +
821 + er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine);
822 + er = CheckForRestartErrorCode(er, pRestart);
823 + ExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath);
824 +
825 +LExit:
826 + return hr;
827 +}
828 +
829 +
830 +extern "C" HRESULT DAPI WiuRemovePatches(
831 + __in_z LPCWSTR wzPatchList,
832 + __in_z LPCWSTR wzProductCode,
833 + __in_z LPCWSTR wzPropertyList,
834 + __out WIU_RESTART* pRestart
835 + )
836 +{
837 + HRESULT hr = S_OK;
838 + DWORD er = ERROR_SUCCESS;
839 +
840 + er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList);
841 + er = CheckForRestartErrorCode(er, pRestart);
842 + ExitOnWin32Error(er, hr, "Failed to remove patches.");
843 +
844 +LExit:
845 + return hr;
846 +}
847 +
848 +
849 +extern "C" HRESULT DAPI WiuSourceListAddSourceEx(
850 + __in_z LPCWSTR wzProductCodeOrPatchCode,
851 + __in_z_opt LPCWSTR wzUserSid,
852 + __in MSIINSTALLCONTEXT dwContext,
853 + __in DWORD dwCode,
854 + __in_z LPCWSTR wzSource,
855 + __in_opt DWORD dwIndex
856 + )
857 +{
858 + HRESULT hr = S_OK;
859 + DWORD er = ERROR_SUCCESS;
860 +
861 + er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex);
862 + ExitOnWin32Error(er, hr, "Failed to add source.");
863 +
864 +LExit:
865 + return hr;
866 +}
867 +
868 +
869 +
870 +static DWORD CheckForRestartErrorCode(
871 + __in DWORD dwErrorCode,
872 + __out WIU_RESTART* pRestart
873 + )
874 +{
875 + switch (dwErrorCode)
876 + {
877 + case ERROR_SUCCESS_REBOOT_REQUIRED:
878 + case ERROR_SUCCESS_RESTART_REQUIRED:
879 + *pRestart = WIU_RESTART_REQUIRED;
880 + dwErrorCode = ERROR_SUCCESS;
881 + break;
882 +
883 + case ERROR_SUCCESS_REBOOT_INITIATED:
884 + case ERROR_INSTALL_SUSPEND:
885 + *pRestart = WIU_RESTART_INITIATED;
886 + dwErrorCode = ERROR_SUCCESS;
887 + break;
888 + }
889 +
890 + return dwErrorCode;
891 +}
892 +
893 +static INT CALLBACK InstallEngineCallback(
894 + __in LPVOID pvContext,
895 + __in UINT uiMessage,
896 + __in_z_opt LPCWSTR wzMessage
897 + )
898 +{
899 + INT nResult = IDNOACTION;
900 + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext;
901 + INSTALLMESSAGE mt = static_cast<INSTALLMESSAGE>(0xFF000000 & uiMessage);
902 + UINT uiFlags = 0x00FFFFFF & uiMessage;
903 +
904 + if (wzMessage)
905 + {
906 + if (INSTALLMESSAGE_PROGRESS == mt)
907 + {
908 + nResult = HandleInstallProgress(pContext, wzMessage, NULL);
909 + }
910 + else
911 + {
912 + nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL);
913 + }
914 + }
915 +
916 + return nResult;
917 +}
918 +
919 +static INT CALLBACK InstallEngineRecordCallback(
920 + __in LPVOID pvContext,
921 + __in UINT uiMessage,
922 + __in_opt MSIHANDLE hRecord
923 + )
924 +{
925 + INT nResult = IDNOACTION;
926 + HRESULT hr = S_OK;
927 + WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext;
928 +
929 + INSTALLMESSAGE mt = static_cast<INSTALLMESSAGE>(0xFF000000 & uiMessage);
930 + UINT uiFlags = 0x00FFFFFF & uiMessage;
931 + LPWSTR sczMessage = NULL;
932 + DWORD cchMessage = 0;
933 +
934 + if (hRecord)
935 + {
936 + if (INSTALLMESSAGE_PROGRESS == mt)
937 + {
938 + nResult = HandleInstallProgress(pContext, NULL, hRecord);
939 + }
940 + else
941 + {
942 + // create formated message string
943 +#pragma prefast(push)
944 +#pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size
945 + DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage);
946 +#pragma prefast(pop)
947 + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
948 + {
949 + hr = StrAlloc(&sczMessage, ++cchMessage);
950 + }
951 + else
952 + {
953 + hr = HRESULT_FROM_WIN32(er);
954 + }
955 + ExitOnFailure(hr, "Failed to allocate string for formated message.");
956 +
957 + er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage);
958 + ExitOnWin32Error(er, hr, "Failed to format message record.");
959 +
960 + // Pass to handler including both the formated message and the original record.
961 + nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord);
962 + }
963 + }
964 +
965 +LExit:
966 + ReleaseStr(sczMessage);
967 + return nResult;
968 +}
969 +
970 +static INT HandleInstallMessage(
971 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
972 + __in INSTALLMESSAGE mt,
973 + __in UINT uiFlags,
974 + __in_z LPCWSTR wzMessage,
975 + __in_opt MSIHANDLE hRecord
976 + )
977 +{
978 + INT nResult = IDNOACTION;
979 +
980 +Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage);
981 +
982 + // Handle the message.
983 + switch (mt)
984 + {
985 + case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data
986 + ResetProgress(pContext);
987 + break;
988 +
989 + case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data
990 + break;
991 +
992 + case INSTALLMESSAGE_ACTIONSTART:
993 + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData)
994 + {
995 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE;
996 + }
997 +
998 + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord);
999 + break;
1000 +
1001 + case INSTALLMESSAGE_ACTIONDATA:
1002 + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData)
1003 + {
1004 + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward)
1005 + {
1006 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep;
1007 + }
1008 + else // rollback.
1009 + {
1010 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep;
1011 + }
1012 +
1013 + nResult = SendProgressUpdate(pContext);
1014 + }
1015 + else
1016 + {
1017 + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord);
1018 + }
1019 + break;
1020 +
1021 + case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough;
1022 + case INSTALLMESSAGE_FATALEXIT: __fallthrough;
1023 + case INSTALLMESSAGE_ERROR:
1024 + nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord);
1025 + break;
1026 +
1027 + case INSTALLMESSAGE_FILESINUSE:
1028 + case INSTALLMESSAGE_RMFILESINUSE:
1029 + nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt);
1030 + break;
1031 +
1032 +/*
1033 +#if 0
1034 + case INSTALLMESSAGE_COMMONDATA:
1035 + if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2])
1036 + {
1037 + if (L'0' == wzMessage[3])
1038 + {
1039 + // TODO: handle the language common data message.
1040 + lres = IDOK;
1041 + return lres;
1042 + }
1043 + else if (L'1' == wzMessage[3])
1044 + {
1045 + // TODO: really handle sending the caption.
1046 + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast<LPARAM>(wzMessage + 3));
1047 + return lres;
1048 + }
1049 + else if (L'2' == wzMessage[3])
1050 + {
1051 + // TODO: really handle sending the cancel button status.
1052 + lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast<LPARAM>(wzMessage + 3));
1053 + return lres;
1054 + }
1055 + }
1056 + break;
1057 +#endif
1058 +*/
1059 +
1060 + //case INSTALLMESSAGE_WARNING:
1061 + //case INSTALLMESSAGE_USER:
1062 + //case INSTALLMESSAGE_INFO:
1063 + //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard
1064 + default:
1065 + nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord);
1066 + break;
1067 + }
1068 +
1069 + // Always return "no action" (0) for resolve source messages.
1070 + return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult;
1071 +}
1072 +
1073 +static INT HandleInstallProgress(
1074 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1075 + __in_z_opt LPCWSTR wzMessage,
1076 + __in_opt MSIHANDLE hRecord
1077 + )
1078 +{
1079 + HRESULT hr = S_OK;
1080 + INT nResult = IDNOACTION;
1081 + INT iFields[4] = { };
1082 + INT cFields = 0;
1083 + LPCWSTR pwz = NULL;
1084 + DWORD cch = 0;
1085 +
1086 + // get field values
1087 + if (hRecord)
1088 + {
1089 + cFields = ::MsiRecordGetFieldCount(hRecord);
1090 + cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold
1091 + for (INT i = 0; i < cFields; ++i)
1092 + {
1093 + iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1);
1094 + }
1095 + }
1096 + else
1097 + {
1098 + Assert(wzMessage);
1099 +
1100 + // parse message string
1101 + pwz = wzMessage;
1102 + while (cFields < 4)
1103 + {
1104 + // check if we have the start of a valid part
1105 + if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2])
1106 + {
1107 + break;
1108 + }
1109 + pwz += 3;
1110 +
1111 + // find character count of number
1112 + cch = 0;
1113 + while (pwz[cch] && L' ' != pwz[cch])
1114 + {
1115 + ++cch;
1116 + }
1117 +
1118 + // parse number
1119 + hr = StrStringToInt32(pwz, cch, &iFields[cFields]);
1120 + ExitOnFailure(hr, "Failed to parse MSI message part.");
1121 +
1122 + // increment field count
1123 + ++cFields;
1124 + }
1125 + }
1126 +
1127 +#ifdef _DEBUG
1128 + WCHAR wz[256];
1129 + ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]);
1130 + Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz);
1131 +#endif
1132 +
1133 + // Verify that we have the enough field values.
1134 + if (1 > cFields)
1135 + {
1136 + ExitFunction(); // unknown message, bail
1137 + }
1138 +
1139 + // Handle based on message type.
1140 + switch (iFields[0])
1141 + {
1142 + case 0: // master progress reset
1143 + if (4 > cFields)
1144 + {
1145 + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage);
1146 + ExitFunction();
1147 + }
1148 + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]);
1149 +
1150 + // Update the index into progress array.
1151 + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex)
1152 + {
1153 + pContext->dwCurrentProgressIndex = 0;
1154 + }
1155 + else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress))
1156 + {
1157 + ++pContext->dwCurrentProgressIndex;
1158 + }
1159 + else
1160 + {
1161 + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1162 + ExitOnRootFailure(hr, "Insufficient space to hold progress information.");
1163 + }
1164 +
1165 + // we only care about the first stage after script execution has started
1166 + //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3])
1167 + //{
1168 + // pEngineInfo->fMsiProgressFinished = TRUE;
1169 + //}
1170 +
1171 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1];
1172 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max
1173 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]);
1174 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE;
1175 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]);
1176 +
1177 + if (0 == pContext->dwCurrentProgressIndex)
1178 + {
1179 + // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase
1180 + // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress
1181 + // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this
1182 + // "close" and deal with the rest.
1183 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50;
1184 + }
1185 + break;
1186 +
1187 + case 1: // action info.
1188 + if (3 > cFields)
1189 + {
1190 + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage);
1191 + ExitFunction();
1192 + }
1193 + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]);
1194 +
1195 + if (0 == iFields[2])
1196 + {
1197 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE;
1198 + }
1199 + else
1200 + {
1201 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE;
1202 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1];
1203 + }
1204 + break;
1205 +
1206 + case 2: // progress report.
1207 + if (2 > cFields)
1208 + {
1209 + Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage);
1210 + break;
1211 + }
1212 +
1213 + //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]);
1214 +
1215 + if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex)
1216 + {
1217 + break;
1218 + }
1219 + else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal)
1220 + {
1221 + break;
1222 + }
1223 +
1224 + // Update progress.
1225 + if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward)
1226 + {
1227 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1];
1228 + }
1229 + else // rollback.
1230 + {
1231 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1];
1232 + }
1233 + break;
1234 +
1235 + case 3: // extend the progress bar.
1236 + pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1];
1237 + break;
1238 +
1239 + default:
1240 + ExitFunction(); // unknown message, bail
1241 + }
1242 +
1243 + // If we have a valid progress index, send an update.
1244 + if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex)
1245 + {
1246 + nResult = SendProgressUpdate(pContext);
1247 + }
1248 +
1249 +LExit:
1250 + return nResult;
1251 +}
1252 +
1253 +static INT SendMsiMessage(
1254 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1255 + __in INSTALLMESSAGE mt,
1256 + __in UINT uiFlags,
1257 + __in_z LPCWSTR wzMessage,
1258 + __in_opt MSIHANDLE hRecord
1259 + )
1260 +{
1261 + INT nResult = IDNOACTION;
1262 + WIU_MSI_EXECUTE_MESSAGE message = { };
1263 + LPWSTR* rgsczData = NULL;
1264 + DWORD cData = 0;
1265 +
1266 + InitializeMessageData(hRecord, &rgsczData, &cData);
1267 +
1268 + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE;
1269 + message.dwAllowedResults = uiFlags;
1270 + message.cData = cData;
1271 + message.rgwzData = (LPCWSTR*)rgsczData;
1272 + message.msiMessage.mt = mt;
1273 + message.msiMessage.wzMessage = wzMessage;
1274 + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1275 +
1276 + UninitializeMessageData(rgsczData, cData);
1277 + return nResult;
1278 +}
1279 +
1280 +static INT SendErrorMessage(
1281 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1282 + __in UINT uiFlags,
1283 + __in_z LPCWSTR wzMessage,
1284 + __in_opt MSIHANDLE hRecord
1285 + )
1286 +{
1287 + INT nResult = IDNOACTION;
1288 + WIU_MSI_EXECUTE_MESSAGE message = { };
1289 + DWORD dwErrorCode = 0;
1290 + LPWSTR* rgsczData = NULL;
1291 + DWORD cData = 0;
1292 +
1293 + if (hRecord)
1294 + {
1295 + dwErrorCode = ::MsiRecordGetInteger(hRecord, 1);
1296 +
1297 + // Set the recommendation if it's a known error code.
1298 + switch (dwErrorCode)
1299 + {
1300 + case 1605: // continue with install even if there isn't enough room for rollback.
1301 + nResult = IDIGNORE;
1302 + break;
1303 +
1304 + case 1704: // rollback suspended installs so our install can continue.
1305 + nResult = IDOK;
1306 + break;
1307 + }
1308 + }
1309 +
1310 + InitializeMessageData(hRecord, &rgsczData, &cData);
1311 +
1312 + message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR;
1313 + message.dwAllowedResults = uiFlags;
1314 + message.nResultRecommendation = nResult;
1315 + message.cData = cData;
1316 + message.rgwzData = (LPCWSTR*)rgsczData;
1317 + message.error.dwErrorCode = dwErrorCode;
1318 + message.error.wzMessage = wzMessage;
1319 + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1320 +
1321 + UninitializeMessageData(rgsczData, cData);
1322 + return nResult;
1323 +}
1324 +
1325 +static INT SendFilesInUseMessage(
1326 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1327 + __in_opt MSIHANDLE hRecord,
1328 + __in BOOL /*fRestartManagerRequest*/
1329 + )
1330 +{
1331 + INT nResult = IDNOACTION;
1332 + WIU_MSI_EXECUTE_MESSAGE message = { };
1333 + LPWSTR* rgsczData = NULL;
1334 + DWORD cData = 0;
1335 +
1336 + InitializeMessageData(hRecord, &rgsczData, &cData);
1337 +
1338 + message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1339 + message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY;
1340 + message.cData = cData;
1341 + message.rgwzData = (LPCWSTR*)rgsczData;
1342 + message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information.
1343 + message.msiFilesInUse.rgwzFiles = message.rgwzData;
1344 + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1345 +
1346 + UninitializeMessageData(rgsczData, cData);
1347 + return nResult;
1348 +}
1349 +
1350 +static INT SendProgressUpdate(
1351 + __in WIU_MSI_EXECUTE_CONTEXT* pContext
1352 + )
1353 +{
1354 + int nResult = IDNOACTION;
1355 + DWORD dwPercentage = 0; // number representing 0 - 100%
1356 + WIU_MSI_EXECUTE_MESSAGE message = { };
1357 +
1358 + //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal;
1359 + //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete);
1360 + //double dProgressGauge = 0;
1361 + //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal;
1362 +
1363 + // Calculate progress for the phases of Windows Installer.
1364 + // TODO: handle upgrade progress which would add another phase.
1365 + dwPercentage += CalculatePhaseProgress(pContext, 0, 15);
1366 + dwPercentage += CalculatePhaseProgress(pContext, 1, 80);
1367 + dwPercentage += CalculatePhaseProgress(pContext, 2, 5);
1368 + dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%.
1369 +
1370 + if (pContext->fRollback)
1371 + {
1372 + dwPercentage = 100 - dwPercentage;
1373 + }
1374 +
1375 + //if (qwTotal) // avoid "divide by zero" if the MSI range is blank.
1376 + //{
1377 + // // calculate gauge.
1378 + // double dProgressGauge = static_cast<double>(qwCompleted) / static_cast<double>(qwTotal);
1379 + // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804;
1380 + // qwCompleted = (DWORD)(dProgressGauge * qwTotal);
1381 +
1382 + // // calculate progress within range
1383 + // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal));
1384 + // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal);
1385 + //}
1386 +
1387 +#ifdef _DEBUG
1388 + DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted;
1389 + DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal;
1390 + Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage);
1391 + //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress.");
1392 +#endif
1393 +
1394 + message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS;
1395 + message.dwAllowedResults = MB_OKCANCEL;
1396 + message.progress.dwPercentage = dwPercentage;
1397 + nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1398 +
1399 + return nResult;
1400 +}
1401 +
1402 +static void ResetProgress(
1403 + __in WIU_MSI_EXECUTE_CONTEXT* pContext
1404 + )
1405 +{
1406 + memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress));
1407 + pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID;
1408 +}
1409 +
1410 +static DWORD CalculatePhaseProgress(
1411 + __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1412 + __in DWORD dwProgressIndex,
1413 + __in DWORD dwWeightPercentage
1414 + )
1415 +{
1416 + DWORD dwPhasePercentage = 0;
1417 +
1418 + // If we've already passed this progress index, return the maximum percentage possible (the weight)
1419 + if (dwProgressIndex < pContext->dwCurrentProgressIndex)
1420 + {
1421 + dwPhasePercentage = dwWeightPercentage;
1422 + }
1423 + else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress.
1424 + {
1425 + WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex;
1426 + if (pProgress->dwTotal)
1427 + {
1428 + DWORD64 dw64Completed = pProgress->dwCompleted;
1429 + dwPhasePercentage = static_cast<DWORD>(dw64Completed * dwWeightPercentage / pProgress->dwTotal);
1430 + }
1431 + }
1432 + // else we're not there yet so it has to be zero.
1433 +
1434 + return dwPhasePercentage;
1435 +}
1436 +
1437 +void InitializeMessageData(
1438 + __in_opt MSIHANDLE hRecord,
1439 + __deref_out_ecount(*pcData) LPWSTR** prgsczData,
1440 + __out DWORD* pcData
1441 + )
1442 +{
1443 + DWORD cData = 0;
1444 + LPWSTR* rgsczData = NULL;
1445 +
1446 + // If we have a record based message, try to get the extra data.
1447 + if (hRecord)
1448 + {
1449 + cData = ::MsiRecordGetFieldCount(hRecord);
1450 + if (cData)
1451 + {
1452 + rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE);
1453 + }
1454 +
1455 + for (DWORD i = 0; rgsczData && i < cData; ++i)
1456 + {
1457 + DWORD cch = 0;
1458 +
1459 + // get string from record
1460 +#pragma prefast(push)
1461 +#pragma prefast(disable:6298)
1462 + DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch);
1463 +#pragma prefast(pop)
1464 + if (ERROR_MORE_DATA == er)
1465 + {
1466 + HRESULT hr = StrAlloc(&rgsczData[i], ++cch);
1467 + if (SUCCEEDED(hr))
1468 + {
1469 + er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch);
1470 + }
1471 + }
1472 + }
1473 + }
1474 +
1475 + *prgsczData = rgsczData;
1476 + *pcData = cData;
1477 +}
1478 +
1479 +void UninitializeMessageData(
1480 + __in LPWSTR* rgsczData,
1481 + __in DWORD cData
1482 + )
1483 +{
1484 + // Clean up if there was any data allocated.
1485 + if (rgsczData)
1486 + {
1487 + for (DWORD i = 0; i < cData; ++i)
1488 + {
1489 + ReleaseStr(rgsczData[i]);
1490 + }
1491 +
1492 + MemFree(rgsczData);
1493 + }
1494 +}
src/dutil/wuautil.cpp new
+89
@@ -0,0 +1,89 @@
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 +
5 +
6 +// internal function declarations
7 +
8 +static HRESULT GetAutomaticUpdatesService(
9 + __out IAutomaticUpdates **ppAutomaticUpdates
10 + );
11 +
12 +
13 +// function definitions
14 +
15 +extern "C" HRESULT DAPI WuaPauseAutomaticUpdates()
16 +{
17 + HRESULT hr = S_OK;
18 + IAutomaticUpdates *pAutomaticUpdates = NULL;
19 +
20 + hr = GetAutomaticUpdatesService(&pAutomaticUpdates);
21 + ExitOnFailure(hr, "Failed to get the Automatic Updates service.");
22 +
23 + hr = pAutomaticUpdates->Pause();
24 + ExitOnFailure(hr, "Failed to pause the Automatic Updates service.");
25 +
26 +LExit:
27 + ReleaseObject(pAutomaticUpdates);
28 +
29 + return hr;
30 +}
31 +
32 +extern "C" HRESULT DAPI WuaResumeAutomaticUpdates()
33 +{
34 + HRESULT hr = S_OK;
35 + IAutomaticUpdates *pAutomaticUpdates = NULL;
36 +
37 + hr = GetAutomaticUpdatesService(&pAutomaticUpdates);
38 + ExitOnFailure(hr, "Failed to get the Automatic Updates service.");
39 +
40 + hr = pAutomaticUpdates->Resume();
41 + ExitOnFailure(hr, "Failed to resume the Automatic Updates service.");
42 +
43 +LExit:
44 + ReleaseObject(pAutomaticUpdates);
45 +
46 + return hr;
47 +}
48 +
49 +extern "C" HRESULT DAPI WuaRestartRequired(
50 + __out BOOL* pfRestartRequired
51 + )
52 +{
53 + HRESULT hr = S_OK;
54 + ISystemInformation* pSystemInformation = NULL;
55 + VARIANT_BOOL bRestartRequired;
56 +
57 + hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast<LPVOID*>(&pSystemInformation));
58 + ExitOnRootFailure(hr, "Failed to get WUA system information interface.");
59 +
60 + hr = pSystemInformation->get_RebootRequired(&bRestartRequired);
61 + ExitOnRootFailure(hr, "Failed to determine if restart is required from WUA.");
62 +
63 + *pfRestartRequired = (VARIANT_FALSE != bRestartRequired);
64 +
65 +LExit:
66 + ReleaseObject(pSystemInformation);
67 +
68 + return hr;
69 +}
70 +
71 +
72 +// internal function definitions
73 +
74 +static HRESULT GetAutomaticUpdatesService(
75 + __out IAutomaticUpdates **ppAutomaticUpdates
76 + )
77 +{
78 + HRESULT hr = S_OK;
79 + CLSID clsidAutomaticUpdates = { };
80 +
81 + hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates);
82 + ExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate.");
83 +
84 + hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast<LPVOID*>(ppAutomaticUpdates));
85 + ExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate.");
86 +
87 +LExit:
88 + return hr;
89 +}
src/dutil/xmlutil.cpp new
+1317
@@ -0,0 +1,1317 @@
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 +
5 +// intialization globals
6 +CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} };
7 +static volatile LONG vcXmlInitialized = 0;
8 +static BOOL vfMsxml40 = FALSE;
9 +static BOOL fComInitialized = FALSE;
10 +BOOL vfMsxml30 = FALSE;
11 +
12 +/********************************************************************
13 + XmlInitialize - finds an appropriate version of the XML DOM
14 +
15 +*********************************************************************/
16 +extern "C" HRESULT DAPI XmlInitialize(
17 + )
18 +{
19 + HRESULT hr = S_OK;
20 +
21 + if (!fComInitialized)
22 + {
23 + hr = ::CoInitialize(0);
24 + if (RPC_E_CHANGED_MODE != hr)
25 + {
26 + ExitOnFailure(hr, "failed to initialize COM");
27 + fComInitialized = TRUE;
28 + }
29 + }
30 +
31 + LONG cInitialized = ::InterlockedIncrement(&vcXmlInitialized);
32 + if (1 == cInitialized)
33 + {
34 + // NOTE: 4.0 behaves differently than 3.0 so there may be problems doing this
35 +#if 0
36 + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument.4.0", &vclsidXMLDOM);
37 + if (S_OK == hr)
38 + {
39 + vfMsxml40 = TRUE;
40 + Trace(REPORT_VERBOSE, "found Msxml2.DOMDocument.4.0");
41 + ExitFunction();
42 + }
43 +#endif
44 + hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument", &vclsidXMLDOM);
45 + if (FAILED(hr))
46 + {
47 + // try to fall back to old MSXML
48 + hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM);
49 + }
50 + ExitOnFailure(hr, "failed to get CLSID for XML DOM");
51 +
52 + Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) ||
53 + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) ||
54 + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument26) ||
55 + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) ||
56 + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument40) ||
57 + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument50) ||
58 + IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument60));
59 + }
60 +
61 + hr = S_OK;
62 +LExit:
63 + return hr;
64 +}
65 +
66 +
67 +/********************************************************************
68 + XmUninitialize -
69 +
70 +*********************************************************************/
71 +extern "C" void DAPI XmlUninitialize(
72 + )
73 +{
74 + AssertSz(vcXmlInitialized, "XmlUninitialize called when not initialized");
75 +
76 + LONG cInitialized = ::InterlockedDecrement(&vcXmlInitialized);
77 +
78 + if (0 == cInitialized)
79 + {
80 + memset(&vclsidXMLDOM, 0, sizeof(vclsidXMLDOM));
81 +
82 + if (fComInitialized)
83 + {
84 + ::CoUninitialize();
85 + }
86 + }
87 +}
88 +
89 +extern "C" HRESULT DAPI XmlCreateElement(
90 + __in IXMLDOMDocument *pixdDocument,
91 + __in_z LPCWSTR wzElementName,
92 + __out IXMLDOMElement **ppixnElement
93 + )
94 +{
95 + if (!ppixnElement || !pixdDocument)
96 + {
97 + return E_INVALIDARG;
98 + }
99 +
100 + HRESULT hr = S_OK;
101 + BSTR bstrElementName = ::SysAllocString(wzElementName);
102 + ExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString");
103 + hr = pixdDocument->createElement(bstrElementName, ppixnElement);
104 +LExit:
105 + ReleaseBSTR(bstrElementName);
106 + return hr;
107 +}
108 +
109 +
110 +/********************************************************************
111 + XmlCreateDocument -
112 +
113 +*********************************************************************/
114 +extern "C" HRESULT DAPI XmlCreateDocument(
115 + __in_opt LPCWSTR pwzElementName,
116 + __out IXMLDOMDocument** ppixdDocument,
117 + __out_opt IXMLDOMElement** ppixeRootElement
118 + )
119 +{
120 + HRESULT hr = S_OK;
121 + BOOL (WINAPI *pfnDisableWow64)(__out PVOID* ) = NULL;
122 + BOOLEAN (WINAPI *pfnEnableWow64)(__in BOOLEAN ) = NULL;
123 + BOOL (WINAPI *pfnRevertWow64)(__in PVOID ) = NULL;
124 + BOOL fWow64Available = FALSE;
125 + void *pvWow64State = NULL;
126 +
127 + // RELEASEME
128 + IXMLDOMElement* pixeRootElement = NULL;
129 + IXMLDOMDocument *pixdDocument = NULL;
130 +
131 + // Test if we have access to the Wow64 API, and store the result in fWow64Available
132 + HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll");
133 + ExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll");
134 +
135 + // This will test if we have access to the Wow64 API
136 + if (NULL != GetProcAddress(hKernel32, "IsWow64Process"))
137 + {
138 + pfnDisableWow64 = (BOOL (WINAPI *)(PVOID *))::GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection");
139 + pfnEnableWow64 = (BOOLEAN (WINAPI *)(BOOLEAN))::GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection");
140 + pfnRevertWow64 = (BOOL (WINAPI *)(PVOID))::GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection");
141 +
142 + fWow64Available = pfnDisableWow64 && pfnEnableWow64 && pfnRevertWow64;
143 + }
144 +
145 + // create the top level XML document
146 + AssertSz(vcXmlInitialized, "XmlInitialize() was not called");
147 +
148 + // Enable Wow64 Redirection, if possible
149 + if (fWow64Available)
150 + {
151 + // We want to enable Wow64 redirection, but the Wow64 API requires us to disable it first to get its current state (so we can revert it later)
152 + pfnDisableWow64(&pvWow64State);
153 + // If we fail to enable it, don't bother trying to disable it later on
154 + fWow64Available = pfnEnableWow64(TRUE);
155 + }
156 +
157 + hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument);
158 + ExitOnFailure(hr, "failed to create XML DOM Document");
159 + Assert(pixdDocument);
160 +
161 + if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20))
162 + {
163 + vfMsxml30 = TRUE;
164 + }
165 +
166 + if (pwzElementName)
167 + {
168 + hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement);
169 + ExitOnFailure(hr, "failed XmlCreateElement");
170 + hr = pixdDocument->appendChild(pixeRootElement, NULL);
171 + ExitOnFailure(hr, "failed appendChild");
172 + }
173 +
174 + *ppixdDocument = pixdDocument;
175 + pixdDocument = NULL;
176 +
177 + if (ppixeRootElement)
178 + {
179 + *ppixeRootElement = pixeRootElement;
180 + pixeRootElement = NULL;
181 + }
182 +
183 +LExit:
184 + // Re-disable Wow64 Redirection, if appropriate
185 + if (fWow64Available && !pfnRevertWow64(pvWow64State))
186 + {
187 + // If we expected to be able to revert, and couldn't, fail in the only graceful way we can
188 + ::ExitProcess(1);
189 + }
190 +
191 + ReleaseObject(pixeRootElement);
192 + ReleaseObject(pixdDocument);
193 + return hr;
194 +}
195 +
196 +
197 +/********************************************************************
198 + XmlLoadDocument -
199 +
200 +*********************************************************************/
201 +extern "C" HRESULT DAPI XmlLoadDocument(
202 + __in_z LPCWSTR wzDocument,
203 + __out IXMLDOMDocument** ppixdDocument
204 + )
205 +{
206 + return XmlLoadDocumentEx(wzDocument, 0, ppixdDocument);
207 +}
208 +
209 +
210 +/********************************************************************
211 + XmlReportParseError -
212 +
213 +*********************************************************************/
214 +static void XmlReportParseError(
215 + __in IXMLDOMParseError* pixpe
216 + )
217 +{
218 + HRESULT hr = S_OK;
219 + long lNumber = 0;
220 + BSTR bstr = NULL;
221 +
222 + Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:");
223 +
224 + hr = pixpe->get_errorCode(&lNumber);
225 + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode.");
226 + Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber);
227 +
228 + hr = pixpe->get_filepos(&lNumber);
229 + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos.");
230 + Trace(REPORT_STANDARD, "filepos = %d", lNumber);
231 +
232 + hr = pixpe->get_line(&lNumber);
233 + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.line.");
234 + Trace(REPORT_STANDARD, "line = %d", lNumber);
235 +
236 + hr = pixpe->get_linepos(&lNumber);
237 + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos.");
238 + Trace(REPORT_STANDARD, "linepos = %d", lNumber);
239 +
240 + hr = pixpe->get_reason(&bstr);
241 + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason.");
242 + Trace(REPORT_STANDARD, "reason = %ls", bstr);
243 + ReleaseNullBSTR(bstr);
244 +
245 + hr = pixpe->get_srcText (&bstr);
246 + ExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText .");
247 + Trace(REPORT_STANDARD, "srcText = %ls", bstr);
248 + ReleaseNullBSTR(bstr);
249 +
250 +LExit:
251 + ReleaseBSTR(bstr);
252 +}
253 +
254 +/********************************************************************
255 + XmlLoadDocumentEx -
256 +
257 +*********************************************************************/
258 +extern "C" HRESULT DAPI XmlLoadDocumentEx(
259 + __in_z LPCWSTR wzDocument,
260 + __in DWORD dwAttributes,
261 + __out IXMLDOMDocument** ppixdDocument
262 + )
263 +{
264 + HRESULT hr = S_OK;
265 + VARIANT_BOOL vbSuccess = 0;
266 +
267 + // RELEASEME
268 + IXMLDOMDocument* pixd = NULL;
269 + IXMLDOMParseError* pixpe = NULL;
270 + BSTR bstrLoad = NULL;
271 +
272 + if (!wzDocument || !*wzDocument)
273 + {
274 + hr = E_UNEXPECTED;
275 + ExitOnFailure(hr, "string must be non-null");
276 + }
277 +
278 + hr = XmlCreateDocument(NULL, &pixd);
279 + if (hr == S_FALSE)
280 + {
281 + hr = E_FAIL;
282 + }
283 + ExitOnFailure(hr, "failed XmlCreateDocument");
284 +
285 + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE)
286 + {
287 + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE);
288 + ExitOnFailure(hr, "failed put_preserveWhiteSpace");
289 + }
290 +
291 + // Security issue. Avoid triggering anything external.
292 + hr = pixd->put_validateOnParse(VARIANT_FALSE);
293 + ExitOnFailure(hr, "failed put_validateOnParse");
294 + hr = pixd->put_resolveExternals(VARIANT_FALSE);
295 + ExitOnFailure(hr, "failed put_resolveExternals");
296 +
297 + bstrLoad = ::SysAllocString(wzDocument);
298 + ExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx");
299 +
300 + hr = pixd->loadXML(bstrLoad, &vbSuccess);
301 + if (S_FALSE == hr)
302 + {
303 + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
304 + }
305 +
306 + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe))
307 + {
308 + XmlReportParseError(pixpe);
309 + }
310 +
311 + ExitOnFailure(hr, "failed loadXML");
312 +
313 +
314 + hr = S_OK;
315 +LExit:
316 + if (ppixdDocument)
317 + {
318 + *ppixdDocument = pixd;
319 + pixd = NULL;
320 + }
321 + ReleaseBSTR(bstrLoad);
322 + ReleaseObject(pixd);
323 + ReleaseObject(pixpe);
324 +
325 + return hr;
326 +}
327 +
328 +
329 +/*******************************************************************
330 + XmlLoadDocumentFromFile
331 +
332 +********************************************************************/
333 +extern "C" HRESULT DAPI XmlLoadDocumentFromFile(
334 + __in_z LPCWSTR wzPath,
335 + __out IXMLDOMDocument** ppixdDocument
336 + )
337 +{
338 + return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument);
339 +}
340 +
341 +
342 +/*******************************************************************
343 + XmlLoadDocumentFromFileEx
344 +
345 +********************************************************************/
346 +extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx(
347 + __in_z LPCWSTR wzPath,
348 + __in DWORD dwAttributes,
349 + __out IXMLDOMDocument** ppixdDocument
350 + )
351 +{
352 + HRESULT hr = S_OK;
353 + VARIANT varPath;
354 + VARIANT_BOOL vbSuccess = 0;
355 +
356 + IXMLDOMDocument* pixd = NULL;
357 + IXMLDOMParseError* pixpe = NULL;
358 +
359 + ::VariantInit(&varPath);
360 + varPath.vt = VT_BSTR;
361 + varPath.bstrVal = ::SysAllocString(wzPath);
362 + ExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx");
363 +
364 + hr = XmlCreateDocument(NULL, &pixd);
365 + if (hr == S_FALSE)
366 + {
367 + hr = E_FAIL;
368 + }
369 + ExitOnFailure(hr, "failed XmlCreateDocument");
370 +
371 + if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE)
372 + {
373 + hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE);
374 + ExitOnFailure(hr, "failed put_preserveWhiteSpace");
375 + }
376 +
377 + // Avoid triggering anything external.
378 + hr = pixd->put_validateOnParse(VARIANT_FALSE);
379 + ExitOnFailure(hr, "failed put_validateOnParse");
380 + hr = pixd->put_resolveExternals(VARIANT_FALSE);
381 + ExitOnFailure(hr, "failed put_resolveExternals");
382 +
383 + pixd->put_async(VARIANT_FALSE);
384 + hr = pixd->load(varPath, &vbSuccess);
385 + if (S_FALSE == hr)
386 + {
387 + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
388 + }
389 +
390 + if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe))
391 + {
392 + XmlReportParseError(pixpe);
393 + }
394 +
395 + ExitOnFailure(hr, "failed to load XML from: %ls", wzPath);
396 +
397 + if (ppixdDocument)
398 + {
399 + *ppixdDocument = pixd;
400 + pixd = NULL;
401 + }
402 +
403 + hr = S_OK;
404 +LExit:
405 + ReleaseVariant(varPath);
406 + ReleaseObject(pixd);
407 + ReleaseObject(pixpe);
408 +
409 + return hr;
410 +}
411 +
412 +
413 +/********************************************************************
414 + XmlLoadDocumentFromBuffer
415 +
416 +*********************************************************************/
417 +extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer(
418 + __in_bcount(cbSource) const BYTE* pbSource,
419 + __in DWORD cbSource,
420 + __out IXMLDOMDocument** ppixdDocument
421 + )
422 +{
423 + HRESULT hr = S_OK;
424 + IXMLDOMDocument* pixdDocument = NULL;
425 + SAFEARRAY sa = { };
426 + VARIANT vtXmlSource;
427 + VARIANT_BOOL vbSuccess = 0;
428 +
429 + ::VariantInit(&vtXmlSource);
430 +
431 + // create document
432 + hr = XmlCreateDocument(NULL, &pixdDocument);
433 + if (hr == S_FALSE)
434 + {
435 + hr = E_FAIL;
436 + }
437 + ExitOnFailure(hr, "failed XmlCreateDocument");
438 +
439 + // Security issue. Avoid triggering anything external.
440 + hr = pixdDocument->put_validateOnParse(VARIANT_FALSE);
441 + ExitOnFailure(hr, "failed put_validateOnParse");
442 + hr = pixdDocument->put_resolveExternals(VARIANT_FALSE);
443 + ExitOnFailure(hr, "failed put_resolveExternals");
444 +
445 + // load document
446 + sa.cDims = 1;
447 + sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE;
448 + sa.cbElements = 1;
449 + sa.pvData = (PVOID)pbSource;
450 + sa.rgsabound[0].cElements = cbSource;
451 + vtXmlSource.vt = VT_ARRAY | VT_UI1;
452 + vtXmlSource.parray = &sa;
453 +
454 + hr = pixdDocument->load(vtXmlSource, &vbSuccess);
455 + if (S_FALSE == hr)
456 + {
457 + hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
458 + }
459 + ExitOnFailure(hr, "failed loadXML");
460 +
461 + // return value
462 + *ppixdDocument = pixdDocument;
463 + pixdDocument = NULL;
464 +
465 +LExit:
466 + ReleaseObject(pixdDocument);
467 + return hr;
468 +}
469 +
470 +
471 +/********************************************************************
472 + XmlSetAttribute -
473 +
474 +*********************************************************************/
475 +extern "C" HRESULT DAPI XmlSetAttribute(
476 + __in IXMLDOMNode* pixnNode,
477 + __in_z LPCWSTR pwzAttribute,
478 + __in_z LPCWSTR pwzAttributeValue
479 + )
480 +{
481 + HRESULT hr = S_OK;
482 + VARIANT varAttributeValue;
483 + ::VariantInit(&varAttributeValue);
484 +
485 + // RELEASEME
486 + IXMLDOMDocument* pixdDocument = NULL;
487 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
488 + IXMLDOMAttribute* pixaAttribute = NULL;
489 + IXMLDOMNode* pixaNode = NULL;
490 + BSTR bstrAttributeName = ::SysAllocString(pwzAttribute);
491 + ExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute");
492 +
493 + hr = pixnNode->get_attributes(&pixnnmAttributes);
494 + ExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute);
495 +
496 + hr = pixnNode->get_ownerDocument(&pixdDocument);
497 + if (hr == S_FALSE)
498 + {
499 + hr = E_FAIL;
500 + }
501 + ExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute");
502 +
503 + hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute);
504 + ExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute);
505 +
506 + varAttributeValue.vt = VT_BSTR;
507 + varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue);
508 + if (!varAttributeValue.bstrVal)
509 + {
510 + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
511 + }
512 + ExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute");
513 +
514 + hr = pixaAttribute->put_nodeValue(varAttributeValue);
515 + ExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute);
516 +
517 + hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode);
518 + ExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute);
519 +
520 +LExit:
521 + ReleaseObject(pixdDocument);
522 + ReleaseObject(pixnnmAttributes);
523 + ReleaseObject(pixaAttribute);
524 + ReleaseObject(pixaNode);
525 + ReleaseBSTR(varAttributeValue.bstrVal);
526 + ReleaseBSTR(bstrAttributeName);
527 +
528 + return hr;
529 +}
530 +
531 +
532 +/********************************************************************
533 + XmlSelectSingleNode -
534 +
535 +*********************************************************************/
536 +extern "C" HRESULT DAPI XmlSelectSingleNode(
537 + __in IXMLDOMNode* pixnParent,
538 + __in_z LPCWSTR wzXPath,
539 + __out IXMLDOMNode **ppixnChild
540 + )
541 +{
542 + HRESULT hr = S_OK;
543 +
544 + BSTR bstrXPath = NULL;
545 +
546 + ExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode");
547 + ExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode");
548 +
549 + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L"");
550 + ExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode");
551 +
552 + hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild);
553 +
554 +LExit:
555 + ReleaseBSTR(bstrXPath);
556 +
557 + return hr;
558 +}
559 +
560 +
561 +/********************************************************************
562 + XmlCreateTextNode -
563 +
564 +*********************************************************************/
565 +extern "C" HRESULT DAPI XmlCreateTextNode(
566 + __in IXMLDOMDocument *pixdDocument,
567 + __in_z LPCWSTR wzText,
568 + __out IXMLDOMText **ppixnTextNode
569 + )
570 +{
571 + if (!ppixnTextNode || !pixdDocument)
572 + {
573 + return E_INVALIDARG;
574 + }
575 +
576 + HRESULT hr = S_OK;
577 + BSTR bstrText = ::SysAllocString(wzText);
578 + ExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString");
579 + hr = pixdDocument->createTextNode(bstrText, ppixnTextNode);
580 +LExit:
581 + ReleaseBSTR(bstrText);
582 +
583 + return hr;
584 +}
585 +
586 +
587 +/********************************************************************
588 + XmlGetText
589 +
590 +*********************************************************************/
591 +extern "C" HRESULT DAPI XmlGetText(
592 + __in IXMLDOMNode* pixnNode,
593 + __deref_out_z BSTR* pbstrText
594 + )
595 +{
596 + return pixnNode->get_text(pbstrText);
597 +}
598 +
599 +
600 +/********************************************************************
601 + XmlGetAttribute
602 +
603 +*********************************************************************/
604 +extern "C" HRESULT DAPI XmlGetAttribute(
605 + __in IXMLDOMNode* pixnNode,
606 + __in_z LPCWSTR pwzAttribute,
607 + __deref_out_z BSTR* pbstrAttributeValue
608 + )
609 +{
610 + Assert(pixnNode);
611 + HRESULT hr = S_OK;
612 +
613 + // RELEASEME
614 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
615 + IXMLDOMNode* pixnAttribute = NULL;
616 + VARIANT varAttributeValue;
617 + BSTR bstrAttribute = SysAllocString(pwzAttribute);
618 +
619 + // INIT
620 + ::VariantInit(&varAttributeValue);
621 +
622 + // get attribute value from source
623 + hr = pixnNode->get_attributes(&pixnnmAttributes);
624 + ExitOnFailure(hr, "failed get_attributes");
625 +
626 + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute);
627 + if (S_FALSE == hr)
628 + {
629 + // hr = E_FAIL;
630 + ExitFunction();
631 + }
632 + ExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute);
633 +
634 + hr = pixnAttribute->get_nodeValue(&varAttributeValue);
635 + ExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute);
636 +
637 + // steal the BSTR from the VARIANT
638 + if (S_OK == hr && pbstrAttributeValue)
639 + {
640 + *pbstrAttributeValue = varAttributeValue.bstrVal;
641 + varAttributeValue.bstrVal = NULL;
642 + }
643 +
644 +LExit:
645 + ReleaseObject(pixnnmAttributes);
646 + ReleaseObject(pixnAttribute);
647 + ReleaseVariant(varAttributeValue);
648 + ReleaseBSTR(bstrAttribute);
649 +
650 + return hr;
651 +}
652 +
653 +
654 +/********************************************************************
655 + XmlGetAttributeEx
656 +
657 +*********************************************************************/
658 +HRESULT DAPI XmlGetAttributeEx(
659 + __in IXMLDOMNode* pixnNode,
660 + __in_z LPCWSTR wzAttribute,
661 + __deref_out_z LPWSTR* psczAttributeValue
662 + )
663 +{
664 + Assert(pixnNode);
665 + HRESULT hr = S_OK;
666 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
667 + IXMLDOMNode* pixnAttribute = NULL;
668 + VARIANT varAttributeValue;
669 + BSTR bstrAttribute = NULL;
670 +
671 + ::VariantInit(&varAttributeValue);
672 +
673 + // get attribute value from source
674 + hr = pixnNode->get_attributes(&pixnnmAttributes);
675 + ExitOnFailure(hr, "Failed get_attributes.");
676 +
677 + bstrAttribute = ::SysAllocString(wzAttribute);
678 + ExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR.");
679 +
680 + hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute);
681 + if (S_FALSE == hr)
682 + {
683 + ExitFunction1(hr = E_NOTFOUND);
684 + }
685 + ExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute);
686 +
687 + hr = pixnAttribute->get_nodeValue(&varAttributeValue);
688 + if (S_FALSE == hr)
689 + {
690 + ExitFunction1(hr = E_NOTFOUND);
691 + }
692 + ExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute);
693 +
694 + // copy value
695 + hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0);
696 + ExitOnFailure(hr, "Failed to copy attribute value.");
697 +
698 +LExit:
699 + ReleaseObject(pixnnmAttributes);
700 + ReleaseObject(pixnAttribute);
701 + ReleaseVariant(varAttributeValue);
702 + ReleaseBSTR(bstrAttribute);
703 +
704 + return hr;
705 +}
706 +
707 +
708 +/********************************************************************
709 + XmlGetYesNoAttribute
710 +
711 +*********************************************************************/
712 +HRESULT DAPI XmlGetYesNoAttribute(
713 + __in IXMLDOMNode* pixnNode,
714 + __in_z LPCWSTR wzAttribute,
715 + __out BOOL* pfYes
716 + )
717 +{
718 + HRESULT hr = S_OK;
719 + LPWSTR sczValue = NULL;
720 +
721 + hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue);
722 + if (E_NOTFOUND != hr)
723 + {
724 + ExitOnFailure(hr, "Failed to get attribute.");
725 +
726 + *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1);
727 + }
728 +
729 +LExit:
730 + ReleaseStr(sczValue);
731 +
732 + return hr;
733 +}
734 +
735 +
736 +
737 +/********************************************************************
738 + XmlGetAttributeNumber
739 +
740 +*********************************************************************/
741 +extern "C" HRESULT DAPI XmlGetAttributeNumber(
742 + __in IXMLDOMNode* pixnNode,
743 + __in_z LPCWSTR pwzAttribute,
744 + __out DWORD* pdwValue
745 + )
746 +{
747 + HRESULT hr = XmlGetAttributeNumberBase(pixnNode, pwzAttribute, 10, pdwValue);
748 + return hr;
749 +}
750 +
751 +
752 +/********************************************************************
753 + XmlGetAttributeNumberBase
754 +
755 +*********************************************************************/
756 +extern "C" HRESULT DAPI XmlGetAttributeNumberBase(
757 + __in IXMLDOMNode* pixnNode,
758 + __in_z LPCWSTR pwzAttribute,
759 + __in int nBase,
760 + __out DWORD* pdwValue
761 + )
762 +{
763 + HRESULT hr = S_OK;
764 + BSTR bstrPointer = NULL;
765 +
766 + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer);
767 + ExitOnFailure(hr, "Failed to get value from attribute.");
768 +
769 + if (S_OK == hr)
770 + {
771 + *pdwValue = wcstoul(bstrPointer, NULL, nBase);
772 + }
773 +
774 +LExit:
775 + ReleaseBSTR(bstrPointer);
776 + return hr;
777 +}
778 +
779 +
780 +/********************************************************************
781 + XmlGetAttributeLargeNumber
782 +
783 +*********************************************************************/
784 +extern "C" HRESULT DAPI XmlGetAttributeLargeNumber(
785 + __in IXMLDOMNode* pixnNode,
786 + __in_z LPCWSTR pwzAttribute,
787 + __out DWORD64* pdw64Value
788 + )
789 +{
790 + HRESULT hr = S_OK;
791 + BSTR bstrValue = NULL;
792 +
793 + hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue);
794 + ExitOnFailure(hr, "failed XmlGetAttribute");
795 +
796 + if (S_OK == hr)
797 + {
798 + LONGLONG ll = 0;
799 + hr = StrStringToInt64(bstrValue, 0, &ll);
800 + ExitOnFailure(hr, "Failed to treat attribute value as number.");
801 +
802 + *pdw64Value = ll;
803 + }
804 + else
805 + {
806 + *pdw64Value = 0;
807 + }
808 +
809 +LExit:
810 + ReleaseBSTR(bstrValue);
811 + return hr;
812 +}
813 +
814 +
815 +/********************************************************************
816 + XmlGetNamedItem -
817 +
818 +*********************************************************************/
819 +extern "C" HRESULT DAPI XmlGetNamedItem(
820 + __in IXMLDOMNamedNodeMap *pixnmAttributes,
821 + __in_opt LPCWSTR wzName,
822 + __out IXMLDOMNode **ppixnNamedItem
823 + )
824 +{
825 + if (!pixnmAttributes || !ppixnNamedItem)
826 + {
827 + return E_INVALIDARG;
828 + }
829 +
830 + HRESULT hr = S_OK;
831 + BSTR bstrName = ::SysAllocString(wzName);
832 + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString");
833 +
834 + hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem);
835 +
836 +LExit:
837 + ReleaseBSTR(bstrName);
838 + return hr;
839 +}
840 +
841 +
842 +/********************************************************************
843 + XmlSetText -
844 +
845 +*********************************************************************/
846 +extern "C" HRESULT DAPI XmlSetText(
847 + __in IXMLDOMNode *pixnNode,
848 + __in_z LPCWSTR pwzText
849 + )
850 +{
851 + Assert(pixnNode && pwzText);
852 + HRESULT hr = S_OK;
853 + DOMNodeType dnType;
854 +
855 + // RELEASEME
856 + IXMLDOMDocument* pixdDocument = NULL;
857 + IXMLDOMNodeList* pixnlNodeList = NULL;
858 + IXMLDOMNode* pixnChildNode = NULL;
859 + IXMLDOMText* pixtTextNode = NULL;
860 + VARIANT varText;
861 +
862 + ::VariantInit(&varText);
863 +
864 + // find the text node
865 + hr = pixnNode->get_childNodes(&pixnlNodeList);
866 + ExitOnFailure(hr, "failed to get child nodes");
867 +
868 + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode)))
869 + {
870 + hr = pixnChildNode->get_nodeType(&dnType);
871 + ExitOnFailure(hr, "failed to get node type");
872 +
873 + if (NODE_TEXT == dnType)
874 + break;
875 + ReleaseNullObject(pixnChildNode);
876 + }
877 + if (S_FALSE == hr)
878 + {
879 + hr = S_OK;
880 + }
881 +
882 + if (pixnChildNode)
883 + {
884 + varText.vt = VT_BSTR;
885 + varText.bstrVal = ::SysAllocString(pwzText);
886 + if (!varText.bstrVal)
887 + {
888 + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
889 + }
890 + ExitOnFailure(hr, "failed SysAllocString in XmlSetText");
891 +
892 + hr = pixnChildNode->put_nodeValue(varText);
893 + ExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue");
894 + }
895 + else
896 + {
897 + hr = pixnNode->get_ownerDocument(&pixdDocument);
898 + if (hr == S_FALSE)
899 + {
900 + hr = E_FAIL;
901 + }
902 + ExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute");
903 +
904 + hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode);
905 + ExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText);
906 +
907 + hr = pixnNode->appendChild(pixtTextNode, NULL);
908 + ExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText);
909 + }
910 +
911 + hr = *pwzText ? S_OK : S_FALSE;
912 +
913 +LExit:
914 + ReleaseObject(pixnlNodeList);
915 + ReleaseObject(pixnChildNode);
916 + ReleaseObject(pixdDocument);
917 + ReleaseObject(pixtTextNode);
918 + ReleaseVariant(varText);
919 + return hr;
920 +}
921 +
922 +
923 +/********************************************************************
924 + XmlSetTextNumber -
925 +
926 +*********************************************************************/
927 +extern "C" HRESULT DAPI XmlSetTextNumber(
928 + __in IXMLDOMNode *pixnNode,
929 + __in DWORD dwValue
930 + )
931 +{
932 + HRESULT hr = S_OK;
933 + WCHAR wzValue[12];
934 +
935 + hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue);
936 + ExitOnFailure(hr, "Failed to format numeric value as string.");
937 +
938 + hr = XmlSetText(pixnNode, wzValue);
939 +
940 +LExit:
941 + return hr;
942 +}
943 +
944 +
945 +/********************************************************************
946 + XmlCreateChild -
947 +
948 +*********************************************************************/
949 +extern "C" HRESULT DAPI XmlCreateChild(
950 + __in IXMLDOMNode* pixnParent,
951 + __in_z LPCWSTR pwzElementType,
952 + __out IXMLDOMNode** ppixnChild
953 + )
954 +{
955 + HRESULT hr = S_OK;
956 +
957 + // RELEASEME
958 + IXMLDOMDocument* pixdDocument = NULL;
959 + IXMLDOMNode* pixnChild = NULL;
960 +
961 + hr = pixnParent->get_ownerDocument(&pixdDocument);
962 + if (hr == S_FALSE)
963 + {
964 + hr = E_FAIL;
965 + }
966 + ExitOnFailure(hr, "failed get_ownerDocument");
967 +
968 + hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild);
969 + if (hr == S_FALSE)
970 + {
971 + hr = E_FAIL;
972 + }
973 + ExitOnFailure(hr, "failed createElement");
974 +
975 + pixnParent->appendChild(pixnChild,NULL);
976 + if (hr == S_FALSE)
977 + {
978 + hr = E_FAIL;
979 + }
980 + ExitOnFailure(hr, "failed appendChild");
981 +
982 + if (ppixnChild)
983 + {
984 + *ppixnChild = pixnChild;
985 + pixnChild = NULL;
986 + }
987 +
988 +LExit:
989 + ReleaseObject(pixdDocument);
990 + ReleaseObject(pixnChild);
991 + return hr;
992 +}
993 +
994 +/********************************************************************
995 + XmlRemoveAttribute -
996 +
997 +*********************************************************************/
998 +extern "C" HRESULT DAPI XmlRemoveAttribute(
999 + __in IXMLDOMNode* pixnNode,
1000 + __in_z LPCWSTR pwzAttribute
1001 + )
1002 +{
1003 + HRESULT hr = S_OK;
1004 +
1005 + // RELEASEME
1006 + IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
1007 + BSTR bstrAttribute = ::SysAllocString(pwzAttribute);
1008 + ExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute");
1009 +
1010 + hr = pixnNode->get_attributes(&pixnnmAttributes);
1011 + ExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute);
1012 +
1013 + hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL);
1014 + ExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute);
1015 +
1016 +LExit:
1017 + ReleaseObject(pixnnmAttributes);
1018 + ReleaseBSTR(bstrAttribute);
1019 +
1020 + return hr;
1021 +}
1022 +
1023 +
1024 +/********************************************************************
1025 + XmlSelectNodes -
1026 +
1027 +*********************************************************************/
1028 +extern "C" HRESULT DAPI XmlSelectNodes(
1029 + __in IXMLDOMNode* pixnParent,
1030 + __in_z LPCWSTR wzXPath,
1031 + __out IXMLDOMNodeList **ppixnlChildren
1032 + )
1033 +{
1034 + HRESULT hr = S_OK;
1035 +
1036 + BSTR bstrXPath = NULL;
1037 +
1038 + ExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes");
1039 + ExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes");
1040 +
1041 + bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L"");
1042 + ExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes");
1043 +
1044 + hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren);
1045 +
1046 +LExit:
1047 + ReleaseBSTR(bstrXPath);
1048 + return hr;
1049 +}
1050 +
1051 +
1052 +/********************************************************************
1053 + XmlNextAttribute - returns the next attribute in a node list
1054 +
1055 + NOTE: pbstrAttribute is optional
1056 + returns S_OK if found an element
1057 + returns S_FALSE if no element found
1058 + returns E_* if something went wrong
1059 +********************************************************************/
1060 +extern "C" HRESULT DAPI XmlNextAttribute(
1061 + __in IXMLDOMNamedNodeMap* pixnnm,
1062 + __out IXMLDOMNode** pixnAttribute,
1063 + __deref_opt_out_z_opt BSTR* pbstrAttribute
1064 + )
1065 +{
1066 + Assert(pixnnm && pixnAttribute);
1067 +
1068 + HRESULT hr = S_OK;
1069 + IXMLDOMNode* pixn = NULL;
1070 + DOMNodeType nt;
1071 +
1072 + // null out the return values
1073 + *pixnAttribute = NULL;
1074 + if (pbstrAttribute)
1075 + {
1076 + *pbstrAttribute = NULL;
1077 + }
1078 +
1079 + hr = pixnnm->nextNode(&pixn);
1080 + ExitOnFailure(hr, "Failed to get next attribute.");
1081 +
1082 + if (S_OK == hr)
1083 + {
1084 + hr = pixn->get_nodeType(&nt);
1085 + ExitOnFailure(hr, "failed to get node type");
1086 +
1087 + if (NODE_ATTRIBUTE != nt)
1088 + {
1089 + hr = E_UNEXPECTED;
1090 + ExitOnFailure(hr, "Failed to get expected node type back: attribute");
1091 + }
1092 +
1093 + // if the caller asked for the attribute name
1094 + if (pbstrAttribute)
1095 + {
1096 + hr = pixn->get_baseName(pbstrAttribute);
1097 + ExitOnFailure(hr, "failed to get attribute name");
1098 + }
1099 +
1100 + *pixnAttribute = pixn;
1101 + pixn = NULL;
1102 + }
1103 +
1104 +LExit:
1105 + ReleaseObject(pixn);
1106 + return hr;
1107 +}
1108 +
1109 +
1110 +/********************************************************************
1111 + XmlNextElement - returns the next element in a node list
1112 +
1113 + NOTE: pbstrElement is optional
1114 + returns S_OK if found an element
1115 + returns S_FALSE if no element found
1116 + returns E_* if something went wrong
1117 +********************************************************************/
1118 +extern "C" HRESULT DAPI XmlNextElement(
1119 + __in IXMLDOMNodeList* pixnl,
1120 + __out IXMLDOMNode** pixnElement,
1121 + __deref_opt_out_z_opt BSTR* pbstrElement
1122 + )
1123 +{
1124 + Assert(pixnl && pixnElement);
1125 +
1126 + HRESULT hr = S_OK;
1127 + IXMLDOMNode* pixn = NULL;
1128 + DOMNodeType nt;
1129 +
1130 + // null out the return values
1131 + *pixnElement = NULL;
1132 + if (pbstrElement)
1133 + {
1134 + *pbstrElement = NULL;
1135 + }
1136 +
1137 + //
1138 + // find the next element in the list
1139 + //
1140 + while (S_OK == (hr = pixnl->nextNode(&pixn)))
1141 + {
1142 + hr = pixn->get_nodeType(&nt);
1143 + ExitOnFailure(hr, "failed to get node type");
1144 +
1145 + if (NODE_ELEMENT == nt)
1146 + break;
1147 +
1148 + ReleaseNullObject(pixn);
1149 + }
1150 + ExitOnFailure(hr, "failed to get next element");
1151 +
1152 + // if we have a node and the caller asked for the element name
1153 + if (pixn && pbstrElement)
1154 + {
1155 + hr = pixn->get_baseName(pbstrElement);
1156 + ExitOnFailure(hr, "failed to get element name");
1157 + }
1158 +
1159 + *pixnElement = pixn;
1160 + pixn = NULL;
1161 +
1162 + hr = *pixnElement ? S_OK : S_FALSE;
1163 +LExit:
1164 + ReleaseObject(pixn);
1165 + return hr;
1166 +}
1167 +
1168 +
1169 +/********************************************************************
1170 + XmlRemoveChildren -
1171 +
1172 +*********************************************************************/
1173 +extern "C" HRESULT DAPI XmlRemoveChildren(
1174 + __in IXMLDOMNode* pixnSource,
1175 + __in_z LPCWSTR pwzXPath
1176 + )
1177 +{
1178 + HRESULT hr = S_OK;
1179 +
1180 + // RELEASEME
1181 + IXMLDOMNodeList* pixnlNodeList = NULL;
1182 + IXMLDOMNode* pixnNode = NULL;
1183 + IXMLDOMNode* pixnRemoveChild = NULL;
1184 +
1185 + if (pwzXPath)
1186 + {
1187 + hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList);
1188 + ExitOnFailure(hr, "failed XmlSelectNodes");
1189 + }
1190 + else
1191 + {
1192 + hr = pixnSource->get_childNodes(&pixnlNodeList);
1193 + ExitOnFailure(hr, "failed childNodes");
1194 + }
1195 + if (S_FALSE == hr)
1196 + {
1197 + ExitFunction();
1198 + }
1199 +
1200 + while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode)))
1201 + {
1202 + hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild);
1203 + ExitOnFailure(hr, "failed removeChild");
1204 +
1205 + ReleaseNullObject(pixnRemoveChild);
1206 + ReleaseNullObject(pixnNode);
1207 + }
1208 + if (S_FALSE == hr)
1209 + {
1210 + hr = S_OK;
1211 + }
1212 +
1213 +LExit:
1214 + ReleaseObject(pixnlNodeList);
1215 + ReleaseObject(pixnNode);
1216 + ReleaseObject(pixnRemoveChild);
1217 +
1218 + return hr;
1219 +}
1220 +
1221 +
1222 +/********************************************************************
1223 + XmlSaveDocument -
1224 +
1225 +*********************************************************************/
1226 +extern "C" HRESULT DAPI XmlSaveDocument(
1227 + __in IXMLDOMDocument* pixdDocument,
1228 + __inout LPCWSTR wzPath
1229 + )
1230 +{
1231 + HRESULT hr = S_OK;
1232 +
1233 + // RELEASEME
1234 + VARIANT varsDestPath;
1235 +
1236 + ::VariantInit(&varsDestPath);
1237 + varsDestPath.vt = VT_BSTR;
1238 + varsDestPath.bstrVal = ::SysAllocString(wzPath);
1239 + if (!varsDestPath.bstrVal)
1240 + {
1241 + hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
1242 + }
1243 + ExitOnFailure(hr, "failed to create BSTR");
1244 +
1245 + hr = pixdDocument->save(varsDestPath);
1246 + if (hr == S_FALSE)
1247 + {
1248 + hr = E_FAIL;
1249 + }
1250 + ExitOnFailure(hr, "failed save in WriteDocument");
1251 +
1252 +LExit:
1253 + ReleaseVariant(varsDestPath);
1254 + return hr;
1255 +}
1256 +
1257 +
1258 +/********************************************************************
1259 + XmlSaveDocumentToBuffer
1260 +
1261 +*********************************************************************/
1262 +extern "C" HRESULT DAPI XmlSaveDocumentToBuffer(
1263 + __in IXMLDOMDocument* pixdDocument,
1264 + __deref_out_bcount(*pcbDest) BYTE** ppbDest,
1265 + __out DWORD* pcbDest
1266 + )
1267 +{
1268 + HRESULT hr = S_OK;
1269 + IStream* pStream = NULL;
1270 + LARGE_INTEGER li = { };
1271 + STATSTG statstg = { };
1272 + BYTE* pbDest = NULL;
1273 + ULONG cbRead = 0;
1274 + VARIANT vtDestination;
1275 +
1276 + ::VariantInit(&vtDestination);
1277 +
1278 + // create stream
1279 + hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream);
1280 + ExitOnFailure(hr, "Failed to create stream.");
1281 +
1282 + // write document to stream
1283 + vtDestination.vt = VT_UNKNOWN;
1284 + vtDestination.punkVal = (IUnknown*)pStream;
1285 + hr = pixdDocument->save(vtDestination);
1286 + ExitOnFailure(hr, "Failed to save document.");
1287 +
1288 + // get stream size
1289 + hr = pStream->Stat(&statstg, STATFLAG_NONAME);
1290 + ExitOnFailure(hr, "Failed to get stream size.");
1291 +
1292 + // allocate buffer
1293 + pbDest = static_cast<BYTE*>(MemAlloc((SIZE_T)statstg.cbSize.LowPart, TRUE));
1294 + ExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer.");
1295 +
1296 + // read data from stream
1297 + li.QuadPart = 0;
1298 + hr = pStream->Seek(li, STREAM_SEEK_SET, NULL);
1299 + ExitOnFailure(hr, "Failed to seek stream.");
1300 +
1301 + hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead);
1302 + if (cbRead < statstg.cbSize.LowPart)
1303 + {
1304 + hr = E_FAIL;
1305 + }
1306 + ExitOnFailure(hr, "Failed to read stream content to buffer.");
1307 +
1308 + // return value
1309 + *ppbDest = pbDest;
1310 + pbDest = NULL;
1311 + *pcbDest = statstg.cbSize.LowPart;
1312 +
1313 +LExit:
1314 + ReleaseObject(pStream);
1315 + ReleaseMem(pbDest);
1316 + return hr;
1317 +}
src/dutil/xsd/thmutil.xsd new
+1188
@@ -0,0 +1,1188 @@
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 +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
6 + xmlns:xse="http://wixtoolset.org/schemas/XmlSchemaExtension"
7 + xmlns:html="http://www.w3.org/1999/xhtml"
8 + targetNamespace="http://wixtoolset.org/schemas/v4/thmutil"
9 + xmlns="http://wixtoolset.org/schemas/v4/thmutil">
10 + <xs:annotation>
11 + <xs:documentation>
12 + Schema for describing Theme files processed by thmutil.
13 + </xs:documentation>
14 + </xs:annotation>
15 +
16 + <xs:import namespace="http://www.w3.org/1999/xhtml" />
17 +
18 + <xs:element name="Theme">
19 + <xs:annotation>
20 + <xs:documentation>
21 + This is the top-level container element for every thmutil Theme file.
22 + </xs:documentation>
23 + </xs:annotation>
24 + <xs:complexType>
25 + <xs:sequence>
26 + <xs:element ref="Font" maxOccurs="unbounded" />
27 + <xs:element ref="Window" />
28 + </xs:sequence>
29 + <xs:attribute name="ImageFile" type="xs:string">
30 + <xs:annotation>
31 + <xs:documentation>
32 + Relative path to an image file that can serve as a single source for images in the rest of the theme.
33 + This image is referenced by controls using the SourceX and SourceY attributes.
34 + Mutually exclusive with the ImageResource attribute.
35 + </xs:documentation>
36 + </xs:annotation>
37 + </xs:attribute>
38 + <xs:attribute name="ImageResource" type="xs:string">
39 + <xs:annotation>
40 + <xs:documentation>
41 + Identifier that references an image resource in the module for the window.
42 + Mutually exclusive with the ImageFile attribute.
43 + </xs:documentation>
44 + </xs:annotation>
45 + </xs:attribute>
46 + </xs:complexType>
47 + </xs:element>
48 +
49 + <xs:element name="Font">
50 + <xs:annotation>
51 + <xs:documentation>Defines a font including the size and color.</xs:documentation>
52 + </xs:annotation>
53 + <xs:complexType>
54 + <xs:simpleContent>
55 + <xs:extension base="xs:string">
56 + <xs:annotation>
57 + <xs:documentation>Name of the font face (required).</xs:documentation>
58 + </xs:annotation>
59 + <xs:attribute name="Id" type="xs:nonNegativeInteger" use="required">
60 + <xs:annotation>
61 + <xs:documentation>Numeric identifier for the font. Due to limitations in thmutil the first Font must start with "0" and each subsequent Font must increment the Id by 1. Failure to ensure the Font identifiers follow this strict ordering will create unexpected behavior or crashes.</xs:documentation>
62 + </xs:annotation>
63 + </xs:attribute>
64 + <xs:attribute name="Height" type="xs:int" use="required">
65 + <xs:annotation>
66 + <xs:documentation>Font size. Use negative numbers to specify the font in pixels.</xs:documentation>
67 + </xs:annotation>
68 + </xs:attribute>
69 + <xs:attribute name="Weight" type="xs:nonNegativeInteger">
70 + <xs:annotation>
71 + <xs:documentation>Font weight.</xs:documentation>
72 + </xs:annotation>
73 + </xs:attribute>
74 + <xs:attribute name="Foreground" type="FontColorType">
75 + <xs:annotation>
76 + <xs:documentation>
77 + A system color id or a hexadecimal value representing BGR foreground color of the font.
78 + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black.
79 + If this attribute is absent the foreground will be transparent.
80 + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext.
81 + </xs:documentation>
82 + </xs:annotation>
83 + </xs:attribute>
84 + <xs:attribute name="Background" type="FontColorType">
85 + <xs:annotation>
86 + <xs:documentation>
87 + A system color id or a hexadecimal value representing BGR background color of the font.
88 + "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black.
89 + If this attribute is absent the background will be transparent.
90 + Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext.
91 + </xs:documentation>
92 + </xs:annotation>
93 + </xs:attribute>
94 + <xs:attribute name="Underline" type="YesNoType">
95 + <xs:annotation>
96 + <xs:documentation>Specifies whether the font is underlined.</xs:documentation>
97 + </xs:annotation>
98 + </xs:attribute>
99 + </xs:extension>
100 + </xs:simpleContent>
101 + </xs:complexType>
102 + </xs:element>
103 +
104 + <xs:element name="ImageList">
105 + <xs:annotation>
106 + <xs:documentation>List of images which can be shared between multiple controls.</xs:documentation>
107 + </xs:annotation>
108 + <xs:complexType>
109 + <xs:choice maxOccurs="unbounded">
110 + <xs:element ref="Image" />
111 + </xs:choice>
112 + <xs:attribute name="Name" type="xs:string" use="required">
113 + <xs:annotation>
114 + <xs:documentation>
115 + Name of the ImageList, to be referenced by other controls.
116 + </xs:documentation>
117 + </xs:annotation>
118 + </xs:attribute>
119 + </xs:complexType>
120 + </xs:element>
121 +
122 + <xs:element name="Page">
123 + <xs:annotation>
124 + <xs:documentation>Named set of controls that can be shown and hidden collectively.</xs:documentation>
125 + </xs:annotation>
126 + <xs:complexType>
127 + <xs:group ref="ControlElements" maxOccurs="unbounded"/>
128 + <xs:attribute name="Name" type="xs:string">
129 + <xs:annotation>
130 + <xs:documentation>
131 + Optional name for the page.
132 + </xs:documentation>
133 + </xs:annotation>
134 + </xs:attribute>
135 + </xs:complexType>
136 + </xs:element>
137 +
138 + <xs:element name="Window">
139 + <xs:annotation>
140 + <xs:documentation>Defines the overall look of the main window.</xs:documentation>
141 + </xs:annotation>
142 + <xs:complexType>
143 + <xs:choice minOccurs="0" maxOccurs="unbounded">
144 + <xs:element ref="ImageList" />
145 + <xs:element ref="Page" />
146 + <xs:group ref="ControlElements" minOccurs="0" maxOccurs="unbounded" />
147 + </xs:choice>
148 + <xs:attribute name="AutoResize" type="YesNoType">
149 + <xs:annotation>
150 + <xs:documentation>Specifies whether the ThmUtil default window proc should process WM_SIZE and WM_SIZING events.</xs:documentation>
151 + </xs:annotation>
152 + </xs:attribute>
153 + <xs:attribute name="Caption" type="xs:string">
154 + <xs:annotation>
155 + <xs:documentation>
156 + Caption for the window.
157 + This is required if not using the StringId attribute.
158 + </xs:documentation>
159 + </xs:annotation>
160 + </xs:attribute>
161 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
162 + <xs:annotation>
163 + <xs:documentation>Numeric identifier to the Font element that serves as the default font for the window.</xs:documentation>
164 + </xs:annotation>
165 + </xs:attribute>
166 + <xs:attribute name="Height" type="xs:positiveInteger" use="required">
167 + <xs:annotation>
168 + <xs:documentation>Height of the window.</xs:documentation>
169 + </xs:annotation>
170 + </xs:attribute>
171 + <xs:attribute name="HexStyle" type="xs:hexBinary">
172 + <xs:annotation>
173 + <xs:documentation>
174 + Hexadecimal window style. If this is not specified the default value is: WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU.
175 + If SourceX and SourceY are specified, then WS_OVERLAPPED is replaced with WS_POPUP.
176 + </xs:documentation>
177 + </xs:annotation>
178 + </xs:attribute>
179 + <xs:attribute name="IconFile" type="xs:string">
180 + <xs:annotation>
181 + <xs:documentation>Relative path to an icon file for the window. Mutually exclusive with IconResource and SourceX and SourceY attributes.</xs:documentation>
182 + </xs:annotation>
183 + </xs:attribute>
184 + <xs:attribute name="IconResource" type="xs:string">
185 + <xs:annotation>
186 + <xs:documentation>
187 + Identifier that references an icon resource in the module for the icon for the window.
188 + Mutually exclusive with IconFile and SourceX and SourceY attributes.
189 + </xs:documentation>
190 + </xs:annotation>
191 + </xs:attribute>
192 + <xs:attribute name="MinimumHeight" type="xs:positiveInteger">
193 + <xs:annotation>
194 + <xs:documentation>Minimum height of the window. Only functions if AutoResize is enabled.</xs:documentation>
195 + </xs:annotation>
196 + </xs:attribute>
197 + <xs:attribute name="MinimumWidth" type="xs:positiveInteger">
198 + <xs:annotation>
199 + <xs:documentation>Minimum width of the window. Only functions if AutoResize is enabled.</xs:documentation>
200 + </xs:annotation>
201 + </xs:attribute>
202 + <xs:attribute name="SourceX" type="xs:nonNegativeInteger">
203 + <xs:annotation>
204 + <xs:documentation>X offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource.</xs:documentation>
205 + </xs:annotation>
206 + </xs:attribute>
207 + <xs:attribute name="SourceY" type="xs:nonNegativeInteger">
208 + <xs:annotation>
209 + <xs:documentation>Y offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource.</xs:documentation>
210 + </xs:annotation>
211 + </xs:attribute>
212 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
213 + <xs:annotation>
214 + <xs:documentation>
215 + Identifier that references a string resource in the module to define the window caption.
216 + Mutually exclusive with the Caption attribute.
217 + </xs:documentation>
218 + </xs:annotation>
219 + </xs:attribute>
220 + <xs:attribute name="Width" type="xs:positiveInteger" use="required">
221 + <xs:annotation>
222 + <xs:documentation>Width of the window.</xs:documentation>
223 + </xs:annotation>
224 + </xs:attribute>
225 + </xs:complexType>
226 + </xs:element>
227 +
228 + <xs:element name="Billboard">
229 + <xs:annotation>
230 + <xs:documentation>Defines a control that rotates through a set of images on a specified interval.</xs:documentation>
231 + </xs:annotation>
232 + <xs:complexType>
233 + <xs:sequence>
234 + <xs:element ref="Image" />
235 + </xs:sequence>
236 + <xs:attributeGroup ref="CommonControlAttributes" />
237 + <xs:attribute name="Interval" type="xs:positiveInteger">
238 + <xs:annotation>
239 + <xs:documentation>
240 + Specifies the time to wait before showing the next image, in milliseconds.
241 + </xs:documentation>
242 + </xs:annotation>
243 + </xs:attribute>
244 + <xs:attribute name="Loop" type="YesNoType">
245 + <xs:annotation>
246 + <xs:documentation>Specifies whether the billboard should loop through the images infinitely.</xs:documentation>
247 + </xs:annotation>
248 + </xs:attribute>
249 + </xs:complexType>
250 + </xs:element>
251 +
252 + <xs:element name="Button">
253 + <xs:annotation>
254 + <xs:documentation>Defines a button.</xs:documentation>
255 + </xs:annotation>
256 + <xs:complexType mixed="true">
257 + <xs:annotation>
258 + <xs:documentation>
259 + Text to display in the button.
260 + Mutually exclusive with the StringId attribute and child Text elements.
261 + </xs:documentation>
262 + </xs:annotation>
263 + <xs:choice minOccurs="0" maxOccurs="unbounded">
264 + <xs:annotation>
265 + <xs:documentation>
266 + If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time).
267 + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute.
268 + </xs:documentation>
269 + </xs:annotation>
270 + <xs:element ref="BrowseDirectoryAction" />
271 + <xs:element ref="ChangePageAction" />
272 + <xs:element ref="CloseWindowAction" />
273 + <xs:element ref="Text" />
274 + <xs:element ref="Tooltip" maxOccurs="1" />
275 + </xs:choice>
276 + <xs:attributeGroup ref="CommonControlAttributes" />
277 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
278 + <xs:annotation>
279 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons.</xs:documentation>
280 + </xs:annotation>
281 + </xs:attribute>
282 + <xs:attribute name="HoverFontId" type="xs:nonNegativeInteger">
283 + <xs:annotation>
284 + <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is hovered over. Only valid when using graphic buttons.</xs:documentation>
285 + </xs:annotation>
286 + </xs:attribute>
287 + <xs:attribute name="ImageFile" type="xs:string">
288 + <xs:annotation>
289 + <xs:documentation>
290 + Relative path to an image file to define a graphic button.
291 + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused.
292 + Mutually exclusive with ImageResource and SourceX and SourceY attributes.
293 + </xs:documentation>
294 + </xs:annotation>
295 + </xs:attribute>
296 + <xs:attribute name="ImageResource" type="xs:string">
297 + <xs:annotation>
298 + <xs:documentation>
299 + Identifier that references an image resource in the module to define a graphic button.
300 + The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused.
301 + Mutually exclusive with ImageFile and SourceX and SourceY attributes.
302 + </xs:documentation>
303 + </xs:annotation>
304 + </xs:attribute>
305 + <xs:attribute name="SelectedFontId" type="xs:nonNegativeInteger">
306 + <xs:annotation>
307 + <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is selected. Only valid when using graphic buttons.</xs:documentation>
308 + </xs:annotation>
309 + </xs:attribute>
310 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
311 + <xs:annotation>
312 + <xs:documentation>
313 + Identifier that references a string resource in the module to define the text for the control.
314 + </xs:documentation>
315 + </xs:annotation>
316 + </xs:attribute>
317 + </xs:complexType>
318 + </xs:element>
319 +
320 + <xs:element name="BrowseDirectoryAction">
321 + <xs:annotation>
322 + <xs:documentation>
323 + When the button is pressed, a directory browser dialog is shown.
324 + </xs:documentation>
325 + </xs:annotation>
326 + <xs:complexType>
327 + <xs:attribute name="Condition" type="xs:string">
328 + <xs:annotation>
329 + <xs:documentation>
330 + The condition that determines if the parent control will execute this action.
331 + </xs:documentation>
332 + </xs:annotation>
333 + </xs:attribute>
334 + <xs:attribute name="VariableName" type="xs:string" use="required">
335 + <xs:annotation>
336 + <xs:documentation>
337 + The name of the variable to update when the user selects a directory from the dialog.
338 + </xs:documentation>
339 + </xs:annotation>
340 + </xs:attribute>
341 + </xs:complexType>
342 + </xs:element>
343 +
344 + <xs:element name="ChangePageAction">
345 + <xs:annotation>
346 + <xs:documentation>
347 + When the button is pressed, the specified page is shown.
348 + </xs:documentation>
349 + </xs:annotation>
350 + <xs:complexType>
351 + <xs:attribute name="Cancel" type="YesNoType">
352 + <xs:annotation>
353 + <xs:documentation>
354 + When set to 'yes', none of the variable changes made on the current page are saved.
355 + </xs:documentation>
356 + </xs:annotation>
357 + </xs:attribute>
358 + <xs:attribute name="Condition" type="xs:string">
359 + <xs:annotation>
360 + <xs:documentation>
361 + The condition that determines if the parent control will execute this action.
362 + </xs:documentation>
363 + </xs:annotation>
364 + </xs:attribute>
365 + <xs:attribute name="Page" type="xs:string" use="required">
366 + <xs:annotation>
367 + <xs:documentation>
368 + The Name of the Page to show.
369 + </xs:documentation>
370 + </xs:annotation>
371 + </xs:attribute>
372 + </xs:complexType>
373 + </xs:element>
374 +
375 + <xs:element name="CloseWindowAction">
376 + <xs:annotation>
377 + <xs:documentation>
378 + When the button is pressed, the WM_CLOSE message is sent to the window.
379 + </xs:documentation>
380 + </xs:annotation>
381 + <xs:complexType>
382 + <xs:attribute name="Condition" type="xs:string">
383 + <xs:annotation>
384 + <xs:documentation>
385 + The condition that determines if the parent control will execute this action.
386 + </xs:documentation>
387 + </xs:annotation>
388 + </xs:attribute>
389 + </xs:complexType>
390 + </xs:element>
391 +
392 + <xs:element name="Checkbox">
393 + <xs:annotation>
394 + <xs:documentation>Defines a checkbox.</xs:documentation>
395 + </xs:annotation>
396 + <xs:complexType mixed="true">
397 + <xs:annotation>
398 + <xs:documentation>
399 + Text to display beside the checkbox.
400 + Mutually exclusive with the StringId attribute and child Text elements.
401 + </xs:documentation>
402 + </xs:annotation>
403 + <xs:choice minOccurs="0" maxOccurs="unbounded">
404 + <xs:element ref="Text" />
405 + <xs:element ref="Tooltip" maxOccurs="1" />
406 + </xs:choice>
407 + <xs:attributeGroup ref="CommonControlAttributes" />
408 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
409 + <xs:annotation>
410 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
411 + </xs:annotation>
412 + </xs:attribute>
413 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
414 + <xs:annotation>
415 + <xs:documentation>
416 + Identifier that references a string resource in the module to define the text for the control.
417 + </xs:documentation>
418 + </xs:annotation>
419 + </xs:attribute>
420 + </xs:complexType>
421 + </xs:element>
422 +
423 + <xs:element name="Combobox">
424 + <xs:annotation>
425 + <xs:documentation>Defines a combobox.</xs:documentation>
426 + </xs:annotation>
427 + <xs:complexType>
428 + <xs:attributeGroup ref="CommonControlAttributes" />
429 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
430 + <xs:annotation>
431 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
432 + </xs:annotation>
433 + </xs:attribute>
434 + </xs:complexType>
435 + </xs:element>
436 +
437 + <xs:element name="CommandLink">
438 + <xs:annotation>
439 + <xs:documentation>Defines a button.</xs:documentation>
440 + </xs:annotation>
441 + <xs:complexType mixed="true">
442 + <xs:annotation>
443 + <xs:documentation>
444 + Text to display in the button.
445 + Mutually exclusive with the StringId attribute and child Text elements.
446 + </xs:documentation>
447 + </xs:annotation>
448 + <xs:choice minOccurs="0" maxOccurs="unbounded">
449 + <xs:annotation>
450 + <xs:documentation>
451 + If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time).
452 + If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute.
453 + </xs:documentation>
454 + </xs:annotation>
455 + <xs:element ref="BrowseDirectoryAction" />
456 + <xs:element ref="ChangePageAction" />
457 + <xs:element ref="CloseWindowAction" />
458 + <xs:element ref="Note" />
459 + <xs:element ref="Text" />
460 + </xs:choice>
461 + <xs:attributeGroup ref="CommonControlAttributes" />
462 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
463 + <xs:annotation>
464 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons.</xs:documentation>
465 + </xs:annotation>
466 + </xs:attribute>
467 + <xs:attribute name="IconFile" type="xs:string">
468 + <xs:annotation>
469 + <xs:documentation>
470 + Relative path to an icon file to define a command link glyph.
471 + Mutually exclusive with ImageResource and SourceX and SourceY attributes.
472 + </xs:documentation>
473 + </xs:annotation>
474 + </xs:attribute>
475 + <xs:attribute name="IconResource" type="xs:string">
476 + <xs:annotation>
477 + <xs:documentation>
478 + Identifier that references an icon resource in the module to define a command link glyph.
479 + Mutually exclusive with ImageFile and SourceX and SourceY attributes.
480 + </xs:documentation>
481 + </xs:annotation>
482 + </xs:attribute>
483 + <xs:attribute name="ImageFile" type="xs:string">
484 + <xs:annotation>
485 + <xs:documentation>
486 + Relative path to an image file to define a command link glyph.
487 + Mutually exclusive with ImageResource and SourceX and SourceY attributes.
488 + </xs:documentation>
489 + </xs:annotation>
490 + </xs:attribute>
491 + <xs:attribute name="ImageResource" type="xs:string">
492 + <xs:annotation>
493 + <xs:documentation>
494 + Identifier that references an image resource in the module to define a command link glyph.
495 + Mutually exclusive with ImageFile and SourceX and SourceY attributes.
496 + </xs:documentation>
497 + </xs:annotation>
498 + </xs:attribute>
499 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
500 + <xs:annotation>
501 + <xs:documentation>
502 + Identifier that references a string resource in the module to define the text for the control.
503 + </xs:documentation>
504 + </xs:annotation>
505 + </xs:attribute>
506 + </xs:complexType>
507 + </xs:element>
508 +
509 + <xs:element name="Editbox">
510 + <xs:annotation>
511 + <xs:documentation>Defines an edit box.</xs:documentation>
512 + </xs:annotation>
513 + <xs:complexType>
514 + <xs:simpleContent>
515 + <xs:extension base="xs:string">
516 + <xs:annotation>
517 + <xs:documentation>
518 + Initial text for the control.
519 + Mutually exclusive with the StringId attribute.
520 + </xs:documentation>
521 + </xs:annotation>
522 + <xs:attributeGroup ref="CommonControlAttributes" />
523 + <xs:attribute name="FileSystemAutoComplete" type="YesNoType">
524 + <xs:annotation>
525 + <xs:documentation>Specifies whether the edit box should auto-complete with file system paths.</xs:documentation>
526 + </xs:annotation>
527 + </xs:attribute>
528 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
529 + <xs:annotation>
530 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
531 + </xs:annotation>
532 + </xs:attribute>
533 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
534 + <xs:annotation>
535 + <xs:documentation>
536 + Identifier that references a string resource in the module to define the initial text for the control.
537 + </xs:documentation>
538 + </xs:annotation>
539 + </xs:attribute>
540 + </xs:extension>
541 + </xs:simpleContent>
542 + </xs:complexType>
543 + </xs:element>
544 +
545 + <xs:element name="Hyperlink">
546 + <xs:annotation>
547 + <xs:documentation>Defines a hyperlink.</xs:documentation>
548 + </xs:annotation>
549 + <xs:complexType mixed="true">
550 + <xs:annotation>
551 + <xs:documentation>
552 + Text to display as the link.
553 + Mutually exclusive with the StringId attribute and child Text elements.
554 + </xs:documentation>
555 + </xs:annotation>
556 + <xs:choice minOccurs="0" maxOccurs="unbounded">
557 + <xs:element ref="Text" />
558 + <xs:element ref="Tooltip" maxOccurs="1" />
559 + </xs:choice>
560 + <xs:attributeGroup ref="CommonControlAttributes" />
561 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
562 + <xs:annotation>
563 + <xs:documentation>Numeric identifier to the Font element that serves as the unselected font.</xs:documentation>
564 + </xs:annotation>
565 + </xs:attribute>
566 + <xs:attribute name="HoverFontId" type="xs:nonNegativeInteger" use="required">
567 + <xs:annotation>
568 + <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is hovered over.</xs:documentation>
569 + </xs:annotation>
570 + </xs:attribute>
571 + <xs:attribute name="SelectedFontId" type="xs:nonNegativeInteger" use="required">
572 + <xs:annotation>
573 + <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is selected.</xs:documentation>
574 + </xs:annotation>
575 + </xs:attribute>
576 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
577 + <xs:annotation>
578 + <xs:documentation>
579 + Identifier that references a string resource in the module to define the text for the control.
580 + </xs:documentation>
581 + </xs:annotation>
582 + </xs:attribute>
583 + </xs:complexType>
584 + </xs:element>
585 +
586 + <xs:element name="Hypertext">
587 + <xs:annotation>
588 + <xs:documentation>Defines a text block with support for HTML &lt;a&gt; tags.</xs:documentation>
589 + </xs:annotation>
590 + <xs:complexType mixed="true">
591 + <xs:annotation>
592 + <xs:documentation>
593 + Text to display as the link.
594 + Use HTML &lt;a href="URL"&gt; to create a link.
595 + Mutually exclusive with the StringId attribute and child Text elements.
596 + </xs:documentation>
597 + </xs:annotation>
598 + <xs:choice minOccurs="0" maxOccurs="unbounded">
599 + <xs:element ref="Text" />
600 + <xs:element ref="Tooltip" maxOccurs="1" />
601 + </xs:choice>
602 + <xs:attributeGroup ref="CommonControlAttributes" />
603 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
604 + <xs:annotation>
605 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
606 + </xs:annotation>
607 + </xs:attribute>
608 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
609 + <xs:annotation>
610 + <xs:documentation>
611 + Identifier that references a string resource in the module to define the text for the control.
612 + </xs:documentation>
613 + </xs:annotation>
614 + </xs:attribute>
615 + </xs:complexType>
616 + </xs:element>
617 +
618 + <xs:element name="Image">
619 + <xs:annotation>
620 + <xs:documentation>Defines an image for an ImageList or Billboard.</xs:documentation>
621 + </xs:annotation>
622 + <xs:complexType>
623 + <xs:attribute name="ImageFile" type="xs:string">
624 + <xs:annotation>
625 + <xs:documentation>Relative path to an image file. Mutually exclusive with ImageResource.</xs:documentation>
626 + </xs:annotation>
627 + </xs:attribute>
628 + <xs:attribute name="ImageResource" type="xs:string">
629 + <xs:annotation>
630 + <xs:documentation>Identifier that references an image resource in the module. Mutually exclusive with ImageFile.</xs:documentation>
631 + </xs:annotation>
632 + </xs:attribute>
633 + </xs:complexType>
634 + </xs:element>
635 +
636 + <xs:element name="ImageControl">
637 + <xs:annotation>
638 + <xs:documentation>Defines an image.</xs:documentation>
639 + </xs:annotation>
640 + <xs:complexType>
641 + <xs:attributeGroup ref="CommonControlAttributes" />
642 + <xs:attribute name="ImageFile" type="xs:string">
643 + <xs:annotation>
644 + <xs:documentation>Relative path to an image file. Mutually exclusive with ImageResource and SourceX and SourceY attributes.</xs:documentation>
645 + </xs:annotation>
646 + </xs:attribute>
647 + <xs:attribute name="ImageResource" type="xs:string">
648 + <xs:annotation>
649 + <xs:documentation>Identifier that references an image resource in the module. Mutually exclusive with ImageFile and SourceX and SourceY attributes.</xs:documentation>
650 + </xs:annotation>
651 + </xs:attribute>
652 + </xs:complexType>
653 + </xs:element>
654 +
655 + <xs:element name="Label">
656 + <xs:annotation>
657 + <xs:documentation>Defines a label.</xs:documentation>
658 + </xs:annotation>
659 + <xs:complexType mixed="true">
660 + <xs:annotation>
661 + <xs:documentation>
662 + Text for the label to display.
663 + Mutually exclusive with the StringId attribute and child Text elements.
664 + </xs:documentation>
665 + </xs:annotation>
666 + <xs:choice minOccurs="0" maxOccurs="unbounded">
667 + <xs:element ref="Text" />
668 + <xs:element ref="Tooltip" maxOccurs="1" />
669 + </xs:choice>
670 + <xs:attributeGroup ref="CommonControlAttributes" />
671 + <xs:attribute name="Center" type="YesNoType" use="optional">
672 + <xs:annotation>
673 + <xs:documentation>Specifies whether the text should be centered horizontally in the width of the control. Default is "no".</xs:documentation>
674 + </xs:annotation>
675 + </xs:attribute>
676 + <xs:attribute name="DisablePrefix" type="YesNoType" use="optional">
677 + <xs:annotation>
678 + <xs:documentation>By default ampersands (&amp;) in the text will underline the next character and treat it as an accelerator key. Set this attribute to "yes" to disable that behavior. Default is "no".</xs:documentation>
679 + </xs:annotation>
680 + </xs:attribute>
681 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
682 + <xs:annotation>
683 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
684 + </xs:annotation>
685 + </xs:attribute>
686 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
687 + <xs:annotation>
688 + <xs:documentation>
689 + Identifier that references a string resource in the module to define the text for the label.
690 + </xs:documentation>
691 + </xs:annotation>
692 + </xs:attribute>
693 + </xs:complexType>
694 + </xs:element>
695 +
696 + <xs:element name="ListView">
697 + <xs:annotation>
698 + <xs:documentation>Defines a listview.</xs:documentation>
699 + </xs:annotation>
700 + <xs:complexType>
701 + <xs:choice maxOccurs="unbounded">
702 + <xs:element ref="Column" />
703 + </xs:choice>
704 + <xs:attributeGroup ref="CommonControlAttributes" />
705 + <xs:attribute name="FontId" type="xs:nonNegativeInteger">
706 + <xs:annotation>
707 + <xs:documentation>Numeric identifier to the Font element that serves as the default font for the ListView.</xs:documentation>
708 + </xs:annotation>
709 + </xs:attribute>
710 + <xs:attribute name="HexExtendedStyle" type="xs:hexBinary">
711 + <xs:annotation>
712 + <xs:documentation>Hexadecimal extended window style.</xs:documentation>
713 + </xs:annotation>
714 + </xs:attribute>
715 + <xs:attribute name="ImageList" type="xs:string">
716 + <xs:annotation>
717 + <xs:documentation>
718 + The name of the ImageList to assign to this listview with type LVSIL_NORMAL.
719 + </xs:documentation>
720 + </xs:annotation>
721 + </xs:attribute>
722 + <xs:attribute name="ImageListSmall" type="xs:string">
723 + <xs:annotation>
724 + <xs:documentation>
725 + The name of the ImageList to assign to this listview with type LVSIL_SMALL.
726 + </xs:documentation>
727 + </xs:annotation>
728 + </xs:attribute>
729 + <xs:attribute name="ImageListState" type="xs:string">
730 + <xs:annotation>
731 + <xs:documentation>
732 + The name of the ImageList to assign to this listview with type LVSIL_STATE.
733 + </xs:documentation>
734 + </xs:annotation>
735 + </xs:attribute>
736 + <xs:attribute name="ImageListGroupHeader" type="xs:string">
737 + <xs:annotation>
738 + <xs:documentation>
739 + The name of the ImageList to assign to this listview with type LVSIL_GROUPHEADER.
740 + </xs:documentation>
741 + </xs:annotation>
742 + </xs:attribute>
743 + </xs:complexType>
744 + </xs:element>
745 +
746 + <xs:element name="Note">
747 + <xs:annotation>
748 + <xs:documentation>
749 + Defines note text for a command link control based on an optional condition.
750 + If multiple Note elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time).
751 + If none of the conditions of a control's Note elements are true, then it uses the text of the Note element without the Condition attribute.
752 + </xs:documentation>
753 + </xs:annotation>
754 + <xs:complexType>
755 + <xs:simpleContent>
756 + <xs:extension base="xs:string">
757 + <xs:annotation>
758 + <xs:documentation>
759 + Note text for the parent command link control.
760 + </xs:documentation>
761 + </xs:annotation>
762 + <xs:attribute name="Condition" type="xs:string">
763 + <xs:annotation>
764 + <xs:documentation>
765 + The condition that determines when the parent control will use this note text.
766 + </xs:documentation>
767 + </xs:annotation>
768 + </xs:attribute>
769 + </xs:extension>
770 + </xs:simpleContent>
771 + </xs:complexType>
772 + </xs:element>
773 +
774 + <xs:element name="Panel">
775 + <xs:annotation>
776 + <xs:documentation>Defines a collection of controls.</xs:documentation>
777 + </xs:annotation>
778 + <xs:complexType>
779 + <xs:group ref="ControlElements" maxOccurs="unbounded"/>
780 + <xs:attributeGroup ref="CommonControlAttributes" />
781 + </xs:complexType>
782 + </xs:element>
783 +
784 + <xs:element name="Progressbar">
785 + <xs:annotation>
786 + <xs:documentation>Defines a progress bar.</xs:documentation>
787 + </xs:annotation>
788 + <xs:complexType>
789 + <xs:attributeGroup ref="CommonControlAttributes" />
790 + <xs:attribute name="ImageFile" type="xs:string">
791 + <xs:annotation>
792 + <xs:documentation>Relative path to an image file for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageResource and SourceX and SourceY attributes.</xs:documentation>
793 + </xs:annotation>
794 + </xs:attribute>
795 + <xs:attribute name="ImageResource" type="xs:string">
796 + <xs:annotation>
797 + <xs:documentation>Identifier that references an image resource in the module for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageFile and SourceX and SourceY attributes.</xs:documentation>
798 + </xs:annotation>
799 + </xs:attribute>
800 + </xs:complexType>
801 + </xs:element>
802 +
803 + <xs:element name="RadioButton">
804 + <xs:annotation>
805 + <xs:documentation>Defines an individual radio button within a set of radio buttons.</xs:documentation>
806 + </xs:annotation>
807 + <xs:complexType mixed="true">
808 + <xs:annotation>
809 + <xs:documentation>
810 + Text to display beside the radio button.
811 + Mutually exclusive with the StringId attribute and child Text elements.
812 + </xs:documentation>
813 + </xs:annotation>
814 + <xs:choice minOccurs="0" maxOccurs="unbounded">
815 + <xs:element ref="Text" />
816 + <xs:element ref="Tooltip" maxOccurs="1" />
817 + </xs:choice>
818 + <xs:attributeGroup ref="CommonControlAttributes" />
819 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
820 + <xs:annotation>
821 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
822 + </xs:annotation>
823 + </xs:attribute>
824 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
825 + <xs:annotation>
826 + <xs:documentation>
827 + Identifier that references a string resource in the module to define the text for the control.
828 + </xs:documentation>
829 + </xs:annotation>
830 + </xs:attribute>
831 + <xs:attribute name="Value" type="xs:string">
832 + <xs:annotation>
833 + <xs:documentation>Optional value used when setting the variable associated with the set of radio buttons.</xs:documentation>
834 + </xs:annotation>
835 + </xs:attribute>
836 + </xs:complexType>
837 + </xs:element>
838 +
839 + <xs:element name="RadioButtons">
840 + <xs:annotation>
841 + <xs:documentation>Defines a set of radio buttons.</xs:documentation>
842 + </xs:annotation>
843 + <xs:complexType>
844 + <xs:choice maxOccurs="unbounded">
845 + <xs:element ref="RadioButton" />
846 + </xs:choice>
847 + <xs:attribute name="Name" type="xs:string">
848 + <xs:annotation>
849 + <xs:documentation>Optional variable name for the set of radio buttons.</xs:documentation>
850 + </xs:annotation>
851 + </xs:attribute>
852 + </xs:complexType>
853 + </xs:element>
854 +
855 + <xs:element name="Richedit">
856 + <xs:annotation>
857 + <xs:documentation>Defines a rich edit control.</xs:documentation>
858 + </xs:annotation>
859 + <xs:complexType mixed="true">
860 + <xs:annotation>
861 + <xs:documentation>
862 + Initial text for the control.
863 + Mutually exclusive with the StringId attribute.
864 + </xs:documentation>
865 + </xs:annotation>
866 + <xs:choice minOccurs="0" maxOccurs="unbounded">
867 + <xs:element ref="Text" />
868 + <xs:element ref="Tooltip" maxOccurs="1" />
869 + </xs:choice>
870 + <xs:attributeGroup ref="CommonControlAttributes" />
871 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
872 + <xs:annotation>
873 + <xs:documentation>
874 + Numeric identifier to the Font element that serves as the font for the control.
875 + </xs:documentation>
876 + </xs:annotation>
877 + </xs:attribute>
878 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
879 + <xs:annotation>
880 + <xs:documentation>
881 + Identifier that references a string resource in the module to define the initial text for the control.
882 + </xs:documentation>
883 + </xs:annotation>
884 + </xs:attribute>
885 + </xs:complexType>
886 + </xs:element>
887 +
888 + <xs:element name="Static">
889 + <xs:annotation>
890 + <xs:documentation>Defines a straight line.</xs:documentation>
891 + </xs:annotation>
892 + <xs:complexType>
893 + <xs:attributeGroup ref="CommonControlAttributes" />
894 + </xs:complexType>
895 + </xs:element>
896 +
897 + <xs:element name="Tab">
898 + <xs:annotation>
899 + <xs:documentation>Defines an individual tab within a set of tabs.</xs:documentation>
900 + </xs:annotation>
901 + <xs:complexType>
902 + <xs:simpleContent>
903 + <xs:extension base="xs:string">
904 + <xs:annotation>
905 + <xs:documentation>
906 + Caption of the tab.
907 + Mutually exclusive with the StringId attribute.
908 + </xs:documentation>
909 + </xs:annotation>
910 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
911 + <xs:annotation>
912 + <xs:documentation>
913 + Identifier that references a string resource in the module to define the caption of the tab.
914 + </xs:documentation>
915 + </xs:annotation>
916 + </xs:attribute>
917 + </xs:extension>
918 + </xs:simpleContent>
919 + </xs:complexType>
920 + </xs:element>
921 +
922 + <xs:element name="Tabs">
923 + <xs:annotation>
924 + <xs:documentation>Defines a set of tabs.</xs:documentation>
925 + </xs:annotation>
926 + <xs:complexType>
927 + <xs:choice maxOccurs="unbounded">
928 + <xs:element ref="Tab" />
929 + </xs:choice>
930 + <xs:attributeGroup ref="CommonControlAttributes" />
931 + <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
932 + <xs:annotation>
933 + <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
934 + </xs:annotation>
935 + </xs:attribute>
936 + </xs:complexType>
937 + </xs:element>
938 +
939 + <xs:element name="Text">
940 + <xs:annotation>
941 + <xs:documentation>
942 + Defines text for the parent control based on an optional condition.
943 + If multiple Text elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time).
944 + If none of the conditions of a control's Text elements are true, then it uses the text of the Text element without the Condition attribute.
945 + </xs:documentation>
946 + </xs:annotation>
947 + <xs:complexType>
948 + <xs:simpleContent>
949 + <xs:extension base="xs:string">
950 + <xs:annotation>
951 + <xs:documentation>
952 + Text for the parent control.
953 + </xs:documentation>
954 + </xs:annotation>
955 + <xs:attribute name="Condition" type="xs:string">
956 + <xs:annotation>
957 + <xs:documentation>
958 + The condition that determines when the parent control will use this text.
959 + </xs:documentation>
960 + </xs:annotation>
961 + </xs:attribute>
962 + </xs:extension>
963 + </xs:simpleContent>
964 + </xs:complexType>
965 + </xs:element>
966 +
967 + <xs:element name="Tooltip">
968 + <xs:annotation>
969 + <xs:documentation>
970 + Defines text for the parent control's tooltip.
971 + </xs:documentation>
972 + </xs:annotation>
973 + <xs:complexType>
974 + <xs:simpleContent>
975 + <xs:extension base="xs:string">
976 + <xs:annotation>
977 + <xs:documentation>
978 + Text for the parent control's tooltip.
979 + </xs:documentation>
980 + </xs:annotation>
981 + </xs:extension>
982 + </xs:simpleContent>
983 + </xs:complexType>
984 + </xs:element>
985 +
986 + <xs:element name="TreeView">
987 + <xs:annotation>
988 + <xs:documentation>Defines a treeview.</xs:documentation>
989 + </xs:annotation>
990 + <xs:complexType>
991 + <xs:attributeGroup ref="CommonControlAttributes"/>
992 + <xs:attribute name="AlwaysShowSelect">
993 + <xs:annotation>
994 + <xs:documentation>Specifies whether the row always appears selected even when the treeview has lost focus.</xs:documentation>
995 + </xs:annotation>
996 + </xs:attribute>
997 + <xs:attribute name="EnableDragDrop">
998 + <xs:annotation>
999 + <xs:documentation>Specifies whether drag and drop is enabled for the treeview.</xs:documentation>
1000 + </xs:annotation>
1001 + </xs:attribute>
1002 + <xs:attribute name="FullRowSelect">
1003 + <xs:annotation>
1004 + <xs:documentation>Specifies whether an entire row is selected for the treeview.</xs:documentation>
1005 + </xs:annotation>
1006 + </xs:attribute>
1007 + <xs:attribute name="HasButtons">
1008 + <xs:annotation>
1009 + <xs:documentation>Specifies whether the treeview will show buttons.</xs:documentation>
1010 + </xs:annotation>
1011 + </xs:attribute>
1012 + <xs:attribute name="HasLines">
1013 + <xs:annotation>
1014 + <xs:documentation>Specifies whether lines appear for all treeview items.</xs:documentation>
1015 + </xs:annotation>
1016 + </xs:attribute>
1017 + <xs:attribute name="LinesAtRoot">
1018 + <xs:annotation>
1019 + <xs:documentation>Specifies whether the root nodes have lines beside them.</xs:documentation>
1020 + </xs:annotation>
1021 + </xs:attribute>
1022 + </xs:complexType>
1023 + </xs:element>
1024 +
1025 + <xs:element name="Column">
1026 + <xs:annotation>
1027 + <xs:documentation>A column of a list.</xs:documentation>
1028 + </xs:annotation>
1029 + <xs:complexType>
1030 + <xs:simpleContent>
1031 + <xs:extension base="xs:string">
1032 + <xs:annotation>
1033 + <xs:documentation>
1034 + Text for the column header.
1035 + Mutually exclusive with the StringId attribute.
1036 + </xs:documentation>
1037 + </xs:annotation>
1038 + <xs:attribute name="Width" type="xs:int">
1039 + <xs:annotation>
1040 + <xs:documentation>Width of the column.</xs:documentation>
1041 + </xs:annotation>
1042 + </xs:attribute>
1043 + <xs:attribute name="Expands" type="YesNoType">
1044 + <xs:annotation>
1045 + <xs:documentation>
1046 + Whether or not this column can grow to fill available width of the listview.
1047 + More than one column can be marked with yes - all expandable columns will share available extra space.
1048 + This is especially useful if the Window/@AutoResize is yes.
1049 + </xs:documentation>
1050 + </xs:annotation>
1051 + </xs:attribute>
1052 + <xs:attribute name="StringId" type="xs:nonNegativeInteger">
1053 + <xs:annotation>
1054 + <xs:documentation>
1055 + Identifier that references a string resource in the module to define the text for the column header.
1056 + </xs:documentation>
1057 + </xs:annotation>
1058 + </xs:attribute>
1059 + </xs:extension>
1060 + </xs:simpleContent>
1061 + </xs:complexType>
1062 + </xs:element>
1063 +
1064 + <xs:group name="ControlElements">
1065 + <xs:choice>
1066 + <xs:element ref="Billboard" />
1067 + <xs:element ref="Button" />
1068 + <xs:element ref="Checkbox" />
1069 + <xs:element ref="Combobox" />
1070 + <xs:element ref="CommandLink" />
1071 + <xs:element ref="Editbox" />
1072 + <xs:element ref="Hyperlink" />
1073 + <xs:element ref="Hypertext" />
1074 + <xs:element ref="ImageControl" />
1075 + <xs:element ref="Label" />
1076 + <xs:element ref="ListView" />
1077 + <xs:element ref="Panel" />
1078 + <xs:element ref="Progressbar" />
1079 + <xs:element ref="RadioButtons" />
1080 + <xs:element ref="Richedit" />
1081 + <xs:element ref="Static" />
1082 + <xs:element ref="Tabs" />
1083 + <xs:element ref="TreeView" />
1084 + </xs:choice>
1085 + </xs:group>
1086 +
1087 + <xs:attributeGroup name="CommonControlAttributes">
1088 + <xs:attribute name="Name" type="xs:string">
1089 + <xs:annotation>
1090 + <xs:documentation>Optional name for the control.</xs:documentation>
1091 + </xs:annotation>
1092 + </xs:attribute>
1093 + <xs:attribute name="DisableAutomaticBehavior" type="YesNoType">
1094 + <xs:annotation>
1095 + <xs:documentation>Set to 'yes' to disable automatic variable getting and setting, EnableCondition, VisibleCondition, and conditional Text elements. The default is 'no'.</xs:documentation>
1096 + </xs:annotation>
1097 + </xs:attribute>
1098 + <xs:attribute name="EnableCondition" type="xs:string">
1099 + <xs:annotation>
1100 + <xs:documentation>A condition that determines if the control is enabled. If this condition is true or omitted, then the control will be enabled.</xs:documentation>
1101 + </xs:annotation>
1102 + </xs:attribute>
1103 + <xs:attribute name="Height" type="xs:int" use="required">
1104 + <xs:annotation>
1105 + <xs:documentation>Height of the control. Non-positive values extend the control to the bottom of the window minus the value.</xs:documentation>
1106 + </xs:annotation>
1107 + </xs:attribute>
1108 + <xs:attribute name="HexStyle" type="xs:hexBinary">
1109 + <xs:annotation>
1110 + <xs:documentation>Hexadecimal window style for the control.</xs:documentation>
1111 + </xs:annotation>
1112 + </xs:attribute>
1113 + <xs:attribute name="HideWhenDisabled" type="YesNoType">
1114 + <xs:annotation>
1115 + <xs:documentation>Specifies whether the control should be hidden when disabled.</xs:documentation>
1116 + </xs:annotation>
1117 + </xs:attribute>
1118 + <xs:attribute name="TabStop" type="YesNoType">
1119 + <xs:annotation>
1120 + <xs:documentation>Specifies whether the control is part of the tab sequence of controls.</xs:documentation>
1121 + </xs:annotation>
1122 + </xs:attribute>
1123 + <xs:attribute name="Visible" type="YesNoType">
1124 + <xs:annotation>
1125 + <xs:documentation>Specifies whether the control is initially visible.</xs:documentation>
1126 + </xs:annotation>
1127 + </xs:attribute>
1128 + <xs:attribute name="VisibleCondition" type="xs:string">
1129 + <xs:annotation>
1130 + <xs:documentation>
1131 + A condition that determines if the control is visible. If this condition is true or omitted, then the control will be visible.
1132 + </xs:documentation>
1133 + </xs:annotation>
1134 + </xs:attribute>
1135 + <xs:attribute name="Width" type="xs:int" use="required">
1136 + <xs:annotation>
1137 + <xs:documentation>Width of the control. Non-positive values extend the control to the right of the window minus the value.</xs:documentation>
1138 + </xs:annotation>
1139 + </xs:attribute>
1140 + <xs:attribute name="X" type="xs:int" use="required">
1141 + <xs:annotation>
1142 + <xs:documentation>X coordinate for the control from the left of the window. Negative values are coordinates from the right of the window minus the width of the control.</xs:documentation>
1143 + </xs:annotation>
1144 + </xs:attribute>
1145 + <xs:attribute name="Y" type="xs:int" use="required">
1146 + <xs:annotation>
1147 + <xs:documentation>Y coordinate for the control from the top of the window. Negative values are coordinates from the bottom of the window minus the height of the control.</xs:documentation>
1148 + </xs:annotation>
1149 + </xs:attribute>
1150 + </xs:attributeGroup>
1151 +
1152 + <xs:simpleType name="YesNoType">
1153 + <xs:annotation>
1154 + <xs:documentation>Values of this type will either be "yes" or "no".</xs:documentation>
1155 + </xs:annotation>
1156 + <xs:restriction base="xs:NMTOKEN">
1157 + <xs:enumeration value="no"/>
1158 + <xs:enumeration value="yes"/>
1159 + </xs:restriction>
1160 + </xs:simpleType>
1161 +
1162 + <xs:simpleType name="SystemColorType">
1163 + <xs:annotation>
1164 + <xs:documentation>
1165 + Indicates a system color for a font.
1166 + </xs:documentation>
1167 + </xs:annotation>
1168 + <xs:restriction base="xs:NMTOKEN">
1169 + <xs:enumeration value="btnface" />
1170 + <xs:enumeration value="btntext" />
1171 + <xs:enumeration value="graytext" />
1172 + <xs:enumeration value="highlight" />
1173 + <xs:enumeration value="highlighttext" />
1174 + <xs:enumeration value="hotlight" />
1175 + <xs:enumeration value="window" />
1176 + <xs:enumeration value="windowtext" />
1177 + </xs:restriction>
1178 + </xs:simpleType>
1179 +
1180 + <xs:simpleType name="FontColorType">
1181 + <xs:annotation>
1182 + <xs:documentation>
1183 + Indicates the foreground or background color of a font.
1184 + </xs:documentation>
1185 + </xs:annotation>
1186 + <xs:union memberTypes="SystemColorType xs:string"/>
1187 + </xs:simpleType>
1188 +</xs:schema>