You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

564 satır
24KB

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