Skip to content

Commit 6f60a1b

Browse files
GabeDeBackerdunhor
andauthored
Move around HEAP API wrappers to a more correct spot, and add ANSI string support for heap strings (#477)
* Move around heap wrappers to a correct spot, and add ANSI string support for process heap strings * Address code review feedback and fix formatting * Fix COM unit tests --------- Co-authored-by: Duncan Horn <[email protected]>
1 parent d0f7e44 commit 6f60a1b

File tree

3 files changed

+190
-101
lines changed

3 files changed

+190
-101
lines changed

include/wil/resource.h

Lines changed: 95 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3717,6 +3717,14 @@ namespace details
37173717
{
37183718
::HeapFree(::GetProcessHeap(), 0, p);
37193719
}
3720+
3721+
struct heap_allocator
3722+
{
3723+
static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT
3724+
{
3725+
return ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size);
3726+
}
3727+
};
37203728
} // namespace details
37213729
/// @endcond
37223730

@@ -3747,25 +3755,6 @@ struct mapview_deleter
37473755
}
37483756
};
37493757

3750-
template <typename T = void>
3751-
using unique_process_heap_ptr = wistd::unique_ptr<details::ensure_trivially_destructible_t<T>, process_heap_deleter>;
3752-
3753-
typedef unique_any<PWSTR, decltype(&details::FreeProcessHeap), details::FreeProcessHeap> unique_process_heap_string;
3754-
3755-
/// @cond
3756-
namespace details
3757-
{
3758-
template <>
3759-
struct string_allocator<unique_process_heap_string>
3760-
{
3761-
static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT
3762-
{
3763-
return ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size);
3764-
}
3765-
};
3766-
} // namespace details
3767-
/// @endcond
3768-
37693758
/** Manages a typed pointer allocated with VirtualAlloc
37703759
A specialization of wistd::unique_ptr<> that frees via VirtualFree(p, 0, MEM_RELEASE).
37713760
*/
@@ -4061,6 +4050,93 @@ typedef weak_any<shared_hfind_change> weak_hfind_change;
40614050

40624051
#endif // __WIL_WINBASE_STL
40634052

4053+
#if (defined(_HEAPAPI_H_) && !defined(__WIL__WIL_HEAP_API) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) && !defined(WIL_KERNEL_MODE)) || \
4054+
defined(WIL_DOXYGEN)
4055+
/// @cond
4056+
#define __WIL__WIL_HEAP_API
4057+
/// @endcond
4058+
4059+
template <typename T = void>
4060+
using unique_process_heap_ptr = wistd::unique_ptr<details::ensure_trivially_destructible_t<T>, process_heap_deleter>;
4061+
typedef unique_any<void*, decltype(&details::FreeProcessHeap), details::FreeProcessHeap> unique_process_heap;
4062+
typedef unique_any<PWSTR, decltype(&details::FreeProcessHeap), details::FreeProcessHeap> unique_process_heap_string;
4063+
4064+
#ifndef WIL_NO_ANSI_STRINGS
4065+
typedef unique_any<PSTR, decltype(&wil::details::FreeProcessHeap), wil::details::FreeProcessHeap> unique_process_heap_ansistring;
4066+
#endif // WIL_NO_ANSI_STRINGS
4067+
4068+
/// @cond
4069+
namespace details
4070+
{
4071+
template <>
4072+
struct string_allocator<wil::unique_process_heap_string> : heap_allocator
4073+
{
4074+
};
4075+
4076+
#ifndef WIL_NO_ANSI_STRINGS
4077+
template <>
4078+
struct string_allocator<unique_process_heap_ansistring> : heap_allocator
4079+
{
4080+
};
4081+
#endif
4082+
} // namespace details
4083+
/// @endcond
4084+
4085+
inline auto make_process_heap_string_nothrow(
4086+
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4087+
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCWSTR source,
4088+
size_t length = static_cast<size_t>(-1)) WI_NOEXCEPT
4089+
{
4090+
return make_unique_string_nothrow<unique_process_heap_string>(source, length);
4091+
}
4092+
4093+
inline auto make_process_heap_string_failfast(
4094+
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4095+
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCWSTR source,
4096+
size_t length = static_cast<size_t>(-1)) WI_NOEXCEPT
4097+
{
4098+
return make_unique_string_failfast<unique_process_heap_string>(source, length);
4099+
}
4100+
4101+
#ifndef WIL_NO_ANSI_STRINGS
4102+
inline auto make_process_heap_ansistring_nothrow(
4103+
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4104+
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCSTR source,
4105+
size_t length = static_cast<size_t>(-1)) WI_NOEXCEPT
4106+
{
4107+
return make_unique_ansistring_nothrow<unique_process_heap_ansistring>(source, length);
4108+
}
4109+
4110+
inline auto make_process_heap_ansistring_failfast(
4111+
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4112+
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCSTR source,
4113+
size_t length = static_cast<size_t>(-1)) WI_NOEXCEPT
4114+
{
4115+
return make_unique_ansistring_failfast<unique_process_heap_ansistring>(source, length);
4116+
}
4117+
#endif // WIL_NO_ANSI_STRINGS
4118+
4119+
#ifdef WIL_ENABLE_EXCEPTIONS
4120+
inline auto make_process_heap_string(
4121+
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4122+
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCWSTR source,
4123+
size_t length = static_cast<size_t>(-1))
4124+
{
4125+
return make_unique_string<unique_process_heap_string>(source, length);
4126+
}
4127+
4128+
#ifndef WIL_NO_ANSI_STRINGS
4129+
inline auto make_process_heap_ansistring(
4130+
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4131+
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCSTR source,
4132+
size_t length = static_cast<size_t>(-1))
4133+
{
4134+
return make_unique_ansistring<unique_process_heap_ansistring>(source, length);
4135+
}
4136+
#endif // WIL_NO_ANSI_STRINGS
4137+
#endif // WIL_ENABLE_EXCEPTIONS
4138+
#endif // _HEAPAPI_H_
4139+
40644140
#if (defined(__WIL_WINBASE_) && defined(__NOTHROW_T_DEFINED) && !defined(__WIL_WINBASE_NOTHROW_T_DEFINED_STL) && defined(WIL_RESOURCE_STL) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) || \
40654141
defined(WIL_DOXYGEN)
40664142
/// @cond
@@ -4508,32 +4584,6 @@ namespace details
45084584
} // namespace details
45094585
/// @endcond
45104586

4511-
inline auto make_process_heap_string_nothrow(
4512-
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4513-
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCWSTR source,
4514-
size_t length = static_cast<size_t>(-1)) WI_NOEXCEPT
4515-
{
4516-
return make_unique_string_nothrow<unique_process_heap_string>(source, length);
4517-
}
4518-
4519-
inline auto make_process_heap_string_failfast(
4520-
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4521-
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCWSTR source,
4522-
size_t length = static_cast<size_t>(-1)) WI_NOEXCEPT
4523-
{
4524-
return make_unique_string_failfast<unique_process_heap_string>(source, length);
4525-
}
4526-
4527-
#ifdef WIL_ENABLE_EXCEPTIONS
4528-
inline auto make_process_heap_string(
4529-
_When_((source != nullptr) && length != static_cast<size_t>(-1), _In_reads_(length))
4530-
_When_((source != nullptr) && length == static_cast<size_t>(-1), _In_z_) PCWSTR source,
4531-
size_t length = static_cast<size_t>(-1))
4532-
{
4533-
return make_unique_string<unique_process_heap_string>(source, length);
4534-
}
4535-
#endif // WIL_ENABLE_EXCEPTIONS
4536-
45374587
typedef unique_any_handle_null<decltype(&::HeapDestroy), ::HeapDestroy> unique_hheap;
45384588
typedef unique_any<DWORD, decltype(&::TlsFree), ::TlsFree, details::pointer_access_all, DWORD, DWORD, TLS_OUT_OF_INDEXES, DWORD> unique_tls;
45394589
typedef unique_any<PSECURITY_DESCRIPTOR, decltype(&::LocalFree), ::LocalFree> unique_hlocal_security_descriptor;

tests/ComTests.cpp

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,9 +3067,6 @@ TEST_CASE("COMEnumerator", "[com][enumerator]")
30673067
#include <winrt/windows.foundation.h>
30683068
#include <windows.foundation.h>
30693069

3070-
HANDLE g_hangHandle{nullptr};
3071-
HANDLE g_doneHangingHandle{nullptr};
3072-
30733070
TEST_CASE("com_timeout", "[com][com_timeout]")
30743071
{
30753072
auto init = wil::CoInitializeEx_failfast();
@@ -3083,24 +3080,32 @@ TEST_CASE("com_timeout", "[com][com_timeout]")
30833080
// apartments (MTA -> STA in this case)
30843081
struct COMTimeoutTestObject : winrt::implements<COMTimeoutTestObject, winrt::Windows::Foundation::IStringable, winrt::non_agile>
30853082
{
3083+
wil::shared_event _hangHandle;
3084+
wil::shared_event _doneHangingHandle;
3085+
std::shared_ptr<bool> _shouldHang;
3086+
COMTimeoutTestObject(wil::shared_event hangHandle, wil::shared_event doneHangingHandle, std::shared_ptr<bool> shouldHang) :
3087+
_hangHandle(hangHandle), _doneHangingHandle(doneHangingHandle), _shouldHang(shouldHang)
3088+
{
3089+
}
3090+
30863091
winrt::hstring ToString()
30873092
{
3088-
// If the global handle exists, block on it. If it doesn't exist then this is a non-hang case
3089-
// so skip waiting.
3090-
if (g_hangHandle)
3093+
// If the test wants to block, then use the hang handles.
3094+
if (*(_shouldHang.get()))
30913095
{
30923096
// Pump messages so this STA thread is healthy while we wait. If this wait fails that means
30933097
// the cancel did not work.
3094-
HANDLE handles[1] = {g_hangHandle};
3098+
HANDLE handles[1] = {_hangHandle.get()};
30953099
DWORD index;
30963100
REQUIRE_SUCCEEDED(CoWaitForMultipleObjects(
30973101
CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES, 10000, ARRAYSIZE(handles), handles, &index));
30983102

3099-
if (g_doneHangingHandle)
3103+
if (_doneHangingHandle)
31003104
{
3101-
SetEvent(g_doneHangingHandle);
3105+
_doneHangingHandle.SetEvent();
31023106
}
31033107
}
3108+
31043109
return L"COMTimeoutTestObject";
31053110
}
31063111
};
@@ -3109,27 +3114,47 @@ TEST_CASE("com_timeout", "[com][com_timeout]")
31093114
// exit.
31103115
wil::shared_event comServerEvent;
31113116
comServerEvent.create();
3117+
31123118
wil::shared_event agileReferencePopulated;
31133119
agileReferencePopulated.create();
3120+
3121+
// These handles are used to coordinate with the COM server thread. The first one causes it to block. The
3122+
// done hanging event lets us know that it is done blocking and we can proceed with a second call that should
3123+
// avoid reentering.
3124+
wil::shared_event hangingHandle;
3125+
hangingHandle.create();
3126+
wil::shared_event doneHangingHandle;
3127+
doneHangingHandle.create();
3128+
3129+
auto shouldHang = std::make_shared<bool>(false);
3130+
31143131
wil::com_agile_ref agileStringable;
31153132

3116-
auto comServerThread = std::thread([comServerEvent, agileReferencePopulated, &agileStringable] {
3117-
// This thread must be STA to pull RPC in as mediator between threads.
3118-
auto init = wil::CoInitializeEx_failfast(COINIT_APARTMENTTHREADED);
3133+
auto comServerThread =
3134+
std::thread([comServerEvent, agileReferencePopulated, hangingHandle, doneHangingHandle, &agileStringable, shouldHang] {
3135+
// This thread must be STA to pull RPC in as mediator between threads.
3136+
auto init = wil::CoInitializeEx_failfast(COINIT_APARTMENTTHREADED);
31193137

3120-
const auto stringable = winrt::make<COMTimeoutTestObject>();
3121-
agileStringable = wil::com_agile_query(stringable.as<ABI::Windows::Foundation::IStringable>().get());
3138+
const auto stringable = winrt::make<COMTimeoutTestObject>(hangingHandle, doneHangingHandle, shouldHang);
3139+
agileStringable = wil::com_agile_query(stringable.as<ABI::Windows::Foundation::IStringable>().get());
31223140

3123-
agileReferencePopulated.SetEvent();
3141+
agileReferencePopulated.SetEvent();
31243142

3125-
// Pump messages so this STA thread is healthy.
3126-
HANDLE handles[1] = {comServerEvent.get()};
3127-
DWORD index;
3128-
REQUIRE_SUCCEEDED(CoWaitForMultipleObjects(
3129-
CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES, 10000, ARRAYSIZE(handles), handles, &index));
3143+
// Pump messages so this STA thread is healthy.
3144+
HANDLE handles[1] = {comServerEvent.get()};
3145+
DWORD index;
3146+
REQUIRE_SUCCEEDED(CoWaitForMultipleObjects(
3147+
CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES, INFINITE, ARRAYSIZE(handles), handles, &index));
3148+
});
3149+
3150+
auto makeSureComServerThreadExits = wil::scope_exit([comServerEvent, &comServerThread] {
3151+
// We are done testing. Tell the STA thread to exit and then block until it is done.
3152+
comServerEvent.SetEvent();
3153+
3154+
comServerThread.join();
31303155
});
31313156

3132-
agileReferencePopulated.wait(5000);
3157+
REQUIRE_SUCCEEDED(agileReferencePopulated.wait(5000));
31333158

31343159
SECTION("Basic construction nothrow")
31353160
{
@@ -3151,42 +3176,33 @@ TEST_CASE("com_timeout", "[com][com_timeout]")
31513176
}
31523177
SECTION("RPC timeout test")
31533178
{
3154-
// These handles are used to coordinate with the COM server thread. The first one causes it to block. The
3155-
// done hanging event lets us know that it is done blocking and we can proceed with a second call that should
3156-
// avoid reentering.
3157-
wil::unique_event hangHandle;
3158-
hangHandle.create();
3159-
g_hangHandle = hangHandle.get();
3160-
3161-
wil::unique_event doneHangingHandle;
3162-
doneHangingHandle.create();
3163-
g_doneHangingHandle = doneHangingHandle.get();
3164-
31653179
wil::com_timeout timeout{100};
31663180

3181+
*(shouldHang.get()) = true;
3182+
31673183
// The timeout is now in place. The blocking call should cancel in a timely manner and fail with RPC_E_CALL_CANCELED.
31683184
wil::com_ptr<ABI::Windows::Foundation::IStringable> localServer =
31693185
agileStringable.query<ABI::Windows::Foundation::IStringable>();
31703186
wil::unique_hstring value;
3171-
REQUIRE(localServer->ToString(&value) == RPC_E_CALL_CANCELED);
3187+
auto localServerResult = localServer->ToString(&value);
3188+
REQUIRE(static_cast<bool>(localServerResult == RPC_E_CALL_CANCELED));
31723189
REQUIRE(timeout.timed_out());
31733190

3174-
hangHandle.SetEvent();
3191+
hangingHandle.SetEvent();
31753192
REQUIRE(doneHangingHandle.wait(5000));
31763193

3177-
hangHandle.ResetEvent();
3194+
hangingHandle.ResetEvent();
31783195

31793196
// Make a second blocking call within the lifetime of the same com_timeout instance. This second call should also
31803197
// cancel and return.
3181-
const auto result = localServer->ToString(&value);
3182-
REQUIRE(result == RPC_E_CALL_CANCELED);
3198+
localServerResult = localServer->ToString(&value);
3199+
REQUIRE(static_cast<bool>(localServerResult == RPC_E_CALL_CANCELED));
31833200
REQUIRE(timeout.timed_out());
31843201

3185-
hangHandle.SetEvent();
3202+
hangingHandle.SetEvent();
31863203
REQUIRE(doneHangingHandle.wait(5000));
31873204

3188-
g_hangHandle = nullptr;
3189-
g_doneHangingHandle = nullptr;
3205+
*(shouldHang.get()) = false;
31903206
}
31913207
SECTION("Non-timeout unaffected test")
31923208
{
@@ -3200,13 +3216,6 @@ TEST_CASE("com_timeout", "[com][com_timeout]")
32003216
REQUIRE(!timeout.timed_out());
32013217
REQUIRE(std::wstring_view{L"COMTimeoutTestObject"} == WindowsGetStringRawBuffer(value.get(), nullptr));
32023218
}
3203-
3204-
// We are done testing. Tell the STA thread to exit and then block until it is done.
3205-
comServerEvent.SetEvent();
3206-
comServerThread.join();
3207-
3208-
g_hangHandle = nullptr;
3209-
g_doneHangingHandle = nullptr;
32103219
}
32113220
#endif // defined(__cpp_impl_coroutine) || defined(__cpp_coroutines) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
32123221
#endif // (NTDDI_VERSION >= NTDDI_WINBLUE)

0 commit comments

Comments
 (0)