main
cpp 2,795 lines 79.1 KB
Raw
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 // Exit macros
7 #define StrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
8 #define StrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
9 #define StrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
10 #define StrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
11 #define StrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
12 #define StrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
13 #define StrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__)
14 #define StrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__)
15 #define StrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__)
16 #define StrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__)
17 #define StrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_STRUTIL, e, x, s, __VA_ARGS__)
18 #define StrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_STRUTIL, g, x, s, __VA_ARGS__)
19
20 #define ARRAY_GROWTH_SIZE 5
21
22 // Forward declarations.
23 static HRESULT AllocHelper(
24 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
25 __in SIZE_T cch,
26 __in BOOL fZeroOnRealloc
27 );
28 static HRESULT AllocStringHelper(
29 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
30 __in_z LPCWSTR wzSource,
31 __in SIZE_T cchSource,
32 __in BOOL fZeroOnRealloc
33 );
34 static HRESULT AllocConcatHelper(
35 __deref_out_z LPWSTR* ppwz,
36 __in_z LPCWSTR wzSource,
37 __in SIZE_T cchSource,
38 __in BOOL fZeroOnRealloc
39 );
40 static HRESULT AllocFormattedArgsHelper(
41 __deref_out_z LPWSTR* ppwz,
42 __in BOOL fZeroOnRealloc,
43 __in __format_string LPCWSTR wzFormat,
44 __in va_list args
45 );
46 static HRESULT StrAllocStringMapInvariant(
47 __deref_out_z LPWSTR* pscz,
48 __in_z LPCWSTR wzSource,
49 __in SIZE_T cchSource,
50 __in DWORD dwMapFlags
51 );
52
53 /********************************************************************
54 StrAlloc - allocates or reuses dynamic string memory
55
56 NOTE: caller is responsible for freeing ppwz even if function fails
57 ********************************************************************/
58 extern "C" HRESULT DAPI StrAlloc(
59 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
60 __in SIZE_T cch
61 )
62 {
63 return AllocHelper(ppwz, cch, FALSE);
64 }
65
66 /********************************************************************
67 StrAllocSecure - allocates or reuses dynamic string memory
68 If the memory needs to reallocated, calls SecureZeroMemory on the
69 original block of memory after it is moved.
70
71 NOTE: caller is responsible for freeing ppwz even if function fails
72 ********************************************************************/
73 extern "C" HRESULT DAPI StrAllocSecure(
74 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
75 __in SIZE_T cch
76 )
77 {
78 return AllocHelper(ppwz, cch, TRUE);
79 }
80
81 /********************************************************************
82 AllocHelper - allocates or reuses dynamic string memory
83 If fZeroOnRealloc is true and the memory needs to reallocated,
84 calls SecureZeroMemory on original block of memory after it is moved.
85
86 NOTE: caller is responsible for freeing ppwz even if function fails
87 ********************************************************************/
88 static HRESULT AllocHelper(
89 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
90 __in SIZE_T cch,
91 __in BOOL fZeroOnRealloc
92 )
93 {
94 Assert(ppwz && cch);
95
96 HRESULT hr = S_OK;
97 LPWSTR pwz = NULL;
98
99 if (cch >= MAXDWORD / sizeof(WCHAR))
100 {
101 hr = E_OUTOFMEMORY;
102 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
103 }
104
105 if (*ppwz)
106 {
107 if (fZeroOnRealloc)
108 {
109 LPVOID pvNew = NULL;
110 hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew);
111 StrExitOnFailure(hr, "Failed to reallocate string");
112 pwz = static_cast<LPWSTR>(pvNew);
113 }
114 else
115 {
116 pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE));
117 }
118 }
119 else
120 {
121 pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
122 }
123
124 StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
125
126 *ppwz = pwz;
127 LExit:
128 return hr;
129 }
130
131
132 /********************************************************************
133 StrTrimCapacity - Frees any unnecessary memory associated with a string.
134 Purely used for optimization, generally only when a string
135 has been changing size, and will no longer grow.
136
137 NOTE: caller is responsible for freeing ppwz even if function fails
138 ********************************************************************/
139 HRESULT DAPI StrTrimCapacity(
140 __deref_out_z LPWSTR* ppwz
141 )
142 {
143 Assert(ppwz);
144
145 HRESULT hr = S_OK;
146 SIZE_T cchLen = 0;
147
148 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
149 StrExitOnRootFailure(hr, "Failed to calculate length of string");
150
151 ++cchLen; // Add 1 for null-terminator
152
153 hr = StrAlloc(ppwz, cchLen);
154 StrExitOnFailure(hr, "Failed to reallocate string");
155
156 LExit:
157 return hr;
158 }
159
160
161 /********************************************************************
162 StrTrimWhitespace - allocates or reuses dynamic string memory and copies
163 in an existing string, excluding whitespace
164
165 NOTE: caller is responsible for freeing ppwz even if function fails
166 ********************************************************************/
167 HRESULT DAPI StrTrimWhitespace(
168 __deref_out_z LPWSTR* ppwz,
169 __in_z LPCWSTR wzSource
170 )
171 {
172 HRESULT hr = S_OK;
173 size_t i = 0;
174 LPWSTR sczResult = NULL;
175
176 // Ignore beginning whitespace
177 while (L' ' == *wzSource || L'\t' == *wzSource)
178 {
179 wzSource++;
180 }
181
182 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &i);
183 StrExitOnRootFailure(hr, "Failed to get length of string");
184
185 // Overwrite ending whitespace with null characters
186 if (0 < i)
187 {
188 // start from the last non-null-terminator character in the array
189 for (i = i - 1; i > 0; --i)
190 {
191 if (L' ' != wzSource[i] && L'\t' != wzSource[i])
192 {
193 break;
194 }
195 }
196
197 ++i;
198 }
199
200 hr = StrAllocString(&sczResult, wzSource, i);
201 StrExitOnFailure(hr, "Failed to copy result string");
202
203 // Output result
204 *ppwz = sczResult;
205 sczResult = NULL;
206
207 LExit:
208 ReleaseStr(sczResult);
209
210 return hr;
211 }
212
213
214 /********************************************************************
215 StrAnsiAlloc - allocates or reuses dynamic ANSI string memory
216
217 NOTE: caller is responsible for freeing ppsz even if function fails
218 ********************************************************************/
219 extern "C" HRESULT DAPI StrAnsiAlloc(
220 __deref_out_ecount_part(cch, 0) LPSTR* ppsz,
221 __in SIZE_T cch
222 )
223 {
224 Assert(ppsz && cch);
225
226 HRESULT hr = S_OK;
227 LPSTR psz = NULL;
228
229 if (cch >= MAXDWORD / sizeof(WCHAR))
230 {
231 hr = E_OUTOFMEMORY;
232 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
233 }
234
235 if (*ppsz)
236 {
237 psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE));
238 }
239 else
240 {
241 psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
242 }
243
244 StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
245
246 *ppsz = psz;
247 LExit:
248 return hr;
249 }
250
251
252 /********************************************************************
253 StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string.
254 Purely used for optimization, generally only when a string
255 has been changing size, and will no longer grow.
256
257 NOTE: caller is responsible for freeing ppwz even if function fails
258 ********************************************************************/
259 HRESULT DAPI StrAnsiTrimCapacity(
260 __deref_out_z LPSTR* ppz
261 )
262 {
263 Assert(ppz);
264
265 HRESULT hr = S_OK;
266 SIZE_T cchLen = 0;
267
268 #pragma prefast(push)
269 #pragma prefast(disable:25068)
270 hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
271 #pragma prefast(pop)
272 StrExitOnFailure(hr, "Failed to calculate length of string");
273
274 ++cchLen; // Add 1 for null-terminator
275
276 hr = StrAnsiAlloc(ppz, cchLen);
277 StrExitOnFailure(hr, "Failed to reallocate string");
278
279 LExit:
280 return hr;
281 }
282
283
284 /********************************************************************
285 StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies
286 in an existing string, excluding whitespace
287
288 NOTE: caller is responsible for freeing ppz even if function fails
289 ********************************************************************/
290 HRESULT DAPI StrAnsiTrimWhitespace(
291 __deref_out_z LPSTR* ppz,
292 __in_z LPCSTR szSource
293 )
294 {
295 HRESULT hr = S_OK;
296 size_t i = 0;
297 LPSTR sczResult = NULL;
298
299 // Ignore beginning whitespace
300 while (' ' == *szSource || '\t' == *szSource)
301 {
302 szSource++;
303 }
304
305 hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, &i);
306 StrExitOnRootFailure(hr, "Failed to get length of string");
307
308 // Overwrite ending whitespace with null characters
309 if (0 < i)
310 {
311 // start from the last non-null-terminator character in the array
312 for (i = i - 1; i > 0; --i)
313 {
314 if (L' ' != szSource[i] && L'\t' != szSource[i])
315 {
316 break;
317 }
318 }
319
320 ++i;
321 }
322
323 hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i);
324 StrExitOnFailure(hr, "Failed to copy result string");
325
326 // Output result
327 *ppz = sczResult;
328 sczResult = NULL;
329
330 LExit:
331 ReleaseStr(sczResult);
332
333 return hr;
334 }
335
336 /********************************************************************
337 StrAllocString - allocates or reuses dynamic string memory and copies in an existing string
338
339 NOTE: caller is responsible for freeing ppwz even if function fails
340 NOTE: cchSource does not have to equal the length of wzSource
341 NOTE: if cchSource == 0, length of wzSource is used instead
342 ********************************************************************/
343 extern "C" HRESULT DAPI StrAllocString(
344 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
345 __in_z LPCWSTR wzSource,
346 __in SIZE_T cchSource
347 )
348 {
349 return AllocStringHelper(ppwz, wzSource, cchSource, FALSE);
350 }
351
352 /********************************************************************
353 StrAllocStringSecure - allocates or reuses dynamic string memory and
354 copies in an existing string. If the memory needs to reallocated,
355 calls SecureZeroMemory on original block of memory after it is moved.
356
357 NOTE: caller is responsible for freeing ppwz even if function fails
358 NOTE: cchSource does not have to equal the length of wzSource
359 NOTE: if cchSource == 0, length of wzSource is used instead
360 ********************************************************************/
361 extern "C" HRESULT DAPI StrAllocStringSecure(
362 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
363 __in_z LPCWSTR wzSource,
364 __in SIZE_T cchSource
365 )
366 {
367 return AllocStringHelper(ppwz, wzSource, cchSource, TRUE);
368 }
369
370 /********************************************************************
371 AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string
372 If fZeroOnRealloc is true and the memory needs to reallocated,
373 calls SecureZeroMemory on original block of memory after it is moved.
374
375 NOTE: caller is responsible for freeing ppwz even if function fails
376 NOTE: cchSource does not have to equal the length of wzSource
377 NOTE: if cchSource == 0, length of wzSource is used instead
378 ********************************************************************/
379 static HRESULT AllocStringHelper(
380 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
381 __in_z LPCWSTR wzSource,
382 __in SIZE_T cchSource,
383 __in BOOL fZeroOnRealloc
384 )
385 {
386 Assert(ppwz && wzSource); // && *wzSource);
387
388 HRESULT hr = S_OK;
389 SIZE_T cch = 0;
390
391 if (*ppwz)
392 {
393 hr = StrMaxLength(*ppwz, &cch);
394 StrExitOnFailure(hr, "failed to get size of destination string");
395 }
396
397 if (0 == cchSource && wzSource)
398 {
399 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSource));
400 StrExitOnRootFailure(hr, "failed to get length of source string");
401 }
402
403 SIZE_T cchNeeded;
404 hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
405 StrExitOnRootFailure(hr, "source string is too long");
406
407 if (cch < cchNeeded)
408 {
409 cch = cchNeeded;
410 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
411 StrExitOnFailure(hr, "failed to allocate string from string.");
412 }
413
414 // copy everything (the NULL terminator will be included)
415 hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
416
417 LExit:
418 return hr;
419 }
420
421
422 /********************************************************************
423 StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string
424
425 NOTE: caller is responsible for freeing ppsz even if function fails
426 NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
427 NOTE: if cchSource == 0, length of wzSource is used instead
428 ********************************************************************/
429 extern "C" HRESULT DAPI StrAnsiAllocString(
430 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
431 __in_z LPCWSTR wzSource,
432 __in SIZE_T cchSource,
433 __in UINT uiCodepage
434 )
435 {
436 Assert(ppsz && wzSource);
437
438 HRESULT hr = S_OK;
439 LPSTR psz = NULL;
440 SIZE_T cch = 0;
441 SIZE_T cchDest = cchSource; // at least enough
442
443 if (*ppsz)
444 {
445 hr = StrMaxLengthAnsi(*ppsz, &cch);
446 StrExitOnFailure(hr, "failed to get size of destination string");
447 }
448
449 if (0 == cchSource)
450 {
451 cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL);
452 if (0 == cchDest)
453 {
454 StrExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource);
455 }
456
457 --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below
458 }
459 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
460 {
461 cchDest = cchSource - 1;
462 }
463
464 if (cch < cchDest + 1)
465 {
466 cch = cchDest + 1; // add one for the NULL terminator
467 if (cch >= MAXDWORD / sizeof(WCHAR))
468 {
469 hr = E_OUTOFMEMORY;
470 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
471 }
472
473 if (*ppsz)
474 {
475 psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE));
476 }
477 else
478 {
479 psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
480 }
481 StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
482
483 *ppsz = psz;
484 }
485
486 if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL))
487 {
488 StrExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource);
489 }
490 (*ppsz)[cchDest] = L'\0';
491
492 LExit:
493 return hr;
494 }
495
496
497 /********************************************************************
498 StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string
499
500 NOTE: caller is responsible for freeing ppwz even if function fails
501 NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
502 NOTE: if cchSource == 0, length of szSource is used instead
503 ********************************************************************/
504 extern "C" HRESULT DAPI StrAllocStringAnsi(
505 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
506 __in_z LPCSTR szSource,
507 __in SIZE_T cchSource,
508 __in UINT uiCodepage
509 )
510 {
511 Assert(ppwz && szSource);
512
513 HRESULT hr = S_OK;
514 LPWSTR pwz = NULL;
515 SIZE_T cch = 0;
516 SIZE_T cchDest = cchSource; // at least enough
517
518 if (*ppwz)
519 {
520 hr = StrMaxLength(*ppwz, &cch);
521 StrExitOnFailure(hr, "failed to get size of destination string");
522 }
523
524 if (0 == cchSource)
525 {
526 cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0);
527 if (0 == cchDest)
528 {
529 StrExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource);
530 }
531
532 --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below
533 }
534 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
535 {
536 cchDest = cchSource - 1;
537 }
538
539 if (cch < cchDest + 1)
540 {
541 cch = cchDest + 1;
542 if (cch >= MAXDWORD / sizeof(WCHAR))
543 {
544 hr = E_OUTOFMEMORY;
545 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
546 }
547
548 if (*ppwz)
549 {
550 pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE));
551 }
552 else
553 {
554 pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
555 }
556
557 StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
558
559 *ppwz = pwz;
560 }
561
562 if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch))
563 {
564 StrExitWithLastError(hr, "failed to convert to unicode: %s", szSource);
565 }
566 (*ppwz)[cchDest] = L'\0';
567
568 LExit:
569 return hr;
570 }
571
572
573 /********************************************************************
574 StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string
575
576 NOTE: caller is responsible for freeing ppsz even if function fails
577 NOTE: cchSource does not have to equal the length of wzSource
578 NOTE: if cchSource == 0, length of wzSource is used instead
579 ********************************************************************/
580 HRESULT DAPI StrAnsiAllocStringAnsi(
581 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
582 __in_z LPCSTR szSource,
583 __in SIZE_T cchSource
584 )
585 {
586 Assert(ppsz && szSource); // && *szSource);
587
588 HRESULT hr = S_OK;
589 SIZE_T cch = 0;
590
591 if (*ppsz)
592 {
593 hr = StrMaxLengthAnsi(*ppsz, &cch);
594 StrExitOnRootFailure(hr, "failed to get size of destination string");
595 }
596
597 if (0 == cchSource && szSource)
598 {
599 hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSource));
600 StrExitOnRootFailure(hr, "failed to get length of source string");
601 }
602
603 SIZE_T cchNeeded;
604 hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
605 StrExitOnRootFailure(hr, "source string is too long");
606
607 if (cch < cchNeeded)
608 {
609 cch = cchNeeded;
610 hr = StrAnsiAlloc(ppsz, cch);
611 StrExitOnFailure(hr, "failed to allocate string from string.");
612 }
613
614 // copy everything (the NULL terminator will be included)
615 #pragma prefast(push)
616 #pragma prefast(disable:25068)
617 hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
618 #pragma prefast(pop)
619
620 LExit:
621 return hr;
622 }
623
624
625 /********************************************************************
626 StrAllocPrefix - allocates or reuses dynamic string memory and
627 prefixes a string
628
629 NOTE: caller is responsible for freeing ppwz even if function fails
630 NOTE: cchPrefix does not have to equal the length of wzPrefix
631 NOTE: if cchPrefix == 0, length of wzPrefix is used instead
632 ********************************************************************/
633 extern "C" HRESULT DAPI StrAllocPrefix(
634 __deref_out_z LPWSTR* ppwz,
635 __in_z LPCWSTR wzPrefix,
636 __in SIZE_T cchPrefix
637 )
638 {
639 Assert(ppwz && wzPrefix);
640
641 HRESULT hr = S_OK;
642 SIZE_T cch = 0;
643 SIZE_T cchLen = 0;
644
645 if (*ppwz)
646 {
647 hr = StrMaxLength(*ppwz, &cch);
648 StrExitOnFailure(hr, "failed to get size of destination string");
649
650 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
651 StrExitOnFailure(hr, "Failed to calculate length of string");
652 }
653
654 Assert(cchLen <= cch);
655
656 if (0 == cchPrefix)
657 {
658 hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchPrefix));
659 StrExitOnFailure(hr, "Failed to calculate length of string");
660 }
661
662 if (cch - cchLen < cchPrefix + 1)
663 {
664 cch = cchPrefix + cchLen + 1;
665 hr = StrAlloc(ppwz, cch);
666 StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix);
667 }
668
669 if (*ppwz)
670 {
671 SIZE_T cb = cch * sizeof(WCHAR);
672 SIZE_T cbPrefix = cchPrefix * sizeof(WCHAR);
673
674 memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix);
675 memcpy(*ppwz, wzPrefix, cbPrefix);
676 }
677 else
678 {
679 hr = E_UNEXPECTED;
680 StrExitOnFailure(hr, "for some reason our buffer is still null");
681 }
682
683 LExit:
684 return hr;
685 }
686
687
688 /********************************************************************
689 StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string
690
691 NOTE: caller is responsible for freeing ppwz even if function fails
692 NOTE: cchSource does not have to equal the length of wzSource
693 NOTE: if cchSource == 0, length of wzSource is used instead
694 ********************************************************************/
695 extern "C" HRESULT DAPI StrAllocConcat(
696 __deref_out_z LPWSTR* ppwz,
697 __in_z LPCWSTR wzSource,
698 __in SIZE_T cchSource
699 )
700 {
701 return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE);
702 }
703
704
705 /********************************************************************
706 StrAllocConcatSecure - allocates or reuses dynamic string memory and
707 adds an existing string. If the memory needs to reallocated, calls
708 SecureZeroMemory on the original block of memory after it is moved.
709
710 NOTE: caller is responsible for freeing ppwz even if function fails
711 NOTE: cchSource does not have to equal the length of wzSource
712 NOTE: if cchSource == 0, length of wzSource is used instead
713 ********************************************************************/
714 extern "C" HRESULT DAPI StrAllocConcatSecure(
715 __deref_out_z LPWSTR* ppwz,
716 __in_z LPCWSTR wzSource,
717 __in SIZE_T cchSource
718 )
719 {
720 return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE);
721 }
722
723
724 /********************************************************************
725 AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string
726 If fZeroOnRealloc is true and the memory needs to reallocated,
727 calls SecureZeroMemory on original block of memory after it is moved.
728
729 NOTE: caller is responsible for freeing ppwz even if function fails
730 NOTE: cchSource does not have to equal the length of wzSource
731 NOTE: if cchSource == 0, length of wzSource is used instead
732 ********************************************************************/
733 static HRESULT AllocConcatHelper(
734 __deref_out_z LPWSTR* ppwz,
735 __in_z LPCWSTR wzSource,
736 __in SIZE_T cchSource,
737 __in BOOL fZeroOnRealloc
738 )
739 {
740 Assert(ppwz && wzSource); // && *wzSource);
741
742 HRESULT hr = S_OK;
743 SIZE_T cch = 0;
744 SIZE_T cchLen = 0;
745
746 if (*ppwz)
747 {
748 hr = StrMaxLength(*ppwz, &cch);
749 StrExitOnFailure(hr, "failed to get size of destination string");
750
751 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
752 StrExitOnFailure(hr, "Failed to calculate length of string");
753 }
754
755 Assert(cchLen <= cch);
756
757 if (0 == cchSource)
758 {
759 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
760 StrExitOnFailure(hr, "Failed to calculate length of string");
761 }
762
763 if (cch - cchLen < cchSource + 1)
764 {
765 cch = (cchSource + cchLen + 1) * 2;
766 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
767 StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource);
768 }
769
770 if (*ppwz)
771 {
772 hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
773 }
774 else
775 {
776 hr = E_UNEXPECTED;
777 StrExitOnFailure(hr, "for some reason our buffer is still null");
778 }
779
780 LExit:
781 return hr;
782 }
783
784
785 /********************************************************************
786 StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string
787
788 NOTE: caller is responsible for freeing ppz even if function fails
789 NOTE: cchSource does not have to equal the length of pzSource
790 NOTE: if cchSource == 0, length of pzSource is used instead
791 ********************************************************************/
792 extern "C" HRESULT DAPI StrAnsiAllocConcat(
793 __deref_out_z LPSTR* ppz,
794 __in_z LPCSTR pzSource,
795 __in SIZE_T cchSource
796 )
797 {
798 Assert(ppz && pzSource); // && *pzSource);
799
800 HRESULT hr = S_OK;
801 SIZE_T cch = 0;
802 SIZE_T cchLen = 0;
803
804 if (*ppz)
805 {
806 hr = StrMaxLengthAnsi(*ppz, &cch);
807 StrExitOnFailure(hr, "failed to get size of destination string");
808
809 #pragma prefast(push)
810 #pragma prefast(disable:25068)
811 hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
812 #pragma prefast(pop)
813 StrExitOnFailure(hr, "Failed to calculate length of string");
814 }
815
816 Assert(cchLen <= cch);
817
818 if (0 == cchSource)
819 {
820 #pragma prefast(push)
821 #pragma prefast(disable:25068)
822 hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
823 #pragma prefast(pop)
824 StrExitOnFailure(hr, "Failed to calculate length of string");
825 }
826
827 if (cch - cchLen < cchSource + 1)
828 {
829 cch = (cchSource + cchLen + 1) * 2;
830 hr = StrAnsiAlloc(ppz, cch);
831 StrExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource);
832 }
833
834 if (*ppz)
835 {
836 #pragma prefast(push)
837 #pragma prefast(disable:25068)
838 hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
839 #pragma prefast(pop)
840 }
841 else
842 {
843 hr = E_UNEXPECTED;
844 StrExitOnFailure(hr, "for some reason our buffer is still null");
845 }
846
847 LExit:
848 return hr;
849 }
850
851
852 /********************************************************************
853 StrAllocFormatted - allocates or reuses dynamic string memory and formats it
854
855 NOTE: caller is responsible for freeing ppwz even if function fails
856 ********************************************************************/
857 extern "C" HRESULT __cdecl StrAllocFormatted(
858 __deref_out_z LPWSTR* ppwz,
859 __in __format_string LPCWSTR wzFormat,
860 ...
861 )
862 {
863 Assert(ppwz && wzFormat && *wzFormat);
864
865 HRESULT hr = S_OK;
866 va_list args;
867
868 va_start(args, wzFormat);
869 hr = StrAllocFormattedArgs(ppwz, wzFormat, args);
870 va_end(args);
871
872 return hr;
873 }
874
875
876 /********************************************************************
877 StrAllocConcatFormatted - allocates or reuses dynamic string memory
878 and adds a formatted string
879
880 NOTE: caller is responsible for freeing ppwz even if function fails
881 ********************************************************************/
882 extern "C" HRESULT __cdecl StrAllocConcatFormatted(
883 __deref_out_z LPWSTR* ppwz,
884 __in __format_string LPCWSTR wzFormat,
885 ...
886 )
887 {
888 Assert(ppwz && wzFormat && *wzFormat);
889
890 HRESULT hr = S_OK;
891 LPWSTR sczFormatted = NULL;
892 va_list args;
893
894 va_start(args, wzFormat);
895 hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args);
896 va_end(args);
897 StrExitOnFailure(hr, "Failed to allocate formatted string");
898
899 hr = StrAllocConcat(ppwz, sczFormatted, 0);
900
901 LExit:
902 ReleaseStr(sczFormatted);
903
904 return hr;
905 }
906
907
908 /********************************************************************
909 StrAllocConcatFormattedSecure - allocates or reuses dynamic string
910 memory and adds a formatted string. If the memory needs to be
911 reallocated, calls SecureZeroMemory on original block of memory after
912 it is moved.
913
914 NOTE: caller is responsible for freeing ppwz even if function fails
915 ********************************************************************/
916 extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure(
917 __deref_out_z LPWSTR* ppwz,
918 __in __format_string LPCWSTR wzFormat,
919 ...
920 )
921 {
922 Assert(ppwz && wzFormat && *wzFormat);
923
924 HRESULT hr = S_OK;
925 LPWSTR sczFormatted = NULL;
926 va_list args;
927
928 va_start(args, wzFormat);
929 hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args);
930 va_end(args);
931 StrExitOnFailure(hr, "Failed to allocate formatted string");
932
933 hr = StrAllocConcatSecure(ppwz, sczFormatted, 0);
934
935 LExit:
936 ReleaseStr(sczFormatted);
937
938 return hr;
939 }
940
941
942 /********************************************************************
943 StrAllocFormattedSecure - allocates or reuses dynamic string memory
944 and formats it. If the memory needs to be reallocated,
945 calls SecureZeroMemory on original block of memory after it is moved.
946
947 NOTE: caller is responsible for freeing ppwz even if function fails
948 ********************************************************************/
949 extern "C" HRESULT __cdecl StrAllocFormattedSecure(
950 __deref_out_z LPWSTR* ppwz,
951 __in __format_string LPCWSTR wzFormat,
952 ...
953 )
954 {
955 Assert(ppwz && wzFormat && *wzFormat);
956
957 HRESULT hr = S_OK;
958 va_list args;
959
960 va_start(args, wzFormat);
961 hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args);
962 va_end(args);
963
964 return hr;
965 }
966
967
968 /********************************************************************
969 StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it
970
971 NOTE: caller is responsible for freeing ppsz even if function fails
972 ********************************************************************/
973 extern "C" HRESULT DAPI StrAnsiAllocFormatted(
974 __deref_out_z LPSTR* ppsz,
975 __in __format_string LPCSTR szFormat,
976 ...
977 )
978 {
979 Assert(ppsz && szFormat && *szFormat);
980
981 HRESULT hr = S_OK;
982 va_list args;
983
984 va_start(args, szFormat);
985 hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args);
986 va_end(args);
987
988 return hr;
989 }
990
991
992 /********************************************************************
993 StrAllocFormattedArgs - allocates or reuses dynamic string memory
994 and formats it with the passed in args
995
996 NOTE: caller is responsible for freeing ppwz even if function fails
997 ********************************************************************/
998 extern "C" HRESULT DAPI StrAllocFormattedArgs(
999 __deref_out_z LPWSTR* ppwz,
1000 __in __format_string LPCWSTR wzFormat,
1001 __in va_list args
1002 )
1003 {
1004 return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args);
1005 }
1006
1007
1008 /********************************************************************
1009 StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory
1010 and formats it with the passed in args.
1011
1012 If the memory needs to reallocated, calls SecureZeroMemory on the
1013 original block of memory after it is moved.
1014
1015 NOTE: caller is responsible for freeing ppwz even if function fails
1016 ********************************************************************/
1017 extern "C" HRESULT DAPI StrAllocFormattedArgsSecure(
1018 __deref_out_z LPWSTR* ppwz,
1019 __in __format_string LPCWSTR wzFormat,
1020 __in va_list args
1021 )
1022 {
1023 return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args);
1024 }
1025
1026
1027 /********************************************************************
1028 AllocFormattedArgsHelper - allocates or reuses dynamic string memory
1029 and formats it with the passed in args.
1030
1031 If fZeroOnRealloc is true and the memory needs to reallocated,
1032 calls SecureZeroMemory on original block of memory after it is moved.
1033
1034 NOTE: caller is responsible for freeing ppwz even if function fails
1035 ********************************************************************/
1036 static HRESULT AllocFormattedArgsHelper(
1037 __deref_out_z LPWSTR* ppwz,
1038 __in BOOL fZeroOnRealloc,
1039 __in __format_string LPCWSTR wzFormat,
1040 __in va_list args
1041 )
1042 {
1043 Assert(ppwz && wzFormat && *wzFormat);
1044
1045 HRESULT hr = S_OK;
1046 SIZE_T cch = 0;
1047 LPWSTR pwzOriginal = NULL;
1048 SIZE_T cbOriginal = 0;
1049 size_t cchOriginal = 0;
1050
1051 if (*ppwz)
1052 {
1053 hr = StrSize(*ppwz, &cbOriginal);
1054 StrExitOnFailure(hr, "failed to get size of destination string");
1055
1056 cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters
1057
1058 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, &cchOriginal);
1059 StrExitOnRootFailure(hr, "failed to get length of original string");
1060 }
1061
1062 if (0 == cch) // if there is no space in the string buffer
1063 {
1064 cch = 256;
1065
1066 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1067 StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1068 }
1069
1070 // format the message (grow until it fits or there is a failure)
1071 do
1072 {
1073 hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args);
1074 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1075 {
1076 if (!pwzOriginal)
1077 {
1078 // this allows you to pass the original string as a formatting argument and not crash
1079 // save the original string and free it after the printf is complete
1080 pwzOriginal = *ppwz;
1081 *ppwz = NULL;
1082
1083 // StringCchVPrintfW starts writing to the string...
1084 // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1085 pwzOriginal[cchOriginal] = 0;
1086 }
1087
1088 cch *= 2;
1089
1090 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1091 StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1092
1093 hr = S_FALSE;
1094 }
1095 } while (S_FALSE == hr);
1096 StrExitOnRootFailure(hr, "failed to format string");
1097
1098 LExit:
1099 if (pwzOriginal && fZeroOnRealloc)
1100 {
1101 SecureZeroMemory(pwzOriginal, cbOriginal);
1102 }
1103
1104 ReleaseStr(pwzOriginal);
1105
1106 return hr;
1107 }
1108
1109
1110 /********************************************************************
1111 StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory
1112 and formats it with the passed in args
1113
1114 NOTE: caller is responsible for freeing ppsz even if function fails
1115 ********************************************************************/
1116 extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs(
1117 __deref_out_z LPSTR* ppsz,
1118 __in __format_string LPCSTR szFormat,
1119 __in va_list args
1120 )
1121 {
1122 Assert(ppsz && szFormat && *szFormat);
1123
1124 HRESULT hr = S_OK;
1125 SIZE_T cch = 0;
1126 LPSTR pszOriginal = NULL;
1127 size_t cchOriginal = 0;
1128
1129 if (*ppsz)
1130 {
1131 hr = StrMaxLengthAnsi(*ppsz, &cch);
1132 StrExitOnFailure(hr, "failed to get size of destination string");
1133
1134 hr = ::StringCchLengthA(*ppsz, STRSAFE_MAX_CCH, &cchOriginal);
1135 StrExitOnRootFailure(hr, "failed to get length of original string");
1136 }
1137
1138 if (0 == cch) // if there is no space in the string buffer
1139 {
1140 cch = 256;
1141 hr = StrAnsiAlloc(ppsz, cch);
1142 StrExitOnFailure(hr, "failed to allocate string to format: %s", szFormat);
1143 }
1144
1145 // format the message (grow until it fits or there is a failure)
1146 do
1147 {
1148 #pragma prefast(push)
1149 #pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1150 hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args);
1151 #pragma prefast(pop)
1152 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1153 {
1154 if (!pszOriginal)
1155 {
1156 // this allows you to pass the original string as a formatting argument and not crash
1157 // save the original string and free it after the printf is complete
1158 pszOriginal = *ppsz;
1159 *ppsz = NULL;
1160 // StringCchVPrintfW starts writing to the string...
1161 // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1162 pszOriginal[cchOriginal] = 0;
1163 }
1164 cch *= 2;
1165 hr = StrAnsiAlloc(ppsz, cch);
1166 StrExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat);
1167 hr = S_FALSE;
1168 }
1169 } while (S_FALSE == hr);
1170 StrExitOnRootFailure(hr, "failed to format string");
1171
1172 LExit:
1173 ReleaseStr(pszOriginal);
1174
1175 return hr;
1176 }
1177
1178
1179 /********************************************************************
1180 StrAllocFromError - returns the string for a particular error.
1181
1182 ********************************************************************/
1183 extern "C" HRESULT DAPI StrAllocFromError(
1184 __inout LPWSTR *ppwzMessage,
1185 __in HRESULT hrError,
1186 __in_opt HMODULE hModule,
1187 ...
1188 )
1189 {
1190 HRESULT hr = S_OK;
1191 DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM;
1192 LPVOID pvMessage = NULL;
1193 DWORD cchMessage = 0;
1194
1195 if (hModule)
1196 {
1197 dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
1198 }
1199
1200 va_list args;
1201 va_start(args, hModule);
1202 cchMessage = ::FormatMessageW(dwFlags, static_cast<LPCVOID>(hModule), hrError, 0, reinterpret_cast<LPWSTR>(&pvMessage), 0, &args);
1203 va_end(args);
1204
1205 if (0 == cchMessage)
1206 {
1207 StrExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError);
1208 }
1209
1210 hr = StrAllocString(ppwzMessage, reinterpret_cast<LPCWSTR>(pvMessage), cchMessage);
1211 StrExitOnFailure(hr, "Failed to allocate string for message.");
1212
1213 LExit:
1214 if (pvMessage)
1215 {
1216 ::LocalFree(pvMessage);
1217 }
1218
1219 return hr;
1220 }
1221
1222
1223 /********************************************************************
1224 StrMaxLength - returns maximum number of characters that can be stored in dynamic string p
1225
1226 NOTE: assumes Unicode string
1227 ********************************************************************/
1228 extern "C" HRESULT DAPI StrMaxLength(
1229 __in LPCVOID p,
1230 __out SIZE_T* pcch
1231 )
1232 {
1233 Assert(pcch);
1234
1235 HRESULT hr = S_OK;
1236
1237 if (p)
1238 {
1239 hr = StrSize(p, pcch);
1240 StrExitOnFailure(hr, "Failed to get size of string buffer.");
1241
1242 *pcch /= sizeof(WCHAR); // reduce to count of characters
1243 }
1244 else
1245 {
1246 *pcch = 0;
1247 }
1248 Assert(S_OK == hr);
1249
1250 LExit:
1251 return hr;
1252 }
1253
1254
1255 /********************************************************************
1256 StrMaxLengthAnsi - returns maximum number of characters that can be stored in dynamic string p
1257
1258 NOTE: assumes non-Unicode string
1259 ********************************************************************/
1260 extern "C" HRESULT DAPI StrMaxLengthAnsi(
1261 __in LPCVOID p,
1262 __out SIZE_T* pcch
1263 )
1264 {
1265 Assert(pcch);
1266
1267 HRESULT hr = S_OK;
1268
1269 if (p)
1270 {
1271 hr = StrSize(p, pcch);
1272 StrExitOnFailure(hr, "Failed to get size of string buffer.");
1273
1274 *pcch /= sizeof(CHAR); // reduce to count of characters
1275 }
1276 else
1277 {
1278 *pcch = 0;
1279 }
1280 Assert(S_OK == hr);
1281
1282 LExit:
1283 return hr;
1284 }
1285
1286
1287 /********************************************************************
1288 StrSize - returns count of bytes in dynamic string p
1289
1290 ********************************************************************/
1291 extern "C" HRESULT DAPI StrSize(
1292 __in LPCVOID p,
1293 __out SIZE_T* pcb
1294 )
1295 {
1296 Assert(p && pcb);
1297
1298 return MemSizeChecked(p, pcb);
1299 }
1300
1301 /********************************************************************
1302 StrFree - releases dynamic string memory allocated by any StrAlloc*() functions
1303
1304 ********************************************************************/
1305 extern "C" HRESULT DAPI StrFree(
1306 __in LPVOID p
1307 )
1308 {
1309 Assert(p);
1310
1311 HRESULT hr = MemFree(p);
1312 StrExitOnFailure(hr, "failed to free string");
1313
1314 LExit:
1315 return hr;
1316 }
1317
1318
1319 /****************************************************************************
1320 StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1321 Replaces all instances.
1322
1323 ****************************************************************************/
1324 extern "C" HRESULT DAPI StrReplaceStringAll(
1325 __inout LPWSTR* ppwzOriginal,
1326 __in_z LPCWSTR wzOldSubString,
1327 __in_z LPCWSTR wzNewSubString
1328 )
1329 {
1330 HRESULT hr = S_OK;
1331 DWORD dwStartIndex = 0;
1332
1333 do
1334 {
1335 hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString);
1336 StrExitOnFailure(hr, "Failed to replace substring");
1337 }
1338 while (S_OK == hr);
1339
1340 hr = (0 == dwStartIndex) ? S_FALSE : S_OK;
1341
1342 LExit:
1343 return hr;
1344 }
1345
1346
1347 /****************************************************************************
1348 StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1349 Search for old substring starts at dwStartIndex. Does only 1 replace.
1350
1351 ****************************************************************************/
1352 extern "C" HRESULT DAPI StrReplaceString(
1353 __inout LPWSTR* ppwzOriginal,
1354 __inout DWORD* pdwStartIndex,
1355 __in_z LPCWSTR wzOldSubString,
1356 __in_z LPCWSTR wzNewSubString
1357 )
1358 {
1359 Assert(ppwzOriginal && wzOldSubString && wzNewSubString);
1360
1361 HRESULT hr = S_FALSE;
1362 LPCWSTR wzSubLocation = NULL;
1363 LPWSTR pwzBuffer = NULL;
1364 size_t cchOldSubString = 0;
1365 size_t cchNewSubString = 0;
1366
1367 if (!*ppwzOriginal)
1368 {
1369 ExitFunction();
1370 }
1371
1372 wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString);
1373 if (!wzSubLocation)
1374 {
1375 ExitFunction();
1376 }
1377
1378 if (wzOldSubString)
1379 {
1380 hr = ::StringCchLengthW(wzOldSubString, STRSAFE_MAX_CCH, &cchOldSubString);
1381 StrExitOnRootFailure(hr, "Failed to get old string length.");
1382 }
1383
1384 if (wzNewSubString)
1385 {
1386 hr = ::StringCchLengthW(wzNewSubString, STRSAFE_MAX_CCH, &cchNewSubString);
1387 StrExitOnRootFailure(hr, "Failed to get new string length.");
1388 }
1389
1390 hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex);
1391 StrExitOnRootFailure(hr, "Failed to diff pointers.");
1392
1393 hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal);
1394 StrExitOnFailure(hr, "Failed to duplicate string.");
1395
1396 pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0';
1397
1398 hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0);
1399 StrExitOnFailure(hr, "Failed to append new string.");
1400
1401 hr = StrAllocConcat(&pwzBuffer, wzSubLocation + cchOldSubString, 0);
1402 StrExitOnFailure(hr, "Failed to append post string.");
1403
1404 hr = StrFree(*ppwzOriginal);
1405 StrExitOnFailure(hr, "Failed to free original string.");
1406
1407 *ppwzOriginal = pwzBuffer;
1408 *pdwStartIndex = *pdwStartIndex + static_cast<DWORD>(cchNewSubString);
1409 hr = S_OK;
1410
1411 LExit:
1412 return hr;
1413 }
1414
1415
1416 static inline BYTE HexCharToByte(
1417 __in WCHAR wc
1418 )
1419 {
1420 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
1421
1422 BYTE b;
1423 if (L'0' <= wc && wc <= L'9')
1424 {
1425 b = (BYTE)(wc - L'0');
1426 }
1427 else if ('a' <= wc && wc <= 'f')
1428 {
1429 b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1));
1430 }
1431 else // must be (L'A' <= wc && wc <= L'F')
1432 {
1433 b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1));
1434 }
1435
1436 Assert(0 <= b && b <= 15);
1437 return b;
1438 }
1439
1440
1441 /****************************************************************************
1442 StrHexEncode - converts an array of bytes to a text string
1443
1444 NOTE: wzDest must have space for cbSource * 2 + 1 characters
1445 ****************************************************************************/
1446 extern "C" HRESULT DAPI StrHexEncode(
1447 __in_ecount(cbSource) const BYTE* pbSource,
1448 __in SIZE_T cbSource,
1449 __out_ecount(cchDest) LPWSTR wzDest,
1450 __in SIZE_T cchDest
1451 )
1452 {
1453 Assert(pbSource && wzDest);
1454
1455 HRESULT hr = S_OK;
1456 DWORD i;
1457 BYTE b;
1458
1459 if (cchDest < 2 * cbSource + 1)
1460 {
1461 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
1462 }
1463
1464 for (i = 0; i < cbSource; ++i)
1465 {
1466 b = (*pbSource) >> 4;
1467 *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1468 b = (*pbSource) & 0xF;
1469 *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1470
1471 ++pbSource;
1472 }
1473
1474 *wzDest = 0;
1475
1476 LExit:
1477 return hr;
1478 }
1479
1480
1481 /****************************************************************************
1482 StrAllocHexEncode - converts an array of bytes to an allocated text string
1483
1484 ****************************************************************************/
1485 HRESULT DAPI StrAllocHexEncode(
1486 __in_ecount(cbSource) const BYTE* pbSource,
1487 __in SIZE_T cbSource,
1488 __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest
1489 )
1490 {
1491 HRESULT hr = S_OK;
1492 SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1);
1493
1494 hr = StrAlloc(ppwzDest, cchSource);
1495 StrExitOnFailure(hr, "Failed to allocate hex string.");
1496
1497 hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource);
1498 StrExitOnFailure(hr, "Failed to encode hex string.");
1499
1500 LExit:
1501 return hr;
1502 }
1503
1504
1505 /****************************************************************************
1506 StrHexDecode - converts a string of text to array of bytes
1507
1508 NOTE: wzSource must contain even number of characters
1509 ****************************************************************************/
1510 extern "C" HRESULT DAPI StrHexDecode(
1511 __in_z LPCWSTR wzSource,
1512 __out_bcount(cbDest) BYTE* pbDest,
1513 __in SIZE_T cbDest
1514 )
1515 {
1516 Assert(wzSource && pbDest);
1517
1518 HRESULT hr = S_OK;
1519 size_t cchSource = 0;
1520 size_t i = 0;
1521 BYTE b = 0;
1522
1523 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource);
1524 StrExitOnRootFailure(hr, "Failed to get length of hex string: %ls", wzSource);
1525
1526 Assert(0 == cchSource % 2);
1527 if (cbDest < cchSource / 2)
1528 {
1529 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1530 StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %Iu into %Iu bytes.", wzSource, cchSource, cbDest);
1531 }
1532
1533 for (i = 0; i < cchSource / 2; ++i)
1534 {
1535 b = HexCharToByte(*wzSource++);
1536 (*pbDest) = b << 4;
1537
1538 b = HexCharToByte(*wzSource++);
1539 (*pbDest) |= b & 0xF;
1540
1541 ++pbDest;
1542 }
1543
1544 LExit:
1545 return hr;
1546 }
1547
1548
1549 /****************************************************************************
1550 StrAllocHexDecode - allocates a byte array hex-converted from string of text
1551
1552 NOTE: wzSource must contain even number of characters
1553 ****************************************************************************/
1554 extern "C" HRESULT DAPI StrAllocHexDecode(
1555 __in_z LPCWSTR wzSource,
1556 __out_bcount(*pcbDest) BYTE** ppbDest,
1557 __out_opt DWORD* pcbDest
1558 )
1559 {
1560 Assert(wzSource && *wzSource && ppbDest);
1561
1562 HRESULT hr = S_OK;
1563 size_t cch = 0;
1564 BYTE* pb = NULL;
1565 DWORD cb = 0;
1566
1567 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch);
1568 StrExitOnFailure(hr, "Failed to calculate length of source string.");
1569
1570 if (cch % 2)
1571 {
1572 hr = E_INVALIDARG;
1573 StrExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded.");
1574 }
1575
1576 cb = static_cast<DWORD>(cch / 2);
1577 pb = static_cast<BYTE*>(MemAlloc(cb, TRUE));
1578 StrExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode.");
1579
1580 hr = StrHexDecode(wzSource, pb, cb);
1581 StrExitOnFailure(hr, "Failed to decode source string.");
1582
1583 *ppbDest = pb;
1584 pb = NULL;
1585
1586 if (pcbDest)
1587 {
1588 *pcbDest = cb;
1589 }
1590
1591 LExit:
1592 ReleaseMem(pb);
1593
1594 return hr;
1595 }
1596
1597
1598 /****************************************************************************
1599 Base85 encoding/decoding data
1600
1601 ****************************************************************************/
1602 const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~";
1603
1604 const BYTE Base85DecodeTable[256] =
1605 {
1606 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1607 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1608 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1609 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23,
1610 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
1611 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54,
1612 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
1613 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1614 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1615 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1616 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1617 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1618 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1619 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1620 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1621 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85
1622 };
1623
1624 const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 };
1625
1626
1627 /****************************************************************************
1628 StrAllocBase85Encode - converts an array of bytes into an XML compatible string
1629
1630 ****************************************************************************/
1631 extern "C" HRESULT DAPI StrAllocBase85Encode(
1632 __in_bcount_opt(cbSource) const BYTE* pbSource,
1633 __in SIZE_T cbSource,
1634 __deref_out_z LPWSTR* pwzDest
1635 )
1636 {
1637 HRESULT hr = S_OK;
1638 SIZE_T cchDest = 0;
1639 LPWSTR wzDest;
1640 DWORD_PTR iSource = 0;
1641 DWORD_PTR iDest = 0;
1642
1643 if (!pwzDest || !pbSource)
1644 {
1645 return E_INVALIDARG;
1646 }
1647
1648 // calc actual size of output
1649 cchDest = cbSource / 4;
1650 cchDest += cchDest * 4;
1651 if (cbSource & 3)
1652 {
1653 cchDest += (cbSource & 3) + 1;
1654 }
1655 ++cchDest; // add room for null terminator
1656
1657 hr = StrAlloc(pwzDest, cchDest);
1658 StrExitOnFailure(hr, "failed to allocate destination string");
1659
1660 wzDest = *pwzDest;
1661
1662 // first, encode full words
1663 for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5)
1664 {
1665 DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24);
1666 DWORD k = n / 85;
1667
1668 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1669 wzDest[iDest] = Base85EncodeTable[n - k * 85];
1670 n = k / 85;
1671
1672 //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1673 wzDest[iDest + 1] = Base85EncodeTable[k - n * 85];
1674 k = n / 85;
1675
1676 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1677 wzDest[iDest + 2] = Base85EncodeTable[n - k * 85];
1678 n = k / 85;
1679
1680 //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1681 wzDest[iDest + 3] = Base85EncodeTable[k - n * 85];
1682
1683 __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85);
1684
1685 //Assert(0 <= n && n < countof(Base85EncodeTable));
1686 wzDest[iDest + 4] = Base85EncodeTable[n];
1687 }
1688
1689 // encode any remaining bytes
1690 if (iSource < cbSource)
1691 {
1692 DWORD n = 0;
1693 for (DWORD i = 0; iSource + i < cbSource; ++i)
1694 {
1695 n += pbSource[iSource + i] << (i << 3);
1696 }
1697
1698 for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest)
1699 {
1700 DWORD k = n / 85;
1701
1702 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1703 wzDest[iDest] = Base85EncodeTable[n - k * 85];
1704
1705 n = k;
1706 }
1707
1708 wzDest[iDest] = Base85EncodeTable[n];
1709 ++iDest;
1710 }
1711 Assert(iSource == cbSource);
1712 Assert(iDest == cchDest - 1);
1713
1714 wzDest[iDest] = L'\0';
1715 hr = S_OK;
1716
1717 LExit:
1718 return hr;
1719 }
1720
1721
1722 /****************************************************************************
1723 StrAllocBase85Decode - converts a string of text to array of bytes
1724
1725 NOTE: Use MemFree() to release the allocated stream of bytes
1726 ****************************************************************************/
1727 extern "C" HRESULT DAPI StrAllocBase85Decode(
1728 __in_z LPCWSTR wzSource,
1729 __deref_out_bcount(*pcbDest) BYTE** ppbDest,
1730 __out SIZE_T* pcbDest
1731 )
1732 {
1733 HRESULT hr = S_OK;
1734 size_t cchSource = 0;
1735 DWORD_PTR i, n, k;
1736
1737 BYTE* pbDest = 0;
1738 SIZE_T cbDest = 0;
1739
1740 if (!wzSource || !ppbDest || !pcbDest)
1741 {
1742 ExitFunction1(hr = E_INVALIDARG);
1743 }
1744
1745 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource);
1746 StrExitOnRootFailure(hr, "failed to get length of base 85 string: %ls", wzSource);
1747
1748 // evaluate size of output and check it
1749 k = cchSource / 5;
1750 cbDest = k << 2;
1751 k = cchSource - k * 5;
1752 if (k)
1753 {
1754 if (1 == k)
1755 {
1756 // decode error -- encoded size cannot equal 1 mod 5
1757 return E_UNEXPECTED;
1758 }
1759
1760 cbDest += k - 1;
1761 }
1762
1763 *ppbDest = static_cast<BYTE*>(MemAlloc(cbDest, FALSE));
1764 StrExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string");
1765
1766 pbDest = *ppbDest;
1767 *pcbDest = cbDest;
1768
1769 // decode full words first
1770 while (5 <= cchSource)
1771 {
1772 k = Base85DecodeTable[wzSource[0]];
1773 if (85 == k)
1774 {
1775 // illegal symbol
1776 return E_UNEXPECTED;
1777 }
1778 n = k;
1779
1780 k = Base85DecodeTable[wzSource[1]];
1781 if (85 == k)
1782 {
1783 // illegal symbol
1784 return E_UNEXPECTED;
1785 }
1786 n += k * 85;
1787
1788 k = Base85DecodeTable[wzSource[2]];
1789 if (85 == k)
1790 {
1791 // illegal symbol
1792 return E_UNEXPECTED;
1793 }
1794 n += k * (85 * 85);
1795
1796 k = Base85DecodeTable[wzSource[3]];
1797 if (85 == k)
1798 {
1799 // illegal symbol
1800 return E_UNEXPECTED;
1801 }
1802 n += k * (85 * 85 * 85);
1803
1804 k = Base85DecodeTable[wzSource[4]];
1805 if (85 == k)
1806 {
1807 // illegal symbol
1808 return E_UNEXPECTED;
1809 }
1810 k *= (85 * 85 * 85 * 85);
1811
1812 // if (k + n > (1u << 32)) <=> (k > ~n) then decode error
1813 if (k > ~n)
1814 {
1815 // overflow
1816 return E_UNEXPECTED;
1817 }
1818
1819 n += k;
1820
1821 pbDest[0] = (BYTE) n;
1822 pbDest[1] = (BYTE) (n >> 8);
1823 pbDest[2] = (BYTE) (n >> 16);
1824 pbDest[3] = (BYTE) (n >> 24);
1825
1826 wzSource += 5;
1827 pbDest += 4;
1828 cchSource -= 5;
1829 }
1830
1831 if (cchSource)
1832 {
1833 n = 0;
1834 for (i = 0; i < cchSource; ++i)
1835 {
1836 k = Base85DecodeTable[wzSource[i]];
1837 if (85 == k)
1838 {
1839 // illegal symbol
1840 return E_UNEXPECTED;
1841 }
1842
1843 n += k * Base85PowerTable[i];
1844 }
1845
1846 for (i = 1; i < cchSource; ++i)
1847 {
1848 *pbDest++ = (BYTE)n;
1849 n >>= 8;
1850 }
1851
1852 if (0 != n)
1853 {
1854 // decode error
1855 return E_UNEXPECTED;
1856 }
1857 }
1858
1859 hr = S_OK;
1860
1861 LExit:
1862 return hr;
1863 }
1864
1865
1866 /****************************************************************************
1867 MultiSzLen - calculates the length of a MULTISZ string including all nulls
1868 including the double null terminator at the end of the MULTISZ.
1869
1870 NOTE: returns 0 if the multisz in not properly terminated with two nulls
1871 ****************************************************************************/
1872 extern "C" HRESULT DAPI MultiSzLen(
1873 __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz,
1874 __out SIZE_T* pcch
1875 )
1876 {
1877 Assert(pcch);
1878
1879 HRESULT hr = S_OK;
1880 LPCWSTR wz = pwzMultiSz;
1881 DWORD_PTR dwMaxSize = 0;
1882
1883 hr = StrMaxLength(pwzMultiSz, &dwMaxSize);
1884 StrExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length");
1885
1886 *pcch = 0;
1887 while (*pcch < dwMaxSize)
1888 {
1889 if (L'\0' == *wz && L'\0' == *(wz + 1))
1890 {
1891 break;
1892 }
1893
1894 ++wz;
1895 *pcch = *pcch + 1;
1896 }
1897
1898 // Add two for the last 2 NULLs (that we looked ahead at)
1899 *pcch = *pcch + 2;
1900
1901 // If we've walked off the end then the length is 0
1902 if (*pcch > dwMaxSize)
1903 {
1904 *pcch = 0;
1905 }
1906
1907 LExit:
1908 return hr;
1909 }
1910
1911
1912 /****************************************************************************
1913 MultiSzPrepend - prepends a string onto the front of a MUTLISZ
1914
1915 ****************************************************************************/
1916 extern "C" HRESULT DAPI MultiSzPrepend(
1917 __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz,
1918 __inout_opt SIZE_T* pcchMultiSz,
1919 __in __nullnullterminated LPCWSTR pwzInsert
1920 )
1921 {
1922 Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
1923
1924 HRESULT hr =S_OK;
1925 LPWSTR pwzResult = NULL;
1926 SIZE_T cchResult = 0;
1927 SIZE_T cchInsert = 0;
1928 SIZE_T cchMultiSz = 0;
1929
1930 // Get the lengths of the MULTISZ (and prime it if it's not initialized)
1931 if (pcchMultiSz && 0 != *pcchMultiSz)
1932 {
1933 cchMultiSz = *pcchMultiSz;
1934 }
1935 else
1936 {
1937 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
1938 StrExitOnFailure(hr, "failed to get length of multisz");
1939 }
1940
1941 hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchInsert));
1942 StrExitOnRootFailure(hr, "failed to get length of insert string");
1943
1944 cchResult = cchInsert + cchMultiSz + 1;
1945
1946 // Allocate the result buffer
1947 hr = StrAlloc(&pwzResult, cchResult + 1);
1948 StrExitOnFailure(hr, "failed to allocate result string");
1949
1950 // Prepend
1951 hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert);
1952 StrExitOnRootFailure(hr, "failed to copy prepend string: %ls", pwzInsert);
1953
1954 // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in
1955 if (0 == cchMultiSz)
1956 {
1957 pwzResult[cchResult] = L'\0';
1958 ++cchResult;
1959 }
1960 else
1961 {
1962 // Copy the rest
1963 ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR));
1964
1965 // Free the old buffer
1966 ReleaseNullStr(*ppwzMultiSz);
1967 }
1968
1969 // Set the result
1970 *ppwzMultiSz = pwzResult;
1971
1972 if (pcchMultiSz)
1973 {
1974 *pcchMultiSz = cchResult;
1975 }
1976
1977 pwzResult = NULL;
1978
1979 LExit:
1980 ReleaseNullStr(pwzResult);
1981
1982 return hr;
1983 }
1984
1985 /****************************************************************************
1986 MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the
1987 specified sub string and returns the index of the
1988 string in the MULTISZ, the address, neither, or both
1989
1990 NOTE: returns S_FALSE if the string is not found
1991 ****************************************************************************/
1992 extern "C" HRESULT DAPI MultiSzFindSubstring(
1993 __in __nullnullterminated LPCWSTR pwzMultiSz,
1994 __in __nullnullterminated LPCWSTR pwzSubstring,
1995 __out_opt DWORD_PTR* pdwIndex,
1996 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn
1997 )
1998 {
1999 Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring);
2000
2001 HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty)
2002 LPCWSTR wz = pwzMultiSz;
2003 DWORD_PTR dwIndex = 0;
2004 SIZE_T cchMultiSz = 0;
2005 SIZE_T cchProgress = 0;
2006
2007 hr = MultiSzLen(pwzMultiSz, &cchMultiSz);
2008 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2009
2010 // Find the string containing the sub string
2011 hr = S_OK;
2012 while (NULL == wcsistr(wz, pwzSubstring))
2013 {
2014 // Slide through to the end of the current string
2015 while (L'\0' != *wz && cchProgress < cchMultiSz)
2016 {
2017 ++wz;
2018 ++cchProgress;
2019 }
2020
2021 // If we're done, we're done
2022 if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
2023 {
2024 hr = S_FALSE;
2025 break;
2026 }
2027
2028 // Move on to the next string
2029 ++wz;
2030 ++dwIndex;
2031 }
2032 Assert(S_OK == hr || S_FALSE == hr);
2033
2034 // If we found it give them what they want
2035 if (S_OK == hr)
2036 {
2037 if (pdwIndex)
2038 {
2039 *pdwIndex = dwIndex;
2040 }
2041
2042 if (ppwzFoundIn)
2043 {
2044 *ppwzFoundIn = wz;
2045 }
2046 }
2047
2048 LExit:
2049 return hr;
2050 }
2051
2052 /****************************************************************************
2053 MultiSzFindString - finds a string in a MULTISZ and returns the index of
2054 the string in the MULTISZ, the address or both
2055
2056 NOTE: returns S_FALSE if the string is not found
2057 ****************************************************************************/
2058 extern "C" HRESULT DAPI MultiSzFindString(
2059 __in __nullnullterminated LPCWSTR pwzMultiSz,
2060 __in __nullnullterminated LPCWSTR pwzString,
2061 __out_opt DWORD_PTR* pdwIndex,
2062 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound
2063 )
2064 {
2065 Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound));
2066
2067 HRESULT hr = S_FALSE; // Assume we won't find it
2068 LPCWSTR wz = pwzMultiSz;
2069 DWORD_PTR dwIndex = 0;
2070 SIZE_T cchMutliSz = 0;
2071 SIZE_T cchProgress = 0;
2072
2073 hr = MultiSzLen(pwzMultiSz, &cchMutliSz);
2074 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2075
2076 // Find the string
2077 hr = S_OK;
2078 while (0 != lstrcmpW(wz, pwzString))
2079 {
2080 // Slide through to the end of the current string
2081 while (L'\0' != *wz && cchProgress < cchMutliSz)
2082 {
2083 ++wz;
2084 ++cchProgress;
2085 }
2086
2087 // If we're done, we're done
2088 if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz)
2089 {
2090 hr = S_FALSE;
2091 break;
2092 }
2093
2094 // Move on to the next string
2095 ++wz;
2096 ++dwIndex;
2097 }
2098 Assert(S_OK == hr || S_FALSE == hr);
2099
2100 // If we found it give them what they want
2101 if (S_OK == hr)
2102 {
2103 if (pdwIndex)
2104 {
2105 *pdwIndex = dwIndex;
2106 }
2107
2108 if (ppwzFound)
2109 {
2110 *ppwzFound = wz;
2111 }
2112 }
2113
2114 LExit:
2115 return hr;
2116 }
2117
2118 /****************************************************************************
2119 MultiSzRemoveString - removes string from a MULTISZ at the specified
2120 index
2121
2122 NOTE: does an in place removal without shrinking the memory allocation
2123
2124 NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex
2125 ****************************************************************************/
2126 extern "C" HRESULT DAPI MultiSzRemoveString(
2127 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2128 __in DWORD_PTR dwIndex
2129 )
2130 {
2131 Assert(ppwzMultiSz && *ppwzMultiSz);
2132
2133 HRESULT hr = S_OK;
2134 LPCWSTR wz = *ppwzMultiSz;
2135 LPCWSTR wzNext = NULL;
2136 DWORD_PTR dwCurrentIndex = 0;
2137 SIZE_T cchMultiSz = 0;
2138 SIZE_T cchProgress = 0;
2139
2140 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2141 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2142
2143 // Find the index we want to remove
2144 hr = S_OK;
2145 while (dwCurrentIndex < dwIndex)
2146 {
2147 // Slide through to the end of the current string
2148 while (L'\0' != *wz && cchProgress < cchMultiSz)
2149 {
2150 ++wz;
2151 ++cchProgress;
2152 }
2153
2154 // If we're done, we're done
2155 if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
2156 {
2157 hr = S_FALSE;
2158 break;
2159 }
2160
2161 // Move on to the next string
2162 ++wz;
2163 ++cchProgress;
2164 ++dwCurrentIndex;
2165 }
2166 Assert(S_OK == hr || S_FALSE == hr);
2167
2168 // If we found the index to be removed
2169 if (S_OK == hr)
2170 {
2171 wzNext = wz;
2172
2173 // Slide through to the end of the current string
2174 while (L'\0' != *wzNext && cchProgress < cchMultiSz)
2175 {
2176 ++wzNext;
2177 ++cchProgress;
2178 }
2179
2180 // Something weird has happened if we're past the end of the MULTISZ
2181 if (cchProgress > cchMultiSz)
2182 {
2183 hr = E_UNEXPECTED;
2184 StrExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ");
2185 }
2186
2187 // Move on to the next character
2188 ++wzNext;
2189 ++cchProgress;
2190
2191 ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2192 }
2193
2194 LExit:
2195 return hr;
2196 }
2197
2198 /****************************************************************************
2199 MultiSzInsertString - inserts new string at the specified index
2200
2201 ****************************************************************************/
2202 extern "C" HRESULT DAPI MultiSzInsertString(
2203 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2204 __inout_opt SIZE_T* pcchMultiSz,
2205 __in DWORD_PTR dwIndex,
2206 __in_z LPCWSTR pwzInsert
2207 )
2208 {
2209 Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
2210
2211 HRESULT hr = S_OK;
2212 LPCWSTR wz = *ppwzMultiSz;
2213 DWORD_PTR dwCurrentIndex = 0;
2214 SIZE_T cchProgress = 0;
2215 LPWSTR pwzResult = NULL;
2216 SIZE_T cchResult = 0;
2217 SIZE_T cchString = 0;
2218 SIZE_T cchMultiSz = 0;
2219
2220 hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchString));
2221 StrExitOnRootFailure(hr, "failed to get length of insert string");
2222
2223 if (pcchMultiSz && 0 != *pcchMultiSz)
2224 {
2225 cchMultiSz = *pcchMultiSz;
2226 }
2227 else
2228 {
2229 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2230 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2231 }
2232
2233 // Find the index we want to insert at
2234 hr = S_OK;
2235 while (dwCurrentIndex < dwIndex)
2236 {
2237 // Slide through to the end of the current string
2238 while (L'\0' != *wz && cchProgress < cchMultiSz)
2239 {
2240 ++wz;
2241 ++cchProgress;
2242 }
2243
2244 // If we're done, we're done
2245 if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz)
2246 {
2247 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
2248 StrExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex);
2249 }
2250
2251 // Move on to the next string
2252 ++wz;
2253 ++cchProgress;
2254 ++dwCurrentIndex;
2255 }
2256
2257 //
2258 // Insert the string
2259 //
2260 cchResult = cchMultiSz + cchString + 1;
2261
2262 hr = StrAlloc(&pwzResult, cchResult);
2263 StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert");
2264
2265 // Copy the part before the insert
2266 ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR));
2267
2268 // Copy the insert part
2269 ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR));
2270
2271 // Copy the part after the insert
2272 ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2273
2274 // Free the old buffer
2275 ReleaseNullStr(*ppwzMultiSz);
2276
2277 // Set the result
2278 *ppwzMultiSz = pwzResult;
2279
2280 // If they wanted the resulting length, let 'em have it
2281 if (pcchMultiSz)
2282 {
2283 *pcchMultiSz = cchResult;
2284 }
2285
2286 pwzResult = NULL;
2287
2288 LExit:
2289 ReleaseStr(pwzResult);
2290
2291 return hr;
2292 }
2293
2294 /****************************************************************************
2295 MultiSzReplaceString - replaces string at the specified index with a new one
2296
2297 ****************************************************************************/
2298 extern "C" HRESULT DAPI MultiSzReplaceString(
2299 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2300 __in DWORD_PTR dwIndex,
2301 __in_z LPCWSTR pwzString
2302 )
2303 {
2304 Assert(ppwzMultiSz && pwzString && *pwzString);
2305
2306 HRESULT hr = S_OK;
2307
2308 hr = MultiSzRemoveString(ppwzMultiSz, dwIndex);
2309 StrExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex);
2310
2311 hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString);
2312 StrExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex);
2313
2314 LExit:
2315 return hr;
2316 }
2317
2318
2319 /****************************************************************************
2320 wcsistr - case insensitive find a substring
2321
2322 ****************************************************************************/
2323 extern "C" LPCWSTR DAPI wcsistr(
2324 __in_z LPCWSTR wzString,
2325 __in_z LPCWSTR wzCharSet
2326 )
2327 {
2328 LPCWSTR wzSource = wzString;
2329 LPCWSTR wzSearch = NULL;
2330 SIZE_T cchSourceIndex = 0;
2331
2332 // Walk through wzString (the source string) one character at a time
2333 while (*wzSource)
2334 {
2335 cchSourceIndex = 0;
2336 wzSearch = wzCharSet;
2337
2338 // Look ahead in the source string until we get a full match or we hit the end of the source
2339 while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch))
2340 {
2341 ++cchSourceIndex;
2342 ++wzSearch;
2343 }
2344
2345 // If we found it, return the point that we found it at
2346 if (L'\0' == *wzSearch)
2347 {
2348 return wzSource;
2349 }
2350
2351 // Walk ahead one character
2352 ++wzSource;
2353 }
2354
2355 return NULL;
2356 }
2357
2358 /****************************************************************************
2359 StrStringToInt16 - converts a string to a signed 16-bit integer.
2360
2361 ****************************************************************************/
2362 extern "C" HRESULT DAPI StrStringToInt16(
2363 __in_z LPCWSTR wzIn,
2364 __in DWORD cchIn,
2365 __out SHORT* psOut
2366 )
2367 {
2368 HRESULT hr = S_OK;
2369 LONGLONG ll = 0;
2370
2371 hr = StrStringToInt64(wzIn, cchIn, &ll);
2372 StrExitOnFailure(hr, "Failed to parse int64.");
2373
2374 if (SHORT_MAX < ll || SHORT_MIN > ll)
2375 {
2376 ExitFunction1(hr = DISP_E_OVERFLOW);
2377 }
2378 *psOut = (SHORT)ll;
2379
2380 LExit:
2381 return hr;
2382 }
2383
2384 /****************************************************************************
2385 StrStringToUInt16 - converts a string to an unsigned 16-bit integer.
2386
2387 ****************************************************************************/
2388 extern "C" HRESULT DAPI StrStringToUInt16(
2389 __in_z LPCWSTR wzIn,
2390 __in DWORD cchIn,
2391 __out USHORT* pusOut
2392 )
2393 {
2394 HRESULT hr = S_OK;
2395 ULONGLONG ull = 0;
2396
2397 hr = StrStringToUInt64(wzIn, cchIn, &ull);
2398 StrExitOnFailure(hr, "Failed to parse uint64 to convert to uint16.");
2399
2400 if (USHORT_MAX < ull)
2401 {
2402 ExitFunction1(hr = DISP_E_OVERFLOW);
2403 }
2404 *pusOut = (USHORT)ull;
2405
2406 LExit:
2407 return hr;
2408 }
2409
2410 /****************************************************************************
2411 StrStringToInt32 - converts a string to a signed 32-bit integer.
2412
2413 ****************************************************************************/
2414 extern "C" HRESULT DAPI StrStringToInt32(
2415 __in_z LPCWSTR wzIn,
2416 __in DWORD cchIn,
2417 __out INT* piOut
2418 )
2419 {
2420 HRESULT hr = S_OK;
2421 LONGLONG ll = 0;
2422
2423 hr = StrStringToInt64(wzIn, cchIn, &ll);
2424 StrExitOnFailure(hr, "Failed to parse int64.");
2425
2426 if (INT_MAX < ll || INT_MIN > ll)
2427 {
2428 ExitFunction1(hr = DISP_E_OVERFLOW);
2429 }
2430 *piOut = (INT)ll;
2431
2432 LExit:
2433 return hr;
2434 }
2435
2436 /****************************************************************************
2437 StrStringToUInt32 - converts a string to an unsigned 32-bit integer.
2438
2439 ****************************************************************************/
2440 extern "C" HRESULT DAPI StrStringToUInt32(
2441 __in_z LPCWSTR wzIn,
2442 __in DWORD cchIn,
2443 __out UINT* puiOut
2444 )
2445 {
2446 HRESULT hr = S_OK;
2447 ULONGLONG ull = 0;
2448
2449 hr = StrStringToUInt64(wzIn, cchIn, &ull);
2450 StrExitOnFailure(hr, "Failed to parse uint64 to convert to uint32.");
2451
2452 if (UINT_MAX < ull)
2453 {
2454 ExitFunction1(hr = DISP_E_OVERFLOW);
2455 }
2456 *puiOut = (UINT)ull;
2457
2458 LExit:
2459 return hr;
2460 }
2461
2462 /****************************************************************************
2463 StrStringToInt64 - converts a string to a signed 64-bit integer.
2464
2465 ****************************************************************************/
2466 extern "C" HRESULT DAPI StrStringToInt64(
2467 __in_z LPCWSTR wzIn,
2468 __in DWORD cchIn,
2469 __out LONGLONG* pllOut
2470 )
2471 {
2472 HRESULT hr = S_OK;
2473 DWORD i = 0;
2474 INT iSign = 1;
2475 INT nDigit = 0;
2476 LARGE_INTEGER liValue = { };
2477 size_t cchString = 0;
2478
2479 // get string length if not provided
2480 if (0 >= cchIn)
2481 {
2482 hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString);
2483 StrExitOnRootFailure(hr, "Failed to get length of string.");
2484
2485 cchIn = (DWORD)cchString;
2486 if (0 >= cchIn)
2487 {
2488 ExitFunction1(hr = E_INVALIDARG);
2489 }
2490 }
2491
2492 // check sign
2493 if (L'-' == wzIn[0])
2494 {
2495 if (1 >= cchIn)
2496 {
2497 ExitFunction1(hr = E_INVALIDARG);
2498 }
2499 i = 1;
2500 iSign = -1;
2501 }
2502
2503 // read digits
2504 while (i < cchIn)
2505 {
2506 nDigit = wzIn[i] - L'0';
2507 if (0 > nDigit || 9 < nDigit)
2508 {
2509 ExitFunction1(hr = E_INVALIDARG);
2510 }
2511 liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign;
2512
2513 if ((liValue.HighPart ^ iSign) & INT_MIN)
2514 {
2515 ExitFunction1(hr = DISP_E_OVERFLOW);
2516 }
2517 ++i;
2518 }
2519
2520 *pllOut = liValue.QuadPart;
2521
2522 LExit:
2523 return hr;
2524 }
2525
2526 /****************************************************************************
2527 StrStringToUInt64 - converts a string to an unsigned 64-bit integer.
2528
2529 ****************************************************************************/
2530 extern "C" HRESULT DAPI StrStringToUInt64(
2531 __in_z LPCWSTR wzIn,
2532 __in DWORD cchIn,
2533 __out ULONGLONG* pullOut
2534 )
2535 {
2536 HRESULT hr = S_OK;
2537 DWORD i = 0;
2538 DWORD nDigit = 0;
2539 ULONGLONG ullValue = 0;
2540 ULONGLONG ull = 0;
2541 size_t cchString = 0;
2542
2543 // get string length if not provided
2544 if (0 >= cchIn)
2545 {
2546 hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString);
2547 StrExitOnRootFailure(hr, "Failed to get length of string.");
2548
2549 cchIn = (DWORD)cchString;
2550 if (0 >= cchIn)
2551 {
2552 ExitFunction1(hr = E_INVALIDARG);
2553 }
2554 }
2555
2556 // read digits
2557 while (i < cchIn)
2558 {
2559 nDigit = wzIn[i] - L'0';
2560 if (0 > nDigit || 9 < nDigit)
2561 {
2562 ExitFunction1(hr = E_INVALIDARG);
2563 }
2564 ull = (ULONGLONG)(ullValue * 10 + nDigit);
2565
2566 if (ull < ullValue)
2567 {
2568 ExitFunction1(hr = DISP_E_OVERFLOW);
2569 }
2570 ullValue = ull;
2571 ++i;
2572 }
2573
2574 *pullOut = ullValue;
2575
2576 LExit:
2577 return hr;
2578 }
2579
2580 /****************************************************************************
2581 StrStringToUpper - alters the given string in-place to be entirely uppercase
2582
2583 ****************************************************************************/
2584 void DAPI StrStringToUpper(
2585 __inout_z LPWSTR wzIn
2586 )
2587 {
2588 ::CharUpperBuffW(wzIn, lstrlenW(wzIn));
2589 }
2590
2591 /****************************************************************************
2592 StrStringToLower - alters the given string in-place to be entirely lowercase
2593
2594 ****************************************************************************/
2595 void DAPI StrStringToLower(
2596 __inout_z LPWSTR wzIn
2597 )
2598 {
2599 ::CharLowerBuffW(wzIn, lstrlenW(wzIn));
2600 }
2601
2602 /****************************************************************************
2603 StrAllocStringToUpperInvariant - creates an upper-case copy of a string.
2604
2605 ****************************************************************************/
2606 extern "C" HRESULT DAPI StrAllocStringToUpperInvariant(
2607 __deref_out_z LPWSTR* pscz,
2608 __in_z LPCWSTR wzSource,
2609 __in SIZE_T cchSource
2610 )
2611 {
2612 return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE);
2613 }
2614
2615 /****************************************************************************
2616 StrAllocStringToLowerInvariant - creates an lower-case copy of a string.
2617
2618 ****************************************************************************/
2619 extern "C" HRESULT DAPI StrAllocStringToLowerInvariant(
2620 __deref_out_z LPWSTR* pscz,
2621 __in_z LPCWSTR wzSource,
2622 __in SIZE_T cchSource
2623 )
2624 {
2625 return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE);
2626 }
2627
2628 /****************************************************************************
2629 StrArrayAllocString - Allocates a string array.
2630
2631 ****************************************************************************/
2632 extern "C" HRESULT DAPI StrArrayAllocString(
2633 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2634 __inout LPUINT pcStrArray,
2635 __in_z LPCWSTR wzSource,
2636 __in SIZE_T cchSource
2637 )
2638 {
2639 HRESULT hr = S_OK;
2640 UINT cNewStrArray;
2641
2642 hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray);
2643 StrExitOnFailure(hr, "Failed to increment the string array element count.");
2644
2645 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE);
2646 StrExitOnFailure(hr, "Failed to allocate memory for the string array.");
2647
2648 hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource);
2649 StrExitOnFailure(hr, "Failed to allocate and assign the string.");
2650
2651 *pcStrArray = cNewStrArray;
2652
2653 LExit:
2654 return hr;
2655 }
2656
2657 /****************************************************************************
2658 StrArrayFree - Frees a string array.
2659
2660 Use ReleaseNullStrArray to nullify the arguments.
2661
2662 ****************************************************************************/
2663 extern "C" HRESULT DAPI StrArrayFree(
2664 __in_ecount(cStrArray) LPWSTR *rgsczStrArray,
2665 __in UINT cStrArray
2666 )
2667 {
2668 HRESULT hr = S_OK;
2669
2670 for (UINT i = 0; i < cStrArray; ++i)
2671 {
2672 if (NULL != rgsczStrArray[i])
2673 {
2674 hr = StrFree(rgsczStrArray[i]);
2675 StrExitOnFailure(hr, "Failed to free the string at index %u.", i);
2676 }
2677 }
2678
2679 hr = MemFree(rgsczStrArray);
2680 StrExitOnFailure(hr, "Failed to free memory for the string array.");
2681
2682 LExit:
2683 return hr;
2684 }
2685
2686 /****************************************************************************
2687 StrSplitAllocArray - Splits a string into an array.
2688
2689 ****************************************************************************/
2690 extern "C" HRESULT DAPI StrSplitAllocArray(
2691 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2692 __inout LPUINT pcStrArray,
2693 __in_z LPCWSTR wzSource,
2694 __in_z LPCWSTR wzDelim
2695 )
2696 {
2697 HRESULT hr = S_OK;
2698 LPWSTR sczCopy = NULL;
2699 LPWSTR wzContext = NULL;
2700
2701 // Copy wzSource so it is not modified.
2702 hr = StrAllocString(&sczCopy, wzSource, 0);
2703 StrExitOnFailure(hr, "Failed to copy the source string.");
2704
2705 for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext))
2706 {
2707 hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0);
2708 StrExitOnFailure(hr, "Failed to add the string to the string array.");
2709 }
2710
2711 LExit:
2712 ReleaseStr(sczCopy);
2713
2714 return hr;
2715 }
2716
2717 /****************************************************************************
2718 StrAllocStringMapInvariant - helper function for the ToUpper and ToLower.
2719
2720 Note: Assumes source and destination buffers will be the same.
2721 ****************************************************************************/
2722 static HRESULT StrAllocStringMapInvariant(
2723 __deref_out_z LPWSTR* pscz,
2724 __in_z LPCWSTR wzSource,
2725 __in SIZE_T cchSource,
2726 __in DWORD dwMapFlags
2727 )
2728 {
2729 HRESULT hr = S_OK;
2730
2731 hr = StrAllocString(pscz, wzSource, cchSource);
2732 StrExitOnFailure(hr, "Failed to allocate a copy of the source string.");
2733
2734 if (0 == cchSource)
2735 {
2736 // Need the actual string size for LCMapString. This includes the null-terminator
2737 // but LCMapString doesn't care either way.
2738 hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast<size_t*>(&cchSource));
2739 StrExitOnRootFailure(hr, "Failed to get the length of the string.");
2740 }
2741 else if (INT_MAX < cchSource)
2742 {
2743 StrExitOnRootFailure(hr = E_INVALIDARG, "Source string is too long: %Iu", cchSource);
2744 }
2745
2746 // Convert the copy of the string to upper or lower case in-place.
2747 if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, static_cast<int>(cchSource), *pscz, static_cast<int>(cchSource)))
2748 {
2749 StrExitWithLastError(hr, "Failed to convert the string case.");
2750 }
2751
2752 LExit:
2753 return hr;
2754 }
2755
2756 /****************************************************************************
2757 StrSecureZeroString - zeroes out string to the make sure the contents
2758 don't remain in memory.
2759
2760 ****************************************************************************/
2761 extern "C" DAPI_(HRESULT) StrSecureZeroString(
2762 __in LPWSTR pwz
2763 )
2764 {
2765 HRESULT hr = S_OK;
2766 SIZE_T cb = 0;
2767
2768 if (pwz)
2769 {
2770 hr = StrSize(pwz, &cb);
2771 StrExitOnFailure(hr, "Failed to get size of string");
2772
2773 SecureZeroMemory(pwz, cb);
2774 }
2775
2776 LExit:
2777 return hr;
2778 }
2779
2780 /****************************************************************************
2781 StrSecureZeroFreeString - zeroes out string to the make sure the contents
2782 don't remain in memory, then frees the string.
2783
2784 ****************************************************************************/
2785 extern "C" DAPI_(HRESULT) StrSecureZeroFreeString(
2786 __in LPWSTR pwz
2787 )
2788 {
2789 HRESULT hr = S_OK;
2790
2791 hr = StrSecureZeroString(pwz);
2792 ReleaseStr(pwz);
2793
2794 return hr;
2795 }