1 //*********************************************************
2 //
3 //    Copyright (c) Microsoft. All rights reserved.
4 //    This code is licensed under the MIT License.
5 //    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
6 //    ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
7 //    TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
8 //    PARTICULAR PURPOSE AND NONINFRINGEMENT.
9 //
10 //*********************************************************
11 #ifndef __WIL_WIN32_HELPERS_INCLUDED
12 #define __WIL_WIN32_HELPERS_INCLUDED
13 
14 #include <minwindef.h> // FILETIME, HINSTANCE
15 #include <sysinfoapi.h> // GetSystemTimeAsFileTime
16 #include <libloaderapi.h> // GetProcAddress
17 #include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
18 #include <PathCch.h>
19 #include <objbase.h>
20 
21 #include "result.h"
22 #include "resource.h"
23 #include "wistd_functional.h"
24 #include "wistd_type_traits.h"
25 
26 namespace wil
27 {
28     //! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
29     //! CDFs has a limit of 254.
30     size_t const max_path_segment_length = 255;
31 
32     //! Character length not including the null, MAX_PATH (260) includes the null.
33     size_t const max_path_length = 259;
34 
35     //! 32743 Character length not including the null. This is a system defined limit.
36     //! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
37     //! It will be 25 when there are more than 9 disks.
38     size_t const max_extended_path_length = 0x7FFF - 24;
39 
40     //! For {guid} string form. Includes space for the null terminator.
41     size_t const guid_string_buffer_length = 39;
42 
43     //! For {guid} string form. Not including the null terminator.
44     size_t const guid_string_length = 38;
45 
46 #pragma region FILETIME helpers
47     // FILETIME duration values. FILETIME is in 100 nanosecond units.
48     namespace filetime_duration
49     {
50         long long const one_millisecond = 10000LL;
51         long long const one_second      = 10000000LL;
52         long long const one_minute      = 10000000LL * 60;           // 600000000    or 600000000LL
53         long long const one_hour        = 10000000LL * 60 * 60;      // 36000000000  or 36000000000LL
54         long long const one_day         = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL
55     };
56 
57     namespace filetime
58     {
to_int64(const FILETIME & ft)59         inline unsigned long long to_int64(const FILETIME &ft)
60         {
61             // Cannot reinterpret_cast FILETIME* to unsigned long long*
62             // due to alignment differences.
63             return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
64         }
65 
from_int64(unsigned long long i64)66         inline FILETIME from_int64(unsigned long long i64)
67         {
68             static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
69             static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
70             return *reinterpret_cast<FILETIME *>(&i64);
71         }
72 
add(_In_ FILETIME const & ft,long long delta)73         inline FILETIME add(_In_ FILETIME const &ft, long long delta)
74         {
75             return from_int64(to_int64(ft) + delta);
76         }
77 
is_empty(const FILETIME & ft)78         inline bool is_empty(const FILETIME &ft)
79         {
80             return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
81         }
82 
get_system_time()83         inline FILETIME get_system_time()
84         {
85             FILETIME ft;
86             GetSystemTimeAsFileTime(&ft);
87             return ft;
88         }
89     }
90 #pragma endregion
91 
92     // Use to adapt Win32 APIs that take a fixed size buffer into forms that return
93     // an allocated buffer. Supports many types of string representation.
94     // See comments below on the expected behavior of the callback.
95     // Adjust stackBufferLength based on typical result sizes to optimize use and
96     // to test the boundary cases.
97     template <typename string_type, size_t stackBufferLength = 256>
AdaptFixedSizeToAllocatedResult(string_type & result,wistd::function<HRESULT (PWSTR,size_t,size_t *)> callback)98     HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback)
99     {
100         details::string_maker<string_type> maker;
101 
102         wchar_t value[stackBufferLength];
103         value[0] = L'\0';
104         size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator.
105         RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull));
106         WI_ASSERT(valueLengthNeededWithNull > 0);
107         if (valueLengthNeededWithNull <= ARRAYSIZE(value))
108         {
109             // Success case as described above, make() adds the space for the null.
110             RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1));
111         }
112         else
113         {
114             // Did not fit in the stack allocated buffer, need to do 2 phase construction.
115             // valueLengthNeededWithNull includes the null so subtract that as make() will add space for it.
116             RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1));
117 
118             size_t secondLength{};
119             RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength));
120 
121             // Ensure callback produces consistent result.
122             FAIL_FAST_IF(valueLengthNeededWithNull != secondLength);
123         }
124         result = maker.release();
125         return S_OK;
126     }
127 
128     /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
129     template <typename string_type, size_t stackBufferLength = 256>
ExpandEnvironmentStringsW(_In_ PCWSTR input,string_type & result)130     HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
131     {
132         return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
133             [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
134         {
135             *valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(valueLength));
136             RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
137             return S_OK;
138         });
139     }
140 
141 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
142     /** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */
143     template <typename string_type, size_t stackBufferLength = 256>
SearchPathW(_In_opt_ PCWSTR path,_In_ PCWSTR fileName,_In_opt_ PCWSTR extension,string_type & result)144     HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
145     {
146         return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
147             [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
148         {
149             *valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(valueLength), value, nullptr);
150 
151             if (*valueLengthNeededWithNul == 0)
152             {
153                 // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
154                 const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError());
155                 RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
156                 RETURN_IF_FAILED(searchResult);
157             }
158 
159             // AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL.
160             // If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL.
161             // If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul.
162             if (*valueLengthNeededWithNul < valueLength)
163             {
164                 (*valueLengthNeededWithNul)++; // It fit, account for the null.
165             }
166             return S_OK;
167         });
168     }
169 
170     // This function does not work beyond the default stack buffer size (255).
171     // Needs to to retry in a loop similar to wil::GetModuleFileNameExW
172     // These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
173     template <typename string_type, size_t stackBufferLength = 256>
QueryFullProcessImageNameW(HANDLE processHandle,_In_ DWORD flags,string_type & result)174     HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
175     {
176         return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
177             [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
178         {
179             DWORD lengthToUse = static_cast<DWORD>(valueLength);
180             BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
181             RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
182             // On both success or insufficient buffer case, add +1 for the null-terminating character
183             *valueLengthNeededWithNul = lengthToUse + 1;
184             return S_OK;
185         });
186     }
187 
188     /** Expands environment strings and checks path existence with SearchPathW */
189     template <typename string_type, size_t stackBufferLength = 256>
ExpandEnvAndSearchPath(_In_ PCWSTR input,string_type & result)190     HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
191     {
192         wil::unique_cotaskmem_string expandedName;
193         RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
194 
195         // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
196         const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(nullptr, expandedName.get(), nullptr, result));
197         RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
198         RETURN_IF_FAILED(searchResult);
199 
200         return S_OK;
201     }
202 #endif
203 
204     /** Looks up the environment variable 'key' and fails if it is not found.
205     'key' should not have '%' prefix and suffix.
206     Dangerous since environment variable generally are optional. */
207     template <typename string_type>
GetEnvironmentVariableW(_In_ PCWSTR key,string_type & result)208     inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
209     {
210         return wil::AdaptFixedSizeToAllocatedResult(result,
211             [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
212         {
213             // If the function succeeds, the return value is the number of characters stored in the buffer
214             // pointed to by lpBuffer, not including the terminating null character.
215             //
216             // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in
217             // characters, required to hold the string and its terminating null character and the contents of
218             // lpBuffer are undefined.
219             //
220             // If the function fails, the return value is zero. If the specified environment variable was not
221             // found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
222 
223             ::SetLastError(ERROR_SUCCESS);
224 
225             *valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast<DWORD>(valueLength));
226             RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS));
227             if (*valueLengthNeededWithNul < valueLength)
228             {
229                 (*valueLengthNeededWithNul)++; // It fit, account for the null.
230             }
231             return S_OK;
232         });
233     }
234 
235     /** Looks up the environment variable 'key' and returns null if it is not found.
236     'key' should not have '%' prefix and suffix. */
237     template <typename string_type>
TryGetEnvironmentVariableW(_In_ PCWSTR key,string_type & result)238     HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
239     {
240         const auto hr = wil::GetEnvironmentVariableW<string_type>(key, result);
241         RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
242         return S_OK;
243     }
244 
245     /** Retrieves the fully qualified path for the file containing the specified module loaded
246     by a given process. Note GetModuleFileNameExW is a macro.*/
247     template <typename string_type, size_t initialBufferLength = 128>
GetModuleFileNameExW(_In_opt_ HANDLE process,_In_opt_ HMODULE module,string_type & path)248     HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path)
249     {
250         // initialBufferLength is a template parameter to allow for testing.  It creates some waste for
251         // shorter paths, but avoids iteration through the loop in common cases where paths are less
252         // than 128 characters.
253         // wil::max_extended_path_length + 1 (for the null char)
254         // + 1 (to be certain GetModuleFileNameExW didn't truncate)
255         size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0;
256         size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation;
257 
258         details::string_maker<string_type> maker;
259 
260         for (size_t lengthWithNull = initialBufferLength;
261              lengthWithNull <= maxExtendedPathLengthWithNull;
262              lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull))
263         {
264             // make() adds space for the trailing null
265             RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1));
266 
267             DWORD copiedCount;
268             bool copyFailed;
269             bool copySucceededWithNoTruncation;
270 
271             if (process != nullptr)
272             {
273                 // GetModuleFileNameExW truncates and provides no error or other indication it has done so.
274                 // The only way to be sure it didn't truncate is if it didn't need the whole buffer.
275                 copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
276                 copyFailed = (0 == copiedCount);
277                 copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1);
278             }
279             else
280             {
281                 // In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
282                 // and set the last error to ERROR_INSUFFICIENT_BUFFER.
283                 copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
284                 copyFailed = (0 == copiedCount);
285                 copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull);
286             }
287 
288             if (copyFailed)
289             {
290                 RETURN_LAST_ERROR();
291             }
292             else if (copySucceededWithNoTruncation)
293             {
294                 path = maker.release();
295                 return S_OK;
296             }
297 
298             WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER));
299 
300             if (lengthWithNull == maxExtendedPathLengthWithNull)
301             {
302                 // If we've reached this point, there's no point in trying a larger buffer size.
303                 break;
304             }
305         }
306 
307         // Any path should fit into the maximum max_extended_path_length. If we reached here, something went
308         // terribly wrong.
309         FAIL_FAST();
310     }
311 
312     /** Retrieves the fully qualified path for the file that contains the specified module.
313     The module must have been loaded by the current process. The path returned will use the
314     same format that was specified when the module was loaded. Therefore, the path can be a
315     long or short file name, and can have the prefix '\\?\'. */
316     template <typename string_type, size_t initialBufferLength = 128>
GetModuleFileNameW(HMODULE module,string_type & path)317     HRESULT GetModuleFileNameW(HMODULE module, string_type& path)
318     {
319         return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
320     }
321 
322     template <typename string_type, size_t stackBufferLength = 256>
GetSystemDirectoryW(string_type & result)323     HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
324     {
325         return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
326             [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
327         {
328             *valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
329             RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
330             if (*valueLengthNeededWithNul < valueLength)
331             {
332                 (*valueLengthNeededWithNul)++; // it fit, account for the null
333             }
334             return S_OK;
335         });
336     }
337 
338 #ifdef WIL_ENABLE_EXCEPTIONS
339     /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
340     template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
ExpandEnvironmentStringsW(_In_ PCWSTR input)341     string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
342     {
343         string_type result;
344         THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, result)));
345         return result;
346     }
347 
348 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
349     /** Searches for a specified file in a specified path using SearchPathW*/
350     template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
TrySearchPathW(_In_opt_ PCWSTR path,_In_ PCWSTR fileName,PCWSTR _In_opt_ extension)351     string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
352     {
353         string_type result;
354         HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(path, fileName, extension, result);
355         THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
356         return result;
357     }
358 #endif
359 
360     /** Looks up the environment variable 'key' and fails if it is not found.
361     'key' should not have '%' prefix and suffix.
362     Dangerous since environment variable generally are optional. */
363     template <typename string_type = wil::unique_cotaskmem_string>
GetEnvironmentVariableW(_In_ PCWSTR key)364     string_type GetEnvironmentVariableW(_In_ PCWSTR key)
365     {
366         string_type result;
367         THROW_IF_FAILED(wil::GetEnvironmentVariableW<string_type>(key, result));
368         return result;
369     }
370 
371     /** Looks up the environment variable 'key' and returns null if it is not found.
372     'key' should not have '%' prefix and suffix. */
373     template <typename string_type = wil::unique_cotaskmem_string>
TryGetEnvironmentVariableW(_In_ PCWSTR key)374     string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
375     {
376         string_type result;
377         THROW_IF_FAILED(wil::TryGetEnvironmentVariableW<string_type>(key, result));
378         return result;
379     }
380 
381     template <typename string_type = wil::unique_cotaskmem_string>
GetModuleFileNameW(HMODULE module)382     string_type GetModuleFileNameW(HMODULE module)
383     {
384         string_type result;
385         THROW_IF_FAILED(wil::GetModuleFileNameW(module, result));
386         return result;
387     }
388 
389     template <typename string_type = wil::unique_cotaskmem_string>
GetModuleFileNameExW(HANDLE process,HMODULE module)390     string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
391     {
392         string_type result;
393         THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result));
394         return result;
395     }
396 
397 #endif
398 
399     /** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
400     the linker provides for every module. This avoids the need for a global HINSTANCE variable
401     and provides access to this value for static libraries. */
402     EXTERN_C IMAGE_DOS_HEADER __ImageBase;
GetModuleInstanceHandle()403     inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
404 
405     /// @cond
406     namespace details
407     {
408         class init_once_completer
409         {
410             INIT_ONCE& m_once;
411             unsigned long m_flags = INIT_ONCE_INIT_FAILED;
412         public:
init_once_completer(_In_ INIT_ONCE & once)413             init_once_completer(_In_ INIT_ONCE& once) : m_once(once)
414             {
415             }
416 
417             #pragma warning(push)
418             #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
success()419             void success()
420             {
421                 m_flags = 0;
422             }
423             #pragma warning(pop)
424 
~init_once_completer()425             ~init_once_completer()
426             {
427                 ::InitOnceComplete(&m_once, m_flags, nullptr);
428             }
429         };
430     }
431     /// @endcond
432 
433     /** Performs one-time initialization
434     Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
435     at most once.
436     ~~~~
437     INIT_ONCE g_init{};
438     ComPtr<IFoo> g_foo;
439     HRESULT MyMethod()
440     {
441         bool winner = false;
442         RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
443         {
444             ComPtr<IFoo> foo;
445             RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
446             RETURN_IF_FAILED(foo->Startup());
447             g_foo = foo;
448         }, &winner);
449         if (winner)
450         {
451             RETURN_IF_FAILED(g_foo->Another());
452         }
453         return S_OK;
454     }
455     ~~~~
456     See MSDN for more information on `InitOnceExecuteOnce`.
457     @param initOnce The INIT_ONCE structure to use as context for initialization.
458     @param func A function that will be invoked to perform initialization. If this fails, the init call
459             fails and the once-init is not marked as initialized. A later caller could attempt to
460             initialize it a second time.
461     @param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise.
462     */
463     template<typename T> HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT
464     {
465         BOOL pending = FALSE;
466         wil::assign_to_opt_param(callerCompleted, false);
467 
468         __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
469 
470         if (pending)
471         {
472             details::init_once_completer completion(initOnce);
473             __WIL_PRIVATE_RETURN_IF_FAILED(func());
474             completion.success();
475             wil::assign_to_opt_param(callerCompleted, true);
476         }
477 
478         return S_OK;
479     }
480 
481     //! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is
482     //! returned to the caller instead of being an out-parameter.
init_once_failfast(_Inout_ INIT_ONCE & initOnce,T && func)483     template<typename T> bool init_once_failfast(_Inout_  INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
484     {
485         bool callerCompleted;
486 
487         FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(func), &callerCompleted));
488 
489         return callerCompleted;
490     };
491 
492     //! Returns 'true' if this `init_once` structure has finished initialization, false otherwise.
init_once_initialized(_Inout_ INIT_ONCE & initOnce)493     inline bool init_once_initialized(_Inout_  INIT_ONCE& initOnce) WI_NOEXCEPT
494     {
495         BOOL pending = FALSE;
496         return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending;
497     }
498 
499 #ifdef WIL_ENABLE_EXCEPTIONS
500     /** Performs one-time initialization
501     Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
502     at most once.
503     ~~~~
504     INIT_ONCE g_init{};
505     ComPtr<IFoo> g_foo;
506     void MyMethod()
507     {
508         bool winner = wil::init_once(g_init, []
509         {
510             ComPtr<IFoo> foo;
511             THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
512             THROW_IF_FAILED(foo->Startup());
513             g_foo = foo;
514         });
515         if (winner)
516         {
517             THROW_IF_FAILED(g_foo->Another());
518         }
519     }
520     ~~~~
521     See MSDN for more information on `InitOnceExecuteOnce`.
522     @param initOnce The INIT_ONCE structure to use as context for initialization.
523     @param func A function that will be invoked to perform initialization. If this fails, the init call
524             fails and the once-init is not marked as initialized. A later caller could attempt to
525             initialize it a second time.
526     @returns 'true' if this was the call that caused initialization, false otherwise.
527     */
init_once(_Inout_ INIT_ONCE & initOnce,T func)528     template<typename T> bool init_once(_Inout_  INIT_ONCE& initOnce, T func)
529     {
530         BOOL pending = FALSE;
531 
532         THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
533 
534         if (pending)
535         {
536             details::init_once_completer completion(initOnce);
537             func();
538             completion.success();
539             return true;
540         }
541         else
542         {
543             return false;
544         }
545     }
546 #endif // WIL_ENABLE_EXCEPTIONS
547 }
548 
549 // Macro for calling GetProcAddress(), with type safety for C++ clients
550 // using the type information from the specified function.
551 // The return value is automatically cast to match the function prototype of the input function.
552 //
553 // Sample usage:
554 //
555 // auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW);
556 // if (sendMail)
557 // {
558 //    sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0);
559 // }
560 //  Declaration
561 #define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast<decltype(::fn)*>(GetProcAddress(hinst, #fn))
562 
563 #endif // __WIL_WIN32_HELPERS_INCLUDED
564