mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-23 20:05:26 +00:00
613 lines
26 KiB
C
613 lines
26 KiB
C
|
//*********************************************************
|
||
|
//
|
||
|
// Copyright (c) Microsoft. All rights reserved.
|
||
|
// This code is licensed under the MIT License.
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||
|
//
|
||
|
//*********************************************************
|
||
|
#ifndef __WIL_WIN32_HELPERS_INCLUDED
|
||
|
#define __WIL_WIN32_HELPERS_INCLUDED
|
||
|
|
||
|
#include <minwindef.h> // FILETIME, HINSTANCE
|
||
|
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
|
||
|
#include <libloaderapi.h> // GetProcAddress
|
||
|
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
|
||
|
#include <PathCch.h>
|
||
|
#include <objbase.h>
|
||
|
|
||
|
#include "result.h"
|
||
|
#include "resource.h"
|
||
|
#include "wistd_functional.h"
|
||
|
#include "wistd_type_traits.h"
|
||
|
|
||
|
namespace wil
|
||
|
{
|
||
|
//! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
|
||
|
//! CDFs has a limit of 254.
|
||
|
size_t const max_path_segment_length = 255;
|
||
|
|
||
|
//! Character length not including the null, MAX_PATH (260) includes the null.
|
||
|
size_t const max_path_length = 259;
|
||
|
|
||
|
//! 32743 Character length not including the null. This is a system defined limit.
|
||
|
//! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
|
||
|
//! It will be 25 when there are more than 9 disks.
|
||
|
size_t const max_extended_path_length = 0x7FFF - 24;
|
||
|
|
||
|
//! For {guid} string form. Includes space for the null terminator.
|
||
|
size_t const guid_string_buffer_length = 39;
|
||
|
|
||
|
//! For {guid} string form. Not including the null terminator.
|
||
|
size_t const guid_string_length = 38;
|
||
|
|
||
|
#pragma region FILETIME helpers
|
||
|
// FILETIME duration values. FILETIME is in 100 nanosecond units.
|
||
|
namespace filetime_duration
|
||
|
{
|
||
|
long long const one_millisecond = 10000LL;
|
||
|
long long const one_second = 10000000LL;
|
||
|
long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL
|
||
|
long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL
|
||
|
long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL
|
||
|
};
|
||
|
|
||
|
namespace filetime
|
||
|
{
|
||
|
constexpr unsigned long long to_int64(const FILETIME &ft)
|
||
|
{
|
||
|
// Cannot reinterpret_cast FILETIME* to unsigned long long*
|
||
|
// due to alignment differences.
|
||
|
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||
|
}
|
||
|
|
||
|
inline FILETIME from_int64(unsigned long long i64)
|
||
|
{
|
||
|
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
|
||
|
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
|
||
|
return *reinterpret_cast<FILETIME *>(&i64);
|
||
|
}
|
||
|
|
||
|
inline FILETIME add(_In_ FILETIME const &ft, long long delta100ns)
|
||
|
{
|
||
|
return from_int64(to_int64(ft) + delta100ns);
|
||
|
}
|
||
|
|
||
|
constexpr bool is_empty(const FILETIME &ft)
|
||
|
{
|
||
|
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
|
||
|
}
|
||
|
|
||
|
inline FILETIME get_system_time()
|
||
|
{
|
||
|
FILETIME ft;
|
||
|
GetSystemTimeAsFileTime(&ft);
|
||
|
return ft;
|
||
|
}
|
||
|
|
||
|
/// Convert time as units of 100 nanoseconds to milliseconds. Fractional milliseconds are truncated.
|
||
|
constexpr unsigned long long convert_100ns_to_msec(unsigned long long time100ns)
|
||
|
{
|
||
|
return time100ns / filetime_duration::one_millisecond;
|
||
|
}
|
||
|
|
||
|
/// Convert time as milliseconds to units of 100 nanoseconds.
|
||
|
constexpr unsigned long long convert_msec_to_100ns(unsigned long long timeMsec)
|
||
|
{
|
||
|
return timeMsec * filetime_duration::one_millisecond;
|
||
|
}
|
||
|
|
||
|
#if defined(_APISETREALTIME_)
|
||
|
/// 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.
|
||
|
///
|
||
|
/// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation.
|
||
|
///
|
||
|
/// 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.
|
||
|
/// For example
|
||
|
///
|
||
|
/// start = GetTickCount64();
|
||
|
/// hibernate();
|
||
|
/// ...wake from hibernation 30 minutes later...;
|
||
|
/// elapsed = GetTickCount64() - start;
|
||
|
/// // elapsed = 30min
|
||
|
///
|
||
|
/// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so).
|
||
|
///
|
||
|
/// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out parameter).
|
||
|
/// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx
|
||
|
inline unsigned long long QueryUnbiasedInterruptTimeAs100ns()
|
||
|
{
|
||
|
ULONGLONG now{};
|
||
|
QueryUnbiasedInterruptTime(&now);
|
||
|
return now;
|
||
|
}
|
||
|
|
||
|
/// 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.
|
||
|
/// @see QueryUnbiasedInterruptTimeAs100ns
|
||
|
inline unsigned long long QueryUnbiasedInterruptTimeAsMSec()
|
||
|
{
|
||
|
return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns());
|
||
|
}
|
||
|
#endif // _APISETREALTIME_
|
||
|
}
|
||
|
#pragma endregion
|
||
|
|
||
|
// Use to adapt Win32 APIs that take a fixed size buffer into forms that return
|
||
|
// an allocated buffer. Supports many types of string representation.
|
||
|
// See comments below on the expected behavior of the callback.
|
||
|
// Adjust stackBufferLength based on typical result sizes to optimize use and
|
||
|
// to test the boundary cases.
|
||
|
template <typename string_type, size_t stackBufferLength = 256>
|
||
|
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback)
|
||
|
{
|
||
|
details::string_maker<string_type> maker;
|
||
|
|
||
|
wchar_t value[stackBufferLength];
|
||
|
value[0] = L'\0';
|
||
|
size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator.
|
||
|
RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull));
|
||
|
WI_ASSERT(valueLengthNeededWithNull > 0);
|
||
|
if (valueLengthNeededWithNull <= ARRAYSIZE(value))
|
||
|
{
|
||
|
// Success case as described above, make() adds the space for the null.
|
||
|
RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Did not fit in the stack allocated buffer, need to do 2 phase construction.
|
||
|
// May need to loop more than once if external conditions cause the value to change.
|
||
|
size_t bufferLength;
|
||
|
do
|
||
|
{
|
||
|
bufferLength = valueLengthNeededWithNull;
|
||
|
// bufferLength includes the null so subtract that as make() will add space for it.
|
||
|
RETURN_IF_FAILED(maker.make(nullptr, bufferLength - 1));
|
||
|
|
||
|
RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull));
|
||
|
WI_ASSERT(valueLengthNeededWithNull > 0);
|
||
|
|
||
|
// If the value shrunk, then adjust the string to trim off the excess buffer.
|
||
|
if (valueLengthNeededWithNull < bufferLength)
|
||
|
{
|
||
|
RETURN_IF_FAILED(maker.trim_at_existing_null(valueLengthNeededWithNull - 1));
|
||
|
}
|
||
|
}
|
||
|
while (valueLengthNeededWithNull > bufferLength);
|
||
|
}
|
||
|
result = maker.release();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
|
||
|
template <typename string_type, size_t stackBufferLength = 256>
|
||
|
HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
|
||
|
{
|
||
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||
|
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||
|
{
|
||
|
*valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(valueLength));
|
||
|
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
|
||
|
return S_OK;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||
|
/** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */
|
||
|
template <typename string_type, size_t stackBufferLength = 256>
|
||
|
HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
|
||
|
{
|
||
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||
|
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||
|
{
|
||
|
*valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(valueLength), value, nullptr);
|
||
|
|
||
|
if (*valueLengthNeededWithNul == 0)
|
||
|
{
|
||
|
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
|
||
|
const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError());
|
||
|
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
||
|
RETURN_IF_FAILED(searchResult);
|
||
|
}
|
||
|
|
||
|
// AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL.
|
||
|
// If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL.
|
||
|
// If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul.
|
||
|
if (*valueLengthNeededWithNul < valueLength)
|
||
|
{
|
||
|
(*valueLengthNeededWithNul)++; // It fit, account for the null.
|
||
|
}
|
||
|
return S_OK;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// This function does not work beyond the default stack buffer size (255).
|
||
|
// Needs to to retry in a loop similar to wil::GetModuleFileNameExW
|
||
|
// These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
|
||
|
template <typename string_type, size_t stackBufferLength = 256>
|
||
|
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
|
||
|
{
|
||
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||
|
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||
|
{
|
||
|
DWORD lengthToUse = static_cast<DWORD>(valueLength);
|
||
|
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
|
||
|
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
|
||
|
// On both success or insufficient buffer case, add +1 for the null-terminating character
|
||
|
*valueLengthNeededWithNul = lengthToUse + 1;
|
||
|
return S_OK;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/** Expands environment strings and checks path existence with SearchPathW */
|
||
|
template <typename string_type, size_t stackBufferLength = 256>
|
||
|
HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
|
||
|
{
|
||
|
wil::unique_cotaskmem_string expandedName;
|
||
|
RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
|
||
|
|
||
|
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
|
||
|
const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(nullptr, expandedName.get(), nullptr, result));
|
||
|
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
||
|
RETURN_IF_FAILED(searchResult);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/** Looks up the environment variable 'key' and fails if it is not found. */
|
||
|
template <typename string_type>
|
||
|
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||
|
{
|
||
|
return wil::AdaptFixedSizeToAllocatedResult(result,
|
||
|
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||
|
{
|
||
|
// If the function succeeds, the return value is the number of characters stored in the buffer
|
||
|
// pointed to by lpBuffer, not including the terminating null character.
|
||
|
//
|
||
|
// If lpBuffer is not large enough to hold the data, the return value is the buffer size, in
|
||
|
// characters, required to hold the string and its terminating null character and the contents of
|
||
|
// lpBuffer are undefined.
|
||
|
//
|
||
|
// If the function fails, the return value is zero. If the specified environment variable was not
|
||
|
// found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
|
||
|
|
||
|
::SetLastError(ERROR_SUCCESS);
|
||
|
|
||
|
*valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast<DWORD>(valueLength));
|
||
|
RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS));
|
||
|
if (*valueLengthNeededWithNul < valueLength)
|
||
|
{
|
||
|
(*valueLengthNeededWithNul)++; // It fit, account for the null.
|
||
|
}
|
||
|
return S_OK;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/** Looks up the environment variable 'key' and returns null if it is not found. */
|
||
|
template <typename string_type>
|
||
|
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||
|
{
|
||
|
const auto hr = wil::GetEnvironmentVariableW<string_type>(key, result);
|
||
|
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/** Retrieves the fully qualified path for the file containing the specified module loaded
|
||
|
by a given process. Note GetModuleFileNameExW is a macro.*/
|
||
|
template <typename string_type, size_t initialBufferLength = 128>
|
||
|
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path)
|
||
|
{
|
||
|
// initialBufferLength is a template parameter to allow for testing. It creates some waste for
|
||
|
// shorter paths, but avoids iteration through the loop in common cases where paths are less
|
||
|
// than 128 characters.
|
||
|
// wil::max_extended_path_length + 1 (for the null char)
|
||
|
// + 1 (to be certain GetModuleFileNameExW didn't truncate)
|
||
|
size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0;
|
||
|
size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation;
|
||
|
|
||
|
details::string_maker<string_type> maker;
|
||
|
|
||
|
for (size_t lengthWithNull = initialBufferLength;
|
||
|
lengthWithNull <= maxExtendedPathLengthWithNull;
|
||
|
lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull))
|
||
|
{
|
||
|
// make() adds space for the trailing null
|
||
|
RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1));
|
||
|
|
||
|
DWORD copiedCount;
|
||
|
bool copyFailed;
|
||
|
bool copySucceededWithNoTruncation;
|
||
|
|
||
|
if (process != nullptr)
|
||
|
{
|
||
|
// GetModuleFileNameExW truncates and provides no error or other indication it has done so.
|
||
|
// The only way to be sure it didn't truncate is if it didn't need the whole buffer.
|
||
|
copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
|
||
|
copyFailed = (0 == copiedCount);
|
||
|
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
|
||
|
// and set the last error to ERROR_INSUFFICIENT_BUFFER.
|
||
|
copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
|
||
|
copyFailed = (0 == copiedCount);
|
||
|
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull);
|
||
|
}
|
||
|
|
||
|
if (copyFailed)
|
||
|
{
|
||
|
RETURN_LAST_ERROR();
|
||
|
}
|
||
|
else if (copySucceededWithNoTruncation)
|
||
|
{
|
||
|
path = maker.release();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER));
|
||
|
|
||
|
if (lengthWithNull == maxExtendedPathLengthWithNull)
|
||
|
{
|
||
|
// If we've reached this point, there's no point in trying a larger buffer size.
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Any path should fit into the maximum max_extended_path_length. If we reached here, something went
|
||
|
// terribly wrong.
|
||
|
FAIL_FAST();
|
||
|
}
|
||
|
|
||
|
/** Retrieves the fully qualified path for the file that contains the specified module.
|
||
|
The module must have been loaded by the current process. The path returned will use the
|
||
|
same format that was specified when the module was loaded. Therefore, the path can be a
|
||
|
long or short file name, and can have the prefix '\\?\'. */
|
||
|
template <typename string_type, size_t initialBufferLength = 128>
|
||
|
HRESULT GetModuleFileNameW(HMODULE module, string_type& path)
|
||
|
{
|
||
|
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
|
||
|
}
|
||
|
|
||
|
template <typename string_type, size_t stackBufferLength = 256>
|
||
|
HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
|
||
|
{
|
||
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||
|
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||
|
{
|
||
|
*valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
|
||
|
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
|
||
|
if (*valueLengthNeededWithNul < valueLength)
|
||
|
{
|
||
|
(*valueLengthNeededWithNul)++; // it fit, account for the null
|
||
|
}
|
||
|
return S_OK;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||
|
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
|
||
|
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||
|
string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
|
||
|
{
|
||
|
string_type result;
|
||
|
THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, result)));
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||
|
/** Searches for a specified file in a specified path using SearchPathW*/
|
||
|
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||
|
string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
|
||
|
{
|
||
|
string_type result;
|
||
|
HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(path, fileName, extension, result);
|
||
|
THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
|
||
|
return result;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/** Looks up the environment variable 'key' and fails if it is not found. */
|
||
|
template <typename string_type = wil::unique_cotaskmem_string>
|
||
|
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
|
||
|
{
|
||
|
string_type result;
|
||
|
THROW_IF_FAILED(wil::GetEnvironmentVariableW<string_type>(key, result));
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/** Looks up the environment variable 'key' and returns null if it is not found. */
|
||
|
template <typename string_type = wil::unique_cotaskmem_string>
|
||
|
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
|
||
|
{
|
||
|
string_type result;
|
||
|
THROW_IF_FAILED(wil::TryGetEnvironmentVariableW<string_type>(key, result));
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
template <typename string_type = wil::unique_cotaskmem_string>
|
||
|
string_type GetModuleFileNameW(HMODULE module)
|
||
|
{
|
||
|
string_type result;
|
||
|
THROW_IF_FAILED(wil::GetModuleFileNameW(module, result));
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
template <typename string_type = wil::unique_cotaskmem_string>
|
||
|
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
|
||
|
{
|
||
|
string_type result;
|
||
|
THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result));
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
|
||
|
the linker provides for every module. This avoids the need for a global HINSTANCE variable
|
||
|
and provides access to this value for static libraries. */
|
||
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||
|
inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
|
||
|
|
||
|
/// @cond
|
||
|
namespace details
|
||
|
{
|
||
|
class init_once_completer
|
||
|
{
|
||
|
INIT_ONCE& m_once;
|
||
|
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
|
||
|
public:
|
||
|
init_once_completer(_In_ INIT_ONCE& once) : m_once(once)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
#pragma warning(push)
|
||
|
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||
|
void success()
|
||
|
{
|
||
|
m_flags = 0;
|
||
|
}
|
||
|
#pragma warning(pop)
|
||
|
|
||
|
~init_once_completer()
|
||
|
{
|
||
|
::InitOnceComplete(&m_once, m_flags, nullptr);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
/// @endcond
|
||
|
|
||
|
/** Performs one-time initialization
|
||
|
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
|
||
|
at most once.
|
||
|
~~~~
|
||
|
INIT_ONCE g_init{};
|
||
|
ComPtr<IFoo> g_foo;
|
||
|
HRESULT MyMethod()
|
||
|
{
|
||
|
bool winner = false;
|
||
|
RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
|
||
|
{
|
||
|
ComPtr<IFoo> foo;
|
||
|
RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
|
||
|
RETURN_IF_FAILED(foo->Startup());
|
||
|
g_foo = foo;
|
||
|
}, &winner);
|
||
|
if (winner)
|
||
|
{
|
||
|
RETURN_IF_FAILED(g_foo->Another());
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
~~~~
|
||
|
See MSDN for more information on `InitOnceExecuteOnce`.
|
||
|
@param initOnce The INIT_ONCE structure to use as context for initialization.
|
||
|
@param func A function that will be invoked to perform initialization. If this fails, the init call
|
||
|
fails and the once-init is not marked as initialized. A later caller could attempt to
|
||
|
initialize it a second time.
|
||
|
@param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise.
|
||
|
*/
|
||
|
template<typename T> HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT
|
||
|
{
|
||
|
BOOL pending = FALSE;
|
||
|
wil::assign_to_opt_param(callerCompleted, false);
|
||
|
|
||
|
__WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
|
||
|
|
||
|
if (pending)
|
||
|
{
|
||
|
details::init_once_completer completion(initOnce);
|
||
|
__WIL_PRIVATE_RETURN_IF_FAILED(func());
|
||
|
completion.success();
|
||
|
wil::assign_to_opt_param(callerCompleted, true);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is
|
||
|
//! returned to the caller instead of being an out-parameter.
|
||
|
template<typename T> bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
|
||
|
{
|
||
|
bool callerCompleted;
|
||
|
|
||
|
FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(func), &callerCompleted));
|
||
|
|
||
|
return callerCompleted;
|
||
|
};
|
||
|
|
||
|
//! Returns 'true' if this `init_once` structure has finished initialization, false otherwise.
|
||
|
inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT
|
||
|
{
|
||
|
BOOL pending = FALSE;
|
||
|
return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending;
|
||
|
}
|
||
|
|
||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||
|
/** Performs one-time initialization
|
||
|
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
|
||
|
at most once.
|
||
|
~~~~
|
||
|
INIT_ONCE g_init{};
|
||
|
ComPtr<IFoo> g_foo;
|
||
|
void MyMethod()
|
||
|
{
|
||
|
bool winner = wil::init_once(g_init, []
|
||
|
{
|
||
|
ComPtr<IFoo> foo;
|
||
|
THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
|
||
|
THROW_IF_FAILED(foo->Startup());
|
||
|
g_foo = foo;
|
||
|
});
|
||
|
if (winner)
|
||
|
{
|
||
|
THROW_IF_FAILED(g_foo->Another());
|
||
|
}
|
||
|
}
|
||
|
~~~~
|
||
|
See MSDN for more information on `InitOnceExecuteOnce`.
|
||
|
@param initOnce The INIT_ONCE structure to use as context for initialization.
|
||
|
@param func A function that will be invoked to perform initialization. If this fails, the init call
|
||
|
fails and the once-init is not marked as initialized. A later caller could attempt to
|
||
|
initialize it a second time.
|
||
|
@returns 'true' if this was the call that caused initialization, false otherwise.
|
||
|
*/
|
||
|
template<typename T> bool init_once(_Inout_ INIT_ONCE& initOnce, T func)
|
||
|
{
|
||
|
BOOL pending = FALSE;
|
||
|
|
||
|
THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
|
||
|
|
||
|
if (pending)
|
||
|
{
|
||
|
details::init_once_completer completion(initOnce);
|
||
|
func();
|
||
|
completion.success();
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||
|
}
|
||
|
|
||
|
// Macro for calling GetProcAddress(), with type safety for C++ clients
|
||
|
// using the type information from the specified function.
|
||
|
// The return value is automatically cast to match the function prototype of the input function.
|
||
|
//
|
||
|
// Sample usage:
|
||
|
//
|
||
|
// auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW);
|
||
|
// if (sendMail)
|
||
|
// {
|
||
|
// sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0);
|
||
|
// }
|
||
|
// Declaration
|
||
|
#define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast<decltype(::fn)*>(GetProcAddress(hinst, #fn))
|
||
|
|
||
|
#endif // __WIL_WIN32_HELPERS_INCLUDED
|