win32_helpers.h 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  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 <winreg.h>
  18. #include <objbase.h>
  19. // detect std::bit_cast
  20. #ifdef __has_include
  21. # if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __has_include(<bit>)
  22. # include <bit>
  23. # endif
  24. #endif
  25. #if __cpp_lib_bit_cast >= 201806L
  26. # define __WI_CONSTEXPR_BIT_CAST constexpr
  27. #else
  28. # define __WI_CONSTEXPR_BIT_CAST inline
  29. #endif
  30. #include "result.h"
  31. #include "resource.h"
  32. #include "wistd_functional.h"
  33. #include "wistd_type_traits.h"
  34. #if _HAS_CXX20 && defined(_STRING_VIEW_) && defined(_COMPARE_)
  35. // If we're using c++20, then <compare> must be included to use the string ordinal functions
  36. # define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
  37. #elif !_HAS_CXX20 && defined(_STRING_VIEW_)
  38. # define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
  39. #endif
  40. namespace wistd
  41. {
  42. #if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
  43. #if _HAS_CXX20
  44. using weak_ordering = std::weak_ordering;
  45. #else // _HAS_CXX20
  46. struct weak_ordering
  47. {
  48. static const weak_ordering less;
  49. static const weak_ordering equivalent;
  50. static const weak_ordering greater;
  51. [[nodiscard]] friend constexpr bool operator==(const weak_ordering left, std::nullptr_t) noexcept
  52. {
  53. return left.m_value == 0;
  54. }
  55. [[nodiscard]] friend constexpr bool operator!=(const weak_ordering left, std::nullptr_t) noexcept
  56. {
  57. return left.m_value != 0;
  58. }
  59. [[nodiscard]] friend constexpr bool operator<(const weak_ordering left, std::nullptr_t) noexcept
  60. {
  61. return left.m_value < 0;
  62. }
  63. [[nodiscard]] friend constexpr bool operator>(const weak_ordering left, std::nullptr_t) noexcept
  64. {
  65. return left.m_value > 0;
  66. }
  67. [[nodiscard]] friend constexpr bool operator<=(const weak_ordering left, std::nullptr_t) noexcept
  68. {
  69. return left.m_value <= 0;
  70. }
  71. [[nodiscard]] friend constexpr bool operator>=(const weak_ordering left, std::nullptr_t) noexcept
  72. {
  73. return left.m_value >= 0;
  74. }
  75. [[nodiscard]] friend constexpr bool operator==(std::nullptr_t, const weak_ordering right) noexcept
  76. {
  77. return right == 0;
  78. }
  79. [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t, const weak_ordering right) noexcept
  80. {
  81. return right != 0;
  82. }
  83. [[nodiscard]] friend constexpr bool operator<(std::nullptr_t, const weak_ordering right) noexcept
  84. {
  85. return right > 0;
  86. }
  87. [[nodiscard]] friend constexpr bool operator>(std::nullptr_t, const weak_ordering right) noexcept
  88. {
  89. return right < 0;
  90. }
  91. [[nodiscard]] friend constexpr bool operator<=(std::nullptr_t, const weak_ordering right) noexcept
  92. {
  93. return right >= 0;
  94. }
  95. [[nodiscard]] friend constexpr bool operator>=(std::nullptr_t, const weak_ordering right) noexcept
  96. {
  97. return right <= 0;
  98. }
  99. signed char m_value;
  100. };
  101. inline constexpr weak_ordering weak_ordering::less{static_cast<signed char>(-1)};
  102. inline constexpr weak_ordering weak_ordering::equivalent{static_cast<signed char>(0)};
  103. inline constexpr weak_ordering weak_ordering::greater{static_cast<signed char>(1)};
  104. #endif // !_HAS_CXX20
  105. #endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
  106. }
  107. namespace wil
  108. {
  109. //! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
  110. //! CDFs has a limit of 254.
  111. constexpr size_t max_path_segment_length = 255;
  112. //! Character length not including the null, MAX_PATH (260) includes the null.
  113. constexpr size_t max_path_length = 259;
  114. //! 32743 Character length not including the null. This is a system defined limit.
  115. //! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
  116. //! It will be 25 when there are more than 9 disks.
  117. constexpr size_t max_extended_path_length = 0x7FFF - 24;
  118. //! For {guid} string form. Includes space for the null terminator.
  119. constexpr size_t guid_string_buffer_length = 39;
  120. //! For {guid} string form. Not including the null terminator.
  121. constexpr size_t guid_string_length = 38;
  122. #pragma region String and identifier comparisons
  123. // Using CompareStringOrdinal functions:
  124. //
  125. // Indentifiers require a locale-less (ordinal), and often case-insensitive, comparison (filenames, registry keys, XML node names, etc).
  126. // DO NOT use locale-sensitive (lexical) comparisons for resource identifiers (e.g.wcs*() functions in the CRT).
  127. #if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
  128. namespace details
  129. {
  130. [[nodiscard]] inline int CompareStringOrdinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
  131. {
  132. // Casting from size_t (unsigned) to int (signed) should be safe from overrun to a negative,
  133. // merely truncating the string. CompareStringOrdinal should be resilient to negatives.
  134. return ::CompareStringOrdinal(left.data(), static_cast<int>(left.size()), right.data(), static_cast<int>(right.size()), caseInsensitive);
  135. }
  136. }
  137. [[nodiscard]] inline wistd::weak_ordering compare_string_ordinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
  138. {
  139. switch (wil::details::CompareStringOrdinal(left, right, caseInsensitive))
  140. {
  141. case CSTR_LESS_THAN:
  142. return wistd::weak_ordering::less;
  143. case CSTR_GREATER_THAN:
  144. return wistd::weak_ordering::greater;
  145. default:
  146. return wistd::weak_ordering::equivalent;
  147. }
  148. }
  149. #endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
  150. #pragma endregion
  151. #pragma region FILETIME helpers
  152. // FILETIME duration values. FILETIME is in 100 nanosecond units.
  153. namespace filetime_duration
  154. {
  155. long long const one_millisecond = 10000LL;
  156. long long const one_second = 10000000LL;
  157. long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL
  158. long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL
  159. long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL
  160. };
  161. namespace filetime
  162. {
  163. constexpr unsigned long long to_int64(const FILETIME &ft) WI_NOEXCEPT
  164. {
  165. #if __cpp_lib_bit_cast >= 201806L
  166. return std::bit_cast<unsigned long long>(ft);
  167. #else
  168. // Cannot reinterpret_cast FILETIME* to unsigned long long*
  169. // due to alignment differences.
  170. return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
  171. #endif
  172. }
  173. __WI_CONSTEXPR_BIT_CAST FILETIME from_int64(unsigned long long i64) WI_NOEXCEPT
  174. {
  175. #if __cpp_lib_bit_cast >= 201806L
  176. return std::bit_cast<FILETIME>(i64);
  177. #else
  178. static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
  179. static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
  180. return *reinterpret_cast<FILETIME *>(&i64);
  181. #endif
  182. }
  183. __WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const &ft, long long delta100ns) WI_NOEXCEPT
  184. {
  185. return from_int64(to_int64(ft) + delta100ns);
  186. }
  187. constexpr bool is_empty(const FILETIME &ft) WI_NOEXCEPT
  188. {
  189. return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
  190. }
  191. inline FILETIME get_system_time() WI_NOEXCEPT
  192. {
  193. FILETIME ft;
  194. GetSystemTimeAsFileTime(&ft);
  195. return ft;
  196. }
  197. /// Convert time as units of 100 nanoseconds to milliseconds. Fractional milliseconds are truncated.
  198. constexpr unsigned long long convert_100ns_to_msec(unsigned long long time100ns) WI_NOEXCEPT
  199. {
  200. return time100ns / filetime_duration::one_millisecond;
  201. }
  202. /// Convert time as milliseconds to units of 100 nanoseconds.
  203. constexpr unsigned long long convert_msec_to_100ns(unsigned long long timeMsec) WI_NOEXCEPT
  204. {
  205. return timeMsec * filetime_duration::one_millisecond;
  206. }
  207. #if defined(_APISETREALTIME_) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
  208. /// Returns the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
  209. ///
  210. /// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation.
  211. ///
  212. /// This is equivalent to GetTickCount64() except it returns units of 100 nanoseconds instead of milliseconds, and it doesn't include time the system spends in sleep or hibernation.
  213. /// For example
  214. ///
  215. /// start = GetTickCount64();
  216. /// hibernate();
  217. /// ...wake from hibernation 30 minutes later...;
  218. /// elapsed = GetTickCount64() - start;
  219. /// // elapsed = 30min
  220. ///
  221. /// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so).
  222. ///
  223. /// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out parameter).
  224. /// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx
  225. inline unsigned long long QueryUnbiasedInterruptTimeAs100ns() WI_NOEXCEPT
  226. {
  227. ULONGLONG now{};
  228. QueryUnbiasedInterruptTime(&now);
  229. return now;
  230. }
  231. /// Returns the current unbiased interrupt-time count, in units of milliseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
  232. /// @see QueryUnbiasedInterruptTimeAs100ns
  233. inline unsigned long long QueryUnbiasedInterruptTimeAsMSec() WI_NOEXCEPT
  234. {
  235. return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns());
  236. }
  237. #endif // _APISETREALTIME_
  238. }
  239. #pragma endregion
  240. #pragma region RECT helpers
  241. template<typename rect_type>
  242. constexpr auto rect_width(rect_type const& rect)
  243. {
  244. return rect.right - rect.left;
  245. }
  246. template<typename rect_type>
  247. constexpr auto rect_height(rect_type const& rect)
  248. {
  249. return rect.bottom - rect.top;
  250. }
  251. template<typename rect_type>
  252. constexpr auto rect_is_empty(rect_type const& rect)
  253. {
  254. return (rect.left >= rect.right) || (rect.top >= rect.bottom);
  255. }
  256. template<typename rect_type, typename point_type>
  257. constexpr auto rect_contains_point(rect_type const& rect, point_type const& point)
  258. {
  259. return (point.x >= rect.left) && (point.x < rect.right) && (point.y >= rect.top) && (point.y < rect.bottom);
  260. }
  261. template<typename rect_type, typename length_type>
  262. constexpr rect_type rect_from_size(length_type x, length_type y, length_type width, length_type height)
  263. {
  264. rect_type rect;
  265. rect.left = x;
  266. rect.top = y;
  267. rect.right = x + width;
  268. rect.bottom = y + height;
  269. return rect;
  270. }
  271. #pragma endregion
  272. // Use to adapt Win32 APIs that take a fixed size buffer into forms that return
  273. // an allocated buffer. Supports many types of string representation.
  274. // See comments below on the expected behavior of the callback.
  275. // Adjust stackBufferLength based on typical result sizes to optimize use and
  276. // to test the boundary cases.
  277. template <typename string_type, size_t stackBufferLength = 256>
  278. HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback) WI_NOEXCEPT
  279. {
  280. details::string_maker<string_type> maker;
  281. wchar_t value[stackBufferLength];
  282. value[0] = L'\0';
  283. size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator.
  284. RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull));
  285. WI_ASSERT(valueLengthNeededWithNull > 0);
  286. if (valueLengthNeededWithNull <= ARRAYSIZE(value))
  287. {
  288. // Success case as described above, make() adds the space for the null.
  289. RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1));
  290. }
  291. else
  292. {
  293. // Did not fit in the stack allocated buffer, need to do 2 phase construction.
  294. // May need to loop more than once if external conditions cause the value to change.
  295. size_t bufferLength;
  296. do
  297. {
  298. bufferLength = valueLengthNeededWithNull;
  299. // bufferLength includes the null so subtract that as make() will add space for it.
  300. RETURN_IF_FAILED(maker.make(nullptr, bufferLength - 1));
  301. RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull));
  302. WI_ASSERT(valueLengthNeededWithNull > 0);
  303. // If the value shrunk, then adjust the string to trim off the excess buffer.
  304. if (valueLengthNeededWithNull < bufferLength)
  305. {
  306. RETURN_IF_FAILED(maker.trim_at_existing_null(valueLengthNeededWithNull - 1));
  307. }
  308. }
  309. while (valueLengthNeededWithNull > bufferLength);
  310. }
  311. result = maker.release();
  312. return S_OK;
  313. }
  314. /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
  315. template <typename string_type, size_t stackBufferLength = 256>
  316. HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
  317. {
  318. return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
  319. [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
  320. {
  321. *valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(valueLength));
  322. RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
  323. return S_OK;
  324. });
  325. }
  326. #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
  327. /** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */
  328. template <typename string_type, size_t stackBufferLength = 256>
  329. HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
  330. {
  331. return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
  332. [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
  333. {
  334. *valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(valueLength), value, nullptr);
  335. if (*valueLengthNeededWithNul == 0)
  336. {
  337. // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
  338. const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError());
  339. RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
  340. RETURN_IF_FAILED(searchResult);
  341. }
  342. // AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL.
  343. // If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL.
  344. // If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul.
  345. if (*valueLengthNeededWithNul < valueLength)
  346. {
  347. (*valueLengthNeededWithNul)++; // It fit, account for the null.
  348. }
  349. return S_OK;
  350. });
  351. }
  352. template <typename string_type, size_t stackBufferLength = 256>
  353. HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
  354. {
  355. return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
  356. [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
  357. {
  358. DWORD lengthToUse = static_cast<DWORD>(valueLength);
  359. BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
  360. RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
  361. // On success, return the amount used; on failure, try doubling
  362. *valueLengthNeededWithNul = success ? (lengthToUse + 1) : (lengthToUse * 2);
  363. return S_OK;
  364. });
  365. }
  366. /** Expands environment strings and checks path existence with SearchPathW */
  367. template <typename string_type, size_t stackBufferLength = 256>
  368. HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
  369. {
  370. wil::unique_cotaskmem_string expandedName;
  371. RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
  372. // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
  373. const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(nullptr, expandedName.get(), nullptr, result));
  374. RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
  375. RETURN_IF_FAILED(searchResult);
  376. return S_OK;
  377. }
  378. #endif
  379. /** Looks up the environment variable 'key' and fails if it is not found. */
  380. template <typename string_type, size_t initialBufferLength = 128>
  381. inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
  382. {
  383. return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(result,
  384. [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
  385. {
  386. // If the function succeeds, the return value is the number of characters stored in the buffer
  387. // pointed to by lpBuffer, not including the terminating null character.
  388. //
  389. // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in
  390. // characters, required to hold the string and its terminating null character and the contents of
  391. // lpBuffer are undefined.
  392. //
  393. // If the function fails, the return value is zero. If the specified environment variable was not
  394. // found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
  395. ::SetLastError(ERROR_SUCCESS);
  396. *valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast<DWORD>(valueLength));
  397. RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS));
  398. if (*valueLengthNeededWithNul < valueLength)
  399. {
  400. (*valueLengthNeededWithNul)++; // It fit, account for the null.
  401. }
  402. return S_OK;
  403. });
  404. }
  405. /** Looks up the environment variable 'key' and returns null if it is not found. */
  406. template <typename string_type, size_t initialBufferLength = 128>
  407. HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
  408. {
  409. const auto hr = wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result);
  410. RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
  411. return S_OK;
  412. }
  413. /** Retrieves the fully qualified path for the file containing the specified module loaded
  414. by a given process. Note GetModuleFileNameExW is a macro.*/
  415. template <typename string_type, size_t initialBufferLength = 128>
  416. HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) WI_NOEXCEPT
  417. {
  418. auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
  419. {
  420. DWORD copiedCount;
  421. size_t valueUsedWithNul;
  422. bool copyFailed;
  423. bool copySucceededWithNoTruncation;
  424. if (process != nullptr)
  425. {
  426. // GetModuleFileNameExW truncates and provides no error or other indication it has done so.
  427. // The only way to be sure it didn't truncate is if it didn't need the whole buffer. The
  428. // count copied to the buffer includes the nul-character as well.
  429. copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast<DWORD>(valueLength));
  430. valueUsedWithNul = copiedCount + 1;
  431. copyFailed = (0 == copiedCount);
  432. copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength - 1);
  433. }
  434. else
  435. {
  436. // In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
  437. // and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include
  438. // the nul-character
  439. copiedCount = ::GetModuleFileNameW(module, value, static_cast<DWORD>(valueLength));
  440. valueUsedWithNul = copiedCount + 1;
  441. copyFailed = (0 == copiedCount);
  442. copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength);
  443. }
  444. RETURN_LAST_ERROR_IF(copyFailed);
  445. // When the copy truncated, request another try with more space.
  446. *valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2);
  447. return S_OK;
  448. };
  449. return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(path, wistd::move(adapter));
  450. }
  451. /** Retrieves the fully qualified path for the file that contains the specified module.
  452. The module must have been loaded by the current process. The path returned will use the
  453. same format that was specified when the module was loaded. Therefore, the path can be a
  454. long or short file name, and can have the prefix '\\?\'. */
  455. template <typename string_type, size_t initialBufferLength = 128>
  456. HRESULT GetModuleFileNameW(HMODULE module, string_type& path) WI_NOEXCEPT
  457. {
  458. return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
  459. }
  460. template <typename string_type, size_t stackBufferLength = 256>
  461. HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
  462. {
  463. return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
  464. [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
  465. {
  466. *valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
  467. RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
  468. if (*valueLengthNeededWithNul < valueLength)
  469. {
  470. (*valueLengthNeededWithNul)++; // it fit, account for the null
  471. }
  472. return S_OK;
  473. });
  474. }
  475. #ifdef WIL_ENABLE_EXCEPTIONS
  476. /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
  477. template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
  478. string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
  479. {
  480. string_type result;
  481. THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, result)));
  482. return result;
  483. }
  484. #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
  485. /** Searches for a specified file in a specified path using SearchPathW*/
  486. template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
  487. string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
  488. {
  489. string_type result;
  490. HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(path, fileName, extension, result);
  491. THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
  492. return result;
  493. }
  494. #endif
  495. /** Looks up the environment variable 'key' and fails if it is not found. */
  496. template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
  497. string_type GetEnvironmentVariableW(_In_ PCWSTR key)
  498. {
  499. string_type result;
  500. THROW_IF_FAILED((wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
  501. return result;
  502. }
  503. /** Looks up the environment variable 'key' and returns null if it is not found. */
  504. template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
  505. string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
  506. {
  507. string_type result;
  508. THROW_IF_FAILED((wil::TryGetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
  509. return result;
  510. }
  511. template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
  512. string_type GetModuleFileNameW(HMODULE module = nullptr /* current process module */)
  513. {
  514. string_type result;
  515. THROW_IF_FAILED((wil::GetModuleFileNameW<string_type, initialBufferLength>(module, result)));
  516. return result;
  517. }
  518. template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
  519. string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
  520. {
  521. string_type result;
  522. THROW_IF_FAILED((wil::GetModuleFileNameExW<string_type, initialBufferLength>(process, module, result)));
  523. return result;
  524. }
  525. template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
  526. string_type QueryFullProcessImageNameW(HANDLE processHandle = GetCurrentProcess(), DWORD flags = 0)
  527. {
  528. string_type result;
  529. THROW_IF_FAILED((wil::QueryFullProcessImageNameW<string_type, stackBufferLength>(processHandle, flags, result)));
  530. return result;
  531. }
  532. #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
  533. // Lookup a DWORD value under HKLM\...\Image File Execution Options\<current process name>
  534. inline DWORD GetCurrentProcessExecutionOption(PCWSTR valueName, DWORD defaultValue = 0)
  535. {
  536. auto filePath = wil::GetModuleFileNameW<wil::unique_cotaskmem_string>();
  537. if (auto lastSlash = wcsrchr(filePath.get(), L'\\'))
  538. {
  539. const auto fileName = lastSlash + 1;
  540. auto keyPath = wil::str_concat<wil::unique_cotaskmem_string>(LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\)",
  541. fileName);
  542. DWORD value{}, sizeofValue = sizeof(value);
  543. if (::RegGetValueW(HKEY_LOCAL_MACHINE, keyPath.get(), valueName,
  544. #ifdef RRF_SUBKEY_WOW6464KEY
  545. RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY,
  546. #else
  547. RRF_RT_REG_DWORD,
  548. #endif
  549. nullptr, &value, &sizeofValue) == ERROR_SUCCESS)
  550. {
  551. return value;
  552. }
  553. }
  554. return defaultValue;
  555. }
  556. // Waits for a debugger to attach to the current process based on registry configuration.
  557. //
  558. // Example:
  559. // HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\explorer.exe
  560. // WaitForDebuggerPresent=1
  561. //
  562. // REG_DWORD value of
  563. // missing or 0 -> don't break
  564. // 1 -> wait for the debugger, continue execution once it is attached
  565. // 2 -> wait for the debugger, break here once attached.
  566. inline void WaitForDebuggerPresent(bool checkRegistryConfig = true)
  567. {
  568. for (;;)
  569. {
  570. auto configValue = checkRegistryConfig ? GetCurrentProcessExecutionOption(L"WaitForDebuggerPresent") : 1;
  571. if (configValue == 0)
  572. {
  573. return; // not configured, don't wait
  574. }
  575. if (IsDebuggerPresent())
  576. {
  577. if (configValue == 2)
  578. {
  579. DebugBreak(); // debugger attached, SHIFT+F11 to return to the caller
  580. }
  581. return; // debugger now attached, continue executing
  582. }
  583. Sleep(500);
  584. }
  585. }
  586. #endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
  587. #endif
  588. /** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
  589. the linker provides for every module. This avoids the need for a global HINSTANCE variable
  590. and provides access to this value for static libraries. */
  591. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  592. inline HINSTANCE GetModuleInstanceHandle() WI_NOEXCEPT { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
  593. // GetModuleHandleExW was added to the app partition in version 22000 of the SDK
  594. #if defined(NTDDI_WIN10_CO) ? \
  595. WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) : \
  596. WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
  597. // Use this in threads that can outlive the object or API call that created them.
  598. // Without this COM, or the API caller, can unload the DLL, resulting in a crash.
  599. // It is very important that this be the first object created in the thread proc
  600. // as when this runs down the thread exits and no destructors of objects created before
  601. // it will run.
  602. [[nodiscard]] inline auto get_module_reference_for_thread() noexcept
  603. {
  604. HMODULE thisModule{};
  605. FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, L"", &thisModule));
  606. return wil::scope_exit([thisModule]
  607. {
  608. FreeLibraryAndExitThread(thisModule, 0);
  609. });
  610. }
  611. #endif
  612. /// @cond
  613. namespace details
  614. {
  615. class init_once_completer
  616. {
  617. INIT_ONCE& m_once;
  618. unsigned long m_flags = INIT_ONCE_INIT_FAILED;
  619. public:
  620. init_once_completer(_In_ INIT_ONCE& once) WI_NOEXCEPT : m_once(once)
  621. {
  622. }
  623. #pragma warning(push)
  624. #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
  625. void success() WI_NOEXCEPT
  626. {
  627. m_flags = 0;
  628. }
  629. #pragma warning(pop)
  630. ~init_once_completer() WI_NOEXCEPT
  631. {
  632. ::InitOnceComplete(&m_once, m_flags, nullptr);
  633. }
  634. };
  635. }
  636. /// @endcond
  637. /** Performs one-time initialization
  638. Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
  639. at most once.
  640. ~~~~
  641. INIT_ONCE g_init{};
  642. ComPtr<IFoo> g_foo;
  643. HRESULT MyMethod()
  644. {
  645. bool winner = false;
  646. RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
  647. {
  648. ComPtr<IFoo> foo;
  649. RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
  650. RETURN_IF_FAILED(foo->Startup());
  651. g_foo = foo;
  652. }, &winner);
  653. if (winner)
  654. {
  655. RETURN_IF_FAILED(g_foo->Another());
  656. }
  657. return S_OK;
  658. }
  659. ~~~~
  660. See MSDN for more information on `InitOnceExecuteOnce`.
  661. @param initOnce The INIT_ONCE structure to use as context for initialization.
  662. @param func A function that will be invoked to perform initialization. If this fails, the init call
  663. fails and the once-init is not marked as initialized. A later caller could attempt to
  664. initialize it a second time.
  665. @param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise.
  666. */
  667. template<typename T> HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT
  668. {
  669. BOOL pending = FALSE;
  670. wil::assign_to_opt_param(callerCompleted, false);
  671. __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
  672. if (pending)
  673. {
  674. details::init_once_completer completion(initOnce);
  675. __WIL_PRIVATE_RETURN_IF_FAILED(func());
  676. completion.success();
  677. wil::assign_to_opt_param(callerCompleted, true);
  678. }
  679. return S_OK;
  680. }
  681. //! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is
  682. //! returned to the caller instead of being an out-parameter.
  683. template<typename T> bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
  684. {
  685. bool callerCompleted;
  686. FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(func), &callerCompleted));
  687. return callerCompleted;
  688. };
  689. //! Returns 'true' if this `init_once` structure has finished initialization, false otherwise.
  690. inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT
  691. {
  692. BOOL pending = FALSE;
  693. return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending;
  694. }
  695. #ifdef WIL_ENABLE_EXCEPTIONS
  696. /** Performs one-time initialization
  697. Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
  698. at most once.
  699. ~~~~
  700. INIT_ONCE g_init{};
  701. ComPtr<IFoo> g_foo;
  702. void MyMethod()
  703. {
  704. bool winner = wil::init_once(g_init, []
  705. {
  706. ComPtr<IFoo> foo;
  707. THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
  708. THROW_IF_FAILED(foo->Startup());
  709. g_foo = foo;
  710. });
  711. if (winner)
  712. {
  713. THROW_IF_FAILED(g_foo->Another());
  714. }
  715. }
  716. ~~~~
  717. See MSDN for more information on `InitOnceExecuteOnce`.
  718. @param initOnce The INIT_ONCE structure to use as context for initialization.
  719. @param func A function that will be invoked to perform initialization. If this fails, the init call
  720. fails and the once-init is not marked as initialized. A later caller could attempt to
  721. initialize it a second time.
  722. @returns 'true' if this was the call that caused initialization, false otherwise.
  723. */
  724. template<typename T> bool init_once(_Inout_ INIT_ONCE& initOnce, T func)
  725. {
  726. BOOL pending = FALSE;
  727. THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
  728. if (pending)
  729. {
  730. details::init_once_completer completion(initOnce);
  731. func();
  732. completion.success();
  733. return true;
  734. }
  735. else
  736. {
  737. return false;
  738. }
  739. }
  740. #endif // WIL_ENABLE_EXCEPTIONS
  741. }
  742. // Macro for calling GetProcAddress(), with type safety for C++ clients
  743. // using the type information from the specified function.
  744. // The return value is automatically cast to match the function prototype of the input function.
  745. //
  746. // Sample usage:
  747. //
  748. // auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW);
  749. // if (sendMail)
  750. // {
  751. // sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0);
  752. // }
  753. // Declaration
  754. #define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast<decltype(::fn)*>(GetProcAddress(hinst, #fn))
  755. #endif // __WIL_WIN32_HELPERS_INCLUDED