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