aboutsummaryrefslogtreecommitdiff
path: root/src/dutil
diff options
context:
space:
mode:
Diffstat (limited to 'src/dutil')
-rw-r--r--src/dutil/acl2util.cpp121
-rw-r--r--src/dutil/aclutil.cpp1030
-rw-r--r--src/dutil/apputil.cpp109
-rw-r--r--src/dutil/apuputil.cpp658
-rw-r--r--src/dutil/atomutil.cpp1284
-rw-r--r--src/dutil/buffutil.cpp419
-rw-r--r--src/dutil/butil.cpp271
-rw-r--r--src/dutil/cabcutil.cpp1532
-rw-r--r--src/dutil/cabutil.cpp567
-rw-r--r--src/dutil/certutil.cpp327
-rw-r--r--src/dutil/condutil.cpp20
-rw-r--r--src/dutil/conutil.cpp656
-rw-r--r--src/dutil/cryputil.cpp379
-rw-r--r--src/dutil/dictutil.cpp769
-rw-r--r--src/dutil/dirutil.cpp395
-rw-r--r--src/dutil/dlutil.cpp783
-rw-r--r--src/dutil/dutil.cpp462
-rw-r--r--src/dutil/dutil.vcxproj300
-rw-r--r--src/dutil/dutil.vcxproj.filters365
-rw-r--r--src/dutil/eseutil.cpp1308
-rw-r--r--src/dutil/fileutil.cpp1860
-rw-r--r--src/dutil/gdiputil.cpp212
-rw-r--r--src/dutil/guidutil.cpp39
-rw-r--r--src/dutil/iis7util.cpp518
-rw-r--r--src/dutil/inc/aclutil.h154
-rw-r--r--src/dutil/inc/apputil.h45
-rw-r--r--src/dutil/inc/apuputil.h86
-rw-r--r--src/dutil/inc/atomutil.h146
-rw-r--r--src/dutil/inc/buffutil.h80
-rw-r--r--src/dutil/inc/butil.h31
-rw-r--r--src/dutil/inc/cabcutil.h62
-rw-r--r--src/dutil/inc/cabutil.h56
-rw-r--r--src/dutil/inc/certutil.h66
-rw-r--r--src/dutil/inc/condutil.h19
-rw-r--r--src/dutil/inc/conutil.h78
-rw-r--r--src/dutil/inc/cryputil.h103
-rw-r--r--src/dutil/inc/dictutil.h69
-rw-r--r--src/dutil/inc/dirutil.h54
-rw-r--r--src/dutil/inc/dlutil.h59
-rw-r--r--src/dutil/inc/dutil.h160
-rw-r--r--src/dutil/inc/eseutil.h223
-rw-r--r--src/dutil/inc/fileutil.h235
-rw-r--r--src/dutil/inc/gdiputil.h38
-rw-r--r--src/dutil/inc/guidutil.h21
-rw-r--r--src/dutil/inc/iis7util.h222
-rw-r--r--src/dutil/inc/inetutil.h39
-rw-r--r--src/dutil/inc/iniutil.h79
-rw-r--r--src/dutil/inc/jsonutil.h112
-rw-r--r--src/dutil/inc/locutil.h120
-rw-r--r--src/dutil/inc/logutil.h190
-rw-r--r--src/dutil/inc/memutil.h80
-rw-r--r--src/dutil/inc/metautil.h52
-rw-r--r--src/dutil/inc/monutil.h108
-rw-r--r--src/dutil/inc/osutil.h39
-rw-r--r--src/dutil/inc/pathutil.h232
-rw-r--r--src/dutil/inc/perfutil.h24
-rw-r--r--src/dutil/inc/polcutil.h39
-rw-r--r--src/dutil/inc/procutil.h75
-rw-r--r--src/dutil/inc/regutil.h233
-rw-r--r--src/dutil/inc/resrutil.h43
-rw-r--r--src/dutil/inc/reswutil.h31
-rw-r--r--src/dutil/inc/rexutil.h54
-rw-r--r--src/dutil/inc/rmutil.h46
-rw-r--r--src/dutil/inc/rssutil.h89
-rw-r--r--src/dutil/inc/sceutil.h273
-rw-r--r--src/dutil/inc/sczutil.h30
-rw-r--r--src/dutil/inc/shelutil.h47
-rw-r--r--src/dutil/inc/sqlutil.h136
-rw-r--r--src/dutil/inc/srputil.h45
-rw-r--r--src/dutil/inc/strutil.h311
-rw-r--r--src/dutil/inc/svcutil.h21
-rw-r--r--src/dutil/inc/thmutil.h711
-rw-r--r--src/dutil/inc/timeutil.h38
-rw-r--r--src/dutil/inc/uncutil.h20
-rw-r--r--src/dutil/inc/uriutil.h100
-rw-r--r--src/dutil/inc/userutil.h32
-rw-r--r--src/dutil/inc/varutil.h126
-rw-r--r--src/dutil/inc/wiutil.h373
-rw-r--r--src/dutil/inc/wuautil.h19
-rw-r--r--src/dutil/inc/xmlutil.h167
-rw-r--r--src/dutil/inetutil.cpp137
-rw-r--r--src/dutil/iniutil.cpp753
-rw-r--r--src/dutil/jsonutil.cpp672
-rw-r--r--src/dutil/locutil.cpp613
-rw-r--r--src/dutil/logutil.cpp942
-rw-r--r--src/dutil/memutil.cpp323
-rw-r--r--src/dutil/metautil.cpp356
-rw-r--r--src/dutil/monutil.cpp2004
-rw-r--r--src/dutil/osutil.cpp188
-rw-r--r--src/dutil/path2utl.cpp89
-rw-r--r--src/dutil/pathutil.cpp1009
-rw-r--r--src/dutil/perfutil.cpp67
-rw-r--r--src/dutil/polcutil.cpp111
-rw-r--r--src/dutil/precomp.h95
-rw-r--r--src/dutil/proc2utl.cpp68
-rw-r--r--src/dutil/proc3utl.cpp114
-rw-r--r--src/dutil/procutil.cpp488
-rw-r--r--src/dutil/regutil.cpp926
-rw-r--r--src/dutil/resrutil.cpp251
-rw-r--r--src/dutil/reswutil.cpp368
-rw-r--r--src/dutil/rexutil.cpp586
-rw-r--r--src/dutil/rmutil.cpp473
-rw-r--r--src/dutil/rssutil.cpp632
-rw-r--r--src/dutil/sceutil.cpp2489
-rw-r--r--src/dutil/shelutil.cpp327
-rw-r--r--src/dutil/sqlutil.cpp868
-rw-r--r--src/dutil/srputil.cpp237
-rw-r--r--src/dutil/strutil.cpp2730
-rw-r--r--src/dutil/svcutil.cpp44
-rw-r--r--src/dutil/thmutil.cpp5226
-rw-r--r--src/dutil/timeutil.cpp370
-rw-r--r--src/dutil/uncutil.cpp54
-rw-r--r--src/dutil/uriutil.cpp538
-rw-r--r--src/dutil/userutil.cpp285
-rw-r--r--src/dutil/varutil.cpp274
-rw-r--r--src/dutil/wiutil.cpp1494
-rw-r--r--src/dutil/wuautil.cpp89
-rw-r--r--src/dutil/xmlutil.cpp1317
-rw-r--r--src/dutil/xsd/thmutil.xsd1188
119 files changed, 49320 insertions, 0 deletions
diff --git a/src/dutil/acl2util.cpp b/src/dutil/acl2util.cpp
new file mode 100644
index 00000000..2261abe3
--- /dev/null
+++ b/src/dutil/acl2util.cpp
@@ -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/********************************************************************
6AclCalculateServiceSidString - gets the SID string for the given service name
7
8NOTE: psczSid should be freed with StrFree()
9********************************************************************/
10extern "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
48LExit:
49 ReleaseMem(pbHash);
50 ReleaseStr(sczUpperServiceName);
51
52 return hr;
53}
54
55
56/********************************************************************
57AclGetAccountSidStringEx - gets a string version of the account's SID
58 calculates a service's SID if lookup fails
59
60NOTE: psczSid should be freed with StrFree()
61********************************************************************/
62extern "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
109LExit:
110 ReleaseStr(sczSid);
111 if (pwz)
112 {
113 ::LocalFree(pwz);
114 }
115 if (psid)
116 {
117 AclFreeSid(psid);
118 }
119
120 return hr;
121}
diff --git a/src/dutil/aclutil.cpp b/src/dutil/aclutil.cpp
new file mode 100644
index 00000000..fc01ecc8
--- /dev/null
+++ b/src/dutil/aclutil.cpp
@@ -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/********************************************************************
6AclCheckAccess - determines if token has appropriate privileges
7
8NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero
9if hToken is NULL, the thread will be checked
10if hToken is not NULL the token must be an impersonation token
11********************************************************************/
12extern "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
44LExit:
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/********************************************************************
55AclCheckAdministratorAccess - determines if token has Administrator privileges
56
57NOTE: if hToken is NULL, the thread will be checked
58if hToken is not NULL the token must be an impersonation token
59********************************************************************/
60extern "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/********************************************************************
78AclCheckLocalSystemAccess - determines if token has LocalSystem privileges
79
80NOTE: if hToken is NULL, the thread will be checked
81if hToken is not NULL the token must be an impersonation token
82********************************************************************/
83extern "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/********************************************************************
100AclGetWellKnownSid - returns a SID for the specified account
101
102********************************************************************/
103extern "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));
183LExit:
184 if (psidTemp)
185 {
186 ::FreeSid(psidTemp);
187 }
188
189 ReleaseMem(psid);
190
191 return hr;
192}
193
194
195/********************************************************************
196AclGetAccountSid - returns a SID for the specified account
197
198********************************************************************/
199extern "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;
260LExit:
261 ReleaseStr(pwzDomainName);
262 ReleaseMem(psid);
263
264 return hr;
265}
266
267
268/********************************************************************
269AclGetAccountSidString - gets a string version of the user's SID
270
271NOTE: ppwzSid should be freed with StrFree()
272********************************************************************/
273extern "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
297LExit:
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/********************************************************************
318AclCreateDacl - creates a DACL from ACL_ACE structures
319
320********************************************************************/
321extern "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);
386LExit:
387 if (pAcl)
388 {
389 AclFreeDacl(pAcl);
390 }
391
392 return hr;
393}
394
395
396/********************************************************************
397AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure
398
399********************************************************************/
400extern "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);
500LExit:
501 ReleaseMem(paaNewAllow);
502 ReleaseMem(paaNewDeny);
503
504 return hr;
505}
506
507
508/********************************************************************
509AclMergeDacls - creates a new DACL from two existing ACLs
510
511********************************************************************/
512extern "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/********************************************************************
531AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure
532
533********************************************************************/
534extern "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
632LExit:
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/********************************************************************
658AclCreateSecurityDescriptorFromDacl - creates a self-relative security
659descriptor from an existing DACL
660
661********************************************************************/
662extern "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
703LExit:
704 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
705 {
706 MemFree(*ppsd);
707 *ppsd = NULL;
708 }
709
710 return hr;
711}
712
713
714/********************************************************************
715AclCreateSecurityDescriptor - creates a self-relative security descriptor from an
716ACL_ACCESS structure
717
718NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
719********************************************************************/
720extern "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
744LExit:
745 return hr;
746}
747
748
749/********************************************************************
750AclCreateSecurityDescriptorFromString - creates a self-relative security
751descriptor from an SDDL string
752
753NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
754********************************************************************/
755extern "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
788LExit:
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/********************************************************************
806AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor
807
808NOTE: passed in security descriptor must be in self-relative format
809********************************************************************/
810extern "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
831LExit:
832 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
833 {
834 MemFree(*ppsd);
835 *ppsd = NULL;
836 }
837
838 return hr;
839}
840
841
842/********************************************************************
843AclGetSecurityDescriptor - returns self-relative security descriptor for named object
844
845NOTE: free ppsd with AclFreeSecurityDescriptor()
846********************************************************************/
847extern "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
875LExit:
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
891extern "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
923LExit:
924 ReleaseStr(sczObject);
925
926 return hr;
927}
928
929
930/********************************************************************
931AclFreeSid - frees a SID created by any Acl* functions
932
933********************************************************************/
934extern "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/********************************************************************
948AclFreeDacl - frees a DACL created by any Acl* functions
949
950********************************************************************/
951extern "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/********************************************************************
965AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions
966
967********************************************************************/
968extern "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/********************************************************************
982AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor
983
984********************************************************************/
985extern "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
1019LExit:
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}
diff --git a/src/dutil/apputil.cpp b/src/dutil/apputil.cpp
new file mode 100644
index 00000000..8562a47a
--- /dev/null
+++ b/src/dutil/apputil.cpp
@@ -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
5const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800;
6typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD);
7typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR);
8
9extern "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/********************************************************************
19AppInitialize - initializes the standard safety precautions for an
20 installation application.
21
22********************************************************************/
23extern "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
73extern "C" void DAPI AppInitializeUnsafe()
74{
75 ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
76}
77
78extern "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
105LExit:
106 ReleaseStr(sczCommandLine);
107
108 return hr;
109}
diff --git a/src/dutil/apuputil.cpp b/src/dutil/apuputil.cpp
new file mode 100644
index 00000000..11aaf3f2
--- /dev/null
+++ b/src/dutil/apuputil.cpp
@@ -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
8static HRESULT ProcessEntry(
9 __in ATOM_ENTRY* pAtomEntry,
10 __in LPCWSTR wzDefaultAppId,
11 __out APPLICATION_UPDATE_ENTRY* pApupEntry
12 );
13static HRESULT ParseEnclosure(
14 __in ATOM_LINK* pLink,
15 __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
16 );
17static __callback int __cdecl CompareEntries(
18 void* pvContext,
19 const void* pvLeft,
20 const void* pvRight
21 );
22static 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 );
29static HRESULT CopyEntry(
30 __in const APPLICATION_UPDATE_ENTRY* pSrc,
31 __in APPLICATION_UPDATE_ENTRY* pDest
32 );
33static HRESULT CopyEnclosure(
34 __in const APPLICATION_UPDATE_ENCLOSURE* pSrc,
35 __in APPLICATION_UPDATE_ENCLOSURE* pDest
36 );
37static void FreeEntry(
38 __in APPLICATION_UPDATE_ENTRY* pApupEntry
39 );
40static 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//
48extern "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
119LExit:
120 ReleaseApupChain(pChain);
121
122 return hr;
123}
124
125
126//
127// ApupFilterChain - remove the unneeded update elements from the chain.
128//
129HRESULT 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
164LExit:
165 ReleaseApupChain(pNewChain);
166 return hr;
167}
168
169
170//
171// ApupFreeChain - frees a previously allocated application update chain.
172//
173extern "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
192static 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
316LExit:
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
327static 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
401LExit:
402 return hr;
403}
404
405
406static __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
438static 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
496LExit:
497 ReleaseMem(pv);
498 return hr;
499}
500
501
502static 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
573LExit:
574 if (FAILED(hr))
575 {
576 FreeEntry(pDest);
577 }
578
579 return hr;
580}
581
582
583static 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
616LExit:
617 if (FAILED(hr))
618 {
619 FreeEnclosure(pDest);
620 }
621
622 return hr;
623}
624
625
626static 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
648static 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}
diff --git a/src/dutil/atomutil.cpp b/src/dutil/atomutil.cpp
new file mode 100644
index 00000000..4a12fb80
--- /dev/null
+++ b/src/dutil/atomutil.cpp
@@ -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
6static HRESULT ParseAtomDocument(
7 __in IXMLDOMDocument *pixd,
8 __out ATOM_FEED **ppFeed
9 );
10static HRESULT ParseAtomFeed(
11 __in IXMLDOMNode *pixnFeed,
12 __out ATOM_FEED **ppFeed
13 );
14static HRESULT ParseAtomAuthor(
15 __in IXMLDOMNode* pixnAuthor,
16 __in ATOM_AUTHOR* pAuthor
17 );
18static HRESULT ParseAtomCategory(
19 __in IXMLDOMNode* pixnCategory,
20 __in ATOM_CATEGORY* pCategory
21 );
22static HRESULT ParseAtomEntry(
23 __in IXMLDOMNode* pixnEntry,
24 __in ATOM_ENTRY* pEntry
25 );
26static HRESULT ParseAtomLink(
27 __in IXMLDOMNode* pixnLink,
28 __in ATOM_LINK* pLink
29 );
30static HRESULT ParseAtomUnknownElement(
31 __in IXMLDOMNode *pNode,
32 __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement
33 );
34static HRESULT ParseAtomUnknownAttribute(
35 __in IXMLDOMNode *pNode,
36 __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
37 );
38static HRESULT AssignDateTime(
39 __in FILETIME* pft,
40 __in IXMLDOMNode* pNode
41 );
42static HRESULT AssignString(
43 __out_z LPWSTR* pwzValue,
44 __in IXMLDOMNode* pNode
45 );
46static void FreeAtomAuthor(
47 __in_opt ATOM_AUTHOR* pAuthor
48 );
49static void FreeAtomContent(
50 __in_opt ATOM_CONTENT* pContent
51 );
52static void FreeAtomCategory(
53 __in_opt ATOM_CATEGORY* pCategory
54 );
55static void FreeAtomEntry(
56 __in_opt ATOM_ENTRY* pEntry
57 );
58static void FreeAtomLink(
59 __in_opt ATOM_LINK* pLink
60 );
61static void FreeAtomUnknownElementList(
62 __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement
63 );
64static void FreeAtomUnknownAttributeList(
65 __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute
66 );
67
68template<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*********************************************************************/
80extern "C" HRESULT DAPI AtomInitialize()
81{
82 return XmlInitialize();
83}
84
85
86/********************************************************************
87 AtomUninitialize - Uninitialize ATOM utilities.
88
89*********************************************************************/
90extern "C" void DAPI AtomUninitialize()
91{
92 XmlUninitialize();
93}
94
95
96/********************************************************************
97 AtomParseFromString - parses out an ATOM feed from a string.
98
99*********************************************************************/
100extern "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
121LExit:
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*********************************************************************/
133extern "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
154LExit:
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*********************************************************************/
166extern "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
183LExit:
184 ReleaseAtomFeed(pNewFeed);
185
186 return hr;
187}
188
189
190/********************************************************************
191 AtomFreeFeed - parses out an ATOM feed from a string.
192
193*********************************************************************/
194extern "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*********************************************************************/
243static 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
273LExit:
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*********************************************************************/
286static 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
423LExit:
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*********************************************************************/
439template<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
478LExit:
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*********************************************************************/
490static 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
529LExit:
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*********************************************************************/
542static 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
597LExit:
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*********************************************************************/
611static 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
662LExit:
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*********************************************************************/
676static 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
800LExit:
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*********************************************************************/
814static 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
889LExit:
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*********************************************************************/
903static 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
971LExit:
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*********************************************************************/
988static 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
1037LExit:
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*********************************************************************/
1052static 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
1080LExit:
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*********************************************************************/
1091static 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
1119LExit:
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*********************************************************************/
1130static 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*********************************************************************/
1147static 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*********************************************************************/
1166static 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*********************************************************************/
1185static 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*********************************************************************/
1226static 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*********************************************************************/
1248static 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*********************************************************************/
1270static 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}
diff --git a/src/dutil/buffutil.cpp b/src/dutil/buffutil.cpp
new file mode 100644
index 00000000..0cc67dcb
--- /dev/null
+++ b/src/dutil/buffutil.cpp
@@ -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
13static HRESULT EnsureBufferSize(
14 __deref_out_bcount(cbSize) BYTE** ppbBuffer,
15 __in SIZE_T cbSize
16 );
17
18
19// functions
20
21extern "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
49LExit:
50 return hr;
51}
52
53extern "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
81LExit:
82 return hr;
83}
84
85extern "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
138LExit:
139 return hr;
140}
141
142extern "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
195LExit:
196 return hr;
197}
198
199extern "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
253LExit:
254 return hr;
255}
256
257extern "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
276LExit:
277 return hr;
278}
279
280extern "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
299LExit:
300 return hr;
301}
302
303extern "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
328LExit:
329 return hr;
330}
331
332extern "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
357LExit:
358 return hr;
359}
360
361extern "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
387LExit:
388 return hr;
389}
390
391
392// helper functions
393
394static 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
417LExit:
418 return hr;
419}
diff --git a/src/dutil/butil.cpp b/src/dutil/butil.cpp
new file mode 100644
index 00000000..243befce
--- /dev/null
+++ b/src/dutil/butil.cpp
@@ -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
8const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
9const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
10const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
11
12// Forward declarations.
13static HRESULT OpenBundleKey(
14 __in LPCWSTR wzBundleId,
15 __in BUNDLE_INSTALL_CONTEXT context,
16 __inout HKEY *key);
17
18/********************************************************************
19BundleGetBundleInfo - Queries the bundle installation metadata for a given property
20
21RETURNS:
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********************************************************************/
35extern "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
103LExit:
104 ReleaseRegKey(hkBundle);
105 ReleaseStr(sczValue);
106
107 return hr;
108}
109
110/********************************************************************
111BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code
112
113NOTE: 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.
115RETURNS:
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********************************************************************/
121HRESULT 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
233LExit:
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/********************************************************************
245OpenBundleKey - Opens the bundle uninstallation key for a given bundle
246
247NOTE: caller is responsible for closing key
248********************************************************************/
249HRESULT 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
267LExit:
268 ReleaseStr(sczKeypath);
269
270 return hr;
271}
diff --git a/src/dutil/cabcutil.cpp b/src/dutil/cabcutil.cpp
new file mode 100644
index 00000000..35fefaba
--- /dev/null
+++ b/src/dutil/cabcutil.cpp
@@ -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
5static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?';
6static 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.
13static const DWORD MINFLUSHTHRESHHOLD = 0;
14
15// structs
16struct 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
33struct MS_CABINET_ITEM
34{
35 DWORD cbFile;
36 DWORD uoffFolderStart;
37 WORD iFolder;
38 WORD date;
39 WORD time;
40 WORD attribs;
41};
42
43struct CABC_INTERNAL_ADDFILEINFO
44{
45 LPCWSTR wzSourcePath;
46 LPCWSTR wzEmptyPath;
47};
48
49struct CABC_DUPLICATEFILE
50{
51 DWORD dwFileArrayIndex;
52 DWORD dwDuplicateCabFileIndex;
53 LPWSTR pwzSourcePath;
54 LPWSTR pwzToken;
55};
56
57
58struct CABC_FILE
59{
60 DWORD dwCabFileIndex;
61 LPWSTR pwzSourcePath;
62 LPWSTR pwzToken;
63 PMSIFILEHASHINFO pmfHash;
64 LONGLONG llFileSize;
65 BOOL fHasDuplicates;
66};
67
68
69struct 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
103const int CABC_HANDLE_BYTES = sizeof(CABC_DATA);
104
105//
106// prototypes
107//
108static void FreeCabCData(
109 __in CABC_DATA* pcd
110 );
111static 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 );
118static 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 );
125static 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 );
133static HRESULT UpdateDuplicateFiles(
134 __in const CABC_DATA *pcd
135 );
136static HRESULT DuplicateFile(
137 __in MS_CABINET_HEADER *pHeader,
138 __in const CABC_DATA *pcd,
139 __in const CABC_DUPLICATEFILE *pDuplicate
140 );
141static HRESULT UtcFileTimeToLocalDosDateTime(
142 __in const FILETIME* pFileTime,
143 __out USHORT* pDate,
144 __out USHORT* pTime
145 );
146
147static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __out_bcount(CABC_HANDLE_BYTES) void *pv);
148static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb);
149static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv);
150static __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);
151static __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);
152static __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);
153static __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);
154static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
155static __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);
158static __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);
159static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __out_bcount(CABC_HANDLE_BYTES) void *pv);
160
161
162/********************************************************************
163CabcBegin - begins creating a cabinet
164
165NOTE: 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********************************************************************/
170extern "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
344LExit:
345 if (FAILED(hr) && pcd && pcd->hfci)
346 {
347 ::FCIDestroy(pcd->hfci);
348 }
349
350 return hr;
351}
352
353
354/********************************************************************
355CabCNextCab - This will be useful when creating multiple cabs.
356Haven't needed it yet.
357********************************************************************/
358extern "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/********************************************************************
369CabcAddFile - adds a file to a cabinet
370
371NOTE: hContext must be the same used in Begin and Finish
372if wzToken is null, the file's original name is used within the cab
373********************************************************************/
374extern "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
425LExit:
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/********************************************************************
439CabcFinish - finishes making a cabinet
440
441NOTE: hContext must be the same used in Begin and AddFile
442*********************************************************************/
443extern "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
642LExit:
643 ::FCIDestroy(pcd->hfci);
644 FreeCabCData(pcd);
645 ReleaseNullStr(pszFileToken);
646
647 return hr;
648}
649
650
651/********************************************************************
652CabCCancel - cancels making a cabinet
653
654NOTE: hContext must be the same used in Begin and AddFile
655*********************************************************************/
656extern "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
672static 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
697static 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
767LExit:
768
769 return hr;
770}
771
772
773static 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
826LExit:
827 ReleaseMem(pv);
828 return hr;
829}
830
831
832static 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
894LExit:
895 ReleaseMem(pv);
896 return hr;
897}
898
899
900static 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
954LExit:
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
969static 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
1027LExit:
1028 return hr;
1029}
1030
1031
1032static 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
1051LExit:
1052 return hr;
1053}
1054
1055
1056/********************************************************************
1057 FCI callback functions
1058
1059*********************************************************************/
1060static __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
1077static __callback void * DIAMONDAPI CabCAlloc(
1078 __in ULONG cb
1079 )
1080{
1081 return MemAlloc(cb, FALSE);
1082}
1083
1084
1085static __callback void DIAMONDAPI CabCFree(
1086 __out_bcount(CABC_HANDLE_BYTES) void *pv
1087 )
1088{
1089 MemFree(pv);
1090}
1091
1092static __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
1154LExit:
1155 if (FAILED(hr))
1156 pcd->hrLastError = *err = hr;
1157
1158 return FAILED(hr) ? -1 : pFile;
1159}
1160
1161
1162static __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
1181LExit:
1182 if (FAILED(hr))
1183 {
1184 pcd->hrLastError = *err = hr;
1185 }
1186
1187 return FAILED(hr) ? -1 : cbRead;
1188}
1189
1190
1191static __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
1210LExit:
1211 if (FAILED(hr))
1212 pcd->hrLastError = *err = hr;
1213
1214 return FAILED(hr) ? -1 : cbWrite;
1215}
1216
1217
1218static __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
1258LExit:
1259 if (FAILED(hr))
1260 {
1261 pcd->hrLastError = *err = hr;
1262 }
1263
1264 return FAILED(hr) ? -1 : lMove;
1265}
1266
1267
1268static __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
1283LExit:
1284 if (FAILED(hr))
1285 {
1286 pcd->hrLastError = *err = hr;
1287 }
1288
1289 return FAILED(hr) ? -1 : 0;
1290}
1291
1292static __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)
1311static __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
1352LExit:
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)
1365static __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
1442LExit:
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
1457static __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
1509LExit:
1510 ReleaseMem(pszFilePlusMagic);
1511 if (FAILED(hr))
1512 {
1513 *err = (int)hr;
1514 }
1515
1516 return FAILED(hr) ? -1 : iResult;
1517}
1518
1519
1520static __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}
diff --git a/src/dutil/cabutil.cpp b/src/dutil/cabutil.cpp
new file mode 100644
index 00000000..e0efb717
--- /dev/null
+++ b/src/dutil/cabutil.cpp
@@ -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
6typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*);
7typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF);
8typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO);
9typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *);
10
11
12//
13// static globals
14//
15static HMODULE vhCabinetDll = NULL;
16
17static HFDI vhfdi = NULL;
18static PFNFDICREATE vpfnFDICreate = NULL;
19static PFNFDICOPY vpfnFDICopy = NULL;
20static PFNFDIISCABINET vpfnFDIIsCabinet = NULL;
21static PFNFDIDESTROY vpfnFDIDestroy = NULL;
22static ERF verf;
23
24static DWORD64 vdw64EmbeddedOffset = 0;
25
26//
27// structs
28//
29struct 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//
43static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize);
44static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData);
45static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode);
46static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb);
47static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb);
48static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf);
49static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype);
50static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify);
51static 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
53static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL;
54
55
56inline 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
78LExit:
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********************************************************************/
93extern "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
105LExit:
106 return hr;
107}
108
109
110/********************************************************************
111 CabUninitialize - initializes internal static variables
112
113********************************************************************/
114extern "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********************************************************************/
145extern "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********************************************************************/
163extern "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********************************************************************/
187static __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********************************************************************/
214static 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
301LExit:
302 ReleaseStr(sczCabinet);
303 v_pfnNetFx11Notify = NULL;
304
305 return hr;
306}
307
308/****************************************************************************
309 default extract routines
310
311****************************************************************************/
312static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize)
313{
314 return MemAlloc(dwSize, FALSE);
315}
316
317
318static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData)
319{
320 MemFree(pvData);
321}
322
323
324static __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
352LExit:
353 ReleaseStr(sczCabFile);
354
355 return FAILED(hr) ? -1 : pFile;
356}
357
358
359static __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
370LExit:
371 return FAILED(hr) ? -1 : cbRead;
372}
373
374
375static __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
386LExit:
387 return FAILED(hr) ? -1 : cbWrite;
388}
389
390
391static __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
423LExit:
424 return FAILED(hr) ? -1 : lMove - static_cast<long>(vdw64EmbeddedOffset);
425}
426
427
428static __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
437LExit:
438 return FAILED(hr) ? -1 : 0;
439}
440
441
442static __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
565LExit:
566 return (S_OK == hr) ? ipResult : -1;
567}
diff --git a/src/dutil/certutil.cpp b/src/dutil/certutil.cpp
new file mode 100644
index 00000000..9c0ee256
--- /dev/null
+++ b/src/dutil/certutil.cpp
@@ -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/********************************************************************
6CertReadProperty - reads a property from the certificate.
7
8NOTE: call MemFree() on the returned pvValue.
9********************************************************************/
10extern "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
42LExit:
43 ReleaseMem(pv);
44 return hr;
45}
46
47
48extern "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
102LExit:
103 ReleaseMem(pCounterSignerInfo);
104
105 return hr;
106}
107
108
109extern "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 }
143LExit:
144 return hr;
145}
146
147extern "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);
168LExit:
169 return hr;
170}
171
172extern "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
208LExit:
209 return hr;
210}
211
212
213extern "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 }
228LExit:
229 return hr;
230}
231
232extern "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
255extern "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 }
314LExit:
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}
diff --git a/src/dutil/condutil.cpp b/src/dutil/condutil.cpp
new file mode 100644
index 00000000..99923c18
--- /dev/null
+++ b/src/dutil/condutil.cpp
@@ -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/********************************************************************
8CondEvaluate - evaluates the condition using the given variables.
9********************************************************************/
10extern "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}
diff --git a/src/dutil/conutil.cpp b/src/dutil/conutil.cpp
new file mode 100644
index 00000000..4c820a1c
--- /dev/null
+++ b/src/dutil/conutil.cpp
@@ -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
6static HANDLE vhStdIn = INVALID_HANDLE_VALUE;
7static HANDLE vhStdOut = INVALID_HANDLE_VALUE;
8static BOOL vfConsoleIn = FALSE;
9static BOOL vfConsoleOut = FALSE;
10static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo;
11
12
13extern "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
69LExit:
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
90extern "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
109extern "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
119extern "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
129extern "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
139extern "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********************************************************************/
155extern "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
200LExit:
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********************************************************************/
212extern "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
267LExit:
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********************************************************************/
278HRESULT 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
303LExit:
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********************************************************************/
314extern "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
360LExit:
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*********************************************************************/
373extern "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
501LExit:
502 ReleaseStr(psz);
503
504 return hr;
505}
506
507
508/********************************************************************
509 ConsoleReadStringA - get console input without libc
510
511*********************************************************************/
512extern "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
561LExit:
562 return hr;
563}
564
565/********************************************************************
566 ConsoleReadStringW - get console input without libc
567
568*********************************************************************/
569extern "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
618LExit:
619 return hr;
620}
621
622/********************************************************************
623 ConsoleSetReadHidden - set console input no echo
624
625*********************************************************************/
626extern "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
636LExit:
637 return hr;
638}
639
640/********************************************************************
641 ConsoleSetReadNormal - reset to echo
642
643*********************************************************************/
644extern "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
653LExit:
654 return hr;
655}
656
diff --git a/src/dutil/cryputil.cpp b/src/dutil/cryputil.cpp
new file mode 100644
index 00000000..214704b4
--- /dev/null
+++ b/src/dutil/cryputil.cpp
@@ -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
5static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL;
6static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL;
7static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL;
8static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL;
9
10static HMODULE vhAdvApi32Dll = NULL;
11static HMODULE vhCrypt32Dll = NULL;
12static BOOL vfCrypInitialized = FALSE;
13
14// function definitions
15
16/********************************************************************
17 CrypInitialize - initializes cryputil
18
19*********************************************************************/
20extern "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
51LExit:
52 return hr;
53}
54
55
56/********************************************************************
57 CrypUninitialize - uninitializes cryputil
58
59*********************************************************************/
60extern "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
82extern "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
116LExit:
117 ReleaseMem(pvObject);
118
119 return hr;
120}
121
122
123extern "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
156LExit:
157 ReleaseMem(pv);
158
159 return hr;
160}
161
162
163extern "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
185LExit:
186 ReleaseFileHandle(hFile);
187
188 return hr;
189}
190
191
192extern "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
254LExit:
255 if (hHash)
256 {
257 ::CryptDestroyHash(hHash);
258 }
259 if (hProv)
260 {
261 ::CryptReleaseContext(hProv, 0);
262 }
263
264 return hr;
265}
266
267HRESULT 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
303LExit:
304 if (hHash)
305 {
306 ::CryptDestroyHash(hHash);
307 }
308 if (hProv)
309 {
310 ::CryptReleaseContext(hProv, 0);
311 }
312
313 return hr;
314}
315
316HRESULT 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");
344LExit:
345 return hr;
346}
347
348HRESULT 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");
376LExit:
377 return hr;
378}
379
diff --git a/src/dutil/dictutil.cpp b/src/dutil/dictutil.cpp
new file mode 100644
index 00000000..1f0f9e43
--- /dev/null
+++ b/src/dutil/dictutil.cpp
@@ -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)
6const 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
24enum DICT_TYPE
25{
26 DICT_INVALID = 0,
27 DICT_EMBEDDED_KEY = 1,
28 DICT_STRING_LIST = 2
29};
30
31struct 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
58const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT);
59
60static HRESULT StringHash(
61 __in const STRINGDICT_STRUCT *psd,
62 __in DWORD dwNumBuckets,
63 __in_z LPCWSTR pszString,
64 __out LPDWORD pdwHash
65 );
66static BOOL IsMatchExact(
67 __in const STRINGDICT_STRUCT *psd,
68 __in DWORD dwMatchIndex,
69 __in_z LPCWSTR wzOriginalString
70 );
71static HRESULT GetValue(
72 __in const STRINGDICT_STRUCT *psd,
73 __in_z LPCWSTR pszString,
74 __out_opt void **ppvValue
75 );
76static 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 );
83static HRESULT GetIndex(
84 __in const STRINGDICT_STRUCT *psd,
85 __in_z LPCWSTR pszString,
86 __out DWORD *pdwOutput
87 );
88static LPCWSTR GetKey(
89 __in const STRINGDICT_STRUCT *psd,
90 __in void *pvValue
91 );
92static 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.
97static void * TranslateOffsetToValue(
98 __in const STRINGDICT_STRUCT *psd,
99 __in void *pvValue
100 );
101static 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.
115extern "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
156LExit:
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.
161extern "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
200LExit:
201 return hr;
202}
203
204extern "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
236LExit:
237 ReleaseDict(sd);
238
239 return hr;
240}
241
242extern "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
262LExit:
263 return hr;
264}
265
266// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
267extern "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
317LExit:
318 return hr;
319}
320
321// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
322extern "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
376LExit:
377 return hr;
378}
379
380extern "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
406LExit:
407 return hr;
408}
409
410extern "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
430LExit:
431 return hr;
432}
433
434extern "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
455static 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
486LExit:
487 ReleaseStr(sczNewKey);
488
489 return hr;
490}
491
492static 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
514static 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
563LExit:
564 if (FAILED(hr) && NULL != ppvValue)
565 {
566 *ppvValue = NULL;
567 }
568
569 return hr;
570}
571
572static 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
613LExit:
614 return hr;
615}
616
617static 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
662LExit:
663 return hr;
664}
665
666static 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
688static 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
728LExit:
729 ReleaseMem(ppvNewBuckets);
730
731 return hr;
732}
733
734static 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
755static 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}
diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp
new file mode 100644
index 00000000..ddd621ac
--- /dev/null
+++ b/src/dutil/dirutil.cpp
@@ -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*******************************************************************/
10extern "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
35LExit:
36 return fExists;
37}
38
39
40/*******************************************************************
41 DirCreateTempPath
42
43 *******************************************************************/
44extern "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
72LExit:
73 return hr;
74}
75
76
77/*******************************************************************
78 DirEnsureExists
79
80*******************************************************************/
81extern "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
141LExit:
142 return hr;
143}
144
145
146/*******************************************************************
147 DirEnsureDelete - removes an entire directory structure
148
149*******************************************************************/
150extern "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*******************************************************************/
171extern "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
332LExit:
333 ReleaseFileFindHandle(hFind);
334 ReleaseStr(sczDelete);
335
336 return hr;
337}
338
339
340/*******************************************************************
341 DirGetCurrent - gets the current directory.
342
343*******************************************************************/
344extern "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
373LExit:
374 return hr;
375}
376
377
378/*******************************************************************
379 DirSetCurrent - sets the current directory.
380
381*******************************************************************/
382extern "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
393LExit:
394 return hr;
395}
diff --git a/src/dutil/dlutil.cpp b/src/dutil/dlutil.cpp
new file mode 100644
index 00000000..81455df0
--- /dev/null
+++ b/src/dutil/dlutil.cpp
@@ -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
8static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024;
9static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL };
10
11// internal function declarations
12
13static HRESULT InitializeResume(
14 __in LPCWSTR wzDestinationPath,
15 __out LPWSTR* psczResumePath,
16 __out HANDLE* phResumeFile,
17 __out DWORD64* pdw64ResumeOffset
18 );
19static 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 );
28static 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 );
41static HRESULT AllocateRangeRequestHeader(
42 __in DWORD64 dw64ResumeOffset,
43 __in DWORD64 dw64ResourceLength,
44 __deref_out_z LPWSTR* psczHeader
45 );
46static 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 );
56static HRESULT UpdateResumeOffset(
57 __inout DWORD64* pdw64ResumeOffset,
58 __in HANDLE hResumeFile,
59 __in DWORD cbData
60 );
61static 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 );
73static 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 );
82static 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 );
89static 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 );
96static HRESULT DownloadGetResumePath(
97 __in_z LPCWSTR wzPayloadWorkingPath,
98 __deref_out_z LPWSTR* psczResumePath
99 );
100static 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
108extern "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
161LExit:
162 ReleaseFileHandle(hResumeFile);
163 ReleaseStr(sczResumePath);
164 ReleaseInternet(hSession);
165 ReleaseStr(sczUrl);
166
167 return hr;
168}
169
170
171// internal helper functions
172
173static 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
214LExit:
215 ReleaseFileHandle(hResumeFile);
216 return hr;
217}
218
219static 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
256LExit:
257 ReleaseInternet(hUrl);
258 ReleaseInternet(hConnect);
259 return hr;
260}
261
262static 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
341LExit:
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
354static 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
384LExit:
385 return hr;
386}
387
388static 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
439LExit:
440 return hr;
441}
442
443static 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
473LExit:
474 return hr;
475}
476
477static 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
534LExit:
535 UriInfoUninitialize(&uri);
536 ReleaseInternet(hUrl);
537 ReleaseInternet(hConnect);
538
539 return hr;
540}
541
542static 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
587LExit:
588 ReleaseInternet(hUrl);
589 ReleaseStr(sczResource);
590 return hr;
591}
592
593static 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
698LExit:
699 return hr;
700}
701
702static 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
725static 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
735LExit:
736 return hr;
737}
738
739static 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
781LExit:
782 return hr;
783}
diff --git a/src/dutil/dutil.cpp b/src/dutil/dutil.cpp
new file mode 100644
index 00000000..3945d8c1
--- /dev/null
+++ b/src/dutil/dutil.cpp
@@ -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
10const int DUTIL_STRING_BUFFER = 1024;
11static HMODULE Dutil_hAssertModule = NULL;
12static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL;
13static BOOL Dutil_fNoAsserts = FALSE;
14static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD;
15static BOOL Dutil_fTraceFilenames = FALSE;
16
17
18/*******************************************************************
19Dutil_SetAssertModule
20
21*******************************************************************/
22extern "C" void DAPI Dutil_SetAssertModule(
23 __in HMODULE hAssertModule
24 )
25{
26 Dutil_hAssertModule = hAssertModule;
27}
28
29
30/*******************************************************************
31Dutil_SetAssertDisplayFunction
32
33*******************************************************************/
34extern "C" void DAPI Dutil_SetAssertDisplayFunction(
35 __in DUTIL_ASSERTDISPLAYFUNCTION pfn
36 )
37{
38 Dutil_pfnDisplayAssert = pfn;
39}
40
41
42/*******************************************************************
43Dutil_AssertMsg
44
45*******************************************************************/
46extern "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
136LExit:
137 ReleaseFileHandle(hAssertFile);
138 ReleaseRegKey(hkDebug);
139 fInAssert = FALSE;
140}
141
142
143/*******************************************************************
144Dutil_Assert
145
146*******************************************************************/
147extern "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/*******************************************************************
167Dutil_AssertSz
168
169*******************************************************************/
170extern "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/*******************************************************************
192Dutil_TraceSetLevel
193
194*******************************************************************/
195extern "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/*******************************************************************
206Dutil_TraceGetLevel
207
208*******************************************************************/
209extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel()
210{
211 return Dutil_rlCurrentTrace;
212}
213
214
215/*******************************************************************
216Dutil_Trace
217
218*******************************************************************/
219extern "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/*******************************************************************
293Dutil_TraceError
294
295*******************************************************************/
296extern "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/*******************************************************************
385Dutil_RootFailure
386
387*******************************************************************/
388extern "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********************************************************************/
411extern "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********************************************************************/
429extern "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
460LExit:
461 return hr;
462}
diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj
new file mode 100644
index 00000000..7bd78f1e
--- /dev/null
+++ b/src/dutil/dutil.vcxproj
@@ -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>
diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters
new file mode 100644
index 00000000..644f45a8
--- /dev/null
+++ b/src/dutil/dutil.vcxproj.filters
@@ -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
diff --git a/src/dutil/eseutil.cpp b/src/dutil/eseutil.cpp
new file mode 100644
index 00000000..1ff8e82c
--- /dev/null
+++ b/src/dutil/eseutil.cpp
@@ -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
5struct 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
19HRESULT 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
96HRESULT 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
140LExit:
141 ReleaseStr(pszAnsiInstance);
142 ReleaseStr(pszAnsiPath);
143
144 return hr;
145}
146
147HRESULT 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
161LExit:
162 return hr;
163}
164
165// Utility function used by EnsureSchema()
166HRESULT 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
222LExit:
223 return hr;
224}
225
226HRESULT 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
242LExit:
243 return hr;
244}
245
246// Utility function used by EnsureSchema()
247HRESULT 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
326LExit:
327 ReleaseStr(pszTempString);
328
329 return hr;
330}
331
332HRESULT 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
430LExit:
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
449HRESULT 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
503LExit:
504 ReleaseStr(pszDir);
505 ReleaseStr(pszAnsiFile);
506
507 return hr;
508}
509
510HRESULT 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
522LExit:
523 return hr;
524}
525
526HRESULT 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
543LExit:
544 ReleaseStr(pszAnsiTable);
545
546 return hr;
547}
548
549HRESULT 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
566LExit:
567 ReleaseStr(pszAnsiTable);
568
569 return hr;
570}
571
572HRESULT 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
583LExit:
584 return hr;
585}
586
587HRESULT 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
645LExit:
646 ReleaseStr(pszAnsiColumnName);
647
648 return hr;
649}
650
651HRESULT 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
679LExit:
680 ReleaseStr(pszAnsiColumnName);
681
682 return hr;
683}
684
685HRESULT 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
697LExit:
698 return hr;
699}
700
701HRESULT 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
712LExit:
713 return hr;
714}
715
716HRESULT 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
726LExit:
727 return hr;
728}
729
730HRESULT 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
741LExit:
742 return hr;
743}
744
745HRESULT 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
755LExit:
756 return hr;
757}
758
759HRESULT 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
771LExit:
772 return hr;
773}
774
775HRESULT 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
800LExit:
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
810HRESULT 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
824LExit:
825 return hr;
826}
827
828HRESULT 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
841LExit:
842 return hr;
843}
844
845HRESULT 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
859LExit:
860 return hr;
861}
862
863HRESULT 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
877LExit:
878 return hr;
879}
880
881HRESULT 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
893LExit:
894 return hr;
895}
896
897HRESULT 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
932LExit:
933 if (FAILED(hr))
934 {
935 ReleaseNullMem(*ppbBuffer);
936 }
937
938 return hr;
939}
940
941HRESULT 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
954LExit:
955 return hr;
956}
957
958HRESULT 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
981LExit:
982 return hr;
983}
984
985HRESULT 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
1009LExit:
1010 return hr;
1011}
1012
1013HRESULT 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
1033LExit:
1034 return hr;
1035}
1036
1037// Utility function used by other functions to set a query column
1038HRESULT 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
1078LExit:
1079 return hr;
1080}
1081
1082HRESULT 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
1113LExit:
1114 return hr;
1115}
1116
1117HRESULT 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
1142LExit:
1143 return hr;
1144}
1145
1146HRESULT 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
1172LExit:
1173 return hr;
1174}
1175
1176HRESULT 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
1205LExit:
1206 return hr;
1207}
1208
1209HRESULT 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
1233LExit:
1234 return hr;
1235}
1236
1237HRESULT 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
1306LExit:
1307 return hr;
1308}
diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp
new file mode 100644
index 00000000..8666da65
--- /dev/null
+++ b/src/dutil/fileutil.cpp
@@ -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
7const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF};
8const BYTE UTF16BOM[] = {0xFF, 0xFE};
9
10const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
11const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations";
12
13/*******************************************************************
14 FileFromPath - returns a pointer to the file part of the path
15
16********************************************************************/
17extern "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********************************************************************/
44extern "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
124LExit:
125 ReleaseStr(pwzFullPath);
126 ReleaseStr(pwzExpandedPath);
127
128 return hr;
129}
130
131
132/*******************************************************************
133FileStripExtension - Strip extension from filename
134********************************************************************/
135extern "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
174LExit:
175 ReleaseStr(pwzFileNameNoExtension);
176
177 return hr;
178}
179
180
181/*******************************************************************
182FileChangeExtension - Changes the extension of a filename
183********************************************************************/
184extern "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
204LExit:
205 ReleaseStr(sczFileName);
206
207 return hr;
208}
209
210
211/*******************************************************************
212FileAddSuffixToBaseName - Adds a suffix the base portion of a file
213name; e.g., file.ext to fileSuffix.ext.
214********************************************************************/
215extern "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
250LExit:
251 ReleaseStr(sczNewFileName);
252
253 return hr;
254}
255
256
257/*******************************************************************
258 FileVersion
259
260********************************************************************/
261extern "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
296LExit:
297 if (pVerBuffer)
298 {
299 ::GlobalFree(pVerBuffer);
300 }
301 return hr;
302}
303
304
305/*******************************************************************
306 FileVersionFromString
307
308*******************************************************************/
309extern "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
387LExit:
388 return hr;
389}
390
391
392/*******************************************************************
393 FileVersionFromStringEx
394
395*******************************************************************/
396extern "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
477LExit:
478 return hr;
479}
480
481/*******************************************************************
482 FileVersionFromStringEx - Formats the DWORD64 as a string version.
483
484*******************************************************************/
485extern "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
506LExit:
507 return hr;
508}
509
510/*******************************************************************
511 FileSetPointer - sets the file pointer.
512
513********************************************************************/
514extern "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
538LExit:
539 return hr;
540}
541
542
543/*******************************************************************
544 FileSize
545
546********************************************************************/
547extern "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
566LExit:
567 ReleaseFileHandle(hFile);
568
569 return hr;
570}
571
572
573/*******************************************************************
574 FileSizeByHandle
575
576********************************************************************/
577extern "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
595LExit:
596 return hr;
597}
598
599
600/*******************************************************************
601 FileExistsEx
602
603********************************************************************/
604extern "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********************************************************************/
638extern "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
692LExit:
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********************************************************************/
705extern "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
778LExit:
779 ReleaseStrArray(rgsczRenames, cRenames);
780 ReleaseRegKey(hkPendingFileRename);
781
782 return hr;
783}
784
785
786/*******************************************************************
787 FileRead - read a file into memory
788
789********************************************************************/
790extern "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********************************************************************/
804extern "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********************************************************************/
819extern "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********************************************************************/
835extern "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********************************************************************/
852extern "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
977LExit:
978 ReleaseMem(pbData);
979 ReleaseFile(hFile);
980
981 return hr;
982}
983
984
985/*******************************************************************
986 FileWrite - write a file from memory
987
988********************************************************************/
989extern "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
1013LExit:
1014 ReleaseFile(hFile);
1015
1016 return hr;
1017}
1018
1019
1020/*******************************************************************
1021 FileWriteHandle - write to a file handle from memory
1022
1023********************************************************************/
1024extern "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
1045LExit:
1046 return hr;
1047}
1048
1049
1050/*******************************************************************
1051 FileCopyUsingHandles
1052
1053*******************************************************************/
1054extern "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
1088LExit:
1089 return hr;
1090}
1091
1092
1093/*******************************************************************
1094 FileEnsureCopy
1095
1096*******************************************************************/
1097extern "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
1153LExit:
1154 return hr;
1155}
1156
1157
1158/*******************************************************************
1159 FileEnsureCopyWithRetry
1160
1161*******************************************************************/
1162extern "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
1191LExit:
1192 return hr;
1193}
1194
1195
1196/*******************************************************************
1197 FileEnsureMove
1198
1199*******************************************************************/
1200extern "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
1281LExit:
1282 return hr;
1283}
1284
1285
1286/*******************************************************************
1287 FileEnsureMoveWithRetry
1288
1289*******************************************************************/
1290extern "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
1315LExit:
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********************************************************************/
1325extern "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
1376LExit:
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*******************************************************************/
1389extern "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
1442LExit:
1443 ReleaseFile(hTempFile);
1444 ReleaseStr(pwzTempFile);
1445
1446 return hr;
1447}
1448
1449
1450/*******************************************************************
1451 FileIsSame
1452
1453********************************************************************/
1454extern "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
1486LExit:
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********************************************************************/
1497extern "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
1520LExit:
1521 return hr;
1522}
1523
1524/*******************************************************************
1525 FileGetTime - Gets the file time of a specified file
1526********************************************************************/
1527extern "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
1545LExit:
1546 ReleaseFile(hFile);
1547 return hr;
1548}
1549
1550/*******************************************************************
1551 FileSetTime - Sets the file time of a specified file
1552********************************************************************/
1553extern "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
1571LExit:
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********************************************************************/
1580extern "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
1601LExit:
1602 ReleaseFile(hFile);
1603 return hr;
1604}
1605
1606
1607/*******************************************************************
1608 FileExecutableArchitecture
1609
1610*******************************************************************/
1611extern "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
1682LExit:
1683 if (hFile != INVALID_HANDLE_VALUE)
1684 {
1685 ::CloseHandle(hFile);
1686 }
1687
1688 return hr;
1689}
1690
1691/*******************************************************************
1692 FileToString
1693
1694*******************************************************************/
1695extern "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
1787LExit:
1788 ReleaseStr(sczFileText);
1789 ReleaseMem(pbFullFileBuffer);
1790
1791 return hr;
1792}
1793
1794/*******************************************************************
1795 FileFromString
1796
1797*******************************************************************/
1798extern "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
1855LExit:
1856 ReleaseStr(sczUtf8String);
1857 ReleaseMem(pbFullFileBuffer);
1858
1859 return hr;
1860}
diff --git a/src/dutil/gdiputil.cpp b/src/dutil/gdiputil.cpp
new file mode 100644
index 00000000..aef6178f
--- /dev/null
+++ b/src/dutil/gdiputil.cpp
@@ -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
5using 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********************************************************************/
13extern "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
27LExit:
28 return hr;
29}
30
31/********************************************************************
32 GdipUninitialize - uninitializes GDI+.
33
34********************************************************************/
35extern "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********************************************************************/
46extern "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
91LExit:
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********************************************************************/
117extern "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
137LExit:
138 if (pBitmap)
139 {
140 delete pBitmap;
141 }
142
143 return hr;
144}
145
146
147HRESULT 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}
diff --git a/src/dutil/guidutil.cpp b/src/dutil/guidutil.cpp
new file mode 100644
index 00000000..c0353892
--- /dev/null
+++ b/src/dutil/guidutil.cpp
@@ -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
5extern "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
21LExit:
22 return hr;
23}
24
25extern "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
37LExit:
38 return hr;
39}
diff --git a/src/dutil/iis7util.cpp b/src/dutil/iis7util.cpp
new file mode 100644
index 00000000..04165a8d
--- /dev/null
+++ b/src/dutil/iis7util.cpp
@@ -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
8extern "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
27LExit:
28 ReleaseBSTR(bstrPropName);
29 // caller responsible for cleaning up variant vtPut
30 ReleaseObject(pProperty);
31
32 return hr;
33}
34
35extern "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
51LExit:
52 ReleaseVariant(vtPut);
53
54 return hr;
55}
56
57extern "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
71extern "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
84extern "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
103LExit:
104 ReleaseBSTR(bstrPropName);
105 // caller responsible for cleaning up variant vtGet
106 ReleaseObject(pProperty);
107
108 return hr;
109}
110
111extern "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
132LExit:
133 ReleaseVariant(vtGet);
134
135 return hr;
136}
137
138BOOL 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
188BOOL 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
212LExit:
213 ReleaseNullStr(wzValue1);
214 ReleaseNullStr(wzValue2);
215 return fEqual;
216}
217
218BOOL 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
228extern "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
259LExit:
260 ReleaseBSTR(bstrElementName);
261 ReleaseVariant(vPropValue);
262 ReleaseObject(pProperty);
263
264 return fResult;
265}
266
267BOOL 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
284LExit:
285 ReleaseBSTR(bstrName);
286
287 return fResult;
288}
289
290extern "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
319LExit:
320 ReleaseVariant(vtValue);
321
322 return hr;
323}
324
325extern "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
349LExit:
350 ReleaseVariant(vtValue);
351
352 return hr;
353}
354
355extern "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
383extern "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
405extern "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
456LExit:
457 ReleaseObject(pElement);
458 ReleaseVariant(vtIndex);
459
460 return hr;
461}
462
463extern "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
513LExit:
514 ReleaseObject(pMethod);
515 ReleaseVariant(vtIndex);
516
517 return hr;
518}
diff --git a/src/dutil/inc/aclutil.h b/src/dutil/inc/aclutil.h
new file mode 100644
index 00000000..144e4613
--- /dev/null
+++ b/src/dutil/inc/aclutil.h
@@ -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
12extern "C" {
13#endif
14
15// structs
16struct 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
29struct ACL_ACE
30{
31 DWORD dwFlags;
32 DWORD dwMask;
33 PSID psid;
34};
35
36
37// functions
38HRESULT DAPI AclCheckAccess(
39 __in HANDLE hToken,
40 __in ACL_ACCESS* paa
41 );
42HRESULT DAPI AclCheckAdministratorAccess(
43 __in HANDLE hToken
44 );
45HRESULT DAPI AclCheckLocalSystemAccess(
46 __in HANDLE hToken
47 );
48
49HRESULT DAPI AclGetWellKnownSid(
50 __in WELL_KNOWN_SID_TYPE wkst,
51 __deref_out PSID* ppsid
52 );
53HRESULT DAPI AclGetAccountSid(
54 __in_opt LPCWSTR wzSystem,
55 __in_z LPCWSTR wzAccount,
56 __deref_out PSID* ppsid
57 );
58HRESULT DAPI AclGetAccountSidString(
59 __in_z LPCWSTR wzSystem,
60 __in_z LPCWSTR wzAccount,
61 __deref_out_z LPWSTR* ppwzSid
62 );
63
64HRESULT 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 );
71HRESULT 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 );
79HRESULT DAPI AclMergeDacls(
80 __in const ACL* pAcl1,
81 __in const ACL* pAcl2,
82 __deref_out ACL** ppAclNew
83 );
84HRESULT DAPI AclCreateDaclOld(
85 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
86 __in DWORD cAclAccesses,
87 __deref_out ACL** ppAcl
88 );
89HRESULT DAPI AclCreateSecurityDescriptor(
90 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
91 __in DWORD cAclAccesses,
92 __deref_out SECURITY_DESCRIPTOR** ppsd
93 );
94HRESULT DAPI AclCreateSecurityDescriptorFromDacl(
95 __in ACL* pACL,
96 __deref_out SECURITY_DESCRIPTOR** ppsd
97 );
98HRESULT __cdecl AclCreateSecurityDescriptorFromString(
99 __deref_out SECURITY_DESCRIPTOR** ppsd,
100 __in_z __format_string LPCWSTR wzSddlFormat,
101 ...
102 );
103HRESULT DAPI AclDuplicateSecurityDescriptor(
104 __in SECURITY_DESCRIPTOR* psd,
105 __deref_out SECURITY_DESCRIPTOR** ppsd
106 );
107HRESULT 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 );
113HRESULT 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
125HRESULT DAPI AclFreeSid(
126 __in PSID psid
127 );
128HRESULT DAPI AclFreeDacl(
129 __in ACL* pACL
130 );
131HRESULT DAPI AclFreeSecurityDescriptor(
132 __in SECURITY_DESCRIPTOR* psd
133 );
134
135HRESULT 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.
141HRESULT DAPI AclCalculateServiceSidString(
142 __in LPCWSTR wzServiceName,
143 __in int cchServiceName,
144 __deref_out_z LPWSTR* psczSid
145 );
146HRESULT 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
diff --git a/src/dutil/inc/apputil.h b/src/dutil/inc/apputil.h
new file mode 100644
index 00000000..1a1e14f7
--- /dev/null
+++ b/src/dutil/inc/apputil.h
@@ -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
6extern "C" {
7#endif
8
9// functions
10
11/********************************************************************
12AppFreeCommandLineArgs - frees argv from AppParseCommandLine.
13
14********************************************************************/
15void DAPI AppFreeCommandLineArgs(
16 __in LPWSTR* argv
17 );
18
19void DAPI AppInitialize(
20 __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[],
21 __in DWORD cSafelyLoadSystemDlls
22 );
23
24/********************************************************************
25AppInitializeUnsafe - initializes without the full standard safety
26 precautions for an application.
27
28********************************************************************/
29void DAPI AppInitializeUnsafe();
30
31/********************************************************************
32AppParseCommandLine - parses the command line using CommandLineToArgvW.
33 The caller must free the value of pArgv on success
34 by calling AppFreeCommandLineArgs.
35
36********************************************************************/
37DAPI_(HRESULT) AppParseCommandLine(
38 __in LPCWSTR wzCommandLine,
39 __in int* argc,
40 __in LPWSTR** pArgv
41 );
42
43#ifdef __cplusplus
44}
45#endif
diff --git a/src/dutil/inc/apuputil.h b/src/dutil/inc/apuputil.h
new file mode 100644
index 00000000..6764bde8
--- /dev/null
+++ b/src/dutil/inc/apuputil.h
@@ -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
6extern "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
13const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn";
14
15typedef 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
24struct 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
38struct 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
59struct APPLICATION_UPDATE_CHAIN
60{
61 LPWSTR wzDefaultApplicationId;
62 LPWSTR wzDefaultApplicationType;
63
64 DWORD cEntries;
65 APPLICATION_UPDATE_ENTRY* rgEntries;
66};
67
68
69HRESULT DAPI ApupAllocChainFromAtom(
70 __in ATOM_FEED* pFeed,
71 __out APPLICATION_UPDATE_CHAIN** ppChain
72 );
73
74HRESULT DAPI ApupFilterChain(
75 __in APPLICATION_UPDATE_CHAIN* pChain,
76 __in DWORD64 dw64Version,
77 __out APPLICATION_UPDATE_CHAIN** ppFilteredChain
78 );
79
80void DAPI ApupFreeChain(
81 __in APPLICATION_UPDATE_CHAIN* pChain
82 );
83
84#ifdef __cplusplus
85}
86#endif
diff --git a/src/dutil/inc/atomutil.h b/src/dutil/inc/atomutil.h
new file mode 100644
index 00000000..ff869c4a
--- /dev/null
+++ b/src/dutil/inc/atomutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); }
10#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; }
11
12
13struct ATOM_UNKNOWN_ATTRIBUTE
14{
15 LPWSTR wzNamespace;
16 LPWSTR wzAttribute;
17 LPWSTR wzValue;
18
19 ATOM_UNKNOWN_ATTRIBUTE* pNext;
20};
21
22struct 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
32struct 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
45struct ATOM_CONTENT
46{
47 LPWSTR wzType;
48 LPWSTR wzUrl;
49 LPWSTR wzValue;
50
51 ATOM_UNKNOWN_ELEMENT* pUnknownElements;
52};
53
54struct ATOM_AUTHOR
55{
56 LPWSTR wzName;
57 LPWSTR wzEmail;
58 LPWSTR wzUrl;
59};
60
61struct ATOM_CATEGORY
62{
63 LPWSTR wzLabel;
64 LPWSTR wzScheme;
65 LPWSTR wzTerm;
66
67 ATOM_UNKNOWN_ELEMENT* pUnknownElements;
68};
69
70struct 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
93struct 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
119HRESULT DAPI AtomInitialize(
120 );
121
122void DAPI AtomUninitialize(
123 );
124
125HRESULT DAPI AtomParseFromString(
126 __in_z LPCWSTR wzAtomString,
127 __out ATOM_FEED **ppFeed
128 );
129
130HRESULT DAPI AtomParseFromFile(
131 __in_z LPCWSTR wzAtomFile,
132 __out ATOM_FEED **ppFeed
133 );
134
135HRESULT DAPI AtomParseFromDocument(
136 __in IXMLDOMDocument* pixdDocument,
137 __out ATOM_FEED **ppFeed
138 );
139
140void DAPI AtomFreeFeed(
141 __in_xcount(pFeed->cItems) ATOM_FEED *pFEED
142 );
143
144#ifdef __cplusplus
145}
146#endif
diff --git a/src/dutil/inc/buffutil.h b/src/dutil/inc/buffutil.h
new file mode 100644
index 00000000..e61cdb58
--- /dev/null
+++ b/src/dutil/inc/buffutil.h
@@ -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
6extern "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
19HRESULT BuffReadNumber(
20 __in_bcount(cbBuffer) const BYTE* pbBuffer,
21 __in SIZE_T cbBuffer,
22 __inout SIZE_T* piBuffer,
23 __out DWORD* pdw
24 );
25HRESULT BuffReadNumber64(
26 __in_bcount(cbBuffer) const BYTE* pbBuffer,
27 __in SIZE_T cbBuffer,
28 __inout SIZE_T* piBuffer,
29 __out DWORD64* pdw64
30 );
31HRESULT 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 );
37HRESULT 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 );
43HRESULT 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
51HRESULT BuffWriteNumber(
52 __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
53 __inout SIZE_T* piBuffer,
54 __in DWORD dw
55 );
56HRESULT BuffWriteNumber64(
57 __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
58 __inout SIZE_T* piBuffer,
59 __in DWORD64 dw64
60 );
61HRESULT BuffWriteString(
62 __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
63 __inout SIZE_T* piBuffer,
64 __in_z_opt LPCWSTR scz
65 );
66HRESULT BuffWriteStringAnsi(
67 __deref_out_bcount(*piBuffer) BYTE** ppbBuffer,
68 __inout SIZE_T* piBuffer,
69 __in_z_opt LPCSTR scz
70 );
71HRESULT 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
diff --git a/src/dutil/inc/butil.h b/src/dutil/inc/butil.h
new file mode 100644
index 00000000..a42cac11
--- /dev/null
+++ b/src/dutil/inc/butil.h
@@ -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
6extern "C" {
7#endif
8
9enum BUNDLE_INSTALL_CONTEXT
10{
11 BUNDLE_INSTALL_CONTEXT_MACHINE,
12 BUNDLE_INSTALL_CONTEXT_USER,
13};
14
15HRESULT 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
22HRESULT 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
diff --git a/src/dutil/inc/cabcutil.h b/src/dutil/inc/cabcutil.h
new file mode 100644
index 00000000..4f0c7b13
--- /dev/null
+++ b/src/dutil/inc/cabcutil.h
@@ -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
13typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR);
14
15#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866)
16
17#ifdef __cplusplus
18extern "C" {
19#endif
20
21extern const int CABC_HANDLE_BYTES;
22
23// time vs. space trade-off
24typedef 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
34HRESULT 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 );
43HRESULT DAPI CabCNextCab(
44 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
45 );
46HRESULT 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 );
52HRESULT DAPI CabCFinish(
53 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext,
54 __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback
55 );
56void DAPI CabCCancel(
57 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
58 );
59
60#ifdef __cplusplus
61}
62#endif
diff --git a/src/dutil/inc/cabutil.h b/src/dutil/inc/cabutil.h
new file mode 100644
index 00000000..0bedba80
--- /dev/null
+++ b/src/dutil/inc/cabutil.h
@@ -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
9extern "C" {
10#endif
11
12// structs
13
14
15// callback function prototypes
16typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile);
17typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead);
18typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead);
19typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod);
20typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile);
21
22typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile);
23typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile);
24typedef 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
28typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin);
29
30
31// functions
32HRESULT DAPI CabInitialize(
33 __in BOOL fDelayLoad
34 );
35void DAPI CabUninitialize(
36 );
37
38HRESULT 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
47HRESULT 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
diff --git a/src/dutil/inc/certutil.h b/src/dutil/inc/certutil.h
new file mode 100644
index 00000000..8565c1cf
--- /dev/null
+++ b/src/dutil/inc/certutil.h
@@ -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
10extern "C" {
11#endif
12
13HRESULT DAPI CertReadProperty(
14 __in PCCERT_CONTEXT pCertContext,
15 __in DWORD dwProperty,
16 __out_bcount(*pcbValue) LPVOID pvValue,
17 __out_opt DWORD* pcbValue
18 );
19
20HRESULT DAPI CertGetAuthenticodeSigningTimestamp(
21 __in CMSG_SIGNER_INFO* pSignerInfo,
22 __out FILETIME* pft
23 );
24
25HRESULT 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
36HRESULT 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
44HRESULT DAPI GetProvSecurityDesc(
45 __in HCRYPTPROV hProv,
46 __deref_out SECURITY_DESCRIPTOR** pSecurity
47 );
48
49HRESULT DAPI SetProvSecurityDesc(
50 __in HCRYPTPROV hProv,
51 __in SECURITY_DESCRIPTOR* pSecurity
52 );
53
54BOOL DAPI CertHasPrivateKey(
55 __in PCCERT_CONTEXT pCertContext,
56 __out_opt DWORD* pdwKeySpec
57 );
58
59HRESULT DAPI CertInstallSingleCertificate(
60 __in HCERTSTORE hStore,
61 __in PCCERT_CONTEXT pCertContext,
62 __in LPCWSTR wzName
63 );
64#ifdef __cplusplus
65}
66#endif
diff --git a/src/dutil/inc/condutil.h b/src/dutil/inc/condutil.h
new file mode 100644
index 00000000..fb960042
--- /dev/null
+++ b/src/dutil/inc/condutil.h
@@ -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)
6extern "C" {
7#endif
8
9// function declarations
10
11HRESULT 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
diff --git a/src/dutil/inc/conutil.h b/src/dutil/inc/conutil.h
new file mode 100644
index 00000000..cfb65332
--- /dev/null
+++ b/src/dutil/inc/conutil.h
@@ -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
6extern "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
21typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR;
22
23// structs
24
25// functions
26HRESULT DAPI ConsoleInitialize();
27void DAPI ConsoleUninitialize();
28
29void DAPI ConsoleGreen();
30void DAPI ConsoleRed();
31void DAPI ConsoleYellow();
32void DAPI ConsoleNormal();
33
34HRESULT DAPI ConsoleWrite(
35 CONSOLE_COLOR cc,
36 __in_z __format_string LPCSTR szFormat,
37 ...
38 );
39HRESULT DAPI ConsoleWriteLine(
40 CONSOLE_COLOR cc,
41 __in_z __format_string LPCSTR szFormat,
42 ...
43 );
44HRESULT DAPI ConsoleWriteError(
45 HRESULT hrError,
46 CONSOLE_COLOR cc,
47 __in_z __format_string LPCSTR szFormat,
48 ...
49 );
50
51HRESULT DAPI ConsoleReadW(
52 __deref_out_z LPWSTR* ppwzBuffer
53 );
54
55HRESULT DAPI ConsoleReadStringA(
56 __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer,
57 CONST DWORD cchCharBuffer,
58 __out DWORD* pcchNumCharReturn
59 );
60HRESULT DAPI ConsoleReadStringW(
61 __deref_out_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer,
62 CONST DWORD cchCharBuffer,
63 __out DWORD* pcchNumCharReturn
64 );
65
66HRESULT DAPI ConsoleReadNonBlockingW(
67 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
68 __out DWORD* pcchSize,
69 BOOL fReadLine
70 );
71
72HRESULT DAPI ConsoleSetReadHidden(void);
73HRESULT DAPI ConsoleSetReadNormal(void);
74
75#ifdef __cplusplus
76}
77#endif
78
diff --git a/src/dutil/inc/cryputil.h b/src/dutil/inc/cryputil.h
new file mode 100644
index 00000000..88aa784d
--- /dev/null
+++ b/src/dutil/inc/cryputil.h
@@ -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
8extern "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
16typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)(
17 __inout PVOID Memory,
18 __in ULONG MemoryLength,
19 __in ULONG OptionFlags
20 );
21
22typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)(
23 __inout PVOID Memory,
24 __in ULONG MemoryLength,
25 __in ULONG OptionFlags
26 );
27
28typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)(
29 __inout LPVOID pData,
30 __in DWORD cbData,
31 __in DWORD dwFlags
32 );
33
34typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)(
35 __inout LPVOID pData,
36 __in DWORD cbData,
37 __in DWORD dwFlags
38 );
39
40// function declarations
41
42HRESULT DAPI CrypInitialize();
43void DAPI CrypUninitialize();
44
45HRESULT 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
54HRESULT 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
62HRESULT 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
71HRESULT 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
80HRESULT 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
89HRESULT DAPI CrypEncryptMemory(
90 __inout LPVOID pData,
91 __in DWORD cbData,
92 __in DWORD dwFlags
93 );
94
95HRESULT DAPI CrypDecryptMemory(
96 __inout LPVOID pData,
97 __in DWORD cbData,
98 __in DWORD dwFlags
99 );
100
101#ifdef __cplusplus
102}
103#endif
diff --git a/src/dutil/inc/dictutil.h b/src/dutil/inc/dictutil.h
new file mode 100644
index 00000000..f0a3bb5a
--- /dev/null
+++ b/src/dutil/inc/dictutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); }
10#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; }
11
12typedef void* STRINGDICT_HANDLE;
13typedef const void* C_STRINGDICT_HANDLE;
14
15extern const int STRINGDICT_HANDLE_BYTES;
16
17typedef enum DICT_FLAG
18{
19 DICT_FLAG_NONE = 0,
20 DICT_FLAG_CASEINSENSITIVE = 1
21} DICT_FLAG;
22
23HRESULT 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 );
30HRESULT DAPI DictCreateStringList(
31 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
32 __in DWORD dwNumExpectedItems,
33 __in DICT_FLAG dfFlags
34 );
35HRESULT 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 );
41HRESULT DAPI DictCompareStringListToArray(
42 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList,
43 __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
44 __in const DWORD cStringArray
45 );
46HRESULT DAPI DictAddKey(
47 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
48 __in_z LPCWSTR szString
49 );
50HRESULT DAPI DictAddValue(
51 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
52 __in void *pvValue
53 );
54HRESULT DAPI DictKeyExists(
55 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
56 __in_z LPCWSTR szString
57 );
58HRESULT DAPI DictGetValue(
59 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
60 __in_z LPCWSTR szString,
61 __out void **ppvValue
62 );
63void DAPI DictDestroy(
64 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle
65 );
66
67#ifdef __cplusplus
68}
69#endif
diff --git a/src/dutil/inc/dirutil.h b/src/dutil/inc/dirutil.h
new file mode 100644
index 00000000..0a19a9c0
--- /dev/null
+++ b/src/dutil/inc/dirutil.h
@@ -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
5typedef 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
13extern "C" {
14#endif
15
16BOOL DAPI DirExists(
17 __in_z LPCWSTR wzPath,
18 __out_opt DWORD *pdwAttributes
19 );
20
21HRESULT DAPI DirCreateTempPath(
22 __in_z LPCWSTR wzPrefix,
23 __out_ecount_z(cchPath) LPWSTR wzPath,
24 __in DWORD cchPath
25 );
26
27HRESULT DAPI DirEnsureExists(
28 __in_z LPCWSTR wzPath,
29 __in_opt LPSECURITY_ATTRIBUTES psa
30 );
31
32HRESULT DAPI DirEnsureDelete(
33 __in_z LPCWSTR wzPath,
34 __in BOOL fDeleteFiles,
35 __in BOOL fRecurse
36 );
37
38HRESULT DAPI DirEnsureDeleteEx(
39 __in_z LPCWSTR wzPath,
40 __in DWORD dwFlags
41 );
42
43HRESULT DAPI DirGetCurrent(
44 __deref_out_z LPWSTR* psczCurrentDirectory
45 );
46
47HRESULT DAPI DirSetCurrent(
48 __in_z LPCWSTR wzDirectory
49 );
50
51#ifdef __cplusplus
52}
53#endif
54
diff --git a/src/dutil/inc/dlutil.h b/src/dutil/inc/dlutil.h
new file mode 100644
index 00000000..3e95103a
--- /dev/null
+++ b/src/dutil/inc/dlutil.h
@@ -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
6extern "C" {
7#endif
8
9typedef 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
17typedef 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
25typedef struct _DOWNLOAD_SOURCE
26{
27 LPWSTR sczUrl;
28 LPWSTR sczUser;
29 LPWSTR sczPassword;
30} DOWNLOAD_SOURCE;
31
32typedef struct _DOWNLOAD_CACHE_CALLBACK
33{
34 LPPROGRESS_ROUTINE pfnProgress;
35 LPCANCEL_ROUTINE pfnCancel;
36 LPVOID pv;
37} DOWNLOAD_CACHE_CALLBACK;
38
39typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK
40{
41 LPAUTHENTICATION_ROUTINE pfnAuthenticate;
42 LPVOID pv;
43} DOWNLOAD_AUTHENTICATION_CALLBACK;
44
45
46// functions
47
48HRESULT 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
diff --git a/src/dutil/inc/dutil.h b/src/dutil/inc/dutil.h
new file mode 100644
index 00000000..3791dd5a
--- /dev/null
+++ b/src/dutil/inc/dutil.h
@@ -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
13typedef 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
24typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz);
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule);
31void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn);
32void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine);
33void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z LPCSTR szMessage);
34
35void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames);
36REPORT_LEVEL DAPI Dutil_TraceGetLevel();
37void __cdecl Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...);
38void __cdecl Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...);
39void 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
115template<typename T> static char countofVerify(void const *, T) throw() { return 0; }
116template<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
131typedef 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
151extern "C" {
152#endif
153
154// other functions
155HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule);
156HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath);
157
158#ifdef __cplusplus
159}
160#endif
diff --git a/src/dutil/inc/eseutil.h b/src/dutil/inc/eseutil.h
new file mode 100644
index 00000000..1c408927
--- /dev/null
+++ b/src/dutil/inc/eseutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); }
10#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; }
11
12struct 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
23struct ESE_TABLE_SCHEMA
24{
25 JET_TABLEID jtTable;
26 LPCWSTR pszName;
27 DWORD dwColumns;
28 ESE_COLUMN_SCHEMA *pcsColumns;
29};
30
31struct ESE_DATABASE_SCHEMA
32{
33 DWORD dwTables;
34 ESE_TABLE_SCHEMA *ptsTables;
35};
36
37typedef enum ESE_QUERY_TYPE
38{
39 ESE_QUERY_EXACT,
40 ESE_QUERY_FROM_TOP,
41 ESE_QUERY_FROM_BOTTOM
42} ESE_QUERY_TYPE;
43
44typedef void* ESE_QUERY_HANDLE;
45
46HRESULT DAPI EseBeginSession(
47 __out JET_INSTANCE *pjiInstance,
48 __out JET_SESID *pjsSession,
49 __in_z LPCWSTR pszInstance,
50 __in_z LPCWSTR pszPath
51 );
52HRESULT DAPI EseEndSession(
53 __in JET_INSTANCE jiInstance,
54 __in JET_SESID jsSession
55 );
56HRESULT 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 );
64HRESULT DAPI EseCloseDatabase(
65 __in JET_SESID jsSession,
66 __in JET_DBID jdbDb
67 );
68HRESULT DAPI EseCreateTable(
69 __in JET_SESID jsSession,
70 __in JET_DBID jdbDb,
71 __in_z LPCWSTR pszTable,
72 __out JET_TABLEID *pjtTable
73 );
74HRESULT DAPI EseOpenTable(
75 __in JET_SESID jsSession,
76 __in JET_DBID jdbDb,
77 __in_z LPCWSTR pszTable,
78 __out JET_TABLEID *pjtTable
79 );
80HRESULT DAPI EseCloseTable(
81 __in JET_SESID jsSession,
82 __in JET_TABLEID jtTable
83 );
84HRESULT 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 );
94HRESULT DAPI EseGetColumn(
95 __in JET_SESID jsSession,
96 __in JET_TABLEID jtTable,
97 __in_z LPCWSTR pszColumnName,
98 __out JET_COLUMNID *pjcColumn
99 );
100HRESULT DAPI EseMoveCursor(
101 __in JET_SESID jsSession,
102 __in JET_TABLEID jtTable,
103 __in LONG lRow
104 );
105HRESULT DAPI EseDeleteRow(
106 __in JET_SESID jsSession,
107 __in JET_TABLEID jtTable
108 );
109HRESULT DAPI EseBeginTransaction(
110 __in JET_SESID jsSession
111 );
112HRESULT DAPI EseRollbackTransaction(
113 __in JET_SESID jsSession,
114 __in BOOL fAll
115 );
116HRESULT DAPI EseCommitTransaction(
117 __in JET_SESID jsSession
118 );
119HRESULT DAPI EsePrepareUpdate(
120 __in JET_SESID jsSession,
121 __in JET_TABLEID jtTable,
122 __in ULONG ulPrep
123 );
124HRESULT DAPI EseFinishUpdate(
125 __in JET_SESID jsSession,
126 __in JET_TABLEID jtTable,
127 __in BOOL fSeekToInsertedRecord
128 );
129HRESULT 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 );
136HRESULT DAPI EseSetColumnDword(
137 __in JET_SESID jsSession,
138 __in ESE_TABLE_SCHEMA tsTable,
139 __in DWORD dwColumn,
140 __in DWORD dwValue
141 );
142HRESULT DAPI EseSetColumnBool(
143 __in JET_SESID jsSession,
144 __in ESE_TABLE_SCHEMA tsTable,
145 __in DWORD dwColumn,
146 __in BOOL fValue
147 );
148HRESULT DAPI EseSetColumnString(
149 __in JET_SESID jsSession,
150 __in ESE_TABLE_SCHEMA tsTable,
151 __in DWORD dwColumn,
152 __in_z LPCWSTR pszValue
153 );
154HRESULT DAPI EseSetColumnEmpty(
155 __in JET_SESID jsSession,
156 __in ESE_TABLE_SCHEMA tsTable,
157 __in DWORD dwColumn
158 );
159HRESULT 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 );
166HRESULT DAPI EseGetColumnDword(
167 __in JET_SESID jsSession,
168 __in ESE_TABLE_SCHEMA tsTable,
169 __in DWORD dwColumn,
170 __out DWORD *pdwValue
171 );
172HRESULT DAPI EseGetColumnBool(
173 __in JET_SESID jsSession,
174 __in ESE_TABLE_SCHEMA tsTable,
175 __in DWORD dwColumn,
176 __out BOOL *pfValue
177 );
178HRESULT 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
186HRESULT 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 );
192HRESULT 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 );
198HRESULT 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 );
203HRESULT 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 );
208HRESULT 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 );
213HRESULT DAPI EseFinishQuery(
214 __in ESE_QUERY_HANDLE eqhHandle
215 );
216// Once all columns have been set up, call this and read the result
217HRESULT DAPI EseRunQuery(
218 __in ESE_QUERY_HANDLE eqhHandle
219 );
220
221#ifdef __cplusplus
222}
223#endif
diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h
new file mode 100644
index 00000000..ddae340f
--- /dev/null
+++ b/src/dutil/inc/fileutil.h
@@ -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
6extern "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
18typedef enum FILE_ARCHITECTURE
19{
20 FILE_ARCHITECTURE_UNKNOWN,
21 FILE_ARCHITECTURE_X86,
22 FILE_ARCHITECTURE_X64,
23 FILE_ARCHITECTURE_IA64,
24} FILE_ARCHITECTURE;
25
26typedef 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
37LPWSTR DAPI FileFromPath(
38 __in_z LPCWSTR wzPath
39 );
40HRESULT DAPI FileResolvePath(
41 __in_z LPCWSTR wzRelativePath,
42 __out LPWSTR *ppwzFullPath
43 );
44HRESULT DAPI FileStripExtension(
45 __in_z LPCWSTR wzFileName,
46 __out LPWSTR *ppwzFileNameNoExtension
47 );
48HRESULT DAPI FileChangeExtension(
49 __in_z LPCWSTR wzFileName,
50 __in_z LPCWSTR wzNewExtension,
51 __out LPWSTR *ppwzFileNameNewExtension
52 );
53HRESULT DAPI FileAddSuffixToBaseName(
54 __in_z LPCWSTR wzFileName,
55 __in_z LPCWSTR wzSuffix,
56 __out_z LPWSTR* psczNewFileName
57 );
58HRESULT DAPI FileVersionFromString(
59 __in_z LPCWSTR wzVersion,
60 __out DWORD *pdwVerMajor,
61 __out DWORD* pdwVerMinor
62 );
63HRESULT DAPI FileVersionFromStringEx(
64 __in_z LPCWSTR wzVersion,
65 __in DWORD cchVersion,
66 __out DWORD64* pqwVersion
67 );
68HRESULT DAPI FileVersionToStringEx(
69 __in DWORD64 qwVersion,
70 __out LPWSTR* psczVersion
71 );
72HRESULT DAPI FileSetPointer(
73 __in HANDLE hFile,
74 __in DWORD64 dw64Move,
75 __out_opt DWORD64* pdw64NewPosition,
76 __in DWORD dwMoveMethod
77 );
78HRESULT DAPI FileSize(
79 __in_z LPCWSTR pwzFileName,
80 __out LONGLONG* pllSize
81 );
82HRESULT DAPI FileSizeByHandle(
83 __in HANDLE hFile,
84 __out LONGLONG* pllSize
85 );
86BOOL DAPI FileExistsEx(
87 __in_z LPCWSTR wzPath,
88 __out_opt DWORD *pdwAttributes
89 );
90BOOL DAPI FileExistsAfterRestart(
91 __in_z LPCWSTR wzPath,
92 __out_opt DWORD *pdwAttributes
93 );
94HRESULT DAPI FileRemoveFromPendingRename(
95 __in_z LPCWSTR wzPath
96 );
97HRESULT DAPI FileRead(
98 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
99 __out DWORD* pcbDest,
100 __in_z LPCWSTR wzSrcPath
101 );
102HRESULT DAPI FileReadEx(
103 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
104 __out DWORD* pcbDest,
105 __in_z LPCWSTR wzSrcPath,
106 __in DWORD dwShareMode
107 );
108HRESULT 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 );
114HRESULT 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 );
123HRESULT 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 );
133HRESULT 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 );
140HRESULT DAPI FileWriteHandle(
141 __in HANDLE hFile,
142 __in_bcount_opt(cbData) LPCBYTE pbData,
143 __in DWORD cbData
144 );
145HRESULT DAPI FileCopyUsingHandles(
146 __in HANDLE hSource,
147 __in HANDLE hTarget,
148 __in DWORD64 cbCopy,
149 __out_opt DWORD64* pcbCopied
150 );
151HRESULT DAPI FileEnsureCopy(
152 __in_z LPCWSTR wzSource,
153 __in_z LPCWSTR wzTarget,
154 __in BOOL fOverwrite
155 );
156HRESULT DAPI FileEnsureCopyWithRetry(
157 __in LPCWSTR wzSource,
158 __in LPCWSTR wzTarget,
159 __in BOOL fOverwrite,
160 __in DWORD cRetry,
161 __in DWORD dwWaitMilliseconds
162 );
163HRESULT DAPI FileEnsureMove(
164 __in_z LPCWSTR wzSource,
165 __in_z LPCWSTR wzTarget,
166 __in BOOL fOverwrite,
167 __in BOOL fAllowCopy
168 );
169HRESULT 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 );
177HRESULT 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 );
183HRESULT 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 );
189HRESULT DAPI FileVersion(
190 __in_z LPCWSTR wzFilename,
191 __out DWORD *pdwVerMajor,
192 __out DWORD* pdwVerMinor
193 );
194HRESULT DAPI FileIsSame(
195 __in_z LPCWSTR wzFile1,
196 __in_z LPCWSTR wzFile2,
197 __out LPBOOL lpfSameFile
198 );
199HRESULT DAPI FileEnsureDelete(
200 __in_z LPCWSTR wzFile
201 );
202HRESULT DAPI FileGetTime(
203 __in_z LPCWSTR wzFile,
204 __out_opt LPFILETIME lpCreationTime,
205 __out_opt LPFILETIME lpLastAccessTime,
206 __out_opt LPFILETIME lpLastWriteTime
207 );
208HRESULT 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 );
214HRESULT DAPI FileResetTime(
215 __in_z LPCWSTR wzFile
216 );
217HRESULT DAPI FileExecutableArchitecture(
218 __in_z LPCWSTR wzFile,
219 __out FILE_ARCHITECTURE *pArchitecture
220 );
221HRESULT DAPI FileToString(
222 __in_z LPCWSTR wzFile,
223 __out LPWSTR *psczString,
224 __out_opt FILE_ENCODING *pfeEncoding
225 );
226HRESULT 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
diff --git a/src/dutil/inc/gdiputil.h b/src/dutil/inc/gdiputil.h
new file mode 100644
index 00000000..3708053c
--- /dev/null
+++ b/src/dutil/inc/gdiputil.h
@@ -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
8extern "C" {
9#endif
10
11HRESULT DAPI GdipInitialize(
12 __in const Gdiplus::GdiplusStartupInput* pInput,
13 __out ULONG_PTR* pToken,
14 __out_opt Gdiplus::GdiplusStartupOutput *pOutput
15 );
16
17void DAPI GdipUninitialize(
18 __in ULONG_PTR token
19 );
20
21HRESULT DAPI GdipBitmapFromResource(
22 __in_opt HINSTANCE hinst,
23 __in_z LPCSTR szId,
24 __out Gdiplus::Bitmap **ppBitmap
25 );
26
27HRESULT DAPI GdipBitmapFromFile(
28 __in_z LPCWSTR wzFileName,
29 __out Gdiplus::Bitmap **ppBitmap
30 );
31
32HRESULT DAPI GdipHresultFromStatus(
33 __in Gdiplus::Status gs
34 );
35
36#ifdef __cplusplus
37}
38#endif
diff --git a/src/dutil/inc/guidutil.h b/src/dutil/inc/guidutil.h
new file mode 100644
index 00000000..478a464f
--- /dev/null
+++ b/src/dutil/inc/guidutil.h
@@ -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
6extern "C" {
7#endif
8
9#define GUID_STRING_LENGTH 39
10
11HRESULT DAPI GuidFixedCreate(
12 _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid
13 );
14
15HRESULT DAPI GuidCreate(
16 __deref_out_z LPWSTR* psczGuid
17 );
18
19#ifdef __cplusplus
20}
21#endif
diff --git a/src/dutil/inc/iis7util.h b/src/dutil/inc/iis7util.h
new file mode 100644
index 00000000..3572b4e9
--- /dev/null
+++ b/src/dutil/inc/iis7util.h
@@ -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
6extern "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
118typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID);
119typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*);
120
121HRESULT DAPI Iis7PutPropertyVariant(
122 __in IAppHostElement *pElement,
123 __in LPCWSTR wzPropName,
124 __in VARIANT vtPut
125 );
126
127HRESULT DAPI Iis7PutPropertyInteger(
128 __in IAppHostElement *pElement,
129 __in LPCWSTR wzPropName,
130 __in DWORD dValue
131 );
132
133HRESULT DAPI Iis7PutPropertyString(
134 __in IAppHostElement *pElement,
135 __in LPCWSTR wzPropName,
136 __in LPCWSTR wzString
137 );
138
139HRESULT DAPI Iis7PutPropertyBool(
140 __in IAppHostElement *pElement,
141 __in LPCWSTR wzPropName,
142 __in BOOL fValue);
143
144HRESULT DAPI Iis7GetPropertyVariant(
145 __in IAppHostElement *pElement,
146 __in LPCWSTR wzPropName,
147 __in VARIANT* vtGet
148 );
149
150HRESULT DAPI Iis7GetPropertyString(
151 __in IAppHostElement *pElement,
152 __in LPCWSTR wzPropName,
153 __in LPWSTR* psczGet
154 );
155
156struct IIS7_APPHOSTELEMENTCOMPARISON
157{
158 LPCWSTR sczElementName;
159 LPCWSTR sczAttributeName;
160 VARIANT* pvAttributeValue;
161 VARIANTCOMPARATORPROC pComparator;
162};
163
164BOOL DAPI Iis7IsMatchingAppHostElement(
165 __in IAppHostElement *pElement,
166 __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison
167 );
168
169HRESULT 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
178HRESULT 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
187HRESULT 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
196HRESULT 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
205HRESULT DAPI Iis7EnumAppHostElements(
206 __in IAppHostElementCollection *pCollection,
207 __in ENUMAPHOSTELEMENTPROC pCallback,
208 __in LPVOID pContext,
209 __out IAppHostElement** ppElement,
210 __out DWORD* pdwIndex
211 );
212
213HRESULT 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
diff --git a/src/dutil/inc/inetutil.h b/src/dutil/inc/inetutil.h
new file mode 100644
index 00000000..4cbf510b
--- /dev/null
+++ b/src/dutil/inc/inetutil.h
@@ -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
6extern "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
14HRESULT DAPI InternetGetSizeByHandle(
15 __in HINTERNET hiFile,
16 __out LONGLONG* pllSize
17 );
18
19HRESULT DAPI InternetGetCreateTimeByHandle(
20 __in HINTERNET hiFile,
21 __out LPFILETIME pft
22 );
23
24HRESULT DAPI InternetQueryInfoString(
25 __in HINTERNET h,
26 __in DWORD dwInfo,
27 __deref_out_z LPWSTR* psczValue
28 );
29
30HRESULT DAPI InternetQueryInfoNumber(
31 __in HINTERNET h,
32 __in DWORD dwInfo,
33 __out LONG* plInfo
34 );
35
36#ifdef __cplusplus
37}
38#endif
39
diff --git a/src/dutil/inc/iniutil.h b/src/dutil/inc/iniutil.h
new file mode 100644
index 00000000..d5b50c17
--- /dev/null
+++ b/src/dutil/inc/iniutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); }
10#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; }
11
12typedef void* INI_HANDLE;
13typedef const void* C_INI_HANDLE;
14
15extern const int INI_HANDLE_BYTES;
16
17struct INI_VALUE
18{
19 LPCWSTR wzName;
20 LPCWSTR wzValue;
21
22 DWORD dwLineNumber;
23};
24
25HRESULT DAPI IniInitialize(
26 __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle
27 );
28void DAPI IniUninitialize(
29 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle
30 );
31HRESULT DAPI IniSetOpenTag(
32 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
33 __in_z_opt LPCWSTR wzOpenTagPrefix,
34 __in_z_opt LPCWSTR wzOpenTagPostfix
35 );
36HRESULT DAPI IniSetValueStyle(
37 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
38 __in_z_opt LPCWSTR wzValuePrefix,
39 __in_z_opt LPCWSTR wzValueSeparator
40 );
41HRESULT DAPI IniSetValueSeparatorException(
42 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
43 __in_z LPCWSTR wzValueNamePrefix
44 );
45HRESULT DAPI IniSetCommentStyle(
46 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
47 __in_z_opt LPCWSTR wzLinePrefix
48 );
49HRESULT 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)
56HRESULT 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 );
61HRESULT DAPI IniGetValue(
62 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
63 __in LPCWSTR wzValueName,
64 __deref_out_z LPWSTR* psczValue
65 );
66HRESULT DAPI IniSetValue(
67 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
68 __in LPCWSTR wzValueName,
69 __in_z_opt LPCWSTR wzValue
70 );
71HRESULT 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
diff --git a/src/dutil/inc/jsonutil.h b/src/dutil/inc/jsonutil.h
new file mode 100644
index 00000000..b05e9970
--- /dev/null
+++ b/src/dutil/inc/jsonutil.h
@@ -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
6extern "C" {
7#endif
8
9typedef 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
22typedef struct _JSON_VALUE
23{
24} JSON_VALUE;
25
26typedef struct _JSON_READER
27{
28 CRITICAL_SECTION cs;
29 LPWSTR sczJson;
30
31 LPWSTR pwz;
32 JSON_TOKEN token;
33} JSON_READER;
34
35typedef 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
46DAPI_(HRESULT) JsonInitializeReader(
47 __in_z LPCWSTR wzJson,
48 __in JSON_READER* pReader
49 );
50
51DAPI_(void) JsonUninitializeReader(
52 __in JSON_READER* pReader
53 );
54
55DAPI_(HRESULT) JsonReadNext(
56 __in JSON_READER* pReader,
57 __out JSON_TOKEN* pToken,
58 __out JSON_VALUE* pValue
59 );
60
61DAPI_(HRESULT) JsonReadValue(
62 __in JSON_READER* pReader,
63 __in JSON_VALUE* pValue
64 );
65
66DAPI_(HRESULT) JsonInitializeWriter(
67 __in JSON_WRITER* pWriter
68 );
69
70DAPI_(void) JsonUninitializeWriter(
71 __in JSON_WRITER* pWriter
72 );
73
74DAPI_(HRESULT) JsonWriteBool(
75 __in JSON_WRITER* pWriter,
76 __in BOOL fValue
77 );
78
79DAPI_(HRESULT) JsonWriteNumber(
80 __in JSON_WRITER* pWriter,
81 __in DWORD dwValue
82 );
83
84DAPI_(HRESULT) JsonWriteString(
85 __in JSON_WRITER* pWriter,
86 __in_z LPCWSTR wzValue
87 );
88
89DAPI_(HRESULT) JsonWriteArrayStart(
90 __in JSON_WRITER* pWriter
91 );
92
93DAPI_(HRESULT) JsonWriteArrayEnd(
94 __in JSON_WRITER* pWriter
95 );
96
97DAPI_(HRESULT) JsonWriteObjectStart(
98 __in JSON_WRITER* pWriter
99 );
100
101DAPI_(HRESULT) JsonWriteObjectKey(
102 __in JSON_WRITER* pWriter,
103 __in_z LPCWSTR wzKey
104 );
105
106DAPI_(HRESULT) JsonWriteObjectEnd(
107 __in JSON_WRITER* pWriter
108 );
109
110#ifdef __cplusplus
111}
112#endif
diff --git a/src/dutil/inc/locutil.h b/src/dutil/inc/locutil.h
new file mode 100644
index 00000000..38ddda20
--- /dev/null
+++ b/src/dutil/inc/locutil.h
@@ -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
6extern "C" {
7#endif
8
9struct LOC_STRING
10{
11 LPWSTR wzId;
12 LPWSTR wzText;
13 BOOL bOverridable;
14};
15
16const int LOC_CONTROL_NOT_SET = INT_MAX;
17
18struct LOC_CONTROL
19{
20 LPWSTR wzControl;
21 int nX;
22 int nY;
23 int nWidth;
24 int nHeight;
25 LPWSTR wzText;
26};
27
28const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX;
29
30struct 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*******************************************************************/
45HRESULT 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*******************************************************************/
56HRESULT 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*******************************************************************/
67HRESULT 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*******************************************************************/
77void 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*******************************************************************/
85HRESULT DAPI LocLocalizeString(
86 __in const WIX_LOCALIZATION* pWixLoc,
87 __inout LPWSTR* psczInput
88 );
89
90/********************************************************************
91 LocGetControl - returns a control's localization information
92*******************************************************************/
93HRESULT DAPI LocGetControl(
94 __in const WIX_LOCALIZATION* pWixLoc,
95 __in_z LPCWSTR wzId,
96 __out LOC_CONTROL** ppLocControl
97 );
98
99/********************************************************************
100LocGetString - returns a string's localization information
101*******************************************************************/
102extern "C" HRESULT DAPI LocGetString(
103 __in const WIX_LOCALIZATION* pWixLoc,
104 __in_z LPCWSTR wzId,
105 __out LOC_STRING** ppLocString
106 );
107
108/********************************************************************
109LocAddString - adds a localization string
110*******************************************************************/
111extern "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
diff --git a/src/dutil/inc/logutil.h b/src/dutil/inc/logutil.h
new file mode 100644
index 00000000..ee0cd065
--- /dev/null
+++ b/src/dutil/inc/logutil.h
@@ -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
6extern "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
13typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)(
14 __in_z LPCSTR szString,
15 __in_opt LPVOID pvContext
16 );
17
18// enums
19
20// structs
21
22// functions
23BOOL DAPI IsLogInitialized();
24
25BOOL DAPI IsLogOpen();
26
27void DAPI LogInitialize(
28 __in HMODULE hModule
29 );
30
31HRESULT 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
41void DAPI LogDisable();
42
43void DAPI LogRedirect(
44 __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw,
45 __in_opt LPVOID pvContext
46 );
47
48HRESULT DAPI LogRename(
49 __in_z LPCWSTR wzNewPath
50 );
51
52void DAPI LogClose(
53 __in BOOL fFooter
54 );
55
56void DAPI LogUninitialize(
57 __in BOOL fFooter
58 );
59
60BOOL DAPI LogIsOpen();
61
62HRESULT DAPI LogSetSpecialParams(
63 __in_z_opt LPCWSTR wzSpecialBeginLine,
64 __in_z_opt LPCWSTR wzSpecialAfterTimeStamp,
65 __in_z_opt LPCWSTR wzSpecialEndLine
66 );
67
68REPORT_LEVEL DAPI LogSetLevel(
69 __in REPORT_LEVEL rl,
70 __in BOOL fLogChange
71 );
72
73REPORT_LEVEL DAPI LogGetLevel();
74
75HRESULT DAPI LogGetPath(
76 __out_ecount_z(cchLogPath) LPWSTR pwzLogPath,
77 __in DWORD cchLogPath
78 );
79
80HANDLE DAPI LogGetHandle();
81
82HRESULT DAPIV LogString(
83 __in REPORT_LEVEL rl,
84 __in_z __format_string LPCSTR szFormat,
85 ...
86 );
87
88HRESULT DAPI LogStringArgs(
89 __in REPORT_LEVEL rl,
90 __in_z __format_string LPCSTR szFormat,
91 __in va_list args
92 );
93
94HRESULT DAPIV LogStringLine(
95 __in REPORT_LEVEL rl,
96 __in_z __format_string LPCSTR szFormat,
97 ...
98 );
99
100HRESULT DAPI LogStringLineArgs(
101 __in REPORT_LEVEL rl,
102 __in_z __format_string LPCSTR szFormat,
103 __in va_list args
104 );
105
106HRESULT 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
117inline 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
138inline 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
147HRESULT DAPIV LogErrorString(
148 __in HRESULT hrError,
149 __in_z __format_string LPCSTR szFormat,
150 ...
151 );
152
153HRESULT DAPI LogErrorStringArgs(
154 __in HRESULT hrError,
155 __in_z __format_string LPCSTR szFormat,
156 __in va_list args
157 );
158
159HRESULT 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
168inline 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
179HRESULT DAPI LogHeader();
180
181HRESULT DAPI LogFooter();
182
183HRESULT LogStringWorkRaw(
184 __in_z LPCSTR szLogData
185 );
186
187#ifdef __cplusplus
188}
189#endif
190
diff --git a/src/dutil/inc/memutil.h b/src/dutil/inc/memutil.h
new file mode 100644
index 00000000..93e53228
--- /dev/null
+++ b/src/dutil/inc/memutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseMem(p) if (p) { MemFree(p); }
10#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; }
11
12HRESULT DAPI MemInitialize();
13void DAPI MemUninitialize();
14
15LPVOID DAPI MemAlloc(
16 __in SIZE_T cbSize,
17 __in BOOL fZero
18 );
19LPVOID DAPI MemReAlloc(
20 __in LPVOID pv,
21 __in SIZE_T cbSize,
22 __in BOOL fZero
23 );
24HRESULT DAPI MemReAllocSecure(
25 __in LPVOID pv,
26 __in SIZE_T cbSize,
27 __in BOOL fZero,
28 __deref_out LPVOID* ppvNew
29 );
30HRESULT DAPI MemAllocArray(
31 __inout LPVOID* ppvArray,
32 __in SIZE_T cbArrayType,
33 __in DWORD dwItemCount
34 );
35HRESULT DAPI MemReAllocArray(
36 __inout LPVOID* ppvArray,
37 __in DWORD cArray,
38 __in SIZE_T cbArrayType,
39 __in DWORD dwNewItemCount
40 );
41HRESULT DAPI MemEnsureArraySize(
42 __deref_out_bcount(cArray * cbArrayType) LPVOID* ppvArray,
43 __in DWORD cArray,
44 __in SIZE_T cbArrayType,
45 __in DWORD dwGrowthCount
46 );
47HRESULT 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 );
55void 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 );
63void DAPI MemArraySwapItems(
64 __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray,
65 __in DWORD dwIndex1,
66 __in DWORD dwIndex2,
67 __in SIZE_T cbArrayType
68 );
69
70HRESULT DAPI MemFree(
71 __in LPVOID pv
72 );
73SIZE_T DAPI MemSize(
74 __in LPCVOID pv
75 );
76
77#ifdef __cplusplus
78}
79#endif
80
diff --git a/src/dutil/inc/metautil.h b/src/dutil/inc/metautil.h
new file mode 100644
index 00000000..31f9ff5c
--- /dev/null
+++ b/src/dutil/inc/metautil.h
@@ -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
11extern "C" {
12#endif
13
14// structs
15
16// prototypes
17HRESULT 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 );
26HRESULT DAPI MetaFindFreeWebBase(
27 __in IMSAdminBaseW* piMetabase,
28 __out_ecount(cchWebBase) LPWSTR wzWebBase,
29 __in DWORD cchWebBase
30 );
31
32HRESULT 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 );
40HRESULT DAPI MetaGetValue(
41 __in IMSAdminBaseW* piMetabase,
42 __in METADATA_HANDLE mhKey,
43 __in_z LPCWSTR wzKey,
44 __inout METADATA_RECORD* pmr
45 );
46void DAPI MetaFreeValue(
47 __in METADATA_RECORD* pmr
48 );
49
50#ifdef __cplusplus
51}
52#endif
diff --git a/src/dutil/inc/monutil.h b/src/dutil/inc/monutil.h
new file mode 100644
index 00000000..f644e205
--- /dev/null
+++ b/src/dutil/inc/monutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseMon(mh) if (mh) { MonDestroy(mh); }
10#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; }
11
12typedef void* MON_HANDLE;
13typedef const void* C_MON_HANDLE;
14
15// Defined in regutil.h
16enum REG_KEY_BITNESS;
17
18extern 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
22typedef 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
27typedef 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.
35typedef 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 );
42typedef 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
56HRESULT 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.
74HRESULT 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 );
81HRESULT 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 );
90HRESULT DAPI MonRemoveDirectory(
91 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
92 __in_z LPCWSTR wzPath,
93 __in BOOL fRecursive
94 );
95HRESULT 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 );
102void DAPI MonDestroy(
103 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle
104 );
105
106#ifdef __cplusplus
107}
108#endif
diff --git a/src/dutil/inc/osutil.h b/src/dutil/inc/osutil.h
new file mode 100644
index 00000000..01f8d9cf
--- /dev/null
+++ b/src/dutil/inc/osutil.h
@@ -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
6extern "C" {
7#endif
8
9typedef 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
23void DAPI OsGetVersion(
24 __out OS_VERSION* pVersion,
25 __out DWORD* pdwServicePack
26 );
27HRESULT DAPI OsCouldRunPrivileged(
28 __out BOOL* pfPrivileged
29 );
30HRESULT DAPI OsIsRunningPrivileged(
31 __out BOOL* pfPrivileged
32 );
33HRESULT DAPI OsIsUacEnabled(
34 __out BOOL* pfUacEnabled
35 );
36
37#ifdef __cplusplus
38}
39#endif
diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h
new file mode 100644
index 00000000..76798172
--- /dev/null
+++ b/src/dutil/inc/pathutil.h
@@ -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
6extern "C" {
7#endif
8
9typedef 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********************************************************************/
21DAPI_(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********************************************************************/
29DAPI_(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********************************************************************/
37DAPI_(LPCWSTR) PathExtension(
38 __in_z LPCWSTR wzPath
39 );
40
41/*******************************************************************
42 PathGetDirectory - extracts the directory from a path.
43********************************************************************/
44DAPI_(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********************************************************************/
53DAPI_(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********************************************************************/
63DAPI_(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********************************************************************/
72DAPI_(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********************************************************************/
81DAPI_(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********************************************************************/
89DAPI_(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********************************************************************/
99DAPI_(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*******************************************************************/
111DAPI_(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********************************************************************/
124DAPI_(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*******************************************************************/
138DAPI_(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*******************************************************************/
149DAPI_(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*******************************************************************/
158DAPI_(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*******************************************************************/
166DAPI_(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*******************************************************************/
177DAPI_(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*******************************************************************/
186DAPI_(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*******************************************************************/
197DAPI_(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*******************************************************************/
207DAPI_(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*******************************************************************/
216DAPI_(HRESULT) PathCanonicalizePath(
217 __in_z LPCWSTR wzPath,
218 __deref_out_z LPWSTR* psczCanonicalized
219 );
220
221/*******************************************************************
222PathDirectoryContainsPath - checks if wzPath is located inside
223 wzDirectory.
224*******************************************************************/
225DAPI_(HRESULT) PathDirectoryContainsPath(
226 __in_z LPCWSTR wzDirectory,
227 __in_z LPCWSTR wzPath
228 );
229
230#ifdef __cplusplus
231}
232#endif
diff --git a/src/dutil/inc/perfutil.h b/src/dutil/inc/perfutil.h
new file mode 100644
index 00000000..7557511d
--- /dev/null
+++ b/src/dutil/inc/perfutil.h
@@ -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
6extern "C" {
7#endif
8
9// structs
10
11
12// functions
13void DAPI PerfInitialize(
14 );
15void DAPI PerfClickTime(
16 __out_opt LARGE_INTEGER* pliElapsed
17 );
18double DAPI PerfConvertToSeconds(
19 __in const LARGE_INTEGER* pli
20 );
21
22#ifdef __cplusplus
23}
24#endif
diff --git a/src/dutil/inc/polcutil.h b/src/dutil/inc/polcutil.h
new file mode 100644
index 00000000..13618043
--- /dev/null
+++ b/src/dutil/inc/polcutil.h
@@ -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
6extern "C" {
7#endif
8
9const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn";
10
11/********************************************************************
12PolcReadNumber - reads a number from policy.
13
14NOTE: S_FALSE returned if policy not set.
15NOTE: out is set to default on S_FALSE or any error.
16********************************************************************/
17HRESULT DAPI PolcReadNumber(
18 __in_z LPCWSTR wzPolicyPath,
19 __in_z LPCWSTR wzPolicyName,
20 __in DWORD dwDefault,
21 __out DWORD* pdw
22 );
23
24/********************************************************************
25PolcReadString - reads a string from policy.
26
27NOTE: S_FALSE returned if policy not set.
28NOTE: out is set to default on S_FALSE or any error.
29********************************************************************/
30HRESULT 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
diff --git a/src/dutil/inc/procutil.h b/src/dutil/inc/procutil.h
new file mode 100644
index 00000000..00f3f358
--- /dev/null
+++ b/src/dutil/inc/procutil.h
@@ -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
6extern "C" {
7#endif
8
9// structs
10typedef struct _PROC_FILESYSTEMREDIRECTION
11{
12 BOOL fDisabled;
13 LPVOID pvRevertState;
14} PROC_FILESYSTEMREDIRECTION;
15
16HRESULT DAPI ProcElevated(
17 __in HANDLE hProcess,
18 __out BOOL* pfElevated
19 );
20
21HRESULT DAPI ProcWow64(
22 __in HANDLE hProcess,
23 __out BOOL* pfWow64
24 );
25HRESULT DAPI ProcDisableWowFileSystemRedirection(
26 __in PROC_FILESYSTEMREDIRECTION* pfsr
27 );
28HRESULT DAPI ProcRevertWowFileSystemRedirection(
29 __in PROC_FILESYSTEMREDIRECTION* pfsr
30 );
31
32HRESULT DAPI ProcExec(
33 __in_z LPCWSTR wzExecutablePath,
34 __in_z_opt LPCWSTR wzCommandLine,
35 __in int nCmdShow,
36 __out HANDLE *phProcess
37 );
38HRESULT DAPI ProcExecute(
39 __in_z LPWSTR wzCommand,
40 __out HANDLE *phProcess,
41 __out_opt HANDLE *phChildStdIn,
42 __out_opt HANDLE *phChildStdOutErr
43 );
44HRESULT DAPI ProcWaitForCompletion(
45 __in HANDLE hProcess,
46 __in DWORD dwTimeout,
47 __out DWORD *pReturnCode
48 );
49HRESULT DAPI ProcWaitForIds(
50 __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
51 __in DWORD cProcessIds,
52 __in DWORD dwMilliseconds
53 );
54HRESULT 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.
60HRESULT 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.
67HRESULT DAPI ProcExecuteAsInteractiveUser(
68 __in_z LPCWSTR wzExecutablePath,
69 __in_z LPCWSTR wzCommand,
70 __out HANDLE *phProcess
71 );
72
73#ifdef __cplusplus
74}
75#endif
diff --git a/src/dutil/inc/regutil.h b/src/dutil/inc/regutil.h
new file mode 100644
index 00000000..897b9d03
--- /dev/null
+++ b/src/dutil/inc/regutil.h
@@ -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
6extern "C" {
7#endif
8
9
10#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; }
11
12typedef 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
19typedef 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 );
30typedef 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 );
37typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)(
38 __in HKEY hKey,
39 __in LPCWSTR lpSubKey,
40 __in REGSAM samDesired,
41 __reserved DWORD Reserved
42 );
43typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)(
44 __in HKEY hKey,
45 __in LPCWSTR lpSubKey
46 );
47typedef 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 );
57typedef 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 );
67typedef 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 );
81typedef 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 );
89typedef 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 );
97typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)(
98 __in HKEY hKey,
99 __in_opt LPCWSTR lpValueName
100 );
101
102HRESULT DAPI RegInitialize();
103void DAPI RegUninitialize();
104
105void 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 );
116HRESULT DAPI RegCreate(
117 __in HKEY hkRoot,
118 __in_z LPCWSTR wzSubKey,
119 __in DWORD dwAccess,
120 __out HKEY* phk
121 );
122HRESULT 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 );
131HRESULT DAPI RegOpen(
132 __in HKEY hkRoot,
133 __in_z LPCWSTR wzSubKey,
134 __in DWORD dwAccess,
135 __out HKEY* phk
136 );
137HRESULT DAPI RegDelete(
138 __in HKEY hkRoot,
139 __in_z LPCWSTR wzSubKey,
140 __in REG_KEY_BITNESS kbKeyBitness,
141 __in BOOL fDeleteTree
142 );
143HRESULT DAPI RegKeyEnum(
144 __in HKEY hk,
145 __in DWORD dwIndex,
146 __deref_out_z LPWSTR* psczKey
147 );
148HRESULT DAPI RegValueEnum(
149 __in HKEY hk,
150 __in DWORD dwIndex,
151 __deref_out_z LPWSTR* psczName,
152 __out_opt DWORD *pdwType
153 );
154HRESULT DAPI RegGetType(
155 __in HKEY hk,
156 __in_z_opt LPCWSTR wzName,
157 __out DWORD *pdwType
158 );
159HRESULT 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 );
165HRESULT DAPI RegReadString(
166 __in HKEY hk,
167 __in_z_opt LPCWSTR wzName,
168 __deref_out_z LPWSTR* psczValue
169 );
170HRESULT 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 );
176HRESULT DAPI RegReadVersion(
177 __in HKEY hk,
178 __in_z_opt LPCWSTR wzName,
179 __out DWORD64* pdw64Version
180 );
181HRESULT DAPI RegReadNumber(
182 __in HKEY hk,
183 __in_z_opt LPCWSTR wzName,
184 __out DWORD* pdwValue
185 );
186HRESULT DAPI RegReadQword(
187 __in HKEY hk,
188 __in_z_opt LPCWSTR wzName,
189 __out DWORD64* pqwValue
190 );
191HRESULT DAPI RegWriteBinary(
192 __in HKEY hk,
193 __in_z_opt LPCWSTR wzName,
194 __in_bcount(cbBuffer) const BYTE *pbBuffer,
195 __in DWORD cbBuffer
196 );
197HRESULT DAPI RegWriteString(
198 __in HKEY hk,
199 __in_z_opt LPCWSTR wzName,
200 __in_z_opt LPCWSTR wzValue
201 );
202HRESULT DAPI RegWriteStringArray(
203 __in HKEY hk,
204 __in_z_opt LPCWSTR wzName,
205 __in_ecount(cValues) LPWSTR *rgwzStrings,
206 __in DWORD cStrings
207 );
208HRESULT DAPI RegWriteStringFormatted(
209 __in HKEY hk,
210 __in_z_opt LPCWSTR wzName,
211 __in __format_string LPCWSTR szFormat,
212 ...
213 );
214HRESULT DAPI RegWriteNumber(
215 __in HKEY hk,
216 __in_z_opt LPCWSTR wzName,
217 __in DWORD dwValue
218 );
219HRESULT DAPI RegWriteQword(
220 __in HKEY hk,
221 __in_z_opt LPCWSTR wzName,
222 __in DWORD64 qwValue
223 );
224HRESULT 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
diff --git a/src/dutil/inc/resrutil.h b/src/dutil/inc/resrutil.h
new file mode 100644
index 00000000..1f4d8e17
--- /dev/null
+++ b/src/dutil/inc/resrutil.h
@@ -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
6extern "C" {
7#endif
8
9HRESULT DAPI ResGetStringLangId(
10 __in_opt LPCWSTR wzPath,
11 __in UINT uID,
12 __out WORD *pwLangId
13 );
14
15HRESULT DAPI ResReadString(
16 __in HINSTANCE hinst,
17 __in UINT uID,
18 __deref_out_z LPWSTR* ppwzString
19 );
20
21HRESULT DAPI ResReadStringAnsi(
22 __in HINSTANCE hinst,
23 __in UINT uID,
24 __deref_out_z LPSTR* ppszString
25 );
26
27HRESULT 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
34HRESULT 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
diff --git a/src/dutil/inc/reswutil.h b/src/dutil/inc/reswutil.h
new file mode 100644
index 00000000..31435ae2
--- /dev/null
+++ b/src/dutil/inc/reswutil.h
@@ -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
6extern "C" {
7#endif
8
9HRESULT DAPI ResWriteString(
10 __in_z LPCWSTR wzResourceFile,
11 __in DWORD dwDataId,
12 __in_z LPCWSTR wzData,
13 __in WORD wLangId
14 );
15
16HRESULT DAPI ResWriteData(
17 __in_z LPCWSTR wzResourceFile,
18 __in_z LPCSTR szDataName,
19 __in PVOID pData,
20 __in DWORD cbData
21 );
22
23HRESULT 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
diff --git a/src/dutil/inc/rexutil.h b/src/dutil/inc/rexutil.h
new file mode 100644
index 00000000..77f5604a
--- /dev/null
+++ b/src/dutil/inc/rexutil.h
@@ -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
9extern "C" {
10#endif
11
12// defines
13#define FILETABLESIZE 40
14
15// structs
16struct MEM_FILE
17{
18 LPCBYTE vpStart;
19 UINT uiCurrent;
20 UINT uiLength;
21};
22
23typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE;
24
25typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
26typedef VOID (*REX_CALLBACK_WRITE)(UINT cb);
27
28
29struct 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
38HRESULT RexInitialize();
39void RexUninitialize();
40
41HRESULT 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
diff --git a/src/dutil/inc/rmutil.h b/src/dutil/inc/rmutil.h
new file mode 100644
index 00000000..ce7bf254
--- /dev/null
+++ b/src/dutil/inc/rmutil.h
@@ -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
6extern "C" {
7#endif
8
9typedef struct _RMU_SESSION *PRMU_SESSION;
10
11HRESULT DAPI RmuJoinSession(
12 __out PRMU_SESSION *ppSession,
13 __in_z LPCWSTR wzSessionKey
14 );
15
16HRESULT DAPI RmuAddFile(
17 __in PRMU_SESSION pSession,
18 __in_z LPCWSTR wzPath
19 );
20
21HRESULT DAPI RmuAddProcessById(
22 __in PRMU_SESSION pSession,
23 __in DWORD dwProcessId
24 );
25
26HRESULT DAPI RmuAddProcessesByName(
27 __in PRMU_SESSION pSession,
28 __in_z LPCWSTR wzProcessName
29 );
30
31HRESULT DAPI RmuAddService(
32 __in PRMU_SESSION pSession,
33 __in_z LPCWSTR wzServiceName
34 );
35
36HRESULT DAPI RmuRegisterResources(
37 __in PRMU_SESSION pSession
38 );
39
40HRESULT DAPI RmuEndSession(
41 __in PRMU_SESSION pSession
42 );
43
44#ifdef __cplusplus
45}
46#endif
diff --git a/src/dutil/inc/rssutil.h b/src/dutil/inc/rssutil.h
new file mode 100644
index 00000000..064ab147
--- /dev/null
+++ b/src/dutil/inc/rssutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); }
10#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; }
11
12
13struct RSS_UNKNOWN_ATTRIBUTE
14{
15 LPWSTR wzNamespace;
16 LPWSTR wzAttribute;
17 LPWSTR wzValue;
18
19 RSS_UNKNOWN_ATTRIBUTE* pNext;
20};
21
22struct 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
32struct 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
48struct 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
61HRESULT DAPI RssInitialize(
62 );
63
64void DAPI RssUninitialize(
65 );
66
67HRESULT DAPI RssParseFromString(
68 __in_z LPCWSTR wzRssString,
69 __out RSS_CHANNEL **ppChannel
70 );
71
72HRESULT 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
82void DAPI RssFreeChannel(
83 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
84 );
85
86#ifdef __cplusplus
87}
88#endif
89
diff --git a/src/dutil/inc/sceutil.h b/src/dutil/inc/sceutil.h
new file mode 100644
index 00000000..9d14eecf
--- /dev/null
+++ b/src/dutil/inc/sceutil.h
@@ -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
6extern "C" {
7#endif
8
9#include <sqlce_oledb.h>
10#include <sqlce_sync.h>
11#include <sqlce_err.h>
12
13typedef void* SCE_DATABASE_HANDLE;
14typedef void* SCE_ROW_HANDLE;
15typedef void* SCE_QUERY_HANDLE;
16typedef void* SCE_QUERY_RESULTS_HANDLE;
17
18extern const int SCE_ROW_HANDLE_BYTES;
19extern const int SCE_QUERY_HANDLE_BYTES;
20extern 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
29struct 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
44struct SCE_INDEX_SCHEMA
45{
46 LPWSTR wzName;
47
48 DWORD *rgColumns;
49 DWORD cColumns;
50};
51
52struct 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
67struct SCE_DATABASE_SCHEMA
68{
69 DWORD cTables;
70 SCE_TABLE_SCHEMA *rgTables;
71};
72
73struct SCE_DATABASE
74{
75 SCE_DATABASE_HANDLE sdbHandle;
76 SCE_DATABASE_SCHEMA *pdsSchema;
77};
78
79HRESULT DAPI SceCreateDatabase(
80 __in_z LPCWSTR sczFile,
81 __in_z_opt LPCWSTR wzSqlCeDllPath,
82 __deref_out SCE_DATABASE **ppDatabase
83 );
84HRESULT 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 );
92HRESULT 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 );
100HRESULT DAPI SceIsTableEmpty(
101 __in SCE_DATABASE *pDatabase,
102 __in DWORD dwTableIndex,
103 __out BOOL *pfEmpty
104 );
105HRESULT DAPI SceGetFirstRow(
106 __in SCE_DATABASE *pDatabase,
107 __in DWORD dwTableIndex,
108 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
109 );
110HRESULT DAPI SceGetNextRow(
111 __in SCE_DATABASE *pDatabase,
112 __in DWORD dwTableIndex,
113 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
114 );
115HRESULT DAPI SceBeginTransaction(
116 __in SCE_DATABASE *pDatabase
117 );
118HRESULT DAPI SceCommitTransaction(
119 __in SCE_DATABASE *pDatabase
120 );
121HRESULT DAPI SceRollbackTransaction(
122 __in SCE_DATABASE *pDatabase
123 );
124HRESULT DAPI SceDeleteRow(
125 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
126 );
127HRESULT DAPI ScePrepareInsert(
128 __in SCE_DATABASE *pDatabase,
129 __in DWORD dwTableIndex,
130 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
131 );
132HRESULT DAPI SceFinishUpdate(
133 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
134 );
135HRESULT 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 );
141HRESULT DAPI SceSetColumnDword(
142 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
143 __in DWORD dwColumnIndex,
144 __in const DWORD dwValue
145 );
146HRESULT DAPI SceSetColumnQword(
147 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
148 __in DWORD dwColumnIndex,
149 __in const DWORD64 qwValue
150 );
151HRESULT DAPI SceSetColumnBool(
152 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
153 __in DWORD dwColumnIndex,
154 __in const BOOL fValue
155 );
156HRESULT DAPI SceSetColumnString(
157 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
158 __in DWORD dwColumnIndex,
159 __in_z_opt LPCWSTR wzValue
160 );
161HRESULT DAPI SceSetColumnSystemTime(
162 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
163 __in DWORD dwColumnIndex,
164 __in const SYSTEMTIME *pst
165 );
166HRESULT DAPI SceSetColumnNull(
167 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
168 __in DWORD dwColumnIndex
169 );
170HRESULT 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 );
176HRESULT DAPI SceGetColumnDword(
177 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
178 __in DWORD dwColumnIndex,
179 __out DWORD *pdwValue
180 );
181HRESULT DAPI SceGetColumnQword(
182 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
183 __in DWORD dwColumnIndex,
184 __out DWORD64 *pqwValue
185 );
186HRESULT DAPI SceGetColumnBool(
187 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
188 __in DWORD dwColumnIndex,
189 __out BOOL *pfValue
190 );
191HRESULT DAPI SceGetColumnString(
192 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
193 __in DWORD dwColumnIndex,
194 __out_z LPWSTR *psczValue
195 );
196HRESULT DAPI SceGetColumnSystemTime(
197 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
198 __in DWORD dwColumnIndex,
199 __out SYSTEMTIME *pst
200 );
201HRESULT 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 );
207HRESULT 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 );
212HRESULT DAPI SceSetQueryColumnDword(
213 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
214 __in const DWORD dwValue
215 );
216HRESULT DAPI SceSetQueryColumnQword(
217 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
218 __in const DWORD64 qwValue
219 );
220HRESULT DAPI SceSetQueryColumnBool(
221 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
222 __in const BOOL fValue
223 );
224HRESULT DAPI SceSetQueryColumnString(
225 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
226 __in_z_opt LPCWSTR wzString
227 );
228HRESULT DAPI SceSetQueryColumnSystemTime(
229 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
230 __in const SYSTEMTIME *pst
231 );
232HRESULT DAPI SceSetQueryColumnEmpty(
233 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle
234 );
235HRESULT 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 );
239HRESULT 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 );
243HRESULT 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 );
247void DAPI SceCloseTable(
248 __in SCE_TABLE_SCHEMA *pTable
249 );
250// Returns whether the data in the database changed. Ignores schema changes.
251BOOL DAPI SceDatabaseChanged(
252 __in SCE_DATABASE *pDatabase
253 );
254// Resets the database changed flag
255void DAPI SceResetDatabaseChanged(
256 __in SCE_DATABASE *pDatabase
257 );
258HRESULT DAPI SceCloseDatabase(
259 __in SCE_DATABASE *pDatabase
260 );
261void DAPI SceFreeRow(
262 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle
263 );
264void DAPI SceFreeQuery(
265 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle
266 );
267void DAPI SceFreeQueryResults(
268 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle
269 );
270
271#ifdef __cplusplus
272}
273#endif
diff --git a/src/dutil/inc/sczutil.h b/src/dutil/inc/sczutil.h
new file mode 100644
index 00000000..fcfbd13a
--- /dev/null
+++ b/src/dutil/inc/sczutil.h
@@ -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
6class PSCZ
7{
8public:
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
27private:
28 LPWSTR m_scz;
29};
30#endif //__cplusplus
diff --git a/src/dutil/inc/shelutil.h b/src/dutil/inc/shelutil.h
new file mode 100644
index 00000000..21e82672
--- /dev/null
+++ b/src/dutil/inc/shelutil.h
@@ -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
10extern "C" {
11#endif
12
13typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)(
14 __inout LPSHELLEXECUTEINFOW lpExecInfo
15 );
16
17void DAPI ShelFunctionOverride(
18 __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW
19 );
20HRESULT 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 );
29HRESULT 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 );
36HRESULT DAPI ShelGetFolder(
37 __out_z LPWSTR* psczFolderPath,
38 __in int csidlFolder
39 );
40HRESULT DAPI ShelGetKnownFolder(
41 __out_z LPWSTR* psczFolderPath,
42 __in REFKNOWNFOLDERID rfidFolder
43 );
44
45#ifdef __cplusplus
46}
47#endif
diff --git a/src/dutil/inc/sqlutil.h b/src/dutil/inc/sqlutil.h
new file mode 100644
index 00000000..ddf09323
--- /dev/null
+++ b/src/dutil/inc/sqlutil.h
@@ -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
11extern "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
20struct 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
31HRESULT 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 );
40HRESULT DAPI SqlStartTransaction(
41 __in IDBCreateSession* pidbSession,
42 __out IDBCreateCommand** ppidbCommand,
43 __out ITransaction** ppit
44 );
45HRESULT DAPI SqlEndTransaction(
46 __in ITransaction* pit,
47 __in BOOL fCommit
48 );
49HRESULT 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 );
58HRESULT DAPI SqlSessionDatabaseExists(
59 __in IDBCreateSession* pidbSession,
60 __in_z LPCWSTR wzDatabase,
61 __out_opt BSTR* pbstrErrorDescription
62 );
63HRESULT 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 );
74HRESULT 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 );
81HRESULT 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 );
92HRESULT 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 );
99HRESULT 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 );
108HRESULT DAPI SqlSessionDropDatabase(
109 __in IDBCreateSession* pidbSession,
110 __in_z LPCWSTR wzDatabase,
111 __out_opt BSTR* pbstrErrorDescription
112 );
113HRESULT 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 );
120HRESULT DAPI SqlCommandExecuteQuery(
121 __in IDBCreateCommand* pidbCommand,
122 __in __sql_command LPCWSTR wzSql,
123 __out IRowset** ppirs,
124 __out DBROWCOUNT* pcRows
125 );
126HRESULT 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
diff --git a/src/dutil/inc/srputil.h b/src/dutil/inc/srputil.h
new file mode 100644
index 00000000..95e96231
--- /dev/null
+++ b/src/dutil/inc/srputil.h
@@ -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
6extern "C" {
7#endif
8
9
10typedef 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*******************************************************************/
23DAPI_(HRESULT) SrpInitialize(
24 __in BOOL fInitializeComSecurity
25 );
26
27/********************************************************************
28 SrpUninitialize - uninitializes system restore point functionality.
29
30*******************************************************************/
31DAPI_(void) SrpUninitialize();
32
33/********************************************************************
34 SrpCreateRestorePoint - creates a system restore point.
35
36*******************************************************************/
37DAPI_(HRESULT) SrpCreateRestorePoint(
38 __in_z LPCWSTR wzApplicationName,
39 __in SRP_ACTION action
40 );
41
42#ifdef __cplusplus
43}
44#endif
45
diff --git a/src/dutil/inc/strutil.h b/src/dutil/inc/strutil.h
new file mode 100644
index 00000000..1a2ed1d8
--- /dev/null
+++ b/src/dutil/inc/strutil.h
@@ -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
6extern "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
20HRESULT DAPI StrAlloc(
21 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
22 __in DWORD_PTR cch
23 );
24HRESULT DAPI StrAllocSecure(
25 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
26 __in DWORD_PTR cch
27 );
28HRESULT DAPI StrTrimCapacity(
29 __deref_out_z LPWSTR* ppwz
30 );
31HRESULT DAPI StrTrimWhitespace(
32 __deref_out_z LPWSTR* ppwz,
33 __in_z LPCWSTR wzSource
34 );
35HRESULT DAPI StrAnsiAlloc(
36 __deref_out_ecount_part(cch, 0) LPSTR* ppz,
37 __in DWORD_PTR cch
38 );
39HRESULT DAPI StrAnsiTrimCapacity(
40 __deref_out_z LPSTR* ppz
41 );
42HRESULT DAPI StrAnsiTrimWhitespace(
43 __deref_out_z LPSTR* ppz,
44 __in_z LPCSTR szSource
45 );
46HRESULT DAPI StrAllocString(
47 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
48 __in_z LPCWSTR wzSource,
49 __in DWORD_PTR cchSource
50 );
51HRESULT DAPI StrAllocStringSecure(
52 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
53 __in_z LPCWSTR wzSource,
54 __in DWORD_PTR cchSource
55 );
56HRESULT 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 );
62HRESULT 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 );
68HRESULT DAPI StrAnsiAllocStringAnsi(
69 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
70 __in_z LPCSTR szSource,
71 __in DWORD_PTR cchSource
72 );
73HRESULT DAPI StrAllocPrefix(
74 __deref_out_z LPWSTR* ppwz,
75 __in_z LPCWSTR wzPrefix,
76 __in DWORD_PTR cchPrefix
77 );
78HRESULT DAPI StrAllocConcat(
79 __deref_out_z LPWSTR* ppwz,
80 __in_z LPCWSTR wzSource,
81 __in DWORD_PTR cchSource
82 );
83HRESULT DAPI StrAllocConcatSecure(
84 __deref_out_z LPWSTR* ppwz,
85 __in_z LPCWSTR wzSource,
86 __in DWORD_PTR cchSource
87 );
88HRESULT DAPI StrAnsiAllocConcat(
89 __deref_out_z LPSTR* ppz,
90 __in_z LPCSTR pzSource,
91 __in DWORD_PTR cchSource
92 );
93HRESULT __cdecl StrAllocFormatted(
94 __deref_out_z LPWSTR* ppwz,
95 __in __format_string LPCWSTR wzFormat,
96 ...
97 );
98HRESULT __cdecl StrAllocConcatFormatted(
99 __deref_out_z LPWSTR* ppwz,
100 __in __format_string LPCWSTR wzFormat,
101 ...
102 );
103HRESULT __cdecl StrAllocFormattedSecure(
104 __deref_out_z LPWSTR* ppwz,
105 __in __format_string LPCWSTR wzFormat,
106 ...
107 );
108HRESULT __cdecl StrAnsiAllocFormatted(
109 __deref_out_z LPSTR* ppsz,
110 __in __format_string LPCSTR szFormat,
111 ...
112 );
113HRESULT DAPI StrAllocFormattedArgs(
114 __deref_out_z LPWSTR* ppwz,
115 __in __format_string LPCWSTR wzFormat,
116 __in va_list args
117 );
118HRESULT DAPI StrAllocFormattedArgsSecure(
119 __deref_out_z LPWSTR* ppwz,
120 __in __format_string LPCWSTR wzFormat,
121 __in va_list args
122 );
123HRESULT DAPI StrAnsiAllocFormattedArgs(
124 __deref_out_z LPSTR* ppsz,
125 __in __format_string LPCSTR szFormat,
126 __in va_list args
127 );
128HRESULT DAPI StrAllocFromError(
129 __inout LPWSTR *ppwzMessage,
130 __in HRESULT hrError,
131 __in_opt HMODULE hModule,
132 ...
133 );
134
135HRESULT DAPI StrMaxLength(
136 __in LPCVOID p,
137 __out DWORD_PTR* pcch
138 );
139HRESULT DAPI StrSize(
140 __in LPCVOID p,
141 __out DWORD_PTR* pcb
142 );
143
144HRESULT DAPI StrFree(
145 __in LPVOID p
146 );
147
148
149HRESULT DAPI StrReplaceStringAll(
150 __inout LPWSTR* ppwzOriginal,
151 __in_z LPCWSTR wzOldSubString,
152 __in_z LPCWSTR wzNewSubString
153 );
154HRESULT DAPI StrReplaceString(
155 __inout LPWSTR* ppwzOriginal,
156 __inout DWORD* pdwStartIndex,
157 __in_z LPCWSTR wzOldSubString,
158 __in_z LPCWSTR wzNewSubString
159 );
160
161HRESULT 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 );
167HRESULT 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 );
172HRESULT DAPI StrHexDecode(
173 __in_z LPCWSTR wzSource,
174 __out_bcount(cbDest) BYTE* pbDest,
175 __in DWORD_PTR cbDest
176 );
177HRESULT DAPI StrAllocHexDecode(
178 __in_z LPCWSTR wzSource,
179 __out_bcount(*pcbDest) BYTE** ppbDest,
180 __out_opt DWORD* pcbDest
181 );
182
183HRESULT DAPI StrAllocBase85Encode(
184 __in_bcount_opt(cbSource) const BYTE* pbSource,
185 __in DWORD_PTR cbSource,
186 __deref_out_z LPWSTR* pwzDest
187 );
188HRESULT DAPI StrAllocBase85Decode(
189 __in_z LPCWSTR wzSource,
190 __deref_out_bcount(*pcbDest) BYTE** hbDest,
191 __out DWORD_PTR* pcbDest
192 );
193
194HRESULT DAPI MultiSzLen(
195 __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz,
196 __out DWORD_PTR* pcch
197 );
198HRESULT DAPI MultiSzPrepend(
199 __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz,
200 __inout_opt DWORD_PTR *pcchMultiSz,
201 __in __nullnullterminated LPCWSTR pwzInsert
202 );
203HRESULT 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 );
209HRESULT 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 );
215HRESULT DAPI MultiSzRemoveString(
216 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
217 __in DWORD_PTR dwIndex
218 );
219HRESULT 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 );
225HRESULT DAPI MultiSzReplaceString(
226 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
227 __in DWORD_PTR dwIndex,
228 __in_z LPCWSTR pwzString
229 );
230
231LPCWSTR DAPI wcsistr(
232 __in_z LPCWSTR wzString,
233 __in_z LPCWSTR wzCharSet
234 );
235
236HRESULT DAPI StrStringToInt16(
237 __in_z LPCWSTR wzIn,
238 __in DWORD cchIn,
239 __out SHORT* psOut
240 );
241HRESULT DAPI StrStringToUInt16(
242 __in_z LPCWSTR wzIn,
243 __in DWORD cchIn,
244 __out USHORT* pusOut
245 );
246HRESULT DAPI StrStringToInt32(
247 __in_z LPCWSTR wzIn,
248 __in DWORD cchIn,
249 __out INT* piOut
250 );
251HRESULT DAPI StrStringToUInt32(
252 __in_z LPCWSTR wzIn,
253 __in DWORD cchIn,
254 __out UINT* puiOut
255 );
256HRESULT DAPI StrStringToInt64(
257 __in_z LPCWSTR wzIn,
258 __in DWORD cchIn,
259 __out LONGLONG* pllOut
260 );
261HRESULT DAPI StrStringToUInt64(
262 __in_z LPCWSTR wzIn,
263 __in DWORD cchIn,
264 __out ULONGLONG* pullOut
265 );
266void DAPI StrStringToUpper(
267 __inout_z LPWSTR wzIn
268 );
269void DAPI StrStringToLower(
270 __inout_z LPWSTR wzIn
271 );
272HRESULT DAPI StrAllocStringToUpperInvariant(
273 __deref_out_z LPWSTR* pscz,
274 __in_z LPCWSTR wzSource,
275 __in int cchSource
276 );
277HRESULT DAPI StrAllocStringToLowerInvariant(
278 __deref_out_z LPWSTR* pscz,
279 __in_z LPCWSTR wzSource,
280 __in int cchSource
281 );
282
283HRESULT 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
290HRESULT DAPI StrArrayFree(
291 __in_ecount(cStrArray) LPWSTR *rgsczStrArray,
292 __in UINT cStrArray
293 );
294
295HRESULT 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
302HRESULT DAPI StrSecureZeroString(
303 __in LPWSTR pwz
304 );
305HRESULT DAPI StrSecureZeroFreeString(
306 __in LPWSTR pwz
307 );
308
309#ifdef __cplusplus
310}
311#endif
diff --git a/src/dutil/inc/svcutil.h b/src/dutil/inc/svcutil.h
new file mode 100644
index 00000000..80d6326c
--- /dev/null
+++ b/src/dutil/inc/svcutil.h
@@ -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
6extern "C" {
7#endif
8
9
10#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; }
11
12
13HRESULT DAPI SvcQueryConfig(
14 __in SC_HANDLE sch,
15 __out QUERY_SERVICE_CONFIGW** ppConfig
16 );
17
18
19#ifdef __cplusplus
20}
21#endif
diff --git a/src/dutil/inc/thmutil.h b/src/dutil/inc/thmutil.h
new file mode 100644
index 00000000..5b3d4667
--- /dev/null
+++ b/src/dutil/inc/thmutil.h
@@ -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
6extern "C" {
7#endif
8
9#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; }
10
11typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)(
12 __in_z LPCWSTR wzCondition,
13 __out BOOL* pf,
14 __in_opt LPVOID pvContext
15 );
16typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)(
17 __in_z LPCWSTR wzFormat,
18 __inout LPWSTR* psczOut,
19 __in_opt LPVOID pvContext
20 );
21typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)(
22 __in_z LPCWSTR wzVariable,
23 __out LONGLONG* pllValue,
24 __in_opt LPVOID pvContext
25 );
26typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)(
27 __in_z LPCWSTR wzVariable,
28 __in LONGLONG llValue,
29 __in_opt LPVOID pvContext
30 );
31typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)(
32 __in_z LPCWSTR wzVariable,
33 __inout LPWSTR* psczValue,
34 __in_opt LPVOID pvContext
35 );
36typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)(
37 __in_z LPCWSTR wzVariable,
38 __in_z_opt LPCWSTR wzValue,
39 __in_opt LPVOID pvContext
40 );
41
42typedef 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
49typedef enum THEME_CONTROL_DATA
50{
51 THEME_CONTROL_DATA_HOVER = 1,
52} THEME_CONTROL_DATA;
53
54typedef 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
77typedef 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
85struct THEME_COLUMN
86{
87 LPWSTR pszName;
88 UINT uStringId;
89 int nBaseWidth;
90 int nWidth;
91 BOOL fExpands;
92};
93
94
95struct THEME_TAB
96{
97 LPWSTR pszName;
98 UINT uStringId;
99};
100
101struct 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
119struct 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).
127struct THEME_ASSIGN_CONTROL_ID
128{
129 WORD wId; // id to apply to control
130 LPCWSTR wzName; // name of control to match
131};
132
133const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned.
134
135struct 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
214struct THEME_IMAGELIST
215{
216 LPWSTR sczName;
217
218 HIMAGELIST hImageList;
219};
220
221struct THEME_SAVEDVARIABLE
222{
223 LPWSTR wzName;
224 LPWSTR sczValue;
225};
226
227struct THEME_PAGE
228{
229 WORD wId;
230 LPWSTR sczName;
231
232 DWORD cControlIndices;
233
234 DWORD cSavedVariables;
235 THEME_SAVEDVARIABLE* rgSavedVariables;
236};
237
238struct THEME_FONT
239{
240 HFONT hFont;
241 COLORREF crForeground;
242 HBRUSH hForeground;
243 COLORREF crBackground;
244 HBRUSH hBackground;
245};
246
247
248struct 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*******************************************************************/
302DAPI_(HRESULT) ThemeInitialize(
303 __in_opt HMODULE hModule
304 );
305
306/********************************************************************
307 ThemeUninitialize - uninitialize theme management.
308
309*******************************************************************/
310DAPI_(void) ThemeUninitialize();
311
312/********************************************************************
313 ThemeLoadFromFile - loads a theme from a loose file.
314
315 *******************************************************************/
316DAPI_(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*******************************************************************/
326DAPI_(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*******************************************************************/
336DAPI_(void) ThemeFree(
337 __in THEME* pTheme
338 );
339
340/********************************************************************
341ThemeRegisterVariableCallbacks - registers a context and callbacks
342 for working with variables.
343
344*******************************************************************/
345DAPI_(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*******************************************************************/
360DAPI_(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*******************************************************************/
372DAPI_(void) ThemeUnloadControls(
373 __in THEME* pTheme
374 );
375
376/********************************************************************
377 ThemeLocalize - Localizes all of the strings in the theme.
378
379*******************************************************************/
380DAPI_(HRESULT) ThemeLocalize(
381 __in THEME *pTheme,
382 __in const WIX_LOCALIZATION *pLocStringSet
383 );
384
385DAPI_(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 *******************************************************************/
394DAPI_(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 *******************************************************************/
405DAPI_(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 *******************************************************************/
417DAPI_(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*******************************************************************/
428DAPI_(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*******************************************************************/
438LRESULT 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*******************************************************************/
450DAPI_(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 *******************************************************************/
461DAPI_(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 *******************************************************************/
470DAPI_(HRESULT) ThemeShowPage(
471 __in THEME* pTheme,
472 __in DWORD dwPage,
473 __in int nCmdShow
474 );
475
476/********************************************************************
477ThemeShowPageEx - 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*******************************************************************/
484DAPI_(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/********************************************************************
493ThemeShowChild - shows a control's specified child control, hiding the rest.
494
495*******************************************************************/
496DAPI_(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 *******************************************************************/
506DAPI_(BOOL) ThemeControlExists(
507 __in const THEME* pTheme,
508 __in DWORD dwControl
509 );
510
511/********************************************************************
512 ThemeControlEnable - enables/disables a control.
513
514 *******************************************************************/
515DAPI_(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 *******************************************************************/
525DAPI_(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 *******************************************************************/
534DAPI_(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 *******************************************************************/
544DAPI_(void) ThemeShowControl(
545 __in THEME* pTheme,
546 __in DWORD dwControl,
547 __in int nCmdShow
548 );
549
550/********************************************************************
551ThemeShowControlEx - shows/hides a control with support for
552conditional text and notes.
553
554*******************************************************************/
555DAPI_(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 *******************************************************************/
565DAPI_(BOOL) ThemeControlVisible(
566 __in THEME* pTheme,
567 __in DWORD dwControl
568 );
569
570DAPI_(BOOL) ThemePostControlMessage(
571 __in THEME* pTheme,
572 __in DWORD dwControl,
573 __in UINT Msg,
574 __in WPARAM wParam,
575 __in LPARAM lParam
576 );
577
578DAPI_(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*******************************************************************/
590DAPI_(HRESULT) ThemeDrawBackground(
591 __in THEME* pTheme,
592 __in PAINTSTRUCT* pps
593 );
594
595/********************************************************************
596 ThemeDrawControl - draw an owner drawn control.
597
598*******************************************************************/
599DAPI_(HRESULT) ThemeDrawControl(
600 __in THEME* pTheme,
601 __in DRAWITEMSTRUCT* pdis
602 );
603
604/********************************************************************
605 ThemeHoverControl - mark a control as hover.
606
607*******************************************************************/
608DAPI_(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*******************************************************************/
619DAPI_(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*******************************************************************/
628DAPI_(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*******************************************************************/
640DAPI_(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*******************************************************************/
651DAPI_(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*******************************************************************/
661DAPI_(HRESULT) ThemeSetTextControl(
662 __in const THEME* pTheme,
663 __in DWORD dwControl,
664 __in_z_opt LPCWSTR wzText
665 );
666
667/********************************************************************
668ThemeSetTextControl - sets the text of a control and optionally
669 invalidates the control.
670
671*******************************************************************/
672DAPI_(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*******************************************************************/
683DAPI_(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*******************************************************************/
693DAPI_(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*******************************************************************/
703DAPI_(void) ThemeSetFocus(
704 __in THEME* pTheme,
705 __in DWORD dwControl
706 );
707
708#ifdef __cplusplus
709}
710#endif
711
diff --git a/src/dutil/inc/timeutil.h b/src/dutil/inc/timeutil.h
new file mode 100644
index 00000000..3655c00a
--- /dev/null
+++ b/src/dutil/inc/timeutil.h
@@ -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
6extern "C" {
7#endif
8
9HRESULT DAPI TimeFromString(
10 __in_z LPCWSTR wzTime,
11 __out FILETIME* pFileTime
12 );
13HRESULT DAPI TimeFromString3339(
14 __in_z LPCWSTR wzTime,
15 __out FILETIME* pFileTime
16 );
17HRESULT DAPI TimeCurrentTime(
18 __deref_out_z LPWSTR* ppwz,
19 __in BOOL fGMT
20 );
21HRESULT DAPI TimeCurrentDateTime(
22 __deref_out_z LPWSTR* ppwz,
23 __in BOOL fGMT
24 );
25HRESULT DAPI TimeSystemDateTime(
26 __deref_out_z LPWSTR* ppwz,
27 __in const SYSTEMTIME *pst,
28 __in BOOL fGMT
29 );
30HRESULT 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
diff --git a/src/dutil/inc/uncutil.h b/src/dutil/inc/uncutil.h
new file mode 100644
index 00000000..6516a801
--- /dev/null
+++ b/src/dutil/inc/uncutil.h
@@ -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
6extern "C" {
7#endif
8
9/*******************************************************************
10 UncConvertFromMountedDrive - Converts the string in-place from a
11 mounted drive path to a UNC path
12*******************************************************************/
13DAPI_(HRESULT) UncConvertFromMountedDrive(
14 __inout LPWSTR *psczUNCPath,
15 __in LPCWSTR sczMountedDrivePath
16 );
17
18#ifdef __cplusplus
19}
20#endif
diff --git a/src/dutil/inc/uriutil.h b/src/dutil/inc/uriutil.h
new file mode 100644
index 00000000..52e78308
--- /dev/null
+++ b/src/dutil/inc/uriutil.h
@@ -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
9extern "C" {
10#endif
11
12typedef 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
23typedef 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
35HRESULT DAPI UriCanonicalize(
36 __inout_z LPWSTR* psczUri
37 );
38
39HRESULT 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
50HRESULT DAPI UriCrackEx(
51 __in_z LPCWSTR wzUri,
52 __in URI_INFO* pUriInfo
53 );
54
55void DAPI UriInfoUninitialize(
56 __in URI_INFO* pUriInfo
57 );
58
59HRESULT 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
70HRESULT DAPI UriCanonicalize(
71 __inout_z LPWSTR* psczUri
72 );
73
74HRESULT DAPI UriFile(
75 __deref_out_z LPWSTR* psczFile,
76 __in_z LPCWSTR wzUri
77 );
78
79HRESULT DAPI UriProtocol(
80 __in_z LPCWSTR wzUri,
81 __out URI_PROTOCOL* pProtocol
82 );
83
84HRESULT DAPI UriRoot(
85 __in_z LPCWSTR wzUri,
86 __out LPWSTR* ppwzRoot,
87 __out_opt URI_PROTOCOL* pProtocol
88 );
89
90HRESULT 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
diff --git a/src/dutil/inc/userutil.h b/src/dutil/inc/userutil.h
new file mode 100644
index 00000000..2c86d229
--- /dev/null
+++ b/src/dutil/inc/userutil.h
@@ -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
6extern "C" {
7#endif
8
9HRESULT 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
16HRESULT 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
24HRESULT DAPI UserCreateADsPath(
25 __in_z LPCWSTR wzObjectDomain,
26 __in_z LPCWSTR wzObjectName,
27 __out BSTR *pbstrAdsPath
28 );
29
30#ifdef __cplusplus
31}
32#endif
diff --git a/src/dutil/inc/varutil.h b/src/dutil/inc/varutil.h
new file mode 100644
index 00000000..86d0aca0
--- /dev/null
+++ b/src/dutil/inc/varutil.h
@@ -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)
6extern "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
14typedef void* VARIABLE_ENUM_HANDLE;
15typedef void* VARIABLES_HANDLE;
16typedef const void* C_VARIABLES_HANDLE;
17
18extern const int VARIABLE_ENUM_HANDLE_BYTES;
19extern const int VARIABLES_HANDLE_BYTES;
20
21typedef void(*PFN_FREEVARIABLECONTEXT)(
22 __in LPVOID pvContext
23 );
24
25typedef 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
33typedef 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
46HRESULT DAPI VarCreate(
47 __out_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE* ppVariables
48 );
49void DAPI VarDestroy(
50 __in_bcount(VARIABLES_HANDLE_BYTES) VARIABLES_HANDLE pVariables,
51 __in_opt PFN_FREEVARIABLECONTEXT vpfFreeVariableContext
52 );
53void DAPI VarFreeValue(
54 __in VARIABLE_VALUE* pValue
55 );
56HRESULT DAPI VarEscapeString(
57 __in_z LPCWSTR wzIn,
58 __out_z LPWSTR* psczOut
59 );
60HRESULT 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 );
66HRESULT DAPI VarGetFormatted(
67 __in C_VARIABLES_HANDLE pVariables,
68 __in_z LPCWSTR wzVariable,
69 __out_z LPWSTR* psczValue
70 );
71HRESULT DAPI VarGetNumeric(
72 __in C_VARIABLES_HANDLE pVariables,
73 __in_z LPCWSTR wzVariable,
74 __out LONGLONG* pllValue
75 );
76HRESULT DAPI VarGetString(
77 __in C_VARIABLES_HANDLE pVariables,
78 __in_z LPCWSTR wzVariable,
79 __out_z LPWSTR* psczValue
80 );
81HRESULT DAPI VarGetVersion(
82 __in C_VARIABLES_HANDLE pVariables,
83 __in_z LPCWSTR wzVariable,
84 __in DWORD64* pqwValue
85 );
86HRESULT DAPI VarGetValue(
87 __in C_VARIABLES_HANDLE pVariables,
88 __in_z LPCWSTR wzVariable,
89 __out VARIABLE_VALUE** ppValue
90 );
91HRESULT DAPI VarSetNumeric(
92 __in VARIABLES_HANDLE pVariables,
93 __in_z LPCWSTR wzVariable,
94 __in LONGLONG llValue
95 );
96HRESULT DAPI VarSetString(
97 __in VARIABLES_HANDLE pVariables,
98 __in_z LPCWSTR wzVariable,
99 __in_z_opt LPCWSTR wzValue
100 );
101HRESULT DAPI VarSetVersion(
102 __in VARIABLES_HANDLE pVariables,
103 __in_z LPCWSTR wzVariable,
104 __in DWORD64 qwValue
105 );
106HRESULT DAPI VarSetValue(
107 __in VARIABLES_HANDLE pVariables,
108 __in_z LPCWSTR wzVariable,
109 __in VARIABLE_VALUE* pValue
110 );
111HRESULT DAPI VarStartEnum(
112 __in VARIABLES_HANDLE pVariables,
113 __out_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE* ppEnum,
114 __out VARIABLE_VALUE** ppValue
115 );
116HRESULT DAPI VarNextVariable(
117 __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum,
118 __out VARIABLE_VALUE** ppValue
119 );
120void DAPI VarFinishEnum(
121 __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum
122 );
123
124#if defined(__cplusplus)
125}
126#endif
diff --git a/src/dutil/inc/wiutil.h b/src/dutil/inc/wiutil.h
new file mode 100644
index 00000000..4264b815
--- /dev/null
+++ b/src/dutil/inc/wiutil.h
@@ -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
6extern "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
26typedef enum WIU_RESTART
27{
28 WIU_RESTART_NONE,
29 WIU_RESTART_REQUIRED,
30 WIU_RESTART_INITIATED,
31} WIU_RESTART;
32
33typedef 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
45typedef 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
79typedef 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
90typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)(
91 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
92 __in_opt LPVOID pvContext
93 );
94
95typedef 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
114typedef UINT (WINAPI *PFN_MSIENABLELOGW)(
115 __in DWORD dwLogMode,
116 __in_z LPCWSTR szLogFile,
117 __in DWORD dwLogAttributes
118 );
119typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)(
120 __in LPCWSTR szProductCode,
121 __in LPCWSTR szProperty,
122 __out_ecount_opt(*pcchValue) LPWSTR szValue,
123 __inout LPDWORD pcchValue
124 );
125typedef 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 );
131typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)(
132 __in LPCWSTR szComponent,
133 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
134 __inout_opt LPDWORD pcchBuf
135 );
136typedef 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 );
144typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)(
145 __in LPCWSTR szProduct,
146 __in LPCWSTR szFeature
147 );
148typedef 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 );
157typedef 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 );
164typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)(
165 __in_z LPCWSTR wzProductPackagePath,
166 __in DWORD cPatchInfo,
167 __in PMSIPATCHSEQUENCEINFOW pPatchInfo
168 );
169typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)(
170 __in LPCWSTR szPackagePath,
171 __in_opt LPCWSTR szCommandLine
172 );
173typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)(
174 __in LPCWSTR szProduct,
175 __in int iInstallLevel,
176 __in INSTALLSTATE eInstallState,
177 __in_opt LPCWSTR szCommandLine
178 );
179typedef 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 );
185typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)(
186 __in INSTALLUILEVEL dwUILevel,
187 __inout_opt HWND *phWnd
188 );
189typedef 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 );
195typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)(
196 __in_opt INSTALLUI_HANDLERW puiHandler,
197 __in DWORD dwMessageFilter,
198 __in_opt LPVOID pvContext
199 );
200typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)(
201 __in DWORD iProductIndex,
202 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf
203 );
204typedef 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
215typedef 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 );
221typedef 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
231HRESULT DAPI WiuInitialize(
232 );
233void DAPI WiuUninitialize(
234 );
235void 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 );
250HRESULT DAPI WiuGetComponentPath(
251 __in_z LPCWSTR wzProductCode,
252 __in_z LPCWSTR wzComponentId,
253 __out INSTALLSTATE* pInstallState,
254 __out_z LPWSTR* psczValue
255 );
256HRESULT DAPI WiuLocateComponent(
257 __in_z LPCWSTR wzComponentId,
258 __out INSTALLSTATE* pInstallState,
259 __out_z LPWSTR* psczValue
260 );
261HRESULT DAPI WiuQueryFeatureState(
262 __in_z LPCWSTR wzProduct,
263 __in_z LPCWSTR wzFeature,
264 __out INSTALLSTATE* pInstallState
265 );
266HRESULT DAPI WiuGetProductInfo(
267 __in_z LPCWSTR wzProductCode,
268 __in_z LPCWSTR wzProperty,
269 __out LPWSTR* psczValue
270 );
271HRESULT 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 );
278HRESULT DAPI WiuGetProductProperty(
279 __in MSIHANDLE hProduct,
280 __in_z LPCWSTR wzProperty,
281 __out LPWSTR* psczValue
282 );
283HRESULT 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 );
291HRESULT 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 );
298HRESULT DAPI WiuDetermineApplicablePatches(
299 __in_z LPCWSTR wzProductPackagePath,
300 __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
301 __in DWORD cPatchInfo
302 );
303HRESULT DAPI WiuEnumProducts(
304 __in DWORD iProductIndex,
305 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
306 );
307HRESULT 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 );
317HRESULT DAPI WiuEnumRelatedProducts(
318 __in_z LPCWSTR wzUpgradeCode,
319 __in DWORD iProductIndex,
320 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
321 );
322HRESULT DAPI WiuEnumRelatedProductCodes(
323 __in_z LPCWSTR wzUpgradeCode,
324 __deref_out_ecount_opt(pcRelatedProducts) LPWSTR** prgsczProductCodes,
325 __out DWORD* pcRelatedProducts,
326 __in BOOL fReturnHighestVersionOnly
327 );
328HRESULT DAPI WiuEnableLog(
329 __in DWORD dwLogMode,
330 __in_z LPCWSTR wzLogFile,
331 __in DWORD dwLogAttributes
332 );
333HRESULT 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 );
341void DAPI WiuUninitializeExternalUI(
342 __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
343 );
344HRESULT 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 );
351HRESULT DAPI WiuInstallProduct(
352 __in_z LPCWSTR wzPackagPath,
353 __in_z LPCWSTR wzCommandLine,
354 __out WIU_RESTART* pRestart
355 );
356HRESULT DAPI WiuRemovePatches(
357 __in_z LPCWSTR wzPatchList,
358 __in_z LPCWSTR wzProductCode,
359 __in_z LPCWSTR wzPropertyList,
360 __out WIU_RESTART* pRestart
361 );
362HRESULT 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
diff --git a/src/dutil/inc/wuautil.h b/src/dutil/inc/wuautil.h
new file mode 100644
index 00000000..b239c4e6
--- /dev/null
+++ b/src/dutil/inc/wuautil.h
@@ -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)
6extern "C" {
7#endif
8
9HRESULT DAPI WuaPauseAutomaticUpdates();
10
11HRESULT DAPI WuaResumeAutomaticUpdates();
12
13HRESULT DAPI WuaRestartRequired(
14 __out BOOL* pfRestartRequired
15 );
16
17#if defined(__cplusplus)
18}
19#endif
diff --git a/src/dutil/inc/xmlutil.h b/src/dutil/inc/xmlutil.h
new file mode 100644
index 00000000..3dc119bd
--- /dev/null
+++ b/src/dutil/inc/xmlutil.h
@@ -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
5extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
6extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}};
7extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}};
8extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}};
9extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
10extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
11extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
12extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
13
14extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
15extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
16extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}};
17
18typedef enum XML_LOAD_ATTRIBUTE
19{
20 XML_LOAD_PRESERVE_WHITESPACE = 1,
21} XML_LOAD_ATTRIBUTE;
22
23
24#ifdef __cplusplus
25extern "C" {
26#endif
27
28HRESULT DAPI XmlInitialize();
29void DAPI XmlUninitialize();
30
31HRESULT DAPI XmlCreateElement(
32 __in IXMLDOMDocument *pixdDocument,
33 __in_z LPCWSTR wzElementName,
34 __out IXMLDOMElement **ppixnElement
35 );
36HRESULT DAPI XmlCreateDocument(
37 __in_opt LPCWSTR pwzElementName,
38 __out IXMLDOMDocument** ppixdDocument,
39 __out_opt IXMLDOMElement** ppixeRootElement = NULL
40 );
41HRESULT DAPI XmlLoadDocument(
42 __in_z LPCWSTR wzDocument,
43 __out IXMLDOMDocument** ppixdDocument
44 );
45HRESULT DAPI XmlLoadDocumentEx(
46 __in_z LPCWSTR wzDocument,
47 __in DWORD dwAttributes,
48 __out IXMLDOMDocument** ppixdDocument
49 );
50HRESULT DAPI XmlLoadDocumentFromFile(
51 __in_z LPCWSTR wzPath,
52 __out IXMLDOMDocument** ppixdDocument
53 );
54HRESULT DAPI XmlLoadDocumentFromBuffer(
55 __in_bcount(cbSource) const BYTE* pbSource,
56 __in DWORD cbSource,
57 __out IXMLDOMDocument** ppixdDocument
58 );
59HRESULT DAPI XmlLoadDocumentFromFileEx(
60 __in_z LPCWSTR wzPath,
61 __in DWORD dwAttributes,
62 __out IXMLDOMDocument** ppixdDocument
63 );
64HRESULT DAPI XmlSelectSingleNode(
65 __in IXMLDOMNode* pixnParent,
66 __in_z LPCWSTR wzXPath,
67 __out IXMLDOMNode **ppixnChild
68 );
69HRESULT DAPI XmlSetAttribute(
70 __in IXMLDOMNode* pixnNode,
71 __in_z LPCWSTR pwzAttribute,
72 __in_z LPCWSTR pwzAttributeValue
73 );
74HRESULT DAPI XmlCreateTextNode(
75 __in IXMLDOMDocument *pixdDocument,
76 __in_z LPCWSTR wzText,
77 __out IXMLDOMText **ppixnTextNode
78 );
79HRESULT DAPI XmlGetText(
80 __in IXMLDOMNode* pixnNode,
81 __deref_out_z BSTR* pbstrText
82 );
83HRESULT DAPI XmlGetAttribute(
84 __in IXMLDOMNode* pixnNode,
85 __in_z LPCWSTR pwzAttribute,
86 __deref_out_z BSTR* pbstrAttributeValue
87 );
88HRESULT DAPI XmlGetAttributeEx(
89 __in IXMLDOMNode* pixnNode,
90 __in_z LPCWSTR wzAttribute,
91 __deref_out_z LPWSTR* psczAttributeValue
92 );
93HRESULT DAPI XmlGetYesNoAttribute(
94 __in IXMLDOMNode* pixnNode,
95 __in_z LPCWSTR wzAttribute,
96 __out BOOL* pfYes
97 );
98HRESULT DAPI XmlGetAttributeNumber(
99 __in IXMLDOMNode* pixnNode,
100 __in_z LPCWSTR pwzAttribute,
101 __out DWORD* pdwValue
102 );
103HRESULT DAPI XmlGetAttributeNumberBase(
104 __in IXMLDOMNode* pixnNode,
105 __in_z LPCWSTR pwzAttribute,
106 __in int nBase,
107 __out DWORD* pdwValue
108 );
109HRESULT DAPI XmlGetAttributeLargeNumber(
110 __in IXMLDOMNode* pixnNode,
111 __in_z LPCWSTR pwzAttribute,
112 __out DWORD64* pdw64Value
113 );
114HRESULT DAPI XmlGetNamedItem(
115 __in IXMLDOMNamedNodeMap *pixnmAttributes,
116 __in_opt LPCWSTR wzName,
117 __out IXMLDOMNode **ppixnNamedItem
118 );
119HRESULT DAPI XmlSetText(
120 __in IXMLDOMNode* pixnNode,
121 __in_z LPCWSTR pwzText
122 );
123HRESULT DAPI XmlSetTextNumber(
124 __in IXMLDOMNode *pixnNode,
125 __in DWORD dwValue
126 );
127HRESULT DAPI XmlCreateChild(
128 __in IXMLDOMNode* pixnParent,
129 __in_z LPCWSTR pwzElementType,
130 __out IXMLDOMNode** ppixnChild
131 );
132HRESULT DAPI XmlRemoveAttribute(
133 __in IXMLDOMNode* pixnNode,
134 __in_z LPCWSTR pwzAttribute
135 );
136HRESULT DAPI XmlSelectNodes(
137 __in IXMLDOMNode* pixnParent,
138 __in_z LPCWSTR wzXPath,
139 __out IXMLDOMNodeList **ppixnChild
140 );
141HRESULT DAPI XmlNextAttribute(
142 __in IXMLDOMNamedNodeMap* pixnnm,
143 __out IXMLDOMNode** pixnAttribute,
144 __deref_opt_out_z_opt BSTR* pbstrAttribute
145 );
146HRESULT DAPI XmlNextElement(
147 __in IXMLDOMNodeList* pixnl,
148 __out IXMLDOMNode** pixnElement,
149 __deref_opt_out_z_opt BSTR* pbstrElement
150 );
151HRESULT DAPI XmlRemoveChildren(
152 __in IXMLDOMNode* pixnSource,
153 __in_z LPCWSTR pwzXPath
154 );
155HRESULT DAPI XmlSaveDocument(
156 __in IXMLDOMDocument* pixdDocument,
157 __inout LPCWSTR wzPath
158 );
159HRESULT 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
diff --git a/src/dutil/inetutil.cpp b/src/dutil/inetutil.cpp
new file mode 100644
index 00000000..69a0176a
--- /dev/null
+++ b/src/dutil/inetutil.cpp
@@ -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*******************************************************************/
10extern "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;
28LExit:
29 return hr;
30}
31
32
33/*******************************************************************
34 InetGetCreateTimeByHandle - returns url creation time
35
36******************************************************************/
37extern "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
58LExit:
59 return hr;
60}
61
62
63/*******************************************************************
64 InternetQueryInfoString - query info string
65
66*******************************************************************/
67extern "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
111LExit:
112 return hr;
113}
114
115
116/*******************************************************************
117 InternetQueryInfoNumber - query info number
118
119*******************************************************************/
120extern "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
135LExit:
136 return hr;
137}
diff --git a/src/dutil/iniutil.cpp b/src/dutil/iniutil.cpp
new file mode 100644
index 00000000..c9ef6c3d
--- /dev/null
+++ b/src/dutil/iniutil.cpp
@@ -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
5const LPCWSTR wzSectionSeparator = L"\\";
6
7struct 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
32const int INI_HANDLE_BYTES = sizeof(INI_STRUCT);
33
34static HRESULT GetSectionPrefixFromName(
35 __in_z LPCWSTR wzName,
36 __deref_out_z LPWSTR* psczOutput
37 );
38static void UninitializeIniValue(
39 INI_VALUE *pivValue
40 );
41
42extern "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
52LExit:
53 return hr;
54}
55
56extern "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
86extern "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
116LExit:
117 return hr;
118}
119
120extern "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
150LExit:
151 return hr;
152}
153
154extern "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
172LExit:
173 return hr;
174}
175
176extern "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
195LExit:
196 return hr;
197}
198
199extern "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
387LExit:
388 ReleaseStr(sczCurrentSection);
389 ReleaseStr(sczContents);
390 ReleaseStr(sczName);
391 ReleaseStr(sczNameTrimmed);
392 ReleaseStr(sczValue);
393 ReleaseStr(sczValueTrimmed);
394
395 return hr;
396}
397
398extern "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
414extern "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
448LExit:
449 return hr;
450}
451
452extern "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
565LExit:
566 ReleaseStr(sczName);
567 ReleaseStr(sczValue);
568
569 return hr;
570}
571
572extern "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
718LExit:
719 ReleaseStr(sczContents);
720 ReleaseStr(sczCurrentSectionPrefix);
721 ReleaseStr(sczNewSectionPrefix);
722
723 return hr;
724}
725
726static void UninitializeIniValue(
727 INI_VALUE *pivValue
728 )
729{
730 ReleaseStr(const_cast<LPWSTR>(pivValue->wzName));
731 ReleaseStr(const_cast<LPWSTR>(pivValue->wzValue));
732}
733
734static 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
751LExit:
752 return hr;
753}
diff --git a/src/dutil/jsonutil.cpp b/src/dutil/jsonutil.cpp
new file mode 100644
index 00000000..ba088705
--- /dev/null
+++ b/src/dutil/jsonutil.cpp
@@ -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
5const DWORD JSON_STACK_INCREMENT = 5;
6
7// Prototypes
8static HRESULT DoStart(
9 __in JSON_WRITER* pWriter,
10 __in JSON_TOKEN tokenStart,
11 __in_z LPCWSTR wzStartString
12 );
13static HRESULT DoEnd(
14 __in JSON_WRITER* pWriter,
15 __in JSON_TOKEN tokenEnd,
16 __in_z LPCWSTR wzEndString
17 );
18static HRESULT DoKey(
19 __in JSON_WRITER* pWriter,
20 __in_z LPCWSTR wzKey
21 );
22static HRESULT DoValue(
23 __in JSON_WRITER* pWriter,
24 __in_z_opt LPCWSTR wzValue
25 );
26static HRESULT EnsureTokenStack(
27 __in JSON_WRITER* pWriter
28 );
29static HRESULT SerializeJsonString(
30 __out_z LPWSTR* psczJsonString,
31 __in_z LPCWSTR wzString
32 );
33
34
35
36DAPI_(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
51LExit:
52 return hr;
53}
54
55
56DAPI_(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
67static 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
139DAPI_(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
167LExit:
168 ::LeaveCriticalSection(&pReader->cs);
169 return hr;
170}
171
172
173DAPI_(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
185DAPI_(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
196DAPI_(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
208DAPI_(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
222LExit:
223 ReleaseStr(sczValue);
224 return hr;
225}
226
227
228DAPI_(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
242LExit:
243 ReleaseStr(sczValue);
244 return hr;
245}
246
247
248DAPI_(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
262LExit:
263 ReleaseStr(sczJsonString);
264 return hr;
265}
266
267
268DAPI_(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
277LExit:
278 return hr;
279}
280
281
282DAPI_(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
291LExit:
292 return hr;
293}
294
295
296DAPI_(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
305LExit:
306 return hr;
307}
308
309
310DAPI_(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
324LExit:
325 ReleaseStr(sczObjectKey);
326 return hr;
327}
328
329
330DAPI_(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
339LExit:
340 return hr;
341}
342
343
344static 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
402LExit:
403 ::LeaveCriticalSection(&pWriter->cs);
404 return hr;
405}
406
407
408static 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
439LExit:
440 ::LeaveCriticalSection(&pWriter->cs);
441 return hr;
442}
443
444
445static 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
488LExit:
489 ::LeaveCriticalSection(&pWriter->cs);
490 return hr;
491}
492
493
494static 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
552LExit:
553 ::LeaveCriticalSection(&pWriter->cs);
554 return hr;
555}
556
557
558static 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
574LExit:
575 return hr;
576}
577
578
579static 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
670LExit:
671 return hr;
672}
diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp
new file mode 100644
index 00000000..b3cc042c
--- /dev/null
+++ b/src/dutil/locutil.cpp
@@ -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
6static HRESULT ParseWxl(
7 __in IXMLDOMDocument* pixd,
8 __out WIX_LOCALIZATION** ppWixLoc
9 );
10static HRESULT ParseWxlStrings(
11 __in IXMLDOMElement* pElement,
12 __in WIX_LOCALIZATION* pWixLoc
13 );
14static HRESULT ParseWxlControls(
15 __in IXMLDOMElement* pElement,
16 __in WIX_LOCALIZATION* pWixLoc
17 );
18static HRESULT ParseWxlString(
19 __in IXMLDOMNode* pixn,
20 __in DWORD dwIdx,
21 __in WIX_LOCALIZATION* pWixLoc
22 );
23static 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
39typedef 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
46extern "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
184LExit:
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
197extern "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
211LExit:
212 ReleaseObject(pixd);
213
214 return hr;
215}
216
217extern "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
241LExit:
242 ReleaseObject(pixd);
243 ReleaseStr(sczXml);
244
245 return hr;
246}
247
248extern "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
272extern "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
286LExit:
287 return hr;
288}
289
290extern "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
312LExit:
313 return hr;
314}
315
316extern "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
340extern "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
363LExit:
364 return hr;
365}
366
367// helper functions
368
369static 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
404LExit:
405 ReleaseObject(pWxlElement);
406 ReleaseMem(pWixLoc);
407
408 return hr;
409}
410
411
412static 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
446LExit:
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
464static 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
498LExit:
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
516static 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
555LExit:
556 ReleaseBSTR(bstrText);
557
558 return hr;
559}
560
561static 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
609LExit:
610 ReleaseBSTR(bstrText);
611
612 return hr;
613}
diff --git a/src/dutil/logutil.cpp b/src/dutil/logutil.cpp
new file mode 100644
index 00000000..c471e002
--- /dev/null
+++ b/src/dutil/logutil.cpp
@@ -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
6static HMODULE LogUtil_hModule = NULL;
7static BOOL LogUtil_fDisabled = FALSE;
8static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE;
9static LPWSTR LogUtil_sczLogPath = NULL;
10static LPSTR LogUtil_sczPreInitBuffer = NULL;
11static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD;
12static CRITICAL_SECTION LogUtil_csLog = { };
13static BOOL LogUtil_fInitializedCriticalSection = FALSE;
14
15// Customization of certain parts of the string, within a line
16static LPWSTR LogUtil_sczSpecialBeginLine = NULL;
17static LPWSTR LogUtil_sczSpecialEndLine = NULL;
18static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL;
19
20static LPCSTR LOGUTIL_UNKNOWN = "unknown";
21static LPCSTR LOGUTIL_WARNING = "warning";
22static LPCSTR LOGUTIL_STANDARD = "standard";
23static LPCSTR LOGUTIL_VERBOSE = "verbose";
24static LPCSTR LOGUTIL_DEBUG = "debug";
25static LPCSTR LOGUTIL_NONE = "none";
26
27// prototypes
28static 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 );
35static 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 );
41static 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
49static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL;
50static LPVOID s_vpvLogStringWorkRawContext = NULL;
51
52
53/********************************************************************
54 IsLogInitialized - Checks if log is currently initialized.
55********************************************************************/
56extern "C" BOOL DAPI IsLogInitialized()
57{
58 return LogUtil_fInitializedCriticalSection;
59}
60
61/********************************************************************
62 IsLogOpen - Checks if log is currently initialized and open.
63********************************************************************/
64extern "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********************************************************************/
74extern "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********************************************************************/
93extern "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
158LExit:
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********************************************************************/
174void 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********************************************************************/
192void 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********************************************************************/
207HRESULT 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
234LExit:
235 if (fEnteredCriticalSection)
236 {
237 ::LeaveCriticalSection(&LogUtil_csLog);
238 }
239
240 return hr;
241}
242
243
244extern "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
259extern "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********************************************************************/
284extern "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********************************************************************/
294HRESULT 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
335LExit:
336 return hr;
337}
338
339/********************************************************************
340 LogSetLevel - sets the logging level
341
342 NOTE: returns previous logging level
343********************************************************************/
344extern "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********************************************************************/
391extern "C" REPORT_LEVEL DAPI LogGetLevel()
392{
393 return LogUtil_rlCurrent;
394}
395
396
397/********************************************************************
398 LogGetPath - gets the current log path
399
400********************************************************************/
401extern "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
417LExit:
418 return hr;
419}
420
421
422/********************************************************************
423 LogGetHandle - gets the current log file handle
424
425********************************************************************/
426extern "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********************************************************************/
437extern "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
453extern "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
469LExit:
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********************************************************************/
478extern "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
494extern "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
510LExit:
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
520extern "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
537LExit:
538 return hr;
539}
540
541extern "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
561LExit:
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********************************************************************/
573extern "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
589extern "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
611LExit:
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********************************************************************/
625extern "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
648LExit:
649 return hr;
650}
651
652/********************************************************************
653 LogHeader - write a standard header to the log
654
655********************************************************************/
656extern "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
728static 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
743extern "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********************************************************************/
757extern "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
790LExit:
791 return hr;
792}
793
794//
795// private worker functions
796//
797static 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
829LExit:
830 if (pwz)
831 {
832 ::LocalFree(pwz);
833 }
834
835 return hr;
836}
837
838
839static 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
862LExit:
863 ReleaseStr(sczFormat);
864 ReleaseStr(sczMessage);
865
866 return hr;
867}
868
869
870static 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
932LExit:
933 if (fEnteredCriticalSection)
934 {
935 ::LeaveCriticalSection(&LogUtil_csLog);
936 }
937
938 ReleaseStr(scz);
939 ReleaseStr(sczMultiByte);
940
941 return hr;
942}
diff --git a/src/dutil/memutil.cpp b/src/dutil/memutil.cpp
new file mode 100644
index 00000000..578c65ee
--- /dev/null
+++ b/src/dutil/memutil.cpp
@@ -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
9static BOOL vfMemInitialized = FALSE;
10#endif
11
12extern "C" HRESULT DAPI MemInitialize()
13{
14#if DEBUG
15 vfMemInitialized = TRUE;
16#endif
17 return S_OK;
18}
19
20extern "C" void DAPI MemUninitialize()
21{
22#if DEBUG
23 vfMemInitialized = FALSE;
24#endif
25}
26
27extern "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
38extern "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
50extern "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
102LExit:
103 ReleaseMem(pvNew);
104
105 return hr;
106}
107
108
109extern "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
119extern "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
156LExit:
157 return hr;
158}
159
160
161extern "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
199LExit:
200 return hr;
201}
202
203
204extern "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
234LExit:
235 return hr;
236}
237
238extern "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
263extern "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
308extern "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
317extern "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}
diff --git a/src/dutil/metautil.cpp b/src/dutil/metautil.cpp
new file mode 100644
index 00000000..612b1127
--- /dev/null
+++ b/src/dutil/metautil.cpp
@@ -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
13static 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********************************************************************/
23extern "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
128LExit:
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********************************************************************/
143extern "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);
217LExit:
218 MetaFreeValue(&mr);
219 return hr;
220}
221
222
223/********************************************************************
224 MetaOpenKey - open key
225
226********************************************************************/
227extern "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********************************************************************/
257extern "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
301LExit:
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********************************************************************/
317extern "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********************************************************************/
335static 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}
diff --git a/src/dutil/monutil.cpp b/src/dutil/monutil.cpp
new file mode 100644
index 00000000..6f280538
--- /dev/null
+++ b/src/dutil/monutil.cpp
@@ -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
5const int MON_THREAD_GROWTH = 5;
6const int MON_ARRAY_GROWTH = 40;
7const int MON_MAX_MONITORS_PER_THREAD = 63;
8const int MON_THREAD_INIT_RETRIES = 1000;
9const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10;
10const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute
11const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently
12const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000;
13const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass";
14
15enum 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
35enum MON_TYPE
36{
37 MON_NONE = 0,
38 MON_DIRECTORY = 1,
39 MON_REGKEY = 2
40};
41
42struct 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
83struct MON_ADD_MESSAGE
84{
85 MON_REQUEST request;
86 HANDLE handle;
87};
88
89struct 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
109struct 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
143struct 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
162struct 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
171struct 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
197const int MON_HANDLE_BYTES = sizeof(MON_STRUCT);
198
199static 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
205static HRESULT InitiateWait(
206 __inout MON_REQUEST *pRequest,
207 __inout HANDLE *pHandle
208 );
209static DWORD WINAPI WaiterThread(
210 __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext
211 );
212static void Notify(
213 __in HRESULT hr,
214 __in MON_WAITER_CONTEXT *pWaiterContext,
215 __in MON_REQUEST *pRequest
216 );
217static void MonRequestDestroy(
218 __in MON_REQUEST *pRequest
219 );
220static void MonAddMessageDestroy(
221 __in MON_ADD_MESSAGE *pMessage
222 );
223static void MonRemoveMessageDestroy(
224 __in MON_REMOVE_MESSAGE *pMessage
225 );
226static BOOL GetRecursiveFlag(
227 __in MON_REQUEST *pRequest,
228 __in DWORD dwIndex
229 );
230static HRESULT FindRequestIndex(
231 __in MON_WAITER_CONTEXT *pWaiterContext,
232 __in MON_REMOVE_MESSAGE *pMessage,
233 __out DWORD *pdwIndex
234 );
235static HRESULT RemoveRequest(
236 __inout MON_WAITER_CONTEXT *pWaiterContext,
237 __in DWORD dwRequestIndex
238 );
239static REGSAM GetRegKeyBitness(
240 __in MON_REQUEST *pRequest
241 );
242static HRESULT DuplicateRemoveMessage(
243 __in MON_REMOVE_MESSAGE *pMessage,
244 __out MON_REMOVE_MESSAGE **ppMessage
245 );
246static LRESULT CALLBACK MonWndProc(
247 __in HWND hWnd,
248 __in UINT uMsg,
249 __in WPARAM wParam,
250 __in LPARAM lParam
251 );
252static 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
257static HRESULT WaitForNetworkChanges(
258 __inout HANDLE *phMonitor,
259 __in MON_STRUCT *pm
260 );
261static HRESULT UpdateWaitStatus(
262 __in HRESULT hrNewStatus,
263 __inout MON_WAITER_CONTEXT *pWaiterContext,
264 __in DWORD dwRequestIndex,
265 __out DWORD *pdwNewRequestIndex
266 );
267
268extern "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
313LExit:
314 return hr;
315}
316
317extern "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
384LExit:
385 ReleaseStr(sczDirectory);
386 ReleaseStr(sczOriginalPathRequest);
387 MonAddMessageDestroy(pMessage);
388
389 return hr;
390}
391
392extern "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
442LExit:
443 ReleaseStr(sczSubKey);
444 MonAddMessageDestroy(pMessage);
445
446 return hr;
447}
448
449extern "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
481LExit:
482 MonRemoveMessageDestroy(pMessage);
483
484 return hr;
485}
486
487extern "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
523LExit:
524 ReleaseStr(sczSubKey);
525 MonRemoveMessageDestroy(pMessage);
526
527 return hr;
528}
529
530extern "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
555LExit:
556 return;
557}
558
559static 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
579static 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
599static 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
621static 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
886LExit:
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
955static 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
1077LExit:
1078 ReleaseRegKey(hk);
1079
1080 return hr;
1081}
1082
1083static 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
1486LExit:
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
1527static 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
1557static 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
1572static 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
1608LExit:
1609 return hr;
1610}
1611
1612static 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
1657LExit:
1658 return hr;
1659}
1660
1661static 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
1679static 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
1709LExit:
1710 return hr;
1711}
1712
1713static 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
1843LExit:
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
1859static 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
1885LExit:
1886 return hr;
1887}
1888
1889static 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
1929LExit:
1930 return hr;
1931}
1932
1933static 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
2002LExit:
2003 return hr;
2004}
diff --git a/src/dutil/osutil.cpp b/src/dutil/osutil.cpp
new file mode 100644
index 00000000..d1a4dd9a
--- /dev/null
+++ b/src/dutil/osutil.cpp
@@ -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
5OS_VERSION vOsVersion = OS_VERSION_UNKNOWN;
6DWORD vdwOsServicePack = 0;
7
8/********************************************************************
9 OsGetVersion
10
11********************************************************************/
12extern "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
73extern "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
106extern "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
147LExit:
148 ReleaseSid(AdministratorsGroup);
149
150 if (hToken)
151 {
152 ::CloseHandle(hToken);
153 }
154
155 return hr;
156}
157
158extern "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
184LExit:
185 ReleaseRegKey(hk);
186
187 return hr;
188}
diff --git a/src/dutil/path2utl.cpp b/src/dutil/path2utl.cpp
new file mode 100644
index 00000000..8f5f03a1
--- /dev/null
+++ b/src/dutil/path2utl.cpp
@@ -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
6DAPI_(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
26LExit:
27 return hr;
28}
29
30DAPI_(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
85LExit:
86 ReleaseStr(sczOriginalPath);
87 ReleaseStr(sczOriginalDirectory);
88 return hr;
89}
diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp
new file mode 100644
index 00000000..c508dd32
--- /dev/null
+++ b/src/dutil/pathutil.cpp
@@ -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
8DAPI_(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
103LExit:
104 ReleaseStr(sczQuotedArg);
105
106 return hr;
107}
108
109
110DAPI_(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
136DAPI_(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
163DAPI_(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
198LExit:
199 return hr;
200}
201
202
203DAPI_(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
315LExit:
316 ReleaseStr(sczFullPath);
317 ReleaseStr(sczExpandedPath);
318
319 return hr;
320}
321
322
323DAPI_(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
361LExit:
362 return hr;
363}
364
365
366DAPI_(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
387LExit:
388 return hr;
389}
390
391
392DAPI_(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
414LExit:
415 return hr;
416}
417
418
419DAPI_(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
448LExit:
449 return hr;
450}
451
452
453DAPI_(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
471LExit:
472 return hr;
473}
474
475
476DAPI_(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
568LExit:
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
582DAPI_(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
674LExit:
675 ReleaseFile(hTempFile);
676 ReleaseStr(sczTempPath);
677 ReleaseStr(sczPrefixFolder);
678 ReleaseStr(sczPrefix);
679
680 return hr;
681}
682
683
684DAPI_(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
758LExit:
759 ReleaseStr(scz);
760 ReleaseStr(sczTempPath);
761
762 return hr;
763}
764
765
766DAPI_(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
782LExit:
783 return hr;
784}
785
786
787DAPI_(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
796DAPI_(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
826LExit:
827 return hr;
828}
829
830
831DAPI_(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
883LExit:
884
885 return hr;
886}
887
888
889DAPI_(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
907LExit:
908 ReleaseStr(sczPath2);
909 ReleaseStr(sczPath1);
910
911 return hr;
912}
913
914
915DAPI_(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
940LExit:
941 ReleaseFile(hPath);
942
943 return hr;
944}
945
946DAPI_(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
1005LExit:
1006 ReleaseStr(sczPathCopy);
1007
1008 return hr;
1009}
diff --git a/src/dutil/perfutil.cpp b/src/dutil/perfutil.cpp
new file mode 100644
index 00000000..5c4e0774
--- /dev/null
+++ b/src/dutil/perfutil.cpp
@@ -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
5static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter
6static double vdFrequency = 1;
7
8
9/********************************************************************
10 PerfInitialize - initializes internal static variables
11
12********************************************************************/
13extern "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********************************************************************/
37extern "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********************************************************************/
61extern "C" double DAPI PerfConvertToSeconds(
62 __in const LARGE_INTEGER* pli
63 )
64{
65 Assert(0 < vdFrequency);
66 return pli->QuadPart / vdFrequency;
67}
diff --git a/src/dutil/polcutil.cpp b/src/dutil/polcutil.cpp
new file mode 100644
index 00000000..1cc29e61
--- /dev/null
+++ b/src/dutil/polcutil.cpp
@@ -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
5const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\";
6
7static HRESULT OpenPolicyKey(
8 __in_z LPCWSTR wzPolicyPath,
9 __out HKEY* phk
10 );
11
12
13extern "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
37LExit:
38 ReleaseRegKey(hk);
39
40 if (S_FALSE == hr || FAILED(hr))
41 {
42 *pdw = dwDefault;
43 }
44
45 return hr;
46}
47
48extern "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
72LExit:
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
93static 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
107LExit:
108 ReleaseStr(sczPath);
109
110 return hr;
111}
diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h
new file mode 100644
index 00000000..3acbca43
--- /dev/null
+++ b/src/dutil/precomp.h
@@ -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
diff --git a/src/dutil/proc2utl.cpp b/src/dutil/proc2utl.cpp
new file mode 100644
index 00000000..8a2fd09b
--- /dev/null
+++ b/src/dutil/proc2utl.cpp
@@ -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*******************************************************************/
9extern "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
64LExit:
65 ReleaseFile(hSnap);
66
67 return hr;
68}
diff --git a/src/dutil/proc3utl.cpp b/src/dutil/proc3utl.cpp
new file mode 100644
index 00000000..038f002b
--- /dev/null
+++ b/src/dutil/proc3utl.cpp
@@ -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
5static HRESULT GetActiveSessionUserToken(
6 __out HANDLE *phToken
7 );
8
9
10/********************************************************************
11 ProcExecuteAsInteractiveUser() - runs process as currently logged in
12 user.
13*******************************************************************/
14extern "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
47LExit:
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
63static 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
105LExit:
106 ReleaseHandle(hToken);
107
108 if (pSessionInfo)
109 {
110 ::WTSFreeMemory(pSessionInfo);
111 }
112
113 return hr;
114}
diff --git a/src/dutil/procutil.cpp b/src/dutil/procutil.cpp
new file mode 100644
index 00000000..4b34c773
--- /dev/null
+++ b/src/dutil/procutil.cpp
@@ -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
7static HRESULT CreatePipes(
8 __out HANDLE *phOutRead,
9 __out HANDLE *phOutWrite,
10 __out HANDLE *phErrWrite,
11 __out HANDLE *phInRead,
12 __out HANDLE *phInWrite
13 );
14
15static BOOL CALLBACK CloseWindowEnumCallback(
16 __in HWND hWnd,
17 __in LPARAM lParam
18 );
19
20
21extern "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
57LExit:
58 ReleaseHandle(hToken);
59
60 return hr;
61}
62
63extern "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
84LExit:
85 return hr;
86}
87
88extern "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
110LExit:
111 return hr;
112}
113
114extern "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
134LExit:
135 return hr;
136}
137
138
139extern "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
164LExit:
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*******************************************************************/
177extern "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
251LExit:
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*******************************************************************/
276extern "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
303LExit:
304 return hr;
305}
306
307/********************************************************************
308 ProcWaitForIds() - waits for multiple processes to complete.
309
310*******************************************************************/
311extern "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
345LExit:
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*******************************************************************/
366extern "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
381LExit:
382 return hr;
383}
384
385
386static 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
456LExit:
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*******************************************************************/
473static 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}
diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp
new file mode 100644
index 00000000..ec370f58
--- /dev/null
+++ b/src/dutil/regutil.cpp
@@ -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
5static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW;
6static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW;
7static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL;
8static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL;
9static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW;
10static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW;
11static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW;
12static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW;
13static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW;
14static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW;
15static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW;
16
17static HMODULE vhAdvApi32Dll = NULL;
18static BOOL vfRegInitialized = FALSE;
19
20/********************************************************************
21 RegInitialize - initializes regutil
22
23*********************************************************************/
24extern "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
42LExit:
43 return hr;
44}
45
46
47/********************************************************************
48 RegUninitialize - uninitializes regutil
49
50*********************************************************************/
51extern "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*********************************************************************/
71extern "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*********************************************************************/
99extern "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
112LExit:
113 return hr;
114}
115
116
117/********************************************************************
118 RegCreate - creates a registry key with extra options.
119
120*********************************************************************/
121HRESULT 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
143LExit:
144 return hr;
145}
146
147
148/********************************************************************
149 RegOpen - opens a registry key.
150
151*********************************************************************/
152extern "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
169LExit:
170 return hr;
171}
172
173
174/********************************************************************
175 RegDelete - deletes a registry key (and optionally it's whole tree).
176
177*********************************************************************/
178extern "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
254LExit:
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*********************************************************************/
267extern "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
315LExit:
316 return hr;
317}
318
319
320/********************************************************************
321 RegValueEnum - enumerates a registry value.
322
323*********************************************************************/
324HRESULT 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
351LExit:
352 return hr;
353}
354
355/********************************************************************
356 RegGetType - reads a registry key value type.
357 *********************************************************************/
358HRESULT 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.");
373LExit:
374
375 return hr;
376}
377
378/********************************************************************
379 RegReadBinary - reads a registry key binary value.
380 NOTE: caller is responsible for freeing *ppbBuffer
381*********************************************************************/
382HRESULT 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
424LExit:
425 ReleaseMem(pbBuffer);
426
427 return hr;
428}
429
430
431/********************************************************************
432 RegReadString - reads a registry key value as a string.
433
434*********************************************************************/
435extern "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
498LExit:
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*********************************************************************/
509HRESULT 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
594LExit:
595 ReleaseStr(sczValue);
596
597 return hr;
598}
599
600
601/********************************************************************
602 RegReadVersion - reads a registry key value as a version.
603
604*********************************************************************/
605extern "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
641LExit:
642 ReleaseStr(sczVersion);
643
644 return hr;
645}
646
647
648/********************************************************************
649 RegReadNumber - reads a DWORD registry key value as a number.
650
651*********************************************************************/
652extern "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
676LExit:
677 return hr;
678}
679
680
681/********************************************************************
682 RegReadQword - reads a QWORD registry key value as a number.
683
684*********************************************************************/
685extern "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
709LExit:
710 return hr;
711}
712
713
714/********************************************************************
715 RegWriteBinary - writes a registry key value as a binary.
716
717*********************************************************************/
718HRESULT 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
731LExit:
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*********************************************************************/
741extern "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
769LExit:
770 return hr;
771}
772
773
774/********************************************************************
775 RegWriteStringFormatted - writes a registry key value as a formatted string.
776
777*********************************************************************/
778extern "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
796LExit:
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*********************************************************************/
807HRESULT 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
862LExit:
863 ReleaseStr(sczWriteValue);
864
865 return hr;
866}
867
868/********************************************************************
869 RegWriteNumber - writes a registry key value as a number.
870
871*********************************************************************/
872extern "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
884LExit:
885 return hr;
886}
887
888/********************************************************************
889 RegWriteQword - writes a registry key value as a Qword.
890
891*********************************************************************/
892extern "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
904LExit:
905 return hr;
906}
907
908/********************************************************************
909 RegQueryKey - queries the key for the number of subkeys and values.
910
911*********************************************************************/
912extern "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
924LExit:
925 return hr;
926}
diff --git a/src/dutil/resrutil.cpp b/src/dutil/resrutil.cpp
new file mode 100644
index 00000000..1da03ed9
--- /dev/null
+++ b/src/dutil/resrutil.cpp
@@ -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
8BOOL 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/********************************************************************
17ResGetStringLangId - get the language id for a string in the string table.
18
19********************************************************************/
20extern "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
49LExit:
50 if (hModule)
51 {
52 ::FreeLibrary(hModule);
53 }
54
55 return hr;
56}
57
58
59/********************************************************************
60ResReadString
61
62NOTE: ppwzString should be freed with StrFree()
63********************************************************************/
64extern "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
97LExit:
98 return hr;
99}
100
101
102/********************************************************************
103 ResReadStringAnsi
104
105 NOTE: ppszString should be freed with StrFree()
106********************************************************************/
107extern "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
143LExit:
144 return hr;
145}
146
147
148/********************************************************************
149ResReadData - returns a pointer to the specified resource data
150
151NOTE: there is no "free" function for this call
152********************************************************************/
153extern "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
187LExit:
188 return hr;
189}
190
191
192/********************************************************************
193ResExportDataToFile - extracts the resource data to the specified target file
194
195********************************************************************/
196extern "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
224LExit:
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
239BOOL 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}
diff --git a/src/dutil/reswutil.cpp b/src/dutil/reswutil.cpp
new file mode 100644
index 00000000..e534fc09
--- /dev/null
+++ b/src/dutil/reswutil.cpp
@@ -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.
9typedef 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
18static HRESULT StringBlockInitialize(
19 __in_opt HINSTANCE hModule,
20 __in DWORD dwBlockId,
21 __in WORD wLangId,
22 __in RES_STRING_BLOCK* pStrBlock
23 );
24static void StringBlockUnitialize(
25 __in RES_STRING_BLOCK* pStrBlock
26 );
27static HRESULT StringBlockChangeString(
28 __in RES_STRING_BLOCK* pStrBlock,
29 __in DWORD dwStringId,
30 __in_z LPCWSTR szData
31 );
32static HRESULT StringBlockConvertToResourceData(
33 __in const RES_STRING_BLOCK* pStrBlock,
34 __deref_out_bcount(*pcbData) LPVOID* ppvData,
35 __out DWORD* pcbData
36 );
37static HRESULT StringBlockConvertFromResourceData(
38 __in RES_STRING_BLOCK* pStrBlock,
39 __in_bcount(cbData) LPCVOID pvData,
40 __in SIZE_T cbData
41 );
42
43
44/********************************************************************
45ResWriteString - sets the string into to the specified file's resource name
46
47********************************************************************/
48extern "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
98LExit:
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/********************************************************************
118ResWriteData - sets the data into to the specified file's resource name
119
120********************************************************************/
121extern "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
151LExit:
152 if (hUpdate)
153 {
154 ::EndUpdateResource(hUpdate, TRUE);
155 }
156
157 return hr;
158}
159
160
161/********************************************************************
162ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name
163
164********************************************************************/
165extern "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
198LExit:
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
215static 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
249LExit:
250 return hr;
251}
252
253
254static 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
268static 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
289LExit:
290 ReleaseMem(pwzData);
291
292 return hr;
293}
294
295
296static 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
335LExit:
336 ReleaseMem(pvData);
337
338 return hr;
339}
340
341
342static 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
366LExit:
367 return hr;
368}
diff --git a/src/dutil/rexutil.cpp b/src/dutil/rexutil.cpp
new file mode 100644
index 00000000..73500630
--- /dev/null
+++ b/src/dutil/rexutil.cpp
@@ -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//
9static HMODULE vhCabinetDll = NULL;
10static HFDI vhfdi = NULL;
11static ERF verf;
12
13static FAKE_FILE vrgffFileTable[FILETABLESIZE];
14static DWORD vcbRes;
15static LPCBYTE vpbRes;
16static CHAR vszResource[MAX_PATH];
17static REX_CALLBACK_WRITE vpfnWrite = NULL;
18
19static HRESULT vhrLastError = S_OK;
20
21//
22// structs
23//
24struct 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//
39static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize);
40static __callback void DIAMONDAPI RexFree(LPVOID pvData);
41static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode);
42static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb);
43static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb);
44static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf);
45static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype);
46static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify);
47
48
49/********************************************************************
50 RexInitialize - initializes internal static variables
51
52*******************************************************************/
53extern "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
68LExit:
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*******************************************************************/
83extern "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*******************************************************************/
100extern "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
165LExit:
166 return hr;
167}
168
169
170/****************************************************************************
171 default extract routines
172
173****************************************************************************/
174static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize)
175{
176 return MemAlloc(dwSize, FALSE);
177}
178
179
180static __callback void DIAMONDAPI RexFree(LPVOID pvData)
181{
182 MemFree(pvData);
183}
184
185
186static __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
236LExit:
237 if (FAILED(hr))
238 {
239 vhrLastError = hr;
240 }
241
242 return FAILED(hr) ? -1 : i;
243}
244
245
246static __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
274LExit:
275 if (FAILED(hr))
276 {
277 vhrLastError = hr;
278 }
279
280 return FAILED(hr) ? -1 : cbRead;
281}
282
283
284static __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
304LExit:
305 if (FAILED(hr))
306 {
307 vhrLastError = hr;
308 }
309
310 return FAILED(hr) ? -1 : cbWrite;
311}
312
313
314static __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
369LExit:
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
405LExit:
406 if (FAILED(hr))
407 {
408 vhrLastError = hr;
409 }
410
411 return FAILED(hr) ? -1 : 0;
412}
413
414
415static __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
579LExit:
580 if (FAILED(hr))
581 {
582 vhrLastError = hr;
583 }
584
585 return (S_OK == hr) ? ipResult : -1;
586}
diff --git a/src/dutil/rmutil.cpp b/src/dutil/rmutil.cpp
new file mode 100644
index 00000000..75d3e277
--- /dev/null
+++ b/src/dutil/rmutil.cpp
@@ -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
8typedef DWORD (WINAPI *PFNRMJOINSESSION)(
9 __out DWORD *pSessionHandle,
10 __in_z const WCHAR strSessionKey[]
11 );
12
13typedef DWORD (WINAPI *PFNRMENDSESSION)(
14 __in DWORD dwSessionHandle
15 );
16
17typedef 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
27typedef 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
45static volatile LONG vcRmuInitialized = 0;
46static HMODULE vhModule = NULL;
47static PFNRMJOINSESSION vpfnRmJoinSession = NULL;
48static PFNRMENDSESSION vpfnRmEndSession = NULL;
49static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL;
50
51static HRESULT RmuInitialize();
52static void RmuUninitialize();
53
54static 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
61static 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/********************************************************************
68RmuJoinSession - Joins an existing Restart Manager session.
69
70********************************************************************/
71extern "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
96LExit:
97 if (FAILED(hr))
98 {
99 ReleaseNullMem(pSession);
100 }
101
102 return hr;
103}
104
105/********************************************************************
106RmuAddFile - Adds the file path to the Restart Manager session.
107
108You should call this multiple times as necessary before calling
109RmuRegisterResources.
110
111********************************************************************/
112extern "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
125LExit:
126 ::LeaveCriticalSection(&pSession->cs);
127 return hr;
128}
129
130/********************************************************************
131RmuAddProcessById - Adds the process ID to the Restart Manager sesion.
132
133You should call this multiple times as necessary before calling
134RmuRegisterResources.
135
136********************************************************************/
137extern "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
220LExit:
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/********************************************************************
243RmuAddProcessesByName - Adds all processes by the given process name
244 to the Restart Manager Session.
245
246You should call this multiple times as necessary before calling
247RmuRegisterResources.
248
249********************************************************************/
250extern "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
283LExit:
284 ReleaseMem(pdwProcessIds);
285
286 return hr;
287}
288
289/********************************************************************
290RmuAddService - Adds the service name to the Restart Manager session.
291
292You should call this multiple times as necessary before calling
293RmuRegisterResources.
294
295********************************************************************/
296extern "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
308LExit:
309 ::LeaveCriticalSection(&pSession->cs);
310 return hr;
311}
312
313/********************************************************************
314RmuRegisterResources - Registers resources for the Restart Manager.
315
316This should be called rarely because it is expensive to run. Call
317functions like RmuAddFile for multiple resources then commit them
318as a batch of updates to RmuRegisterResources.
319
320Duplicate resources appear to be handled by Restart Manager.
321Only one WM_QUERYENDSESSION is being sent for each top-level window.
322
323********************************************************************/
324extern "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
351LExit:
352 ::LeaveCriticalSection(&pSession->cs);
353 return hr;
354}
355
356/********************************************************************
357RmuEndSession - Ends the session.
358
359If the session was joined by RmuJoinSession, any remaining resources
360are registered before the session is ended (left).
361
362********************************************************************/
363extern "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
382LExit:
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
398static 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
421LExit:
422 return hr;
423}
424
425static 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
439static 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
458LExit:
459 return hr;
460}
461
462static 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
471LExit:
472 return hr;
473}
diff --git a/src/dutil/rssutil.cpp b/src/dutil/rssutil.cpp
new file mode 100644
index 00000000..db49d954
--- /dev/null
+++ b/src/dutil/rssutil.cpp
@@ -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
5static HRESULT ParseRssDocument(
6 __in IXMLDOMDocument *pixd,
7 __out RSS_CHANNEL **ppChannel
8 );
9static HRESULT ParseRssChannel(
10 __in IXMLDOMNode *pixnChannel,
11 __out RSS_CHANNEL **ppChannel
12 );
13static HRESULT ParseRssItem(
14 __in IXMLDOMNode *pixnItem,
15 __in DWORD cItem,
16 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
17 );
18static HRESULT ParseRssUnknownElement(
19 __in IXMLDOMNode *pNode,
20 __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement
21 );
22static HRESULT ParseRssUnknownAttribute(
23 __in IXMLDOMNode *pNode,
24 __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
25 );
26static void FreeRssUnknownElementList(
27 __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement
28 );
29static void FreeRssUnknownAttributeList(
30 __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute
31 );
32
33
34/********************************************************************
35 RssInitialize - Initialize RSS utilities.
36
37*********************************************************************/
38extern "C" HRESULT DAPI RssInitialize()
39{
40 return XmlInitialize();
41}
42
43
44/********************************************************************
45 RssUninitialize - Uninitialize RSS utilities.
46
47*********************************************************************/
48extern "C" void DAPI RssUninitialize()
49{
50 XmlUninitialize();
51}
52
53
54/********************************************************************
55 RssParseFromString - parses out an RSS channel from a string.
56
57*********************************************************************/
58extern "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
79LExit:
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*********************************************************************/
92extern "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
113LExit:
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*********************************************************************/
126extern "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*********************************************************************/
158static 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
206LExit:
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*********************************************************************/
222static 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
316LExit:
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*********************************************************************/
331static 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
433LExit:
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*********************************************************************/
447static 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
515LExit:
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*********************************************************************/
532static 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
581LExit:
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*********************************************************************/
596static 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*********************************************************************/
618static 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}
diff --git a/src/dutil/sceutil.cpp b/src/dutil/sceutil.cpp
new file mode 100644
index 00000000..cdb1623b
--- /dev/null
+++ b/src/dutil/sceutil.cpp
@@ -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
16typedef DWORD DBLENGTH;
17typedef LONG DBROWOFFSET;
18typedef LONG DBROWCOUNT;
19typedef DWORD DBCOUNTITEM;
20typedef DWORD DBORDINAL;
21typedef LONG DB_LORDINAL;
22typedef DWORD DBBKMARK;
23typedef DWORD DBBYTEOFFSET;
24typedef DWORD DBREFCOUNT;
25typedef DWORD DB_UPARAMS;
26typedef LONG DB_LPARAMS;
27typedef DWORD DBHASHVALUE;
28typedef DWORD DB_DWRESERVE;
29typedef LONG DB_LRESERVE;
30typedef 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
38struct 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
60struct 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
75struct 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
88struct SCE_QUERY_RESULTS
89{
90 SCE_DATABASE_INTERNAL *pDatabaseInternal;
91 IRowset *pIRowset;
92 SCE_TABLE_SCHEMA *pTableSchema;
93};
94
95extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW);
96extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY);
97extern 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
100const 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
126const 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
142static HRESULT CreateSqlCe(
143 __in_z_opt LPCWSTR wzSqlCeDllPath,
144 __out IDBInitialize **ppIDBInitialize,
145 __out_opt HMODULE *phSqlCeDll
146 );
147static HRESULT RunQuery(
148 __in BOOL fRange,
149 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle,
150 __out SCE_QUERY_RESULTS **ppsqrhHandle
151 );
152static HRESULT FillOutColumnDescFromSchema(
153 __in const SCE_COLUMN_SCHEMA *pSchema,
154 __out DBCOLUMNDESC pColumnDesc
155 );
156static HRESULT EnsureSchema(
157 __in SCE_DATABASE *pDatabase,
158 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
159 );
160static HRESULT OpenSchema(
161 __in SCE_DATABASE *pDatabase,
162 __in SCE_DATABASE_SCHEMA *pdsSchema
163 );
164static 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 );
173static HRESULT GetColumnValue(
174 __in SCE_ROW *pRow,
175 __in DWORD dwColumnIndex,
176 __out_opt BYTE **ppbData,
177 __out SIZE_T *pcbSize
178 );
179static HRESULT GetColumnValueFixed(
180 __in SCE_ROW *pRow,
181 __in DWORD dwColumnIndex,
182 __in DWORD cbSize,
183 __out BYTE *pbData
184 );
185static HRESULT EnsureLocalColumnConstraints(
186 __in ITableDefinition *pTableDefinition,
187 __in DBID *pTableID,
188 __in SCE_TABLE_SCHEMA *pTableSchema
189 );
190static HRESULT EnsureForeignColumnConstraints(
191 __in ITableDefinition *pTableDefinition,
192 __in DBID *pTableID,
193 __in SCE_TABLE_SCHEMA *pTableSchema,
194 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
195 );
196static HRESULT SetSessionProperties(
197 __in ISessionProperties *pISessionProperties
198 );
199static HRESULT GetDatabaseSchemaInfo(
200 __in SCE_DATABASE *pDatabase,
201 __out LPWSTR *psczSchemaType,
202 __out DWORD *pdwVersion
203 );
204static HRESULT SetDatabaseSchemaInfo(
205 __in SCE_DATABASE *pDatabase,
206 __in LPCWSTR wzSchemaType,
207 __in DWORD dwVersion
208 );
209static void ReleaseDatabase(
210 SCE_DATABASE *pDatabase
211 );
212static void ReleaseDatabaseInternal(
213 SCE_DATABASE_INTERNAL *pDatabaseInternal
214 );
215
216// function definitions
217extern "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
300LExit:
301 ReleaseStr(sczDirectory);
302 ReleaseObject(pIDBDataSourceAdmin);
303 ReleaseDatabase(pNewSceDatabase);
304 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
305
306 return hr;
307}
308
309extern "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
424LExit:
425 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
426 ReleaseStr(sczSchemaType);
427 ReleaseDatabase(pNewSceDatabase);
428
429 return hr;
430}
431
432extern "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
467LExit:
468 ReleaseDatabase(pDatabase);
469
470 return hr;
471}
472
473extern "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
492LExit:
493 ReleaseSceRow(row);
494
495 return hr;
496}
497
498extern "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
532LExit:
533 return hr;
534}
535
536HRESULT 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
567LExit:
568 return hr;
569}
570
571extern "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
592LExit:
593 if (FAILED(hr))
594 {
595 ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
596 }
597
598 return hr;
599}
600
601extern "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
633LExit:
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
643extern "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
666LExit:
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
678extern "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
695LExit:
696 ReleaseObject(pIRowsetChange);
697
698 return hr;
699}
700
701extern "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
723LExit:
724 ReleaseSceRow(pRow);
725
726 return hr;
727}
728
729extern "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
784LExit:
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
796extern "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
819LExit:
820 return hr;
821}
822
823extern "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
845LExit:
846 return hr;
847}
848
849extern "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
871LExit:
872 return hr;
873}
874
875extern "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
898LExit:
899 return hr;
900}
901
902extern "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
925LExit:
926 return hr;
927}
928
929extern "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
950LExit:
951 return hr;
952}
953
954extern "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
988LExit:
989 return hr;
990}
991
992extern "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
1009LExit:
1010 return hr;
1011}
1012
1013extern "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
1029LExit:
1030 return hr;
1031}
1032
1033extern "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
1049LExit:
1050 return hr;
1051}
1052
1053extern "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
1079LExit:
1080 return hr;
1081}
1082
1083extern "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
1100LExit:
1101 return hr;
1102}
1103
1104extern "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
1128LExit:
1129 return hr;
1130}
1131
1132extern "C" void DAPI SceCloseTable(
1133 __in SCE_TABLE_SCHEMA *pTable
1134 )
1135{
1136 ReleaseNullObject(pTable->pIRowsetChange);
1137 ReleaseNullObject(pTable->pIRowset);
1138}
1139
1140extern "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
1149void 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
1158extern "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
1169extern "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
1194LExit:
1195 ReleaseSceQuery(psq);
1196
1197 return hr;
1198}
1199
1200HRESULT 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
1214LExit:
1215 return hr;
1216}
1217
1218HRESULT 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
1231LExit:
1232 return hr;
1233}
1234
1235HRESULT 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
1248LExit:
1249 return hr;
1250}
1251
1252HRESULT 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
1266LExit:
1267 return hr;
1268}
1269
1270HRESULT 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
1284LExit:
1285 return hr;
1286}
1287
1288HRESULT 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
1309LExit:
1310 return hr;
1311}
1312
1313HRESULT 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
1325LExit:
1326 return hr;
1327}
1328
1329HRESULT 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
1351LExit:
1352 ReleaseNullSceQuery(*psqhHandle);
1353 ReleaseSceQueryResults(pQueryResults);
1354
1355 return hr;
1356}
1357
1358extern "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
1373LExit:
1374 ReleaseNullSceQuery(*psqhHandle);
1375
1376 return hr;
1377}
1378
1379extern "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
1413LExit:
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
1423extern "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
1439void 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
1450void 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
1461static 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
1516LExit:
1517 ReleaseStr(sczPath);
1518 ReleaseStr(sczDirectory);
1519 ReleaseStr(sczDllFullPath);
1520
1521 return hr;
1522}
1523
1524static 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
1612LExit:
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
1626static 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
1697LExit:
1698 return hr;
1699}
1700
1701static 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
1890LExit:
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
1911static 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
1946LExit:
1947 return hr;
1948}
1949
1950static 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
2007LExit:
2008 return hr;
2009}
2010
2011static 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
2083LExit:
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
2099static 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
2155LExit:
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
2169static 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
2224LExit:
2225 ReleaseObject(pTableDefinitionWithConstraints);
2226
2227 return hr;
2228}
2229
2230static 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
2294LExit:
2295 ReleaseObject(pTableDefinitionWithConstraints);
2296
2297 return hr;
2298}
2299
2300static 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
2320LExit:
2321 return hr;
2322}
2323
2324static 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
2355LExit:
2356 SceCloseTable(&schemaTable); // ignore failure
2357 ReleaseStr(sczSchemaType);
2358 ReleaseSceRow(sceRow);
2359
2360 return hr;
2361}
2362
2363static 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
2408LExit:
2409 SceCloseTable(&schemaTable); // ignore failure
2410 ReleaseSceRow(sceRow);
2411 if (fInSceTransaction)
2412 {
2413 SceRollbackTransaction(&database);
2414 }
2415
2416 return hr;
2417}
2418
2419static 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
2441static 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
diff --git a/src/dutil/shelutil.cpp b/src/dutil/shelutil.cpp
new file mode 100644
index 00000000..a69c9eaa
--- /dev/null
+++ b/src/dutil/shelutil.cpp
@@ -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
5static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW;
6
7static HRESULT GetDesktopShellView(
8 __in REFIID riid,
9 __out void **ppv
10 );
11static 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*********************************************************************/
22extern "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*******************************************************************/
34extern "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
67LExit:
68 ReleaseHandle(shExecInfo.hProcess);
69
70 return hr;
71}
72
73
74/********************************************************************
75 ShelExecUnelevated() - executes a target unelevated.
76
77*******************************************************************/
78extern "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
135LExit:
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*******************************************************************/
151extern "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
168LExit:
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
186EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)(
187 REFKNOWNFOLDERID rfid,
188 DWORD dwFlags,
189 HANDLE hToken,
190 PWSTR *ppszPath
191 );
192
193extern "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
223LExit:
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
240static 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
286LExit:
287 ReleaseObject(psv);
288 ReleaseObject(psb);
289 ReleaseObject(psf);
290 ReleaseObject(pdisp);
291 ReleaseObject(psw);
292
293 return hr;
294}
295
296static 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
321LExit:
322 ReleaseObject(pdisp);
323 ReleaseObject(psfvd);
324 ReleaseObject(pdispBackground);
325
326 return hr;
327}
diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp
new file mode 100644
index 00000000..099c6ae9
--- /dev/null
+++ b/src/dutil/sqlutil.cpp
@@ -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
13static HRESULT FileSpecToString(
14 __in const SQL_FILESPEC* psf,
15 __out LPWSTR* ppwz
16 );
17
18static 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********************************************************************/
30extern "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
137LExit:
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*********************************************************************/
155extern "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
173LExit:
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*********************************************************************/
183extern "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
203LExit:
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********************************************************************/
218extern "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
238LExit:
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********************************************************************/
253extern "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
292LExit:
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********************************************************************/
306extern "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);
333LExit:
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********************************************************************/
345extern "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);
368LExit:
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********************************************************************/
380extern "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);
407LExit:
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********************************************************************/
419extern "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
454LExit:
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********************************************************************/
470extern "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
493LExit:
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********************************************************************/
505extern "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
532LExit:
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********************************************************************/
545extern "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
598LExit:
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********************************************************************/
622extern "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
671LExit:
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********************************************************************/
684extern "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
761LExit:
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*********************************************************************/
779static 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
825LExit:
826 ReleaseStr(pwz);
827 return hr;
828}
829
830static 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
865LExit:
866 ReleaseStr(pwz);
867 return hr;
868}
diff --git a/src/dutil/srputil.cpp b/src/dutil/srputil.cpp
new file mode 100644
index 00000000..9fc2f94a
--- /dev/null
+++ b/src/dutil/srputil.cpp
@@ -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
6typedef BOOL (WINAPI *PFN_SETRESTOREPTW)(
7 __in PRESTOREPOINTINFOW pRestorePtSpec,
8 __out PSTATEMGRSTATUS pSMgrStatus
9 );
10
11static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL;
12static HMODULE vhSrClientDll = NULL;
13
14
15static HRESULT InitializeComSecurity();
16
17
18DAPI_(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
43LExit:
44 if (FAILED(hr) && vhSrClientDll)
45 {
46 SrpUninitialize();
47 }
48
49 return hr;
50}
51
52DAPI_(void) SrpUninitialize()
53{
54 if (vhSrClientDll)
55 {
56 ::FreeLibrary(vhSrClientDll);
57 vhSrClientDll = NULL;
58 vpfnSRSetRestorePointW = NULL;
59 }
60}
61
62DAPI_(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
85LExit:
86 return hr;
87}
88
89
90// internal functions.
91
92static 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
230LExit:
231 if (pAcl)
232 {
233 ::LocalFree(pAcl);
234 }
235
236 return hr;
237}
diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp
new file mode 100644
index 00000000..2e5e2f96
--- /dev/null
+++ b/src/dutil/strutil.cpp
@@ -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.
8static HRESULT AllocHelper(
9 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
10 __in DWORD_PTR cch,
11 __in BOOL fZeroOnRealloc
12 );
13static 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 );
19static HRESULT AllocConcatHelper(
20 __deref_out_z LPWSTR* ppwz,
21 __in_z LPCWSTR wzSource,
22 __in DWORD_PTR cchSource,
23 __in BOOL fZeroOnRealloc
24 );
25static HRESULT AllocFormattedArgsHelper(
26 __deref_out_z LPWSTR* ppwz,
27 __in BOOL fZeroOnRealloc,
28 __in __format_string LPCWSTR wzFormat,
29 __in va_list args
30 );
31static HRESULT StrAllocStringMapInvariant(
32 __deref_out_z LPWSTR* pscz,
33 __in_z LPCWSTR wzSource,
34 __in int cchSource,
35 __in DWORD dwMapFlags
36 );
37
38/********************************************************************
39StrAlloc - allocates or reuses dynamic string memory
40
41NOTE: caller is responsible for freeing ppwz even if function fails
42********************************************************************/
43extern "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/********************************************************************
52StrAllocSecure - allocates or reuses dynamic string memory
53If the memory needs to reallocated, calls SecureZeroMemory on the
54original block of memory after it is moved.
55
56NOTE: caller is responsible for freeing ppwz even if function fails
57********************************************************************/
58extern "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/********************************************************************
67AllocHelper - allocates or reuses dynamic string memory
68If fZeroOnRealloc is true and the memory needs to reallocated,
69calls SecureZeroMemory on original block of memory after it is moved.
70
71NOTE: caller is responsible for freeing ppwz even if function fails
72********************************************************************/
73static 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;
112LExit:
113 return hr;
114}
115
116
117/********************************************************************
118StrTrimCapacity - 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
122NOTE: caller is responsible for freeing ppwz even if function fails
123********************************************************************/
124HRESULT 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
141LExit:
142 return hr;
143}
144
145
146/********************************************************************
147StrTrimWhitespace - allocates or reuses dynamic string memory and copies
148 in an existing string, excluding whitespace
149
150NOTE: caller is responsible for freeing ppwz even if function fails
151********************************************************************/
152HRESULT 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
190LExit:
191 ReleaseStr(sczResult);
192
193 return hr;
194}
195
196
197/********************************************************************
198StrAnsiAlloc - allocates or reuses dynamic ANSI string memory
199
200NOTE: caller is responsible for freeing ppsz even if function fails
201********************************************************************/
202extern "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;
230LExit:
231 return hr;
232}
233
234
235/********************************************************************
236StrAnsiTrimCapacity - 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
240NOTE: caller is responsible for freeing ppwz even if function fails
241********************************************************************/
242HRESULT 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
262LExit:
263 return hr;
264}
265
266
267/********************************************************************
268StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies
269 in an existing string, excluding whitespace
270
271NOTE: caller is responsible for freeing ppz even if function fails
272********************************************************************/
273HRESULT 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
311LExit:
312 ReleaseStr(sczResult);
313
314 return hr;
315}
316
317/********************************************************************
318StrAllocString - allocates or reuses dynamic string memory and copies in an existing string
319
320NOTE: caller is responsible for freeing ppwz even if function fails
321NOTE: cchSource does not have to equal the length of wzSource
322NOTE: if cchSource == 0, length of wzSource is used instead
323********************************************************************/
324extern "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/********************************************************************
334StrAllocStringSecure - allocates or reuses dynamic string memory and
335copies in an existing string. If the memory needs to reallocated,
336calls SecureZeroMemory on original block of memory after it is moved.
337
338NOTE: caller is responsible for freeing ppwz even if function fails
339NOTE: cchSource does not have to equal the length of wzSource
340NOTE: if cchSource == 0, length of wzSource is used instead
341********************************************************************/
342extern "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/********************************************************************
352AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string
353If fZeroOnRealloc is true and the memory needs to reallocated,
354calls SecureZeroMemory on original block of memory after it is moved.
355
356NOTE: caller is responsible for freeing ppwz even if function fails
357NOTE: cchSource does not have to equal the length of wzSource
358NOTE: if cchSource == 0, length of wzSource is used instead
359********************************************************************/
360static 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
402LExit:
403 return hr;
404}
405
406
407/********************************************************************
408StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string
409
410NOTE: caller is responsible for freeing ppsz even if function fails
411NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
412NOTE: if cchSource == 0, length of wzSource is used instead
413********************************************************************/
414extern "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
482LExit:
483 return hr;
484}
485
486
487/********************************************************************
488StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string
489
490NOTE: caller is responsible for freeing ppwz even if function fails
491NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
492NOTE: if cchSource == 0, length of wzSource is used instead
493********************************************************************/
494extern "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
563LExit:
564 return hr;
565}
566
567
568/********************************************************************
569StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string
570
571NOTE: caller is responsible for freeing ppsz even if function fails
572NOTE: cchSource does not have to equal the length of wzSource
573NOTE: if cchSource == 0, length of wzSource is used instead
574********************************************************************/
575HRESULT 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
619LExit:
620 return hr;
621}
622
623
624/********************************************************************
625StrAllocPrefix - allocates or reuses dynamic string memory and
626 prefixes a string
627
628NOTE: caller is responsible for freeing ppwz even if function fails
629NOTE: cchPrefix does not have to equal the length of wzPrefix
630NOTE: if cchPrefix == 0, length of wzPrefix is used instead
631********************************************************************/
632extern "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
687LExit:
688 return hr;
689}
690
691
692/********************************************************************
693StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string
694
695NOTE: caller is responsible for freeing ppwz even if function fails
696NOTE: cchSource does not have to equal the length of wzSource
697NOTE: if cchSource == 0, length of wzSource is used instead
698********************************************************************/
699extern "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/********************************************************************
710StrAllocConcatSecure - allocates or reuses dynamic string memory and
711adds an existing string. If the memory needs to reallocated, calls
712SecureZeroMemory on the original block of memory after it is moved.
713
714NOTE: caller is responsible for freeing ppwz even if function fails
715NOTE: cchSource does not have to equal the length of wzSource
716NOTE: if cchSource == 0, length of wzSource is used instead
717********************************************************************/
718extern "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/********************************************************************
729AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string
730If fZeroOnRealloc is true and the memory needs to reallocated,
731calls SecureZeroMemory on original block of memory after it is moved.
732
733NOTE: caller is responsible for freeing ppwz even if function fails
734NOTE: cchSource does not have to equal the length of wzSource
735NOTE: if cchSource == 0, length of wzSource is used instead
736********************************************************************/
737static 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
789LExit:
790 return hr;
791}
792
793
794/********************************************************************
795StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string
796
797NOTE: caller is responsible for freeing ppz even if function fails
798NOTE: cchSource does not have to equal the length of pzSource
799NOTE: if cchSource == 0, length of pzSource is used instead
800********************************************************************/
801extern "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
861LExit:
862 return hr;
863}
864
865
866/********************************************************************
867StrAllocFormatted - allocates or reuses dynamic string memory and formats it
868
869NOTE: caller is responsible for freeing ppwz even if function fails
870********************************************************************/
871extern "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/********************************************************************
891StrAllocConcatFormatted - allocates or reuses dynamic string memory
892and adds a formatted string
893
894NOTE: caller is responsible for freeing ppwz even if function fails
895********************************************************************/
896extern "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
915LExit:
916 ReleaseStr(sczFormatted);
917
918 return hr;
919}
920
921
922/********************************************************************
923StrAllocFormattedSecure - allocates or reuses dynamic string memory
924and formats it. If the memory needs to reallocated,
925calls SecureZeroMemory on original block of memory after it is moved.
926
927NOTE: caller is responsible for freeing ppwz even if function fails
928********************************************************************/
929extern "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/********************************************************************
949StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it
950
951NOTE: caller is responsible for freeing ppsz even if function fails
952********************************************************************/
953extern "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/********************************************************************
973StrAllocFormattedArgs - allocates or reuses dynamic string memory
974and formats it with the passed in args
975
976NOTE: caller is responsible for freeing ppwz even if function fails
977********************************************************************/
978extern "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/********************************************************************
989StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory
990and formats it with the passed in args.
991
992If the memory needs to reallocated, calls SecureZeroMemory on the
993original block of memory after it is moved.
994
995NOTE: caller is responsible for freeing ppwz even if function fails
996********************************************************************/
997extern "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/********************************************************************
1008AllocFormattedArgsHelper - allocates or reuses dynamic string memory
1009and formats it with the passed in args.
1010
1011If fZeroOnRealloc is true and the memory needs to reallocated,
1012calls SecureZeroMemory on original block of memory after it is moved.
1013
1014NOTE: caller is responsible for freeing ppwz even if function fails
1015********************************************************************/
1016static 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
1080LExit:
1081 if (pwzOriginal && fZeroOnRealloc)
1082 {
1083 SecureZeroMemory(pwzOriginal, cbOriginal);
1084 }
1085
1086 ReleaseStr(pwzOriginal);
1087
1088 return hr;
1089}
1090
1091
1092/********************************************************************
1093StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory
1094and formats it with the passed in args
1095
1096NOTE: caller is responsible for freeing ppsz even if function fails
1097********************************************************************/
1098extern "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
1158LExit:
1159 ReleaseStr(pszOriginal);
1160
1161 return hr;
1162}
1163
1164
1165/********************************************************************
1166StrAllocFromError - returns the string for a particular error.
1167
1168********************************************************************/
1169extern "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
1199LExit:
1200 if (pvMessage)
1201 {
1202 ::LocalFree(pvMessage);
1203 }
1204
1205 return hr;
1206}
1207
1208
1209/********************************************************************
1210StrMaxLength - returns maximum number of characters that can be stored in dynamic string p
1211
1212NOTE: assumes Unicode string
1213********************************************************************/
1214extern "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
1239LExit:
1240 return hr;
1241}
1242
1243
1244/********************************************************************
1245StrSize - returns count of bytes in dynamic string p
1246
1247********************************************************************/
1248extern "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/********************************************************************
1267StrFree - releases dynamic string memory allocated by any StrAlloc*() functions
1268
1269********************************************************************/
1270extern "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
1279LExit:
1280 return hr;
1281}
1282
1283
1284/****************************************************************************
1285StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1286Replaces all instances.
1287
1288****************************************************************************/
1289extern "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
1307LExit:
1308 return hr;
1309}
1310
1311
1312/****************************************************************************
1313StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1314Search for old substring starts at dwStartIndex. Does only 1 replace.
1315
1316****************************************************************************/
1317extern "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
1362LExit:
1363 return hr;
1364}
1365
1366
1367static 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/****************************************************************************
1393StrHexEncode - converts an array of bytes to a text string
1394
1395NOTE: wzDest must have space for cbSource * 2 + 1 characters
1396****************************************************************************/
1397extern "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
1427LExit:
1428 return hr;
1429}
1430
1431
1432/****************************************************************************
1433StrAllocHexEncode - converts an array of bytes to an allocated text string
1434
1435****************************************************************************/
1436HRESULT 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
1451LExit:
1452 return hr;
1453}
1454
1455
1456/****************************************************************************
1457StrHexDecode - converts a string of text to array of bytes
1458
1459NOTE: wzSource must contain even number of characters
1460****************************************************************************/
1461extern "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
1492LExit:
1493 return hr;
1494}
1495
1496
1497/****************************************************************************
1498StrAllocHexDecode - allocates a byte array hex-converted from string of text
1499
1500NOTE: wzSource must contain even number of characters
1501****************************************************************************/
1502extern "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
1539LExit:
1540 ReleaseMem(pb);
1541
1542 return hr;
1543}
1544
1545
1546/****************************************************************************
1547Base85 encoding/decoding data
1548
1549****************************************************************************/
1550const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~";
1551
1552const 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
1572const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 };
1573
1574
1575/****************************************************************************
1576StrAllocBase85Encode - converts an array of bytes into an XML compatible string
1577
1578****************************************************************************/
1579extern "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
1665LExit:
1666 return hr;
1667}
1668
1669
1670/****************************************************************************
1671StrAllocBase85Decode - converts a string of text to array of bytes
1672
1673NOTE: Use MemFree() to release the allocated stream of bytes
1674****************************************************************************/
1675extern "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
1806LExit:
1807 return hr;
1808}
1809
1810
1811/****************************************************************************
1812MultiSzLen - calculates the length of a MULTISZ string including all nulls
1813including the double null terminator at the end of the MULTISZ.
1814
1815NOTE: returns 0 if the multisz in not properly terminated with two nulls
1816****************************************************************************/
1817extern "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
1852LExit:
1853 return hr;
1854}
1855
1856
1857/****************************************************************************
1858MultiSzPrepend - prepends a string onto the front of a MUTLISZ
1859
1860****************************************************************************/
1861extern "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
1923LExit:
1924 ReleaseNullStr(pwzResult);
1925
1926 return hr;
1927}
1928
1929/****************************************************************************
1930MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the
1931specified sub string and returns the index of the
1932string in the MULTISZ, the address, neither, or both
1933
1934NOTE: returns S_FALSE if the string is not found
1935****************************************************************************/
1936extern "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
1992LExit:
1993 return hr;
1994}
1995
1996/****************************************************************************
1997MultiSzFindString - finds a string in a MULTISZ and returns the index of
1998the string in the MULTISZ, the address or both
1999
2000NOTE: returns S_FALSE if the string is not found
2001****************************************************************************/
2002extern "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
2058LExit:
2059 return hr;
2060}
2061
2062/****************************************************************************
2063MultiSzRemoveString - removes string from a MULTISZ at the specified
2064index
2065
2066NOTE: does an in place removal without shrinking the memory allocation
2067
2068NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex
2069****************************************************************************/
2070extern "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
2138LExit:
2139 return hr;
2140}
2141
2142/****************************************************************************
2143MultiSzInsertString - inserts new string at the specified index
2144
2145****************************************************************************/
2146extern "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
2229LExit:
2230 ReleaseStr(pwzResult);
2231
2232 return hr;
2233}
2234
2235/****************************************************************************
2236MultiSzReplaceString - replaces string at the specified index with a new one
2237
2238****************************************************************************/
2239extern "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
2255LExit:
2256 return hr;
2257}
2258
2259
2260/****************************************************************************
2261wcsistr - case insensitive find a substring
2262
2263****************************************************************************/
2264extern "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/****************************************************************************
2300StrStringToInt16 - converts a string to a signed 16-bit integer.
2301
2302****************************************************************************/
2303extern "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
2321LExit:
2322 return hr;
2323}
2324
2325/****************************************************************************
2326StrStringToUInt16 - converts a string to an unsigned 16-bit integer.
2327
2328****************************************************************************/
2329extern "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
2347LExit:
2348 return hr;
2349}
2350
2351/****************************************************************************
2352StrStringToInt32 - converts a string to a signed 32-bit integer.
2353
2354****************************************************************************/
2355extern "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
2373LExit:
2374 return hr;
2375}
2376
2377/****************************************************************************
2378StrStringToUInt32 - converts a string to an unsigned 32-bit integer.
2379
2380****************************************************************************/
2381extern "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
2399LExit:
2400 return hr;
2401}
2402
2403/****************************************************************************
2404StrStringToInt64 - converts a string to a signed 64-bit integer.
2405
2406****************************************************************************/
2407extern "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
2459LExit:
2460 return hr;
2461}
2462
2463/****************************************************************************
2464StrStringToUInt64 - converts a string to an unsigned 64-bit integer.
2465
2466****************************************************************************/
2467extern "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
2509LExit:
2510 return hr;
2511}
2512
2513/****************************************************************************
2514StrStringToUpper - alters the given string in-place to be entirely uppercase
2515
2516****************************************************************************/
2517void DAPI StrStringToUpper(
2518 __inout_z LPWSTR wzIn
2519 )
2520{
2521 ::CharUpperBuffW(wzIn, lstrlenW(wzIn));
2522}
2523
2524/****************************************************************************
2525StrStringToLower - alters the given string in-place to be entirely lowercase
2526
2527****************************************************************************/
2528void DAPI StrStringToLower(
2529 __inout_z LPWSTR wzIn
2530 )
2531{
2532 ::CharLowerBuffW(wzIn, lstrlenW(wzIn));
2533}
2534
2535/****************************************************************************
2536StrAllocStringToUpperInvariant - creates an upper-case copy of a string.
2537
2538****************************************************************************/
2539extern "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/****************************************************************************
2549StrAllocStringToLowerInvariant - creates an lower-case copy of a string.
2550
2551****************************************************************************/
2552extern "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/****************************************************************************
2562StrArrayAllocString - Allocates a string array.
2563
2564****************************************************************************/
2565extern "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
2586LExit:
2587 return hr;
2588}
2589
2590/****************************************************************************
2591StrArrayFree - Frees a string array.
2592
2593Use ReleaseNullStrArray to nullify the arguments.
2594
2595****************************************************************************/
2596extern "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
2615LExit:
2616 return hr;
2617}
2618
2619/****************************************************************************
2620StrSplitAllocArray - Splits a string into an array.
2621
2622****************************************************************************/
2623extern "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
2644LExit:
2645 ReleaseStr(sczCopy);
2646
2647 return hr;
2648}
2649
2650/****************************************************************************
2651StrAllocStringMapInvariant - helper function for the ToUpper and ToLower.
2652
2653Note: Assumes source and destination buffers will be the same.
2654****************************************************************************/
2655static 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
2681LExit:
2682 return hr;
2683}
2684
2685/****************************************************************************
2686StrSecureZeroString - zeroes out string to the make sure the contents
2687don't remain in memory.
2688
2689****************************************************************************/
2690extern "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
2711LExit:
2712 return hr;
2713}
2714
2715/****************************************************************************
2716StrSecureZeroFreeString - zeroes out string to the make sure the contents
2717don't remain in memory, then frees the string.
2718
2719****************************************************************************/
2720extern "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}
diff --git a/src/dutil/svcutil.cpp b/src/dutil/svcutil.cpp
new file mode 100644
index 00000000..9da2b5b3
--- /dev/null
+++ b/src/dutil/svcutil.cpp
@@ -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/********************************************************************
6SvcQueryConfig - queries the configuration of a service
7
8********************************************************************/
9extern "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
40LExit:
41 ReleaseMem(pConfig);
42
43 return hr;
44}
diff --git a/src/dutil/thmutil.cpp b/src/dutil/thmutil.cpp
new file mode 100644
index 00000000..cae92d92
--- /dev/null
+++ b/src/dutil/thmutil.cpp
@@ -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
22const DWORD THEME_INVALID_ID = 0xFFFFFFFF;
23const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF;
24const DWORD GROW_WINDOW_TEXT = 250;
25const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink";
26const LPCWSTR THEME_WC_PANEL = L"ThemePanel";
27
28static Gdiplus::GdiplusStartupInput vgsi;
29static Gdiplus::GdiplusStartupOutput vgso = { };
30static ULONG_PTR vgdiToken = 0;
31static ULONG_PTR vgdiHookToken = 0;
32static HMODULE vhHyperlinkRegisteredModule = NULL;
33static HMODULE vhPanelRegisteredModule = NULL;
34static HMODULE vhModuleRichEd = NULL;
35static HCURSOR vhCursorHand = NULL;
36
37enum 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
46struct MEMBUFFER_FOR_RICHEDIT
47{
48 BYTE* rgbData;
49 DWORD cbData;
50
51 DWORD iData;
52};
53
54
55// prototypes
56static HRESULT RegisterWindowClasses(
57 __in_opt HMODULE hModule
58 );
59static HRESULT ParseTheme(
60 __in_opt HMODULE hModule,
61 __in_opt LPCWSTR wzRelativePath,
62 __in IXMLDOMDocument* pixd,
63 __out THEME** ppTheme
64 );
65static HRESULT ParseImage(
66 __in_opt HMODULE hModule,
67 __in_z_opt LPCWSTR wzRelativePath,
68 __in IXMLDOMNode* pElement,
69 __out HBITMAP* phImage
70 );
71static HRESULT ParseIcon(
72 __in_opt HMODULE hModule,
73 __in_z_opt LPCWSTR wzRelativePath,
74 __in IXMLDOMNode* pElement,
75 __out HICON* phIcon
76 );
77static HRESULT ParseWindow(
78 __in_opt HMODULE hModule,
79 __in_opt LPCWSTR wzRelativePath,
80 __in IXMLDOMElement* pElement,
81 __in THEME* pTheme
82 );
83static HRESULT GetFontColor(
84 __in IXMLDOMNode* pixn,
85 __in_z LPCWSTR wzAttributeName,
86 __out COLORREF* pColorRef,
87 __out DWORD* pdwSystemColor
88 );
89static HRESULT ParseFonts(
90 __in IXMLDOMElement* pElement,
91 __in THEME* pTheme
92 );
93static HRESULT ParsePages(
94 __in_opt HMODULE hModule,
95 __in_opt LPCWSTR wzRelativePath,
96 __in IXMLDOMNode* pElement,
97 __in THEME* pTheme
98 );
99static HRESULT ParseImageLists(
100 __in_opt HMODULE hModule,
101 __in_opt LPCWSTR wzRelativePath,
102 __in IXMLDOMNode* pElement,
103 __in THEME* pTheme
104 );
105static 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 );
113static 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 );
122static HRESULT ParseActions(
123 __in IXMLDOMNode* pixn,
124 __in THEME_CONTROL* pControl
125 );
126static HRESULT ParseColumns(
127 __in IXMLDOMNode* pixn,
128 __in THEME_CONTROL* pControl
129 );
130static 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 );
138static HRESULT ParseTabs(
139 __in IXMLDOMNode* pixn,
140 __in THEME_CONTROL* pControl
141 );
142static HRESULT ParseText(
143 __in IXMLDOMNode* pixn,
144 __in THEME_CONTROL* pControl,
145 __inout BOOL* pfAnyChildren
146);
147static HRESULT ParseTooltips(
148 __in IXMLDOMNode* pixn,
149 __in THEME_CONTROL* pControl,
150 __inout BOOL* pfAnyChildren
151 );
152static HRESULT ParseNotes(
153 __in IXMLDOMNode* pixn,
154 __in THEME_CONTROL* pControl,
155 __out BOOL* pfAnyChildren
156 );
157static HRESULT StopBillboard(
158 __in THEME* pTheme,
159 __in DWORD dwControl
160 );
161static HRESULT StartBillboard(
162 __in THEME* pTheme,
163 __in DWORD dwControl
164 );
165static HRESULT FindImageList(
166 __in THEME* pTheme,
167 __in_z LPCWSTR wzImageListName,
168 __out HIMAGELIST *phImageList
169 );
170static 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 );
177static 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 );
186static 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 );
194static HRESULT DrawButton(
195 __in THEME* pTheme,
196 __in DRAWITEMSTRUCT* pdis,
197 __in const THEME_CONTROL* pControl
198 );
199static 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 );
206static HRESULT DrawHyperlink(
207 __in THEME* pTheme,
208 __in DRAWITEMSTRUCT* pdis,
209 __in const THEME_CONTROL* pControl
210 );
211static HRESULT DrawImage(
212 __in THEME* pTheme,
213 __in DRAWITEMSTRUCT* pdis,
214 __in const THEME_CONTROL* pControl
215 );
216static HRESULT DrawProgressBar(
217 __in THEME* pTheme,
218 __in DRAWITEMSTRUCT* pdis,
219 __in const THEME_CONTROL* pControl
220 );
221static BOOL DrawHoverControl(
222 __in THEME* pTheme,
223 __in BOOL fHover
224 );
225static DWORD CALLBACK RichEditStreamFromFileHandleCallback(
226 __in DWORD_PTR dwCookie,
227 __in_bcount(cb) LPBYTE pbBuff,
228 __in LONG cb,
229 __in LONG *pcb
230 );
231static DWORD CALLBACK RichEditStreamFromMemoryCallback(
232 __in DWORD_PTR dwCookie,
233 __in_bcount(cb) LPBYTE pbBuff,
234 __in LONG cb,
235 __in LONG *pcb
236 );
237static void FreeFont(
238 __in THEME_FONT* pFont
239 );
240static void FreePage(
241 __in THEME_PAGE* pPage
242 );
243static void FreeControl(
244 __in THEME_CONTROL* pControl
245 );
246static void FreeConditionalText(
247 __in THEME_CONDITIONAL_TEXT* pConditionalText
248 );
249static void FreeImageList(
250 __in THEME_IMAGELIST* pImageList
251 );
252static void FreeAction(
253 __in THEME_ACTION* pAction
254 );
255static void FreeColumn(
256 __in THEME_COLUMN* pColumn
257 );
258static void FreeTab(
259 __in THEME_TAB* pTab
260 );
261static void CALLBACK OnBillboardTimer(
262 __in THEME* pTheme,
263 __in HWND hwnd,
264 __in UINT_PTR idEvent
265 );
266static void OnBrowseDirectory(
267 __in THEME* pTheme,
268 __in HWND hWnd,
269 __in const THEME_ACTION* pAction
270 );
271static BOOL OnButtonClicked(
272 __in THEME* pTheme,
273 __in HWND hWnd,
274 __in const THEME_CONTROL* pControl
275 );
276static HRESULT OnRichEditEnLink(
277 __in LPARAM lParam,
278 __in HWND hWndRichEdit,
279 __in HWND hWnd
280 );
281static BOOL ControlIsType(
282 __in const THEME* pTheme,
283 __in DWORD dwControl,
284 __in THEME_CONTROL_TYPE type
285 );
286static const THEME_CONTROL* FindControlFromHWnd(
287 __in const THEME* pTheme,
288 __in HWND hWnd,
289 __in_opt const THEME_CONTROL* pParentControl = NULL
290 );
291static 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)
301static HRESULT SizeListViewColumns(
302 __inout THEME_CONTROL* pControl
303 );
304static LRESULT CALLBACK PanelWndProc(
305 __in HWND hWnd,
306 __in UINT uMsg,
307 __in WPARAM wParam,
308 __in LPARAM lParam
309 );
310static HRESULT LocalizeControls(
311 __in DWORD cControls,
312 __in THEME_CONTROL* rgControls,
313 __in const WIX_LOCALIZATION *pWixLoc
314 );
315static HRESULT LocalizeControl(
316 __in THEME_CONTROL* pControl,
317 __in const WIX_LOCALIZATION *pWixLoc
318 );
319static HRESULT LoadControlsString(
320 __in DWORD cControls,
321 __in THEME_CONTROL* rgControls,
322 __in HMODULE hResModule
323 );
324static HRESULT LoadControlString(
325 __in THEME_CONTROL* pControl,
326 __in HMODULE hResModule
327 );
328static void ResizeControls(
329 __in DWORD cControls,
330 __in THEME_CONTROL* rgControls,
331 __in const RECT* prcParent
332 );
333static void ResizeControl(
334 __in THEME_CONTROL* pControl,
335 __in const RECT* prcParent
336 );
337static void GetControls(
338 __in THEME* pTheme,
339 __in_opt THEME_CONTROL* pParentControl,
340 __out DWORD** ppcControls,
341 __out THEME_CONTROL*** pprgControls
342 );
343static void GetControls(
344 __in const THEME* pTheme,
345 __in_opt const THEME_CONTROL* pParentControl,
346 __out DWORD& cControls,
347 __out THEME_CONTROL*& rgControls
348 );
349static void UnloadControls(
350 __in DWORD cControls,
351 __in THEME_CONTROL* rgControls
352 );
353
354
355// Public functions.
356
357DAPI_(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
382LExit:
383 return hr;
384}
385
386
387DAPI_(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
417DAPI_(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
435LExit:
436 ReleaseStr(sczRelativePath);
437 ReleaseObject(pixd);
438
439 return hr;
440}
441
442
443DAPI_(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
467LExit:
468 ReleaseObject(pixd);
469 ReleaseStr(sczXml);
470
471 return hr;
472}
473
474
475DAPI_(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
515DAPI_(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
537LExit:
538 return hr;
539}
540
541
542DAPI_(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
553DAPI_(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
563DAPI_(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
585LExit:
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*******************************************************************/
596DAPI_(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
612LExit:
613 return hr;
614}
615
616
617DAPI_(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
655LExit:
656 ReleaseStr(sczFile);
657 ReleaseFile(hFile);
658
659 return hr;
660}
661
662
663DAPI_(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
674DAPI_(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
694LExit:
695 return hr;
696}
697
698
699DAPI_(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
709extern "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
879DAPI_(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
902DAPI_(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
919DAPI_(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
929DAPI_(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
1003LExit:
1004 return hr;
1005}
1006
1007
1008DAPI_(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
1025DAPI_(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
1046DAPI_(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
1057DAPI_(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
1068DAPI_(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
1086DAPI_(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
1101DAPI_(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
1111DAPI_(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
1133DAPI_(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
1146DAPI_(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
1170DAPI_(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
1210LExit:
1211 return hr;
1212}
1213
1214
1215DAPI_(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
1241DAPI_(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
1251DAPI_(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
1299DAPI_(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
1341LExit:
1342 return hr;
1343}
1344
1345
1346DAPI_(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
1378LExit:
1379 return hr;
1380}
1381
1382
1383DAPI_(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
1393DAPI_(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
1421LExit:
1422 return hr;
1423}
1424
1425
1426DAPI_(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
1467LExit:
1468 return hr;
1469}
1470
1471
1472DAPI_(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
1482LExit:
1483 return hr;
1484}
1485
1486
1487DAPI_(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
1505DAPI_(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
1522static 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
1562LExit:
1563 return hr;
1564}
1565
1566static 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
1602LExit:
1603 ReleaseObject(pThemeElement);
1604
1605 if (pTheme)
1606 {
1607 ThemeFree(pTheme);
1608 }
1609
1610 return hr;
1611}
1612
1613static 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
1673LExit:
1674 if (pBitmap)
1675 {
1676 delete pBitmap;
1677 }
1678
1679 ReleaseStr(sczImageFile);
1680 ReleaseBSTR(bstr);
1681
1682 return hr;
1683}
1684
1685
1686static 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
1733LExit:
1734 ReleaseStr(sczImageFile);
1735 ReleaseBSTR(bstr);
1736
1737 return hr;
1738}
1739
1740
1741static 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
1896LExit:
1897 ReleaseStr(sczIconFile);
1898 ReleaseBSTR(bstr);
1899 ReleaseObject(pixn);
1900
1901 return hr;
1902}
1903
1904
1905static 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
2025LExit:
2026 ReleaseBSTR(bstrName);
2027 ReleaseObject(pixn);
2028 ReleaseObject(pixnl);
2029
2030 return hr;
2031}
2032
2033
2034static 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
2099LExit:
2100 ReleaseBSTR(bstr);
2101
2102 return hr;
2103}
2104
2105static 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
2161LExit:
2162 ReleaseBSTR(bstrType);
2163 ReleaseObject(pixn);
2164 ReleaseObject(pixnl);
2165
2166 return hr;
2167}
2168
2169
2170static 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
2255LExit:
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
2269static 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
2288static 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
2307static 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
2475LExit:
2476 ReleaseBSTR(bstrType);
2477 ReleaseObject(pixn);
2478 ReleaseObject(pixnl);
2479
2480 return hr;
2481}
2482
2483
2484static 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
2908LExit:
2909 ReleaseBSTR(bstrText);
2910
2911 return hr;
2912}
2913
2914
2915static 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
3001LExit:
3002 ReleaseObject(pixnl);
3003 ReleaseObject(pixnChild);
3004 ReleaseBSTR(bstrType);
3005
3006 return hr;
3007}
3008
3009
3010static 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
3060LExit:
3061 ReleaseObject(pixnl);
3062 ReleaseObject(pixnChild);
3063 ReleaseBSTR(bstrText);
3064
3065 return hr;
3066}
3067
3068
3069static 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
3161LExit:
3162 ReleaseStr(sczName);
3163 ReleaseObject(pixnl);
3164 ReleaseObject(pixnChild);
3165 ReleaseObject(pixnlRadioButtons);
3166 ReleaseObject(pixnRadioButtons);
3167
3168 return hr;
3169}
3170
3171
3172static 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
3208LExit:
3209 ReleaseObject(pixnl);
3210 ReleaseObject(pixnChild);
3211 ReleaseBSTR(bstrText);
3212
3213 return hr;
3214}
3215
3216
3217static 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
3286LExit:
3287 ReleaseObject(pixnl);
3288 ReleaseObject(pixnChild);
3289 ReleaseBSTR(bstrText);
3290
3291 return hr;
3292}
3293
3294
3295static 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
3322LExit:
3323 ReleaseObject(pixnChild);
3324 ReleaseBSTR(bstrText);
3325
3326 return hr;
3327}
3328
3329
3330static 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
3402LExit:
3403 ReleaseObject(pixnl);
3404 ReleaseObject(pixnChild);
3405 ReleaseBSTR(bstrText);
3406
3407 return hr;
3408}
3409
3410
3411static 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
3437LExit:
3438 return hr;
3439}
3440
3441
3442static 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
3468static 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
3487LExit:
3488 return hr;
3489}
3490
3491
3492static 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
3532static 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
3543static 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
3588static 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
3620static 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
3660static 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
3689static 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
3710static 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
3721static 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
3786static 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
3804static void FreeColumn(
3805 __in THEME_COLUMN* pColumn
3806 )
3807{
3808 ReleaseStr(pColumn->pszName);
3809}
3810
3811
3812static void FreeConditionalText(
3813 __in THEME_CONDITIONAL_TEXT* pConditionalText
3814 )
3815{
3816 ReleaseStr(pConditionalText->sczCondition);
3817 ReleaseStr(pConditionalText->sczText);
3818}
3819
3820
3821static void FreeTab(
3822 __in THEME_TAB* pTab
3823 )
3824{
3825 ReleaseStr(pTab->pszName);
3826}
3827
3828
3829static 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
3856static 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
3871LExit:
3872 return hr;
3873}
3874
3875
3876static 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
3901static 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
3934static 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
3986LExit:
3987 if (pidl)
3988 {
3989 ::CoTaskMemFree(pidl);
3990 }
3991}
3992
3993static 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
4092LExit:
4093 return fHandled;
4094}
4095
4096static 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
4132LExit:
4133 ReleaseStr(sczLink);
4134
4135 return hr;
4136}
4137
4138static 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
4155static 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
4186static 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
4201static 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
4245LExit:
4246 return hr;
4247}
4248
4249
4250static 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
4525LExit:
4526 ReleaseStr(sczText);
4527
4528 return hr;
4529}
4530
4531static 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
4564LExit:
4565 return hr;
4566}
4567
4568
4569static 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
4606static 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
4968LExit:
4969 ReleaseStr(sczText);
4970
4971 return hr;
4972}
4973
4974static 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
4989LExit:
4990 return hr;
4991}
4992
4993static 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;
5000 LPWSTR sczLocStringId = NULL;
5001
5002 if (pControl->sczText && *pControl->sczText)
5003 {
5004 hr = LocLocalizeString(pWixLoc, &pControl->sczText);
5005 ExitOnFailure(hr, "Failed to localize control text.");
5006 }
5007 else if (pControl->sczName)
5008 {
5009 LOC_STRING* plocString = NULL;
5010
5011 hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName);
5012 ExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName);
5013
5014 hr = LocGetString(pWixLoc, sczLocStringId, &plocString);
5015 if (E_NOTFOUND != hr)
5016 {
5017 ExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName);
5018
5019 hr = StrAllocString(&pControl->sczText, plocString->wzText, 0);
5020 ExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText);
5021 }
5022 }
5023
5024 if (pControl->sczTooltip && *pControl->sczTooltip)
5025 {
5026 hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip);
5027 ExitOnFailure(hr, "Failed to localize control tooltip text.");
5028 }
5029
5030 if (pControl->sczNote && *pControl->sczNote)
5031 {
5032 hr = LocLocalizeString(pWixLoc, &pControl->sczNote);
5033 ExitOnFailure(hr, "Failed to localize control note text.");
5034 }
5035
5036 for (DWORD j = 0; j < pControl->cConditionalText; ++j)
5037 {
5038 hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText);
5039 ExitOnFailure(hr, "Failed to localize conditional text.");
5040 }
5041
5042 for (DWORD j = 0; j < pControl->cConditionalNotes; ++j)
5043 {
5044 hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText);
5045 ExitOnFailure(hr, "Failed to localize conditional note.");
5046 }
5047
5048 for (DWORD j = 0; j < pControl->cColumns; ++j)
5049 {
5050 hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName);
5051 ExitOnFailure(hr, "Failed to localize column text.");
5052 }
5053
5054 for (DWORD j = 0; j < pControl->cTabs; ++j)
5055 {
5056 hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName);
5057 ExitOnFailure(hr, "Failed to localize tab text.");
5058 }
5059
5060 // Localize control's size, location, and text.
5061 if (pControl->sczName)
5062 {
5063 hr = LocGetControl(pWixLoc, pControl->sczName, &pLocControl);
5064 if (E_NOTFOUND == hr)
5065 {
5066 ExitFunction1(hr = S_OK);
5067 }
5068 ExitOnFailure(hr, "Failed to localize control.");
5069
5070 if (LOC_CONTROL_NOT_SET != pLocControl->nX)
5071 {
5072 pControl->nX = pLocControl->nX;
5073 }
5074
5075 if (LOC_CONTROL_NOT_SET != pLocControl->nY)
5076 {
5077 pControl->nY = pLocControl->nY;
5078 }
5079
5080 if (LOC_CONTROL_NOT_SET != pLocControl->nWidth)
5081 {
5082 pControl->nWidth = pLocControl->nWidth;
5083 }
5084
5085 if (LOC_CONTROL_NOT_SET != pLocControl->nHeight)
5086 {
5087 pControl->nHeight = pLocControl->nHeight;
5088 }
5089
5090 if (pLocControl->wzText && *pLocControl->wzText)
5091 {
5092 hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0);
5093 ExitOnFailure(hr, "Failed to localize control text.");
5094 }
5095 }
5096
5097 hr = LocalizeControls(pControl->cControls, pControl->rgControls, pWixLoc);
5098
5099LExit:
5100 ReleaseStr(sczLocStringId);
5101
5102 return hr;
5103}
5104
5105static HRESULT LoadControlsString(
5106 __in DWORD cControls,
5107 __in THEME_CONTROL* rgControls,
5108 __in HMODULE hResModule
5109 )
5110{
5111 HRESULT hr = S_OK;
5112
5113 for (DWORD i = 0; i < cControls; ++i)
5114 {
5115 THEME_CONTROL* pControl = rgControls + i;
5116 hr = LoadControlString(pControl, hResModule);
5117 ExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName);
5118 }
5119
5120LExit:
5121 return hr;
5122}
5123
5124static HRESULT LoadControlString(
5125 __in THEME_CONTROL* pControl,
5126 __in HMODULE hResModule
5127 )
5128{
5129 HRESULT hr = S_OK;
5130 if (UINT_MAX != pControl->uStringId)
5131 {
5132 hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText);
5133 ExitOnFailure(hr, "Failed to load control text.");
5134
5135 for (DWORD j = 0; j < pControl->cColumns; ++j)
5136 {
5137 if (UINT_MAX != pControl->ptcColumns[j].uStringId)
5138 {
5139 hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName);
5140 ExitOnFailure(hr, "Failed to load column text.");
5141 }
5142 }
5143
5144 for (DWORD j = 0; j < pControl->cTabs; ++j)
5145 {
5146 if (UINT_MAX != pControl->pttTabs[j].uStringId)
5147 {
5148 hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName);
5149 ExitOnFailure(hr, "Failed to load tab text.");
5150 }
5151 }
5152 }
5153
5154 hr = LoadControlsString(pControl->cControls, pControl->rgControls, hResModule);
5155
5156LExit:
5157 return hr;
5158}
5159
5160static void ResizeControls(
5161 __in DWORD cControls,
5162 __in THEME_CONTROL* rgControls,
5163 __in const RECT* prcParent
5164 )
5165{
5166 for (DWORD i = 0; i < cControls; ++i)
5167 {
5168 THEME_CONTROL* pControl = rgControls + i;
5169 ResizeControl(pControl, prcParent);
5170 }
5171}
5172
5173static void ResizeControl(
5174 __in THEME_CONTROL* pControl,
5175 __in const RECT* prcParent
5176 )
5177{
5178 int w, h, x, y;
5179
5180 GetControlDimensions(prcParent, pControl, &w, &h, &x, &y);
5181 ::MoveWindow(pControl->hWnd, x, y, w, h, TRUE);
5182
5183#ifdef DEBUG
5184 if (THEME_CONTROL_TYPE_BUTTON == pControl->type)
5185 {
5186 Trace(REPORT_STANDARD, "Resizing button (%ls/%ls) to (%d,%d)+(%d,%d) for parent (%d,%d)-(%d,%d)",
5187 pControl->sczName, pControl->sczText, x, y, w, h, prcParent->left, prcParent->top, prcParent->right, prcParent->bottom);
5188 }
5189#endif
5190
5191 if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
5192 {
5193 SizeListViewColumns(pControl);
5194
5195 for (DWORD j = 0; j < pControl->cColumns; ++j)
5196 {
5197 if (-1 == ::SendMessageW(pControl->hWnd, LVM_SETCOLUMNWIDTH, (WPARAM) (int) (j), (LPARAM) (pControl->ptcColumns[j].nWidth)))
5198 {
5199 Trace(REPORT_DEBUG, "Failed to resize listview column %u with error %u", j, ::GetLastError());
5200 return;
5201 }
5202 }
5203 }
5204
5205 if (pControl->cControls)
5206 {
5207 RECT rcControl = { };
5208 ::GetClientRect(pControl->hWnd, &rcControl);
5209 ResizeControls(pControl->cControls, pControl->rgControls, &rcControl);
5210 }
5211}
5212
5213static void UnloadControls(
5214 __in DWORD cControls,
5215 __in THEME_CONTROL* rgControls
5216 )
5217{
5218 for (DWORD i = 0; i < cControls; ++i)
5219 {
5220 THEME_CONTROL* pControl = rgControls + i;
5221 pControl->hWnd = NULL;
5222
5223 UnloadControls(pControl->cControls, pControl->rgControls);
5224 }
5225}
5226
diff --git a/src/dutil/timeutil.cpp b/src/dutil/timeutil.cpp
new file mode 100644
index 00000000..dacb2660
--- /dev/null
+++ b/src/dutil/timeutil.cpp
@@ -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
5const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" };
6const 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" };
7enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone };
8enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone };
9
10// prototypes
11static HRESULT DayFromString(
12 __in_z LPCWSTR wzDay,
13 __out WORD* pwDayOfWeek
14 );
15static HRESULT MonthFromString(
16 __in_z LPCWSTR wzMonth,
17 __out WORD* pwMonthOfYear
18 );
19
20
21/********************************************************************
22 TimeFromString - converts string to FILETIME
23
24*******************************************************************/
25extern "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
110LExit:
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*******************************************************************/
120extern "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
194LExit:
195 ReleaseStr(pwzTime);
196
197 return hr;
198}
199/****************************************************************************
200TimeCurrentTime - gets the current time in string format
201
202****************************************************************************/
203extern "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/****************************************************************************
229TimeCurrentDateTime - gets the current date and time in string format,
230 per format described in RFC 3339
231****************************************************************************/
232extern "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/****************************************************************************
246TimeSystemDateTime - converts the provided system time struct to string format,
247 per format described in RFC 3339
248****************************************************************************/
249extern "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/****************************************************************************
276TimeSystemToDateTimeString - converts the provided system time struct to
277 string format representing date and time for the specified locale
278****************************************************************************/
279HRESULT 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
319LExit:
320 return hr;
321}
322
323/********************************************************************
324 DayFromString - converts string to day
325
326*******************************************************************/
327static 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*******************************************************************/
352static 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}
diff --git a/src/dutil/uncutil.cpp b/src/dutil/uncutil.cpp
new file mode 100644
index 00000000..6deb43bd
--- /dev/null
+++ b/src/dutil/uncutil.cpp
@@ -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
5DAPI_(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
50LExit:
51 ReleaseStr(sczDrive);
52
53 return hr;
54}
diff --git a/src/dutil/uriutil.cpp b/src/dutil/uriutil.cpp
new file mode 100644
index 00000000..fc192b3f
--- /dev/null
+++ b/src/dutil/uriutil.cpp
@@ -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//
9extern "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
25LExit:
26 return hr;
27}
28
29
30//
31// UriCrack - cracks a URI into constituent parts.
32//
33extern "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
129LExit:
130 return hr;
131}
132
133
134//
135// UriCrackEx - cracks a URI into URI_INFO.
136//
137extern "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
147LExit:
148 return hr;
149}
150
151
152//
153// UriInfoUninitialize - frees the memory in a URI_INFO struct.
154//
155extern "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//
171extern "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
204LExit:
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//
214extern "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
238LExit:
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//
252extern "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
276LExit:
277 return hr;
278}
279
280
281/*******************************************************************
282 UriProtocol - determines the protocol of a URI.
283
284********************************************************************/
285extern "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********************************************************************/
356extern "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
467LExit:
468 return hr;
469}
470
471
472extern "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
535LExit:
536#endif
537 return hr;
538}
diff --git a/src/dutil/userutil.cpp b/src/dutil/userutil.cpp
new file mode 100644
index 00000000..2e77f1df
--- /dev/null
+++ b/src/dutil/userutil.cpp
@@ -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
5static 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********************************************************************/
15extern "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
69LExit:
70 return hr;
71}
72
73
74/*******************************************************************
75 Checks whether a user is a member of a group - outputs the result via lpfMember
76********************************************************************/
77extern "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
194LExit:
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********************************************************************/
213extern "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
252LExit:
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********************************************************************/
264static 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}
diff --git a/src/dutil/varutil.cpp b/src/dutil/varutil.cpp
new file mode 100644
index 00000000..88716105
--- /dev/null
+++ b/src/dutil/varutil.cpp
@@ -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
5struct VARIABLE_ENUM_STRUCT
6{
7};
8
9struct VARIABLES_STRUCT
10{
11};
12
13const int VARIABLE_ENUM_HANDLE_BYTES = sizeof(VARIABLE_ENUM_STRUCT);
14const int VARIABLES_HANDLE_BYTES = sizeof(VARIABLES_STRUCT);
15
16// function definitions
17
18/********************************************************************
19VarCreate - creates a variables group.
20********************************************************************/
21extern "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/********************************************************************
30VarDestroy - destroys a variables group, accepting an optional callback
31 to help free the variable contexts.
32********************************************************************/
33extern "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/********************************************************************
43VarFreeValue - frees a variable value.
44********************************************************************/
45extern "C" void DAPI VarFreeValue(
46 __in VARIABLE_VALUE* pValue
47 )
48{
49 UNREFERENCED_PARAMETER(pValue);
50}
51
52/********************************************************************
53VarEscapeString - escapes special characters in wzIn so that it can
54 be used in conditions or variable values.
55********************************************************************/
56extern "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/********************************************************************
67VarFormatString - similar to MsiFormatRecord.
68********************************************************************/
69extern "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/********************************************************************
84VarGetFormatted - gets the formatted value of a single variable.
85********************************************************************/
86extern "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/********************************************************************
99VarGetNumeric - 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********************************************************************/
103extern "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/********************************************************************
116VarGetString - 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********************************************************************/
120extern "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/********************************************************************
133VarGetVersion - 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********************************************************************/
137extern "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/********************************************************************
150VarGetValue - gets the value of a variable along with its metadata.
151********************************************************************/
152extern "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/********************************************************************
165VarSetNumeric - 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********************************************************************/
169extern "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/********************************************************************
182VarSetString - 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********************************************************************/
186extern "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/********************************************************************
199VarSetVersion - 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********************************************************************/
203extern "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/********************************************************************
216VarSetValue - sets the value of the variable along with its metadata.
217 Also adds the variable to the group if necessary.
218********************************************************************/
219extern "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/********************************************************************
232VarStartEnum - starts the enumeration of the variable group. There
233 is no guarantee for the order of the variable enumeration.
234
235NOTE: caller is responsible for calling VarFinishEnum even if function fails
236********************************************************************/
237extern "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/********************************************************************
250VarNextVariable - continues the enumeration of the variable group. It
251 will fail if any variables were added or removed
252 during the enumeration.
253
254NOTE: caller is responsible for calling VarFinishEnum even if function fails
255********************************************************************/
256extern "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/********************************************************************
267VarFinishEnum - cleans up resources used for the enumeration.
268********************************************************************/
269extern "C" void DAPI VarFinishEnum(
270 __in_bcount(VARIABLE_ENUM_HANDLE_BYTES) VARIABLE_ENUM_HANDLE pEnum
271 )
272{
273 UNREFERENCED_PARAMETER(pEnum);
274}
diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp
new file mode 100644
index 00000000..1a489d54
--- /dev/null
+++ b/src/dutil/wiutil.cpp
@@ -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
8const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF;
9const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64;
10
11
12// structs
13
14
15static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW;
16static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW;
17static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW;
18static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW;
19static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW;
20static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW;
21static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW;
22static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW;
23static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI;
24static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW;
25static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW;
26static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW;
27
28// MSI 3.0+
29static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL;
30static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL;
31static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL;
32static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL;
33static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL;
34static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL;
35static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL;
36
37static HMODULE vhMsiDll = NULL;
38static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL;
39static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL;
40static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL;
41static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL;
42static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL;
43static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL;
44static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL;
45static BOOL vfWiuInitialized = FALSE;
46
47// globals
48static DWORD vdwMsiDllMajorMinor = 0;
49static DWORD vdwMsiDllBuildRevision = 0;
50
51
52// internal function declarations
53
54static DWORD CheckForRestartErrorCode(
55 __in DWORD dwErrorCode,
56 __out WIU_RESTART* pRestart
57 );
58static INT CALLBACK InstallEngineCallback(
59 __in LPVOID pvContext,
60 __in UINT uiMessage,
61 __in_z_opt LPCWSTR wzMessage
62 );
63static INT CALLBACK InstallEngineRecordCallback(
64 __in LPVOID pvContext,
65 __in UINT uiMessage,
66 __in_opt MSIHANDLE hRecord
67 );
68static 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 );
75static INT HandleInstallProgress(
76 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
77 __in_z_opt LPCWSTR wzMessage,
78 __in_opt MSIHANDLE hRecord
79 );
80static 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 );
87static INT SendErrorMessage(
88 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
89 __in UINT uiFlags,
90 __in_z LPCWSTR wzMessage,
91 __in_opt MSIHANDLE hRecord
92 );
93static INT SendFilesInUseMessage(
94 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
95 __in_opt MSIHANDLE hRecord,
96 __in BOOL fRestartManagerRequest
97 );
98static INT SendProgressUpdate(
99 __in WIU_MSI_EXECUTE_CONTEXT* pContext
100 );
101static void ResetProgress(
102 __in WIU_MSI_EXECUTE_CONTEXT* pContext
103 );
104static DWORD CalculatePhaseProgress(
105 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
106 __in DWORD dwProgressIndex,
107 __in DWORD dwWeightPercentage
108 );
109void InitializeMessageData(
110 __in MSIHANDLE hRecord,
111 __out LPWSTR** prgsczData,
112 __out DWORD* pcData
113 );
114void UninitializeMessageData(
115 __in LPWSTR* rgsczData,
116 __in DWORD cData
117 );
118
119
120/********************************************************************
121 WiuInitialize - initializes wiutil
122
123*********************************************************************/
124extern "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
181LExit:
182 ReleaseStr(sczMsiDllPath);
183 return hr;
184}
185
186
187/********************************************************************
188 WiuUninitialize - uninitializes wiutil
189
190*********************************************************************/
191extern "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*********************************************************************/
216extern "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
248extern "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
296LExit:
297 return hr;
298}
299
300
301extern "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
348LExit:
349 return hr;
350}
351
352
353extern "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
368LExit:
369 return hr;
370}
371
372
373extern "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
397LExit:
398 return hr;
399}
400
401
402extern "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
436LExit:
437 return hr;
438}
439
440
441extern "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
465LExit:
466 return hr;
467}
468
469
470extern "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
502LExit:
503 return hr;
504}
505
506
507extern "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
526LExit:
527 return hr;
528}
529
530
531extern "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
548LExit:
549 return hr;
550}
551
552
553extern "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
568LExit:
569 return hr;
570}
571
572
573extern "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
599LExit:
600 return hr;
601}
602
603
604extern "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
620LExit:
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********************************************************************/
633extern "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
695LExit:
696 ReleaseStr(sczInstalledVersion);
697 return hr;
698}
699
700
701extern "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
713LExit:
714 return hr;
715}
716
717
718extern "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
764LExit:
765 return hr;
766}
767
768
769extern "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
792extern "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
807LExit:
808 return hr;
809}
810
811
812extern "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
825LExit:
826 return hr;
827}
828
829
830extern "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
844LExit:
845 return hr;
846}
847
848
849extern "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
864LExit:
865 return hr;
866}
867
868
869
870static 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
893static 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
919static 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
965LExit:
966 ReleaseStr(sczMessage);
967 return nResult;
968}
969
970static 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
980Trace(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
1073static 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
1249LExit:
1250 return nResult;
1251}
1252
1253static 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
1280static 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
1325static 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
1350static 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
1402static 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
1410static 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
1437void 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
1479void 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}
diff --git a/src/dutil/wuautil.cpp b/src/dutil/wuautil.cpp
new file mode 100644
index 00000000..94ab659d
--- /dev/null
+++ b/src/dutil/wuautil.cpp
@@ -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
8static HRESULT GetAutomaticUpdatesService(
9 __out IAutomaticUpdates **ppAutomaticUpdates
10 );
11
12
13// function definitions
14
15extern "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
26LExit:
27 ReleaseObject(pAutomaticUpdates);
28
29 return hr;
30}
31
32extern "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
43LExit:
44 ReleaseObject(pAutomaticUpdates);
45
46 return hr;
47}
48
49extern "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
65LExit:
66 ReleaseObject(pSystemInformation);
67
68 return hr;
69}
70
71
72// internal function definitions
73
74static 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
87LExit:
88 return hr;
89}
diff --git a/src/dutil/xmlutil.cpp b/src/dutil/xmlutil.cpp
new file mode 100644
index 00000000..e07c205d
--- /dev/null
+++ b/src/dutil/xmlutil.cpp
@@ -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
6CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} };
7static volatile LONG vcXmlInitialized = 0;
8static BOOL vfMsxml40 = FALSE;
9static BOOL fComInitialized = FALSE;
10BOOL vfMsxml30 = FALSE;
11
12/********************************************************************
13 XmlInitialize - finds an appropriate version of the XML DOM
14
15*********************************************************************/
16extern "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;
62LExit:
63 return hr;
64}
65
66
67/********************************************************************
68 XmUninitialize -
69
70*********************************************************************/
71extern "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
89extern "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);
104LExit:
105 ReleaseBSTR(bstrElementName);
106 return hr;
107}
108
109
110/********************************************************************
111 XmlCreateDocument -
112
113*********************************************************************/
114extern "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
183LExit:
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*********************************************************************/
201extern "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*********************************************************************/
214static 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
250LExit:
251 ReleaseBSTR(bstr);
252}
253
254/********************************************************************
255 XmlLoadDocumentEx -
256
257*********************************************************************/
258extern "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;
315LExit:
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********************************************************************/
333extern "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********************************************************************/
346extern "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;
404LExit:
405 ReleaseVariant(varPath);
406 ReleaseObject(pixd);
407 ReleaseObject(pixpe);
408
409 return hr;
410}
411
412
413/********************************************************************
414 XmlLoadDocumentFromBuffer
415
416*********************************************************************/
417extern "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
465LExit:
466 ReleaseObject(pixdDocument);
467 return hr;
468}
469
470
471/********************************************************************
472 XmlSetAttribute -
473
474*********************************************************************/
475extern "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
520LExit:
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*********************************************************************/
536extern "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
554LExit:
555 ReleaseBSTR(bstrXPath);
556
557 return hr;
558}
559
560
561/********************************************************************
562 XmlCreateTextNode -
563
564*********************************************************************/
565extern "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);
580LExit:
581 ReleaseBSTR(bstrText);
582
583 return hr;
584}
585
586
587/********************************************************************
588 XmlGetText
589
590*********************************************************************/
591extern "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*********************************************************************/
604extern "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
644LExit:
645 ReleaseObject(pixnnmAttributes);
646 ReleaseObject(pixnAttribute);
647 ReleaseVariant(varAttributeValue);
648 ReleaseBSTR(bstrAttribute);
649
650 return hr;
651}
652
653
654/********************************************************************
655 XmlGetAttributeEx
656
657*********************************************************************/
658HRESULT 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
698LExit:
699 ReleaseObject(pixnnmAttributes);
700 ReleaseObject(pixnAttribute);
701 ReleaseVariant(varAttributeValue);
702 ReleaseBSTR(bstrAttribute);
703
704 return hr;
705}
706
707
708/********************************************************************
709 XmlGetYesNoAttribute
710
711*********************************************************************/
712HRESULT 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
729LExit:
730 ReleaseStr(sczValue);
731
732 return hr;
733}
734
735
736
737/********************************************************************
738 XmlGetAttributeNumber
739
740*********************************************************************/
741extern "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*********************************************************************/
756extern "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
774LExit:
775 ReleaseBSTR(bstrPointer);
776 return hr;
777}
778
779
780/********************************************************************
781 XmlGetAttributeLargeNumber
782
783*********************************************************************/
784extern "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
809LExit:
810 ReleaseBSTR(bstrValue);
811 return hr;
812}
813
814
815/********************************************************************
816 XmlGetNamedItem -
817
818*********************************************************************/
819extern "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
836LExit:
837 ReleaseBSTR(bstrName);
838 return hr;
839}
840
841
842/********************************************************************
843 XmlSetText -
844
845*********************************************************************/
846extern "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
913LExit:
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*********************************************************************/
927extern "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
940LExit:
941 return hr;
942}
943
944
945/********************************************************************
946 XmlCreateChild -
947
948*********************************************************************/
949extern "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
988LExit:
989 ReleaseObject(pixdDocument);
990 ReleaseObject(pixnChild);
991 return hr;
992}
993
994/********************************************************************
995 XmlRemoveAttribute -
996
997*********************************************************************/
998extern "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
1016LExit:
1017 ReleaseObject(pixnnmAttributes);
1018 ReleaseBSTR(bstrAttribute);
1019
1020 return hr;
1021}
1022
1023
1024/********************************************************************
1025 XmlSelectNodes -
1026
1027*********************************************************************/
1028extern "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
1046LExit:
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********************************************************************/
1060extern "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
1104LExit:
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********************************************************************/
1118extern "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;
1163LExit:
1164 ReleaseObject(pixn);
1165 return hr;
1166}
1167
1168
1169/********************************************************************
1170 XmlRemoveChildren -
1171
1172*********************************************************************/
1173extern "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
1213LExit:
1214 ReleaseObject(pixnlNodeList);
1215 ReleaseObject(pixnNode);
1216 ReleaseObject(pixnRemoveChild);
1217
1218 return hr;
1219}
1220
1221
1222/********************************************************************
1223 XmlSaveDocument -
1224
1225*********************************************************************/
1226extern "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
1252LExit:
1253 ReleaseVariant(varsDestPath);
1254 return hr;
1255}
1256
1257
1258/********************************************************************
1259 XmlSaveDocumentToBuffer
1260
1261*********************************************************************/
1262extern "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
1313LExit:
1314 ReleaseObject(pStream);
1315 ReleaseMem(pbDest);
1316 return hr;
1317}
diff --git a/src/dutil/xsd/thmutil.xsd b/src/dutil/xsd/thmutil.xsd
new file mode 100644
index 00000000..ccf951c0
--- /dev/null
+++ b/src/dutil/xsd/thmutil.xsd
@@ -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>