Large diffs are not rendered by default.

@@ -0,0 +1,145 @@
#pragma once

#include "catch.hpp"
#include <objbase.h>
#include <wil/wistd_functional.h>
#include <wrl/implements.h>

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)

// IMallocSpy requires you to implement all methods, but we often only want one or two...
struct MallocSpy : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IMallocSpy>
{
wistd::function<SIZE_T(SIZE_T)> PreAllocCallback;
virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T requestSize) override
{
if (PreAllocCallback)
{
return PreAllocCallback(requestSize);
}

return requestSize;
}

wistd::function<void*(void*)> PostAllocCallback;
virtual void* STDMETHODCALLTYPE PostAlloc(void* ptr) override
{
if (PostAllocCallback)
{
return PostAllocCallback(ptr);
}

return ptr;
}

wistd::function<void*(void*)> PreFreeCallback;
virtual void* STDMETHODCALLTYPE PreFree(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreFreeCallback)
{
return PreFreeCallback(ptr);
}

return ptr;
}

virtual void STDMETHODCALLTYPE PostFree(BOOL /*wasSpyed*/) override
{
}

wistd::function<SIZE_T(void*, SIZE_T, void**)> PreReallocCallback;
virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void* ptr, SIZE_T requestSize, void** newPtr, BOOL wasSpyed) override
{
*newPtr = ptr;
if (wasSpyed && PreReallocCallback)
{
return PreReallocCallback(ptr, requestSize, newPtr);
}

return requestSize;
}

wistd::function<void*(void*)> PostReallocCallback;
virtual void* STDMETHODCALLTYPE PostRealloc(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PostReallocCallback)
{
return PostReallocCallback(ptr);
}

return ptr;
}

wistd::function<void*(void*)> PreGetSizeCallback;
virtual void* STDMETHODCALLTYPE PreGetSize(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreGetSizeCallback)
{
return PreGetSizeCallback(ptr);
}

return ptr;
}

wistd::function<SIZE_T(SIZE_T)> PostGetSizeCallback;
virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T size, BOOL wasSpyed) override
{
if (wasSpyed && PostGetSizeCallback)
{
return PostGetSizeCallback(size);
}

return size;
}

wistd::function<void*(void*)> PreDidAllocCallback;
virtual void* STDMETHODCALLTYPE PreDidAlloc(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreDidAllocCallback)
{
return PreDidAllocCallback(ptr);
}

return ptr;
}

virtual int STDMETHODCALLTYPE PostDidAlloc(void* /*ptr*/, BOOL /*wasSpyed*/, int result) override
{
return result;
}

virtual void STDMETHODCALLTYPE PreHeapMinimize() override
{
}

virtual void STDMETHODCALLTYPE PostHeapMinimize() override
{
}
};

Microsoft::WRL::ComPtr<MallocSpy> MakeSecureDeleterMallocSpy()
{
using namespace Microsoft::WRL;
auto result = Make<MallocSpy>();
REQUIRE(result);

result->PreFreeCallback = [](void* ptr)
{
ComPtr<IMalloc> malloc;
if (SUCCEEDED(::CoGetMalloc(1, &malloc)))
{
auto size = malloc->GetSize(ptr);
auto buffer = static_cast<byte*>(ptr);
for (size_t i = 0; i < size; ++i)
{
REQUIRE(buffer[i] == 0);
}
}

return ptr;
};

return result;
}

#endif

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,137 @@
#include "common.h"

#include <wil/rpc_helpers.h>

void RpcMethodReturnsVoid(ULONG toRaise)
{
if (toRaise)
{
RaiseException(toRaise, 0, 0, nullptr);
}
}

struct FOO_CONTEXT_T {};
typedef FOO_CONTEXT_T* FOO_CONTEXT;
typedef FOO_CONTEXT* PFOO_CONTEXT;

void CloseContextHandle(_Inout_ PFOO_CONTEXT)
{
}

void CloseContextHandleRaise(_Inout_ PFOO_CONTEXT)
{
return RpcMethodReturnsVoid(RPC_X_BAD_STUB_DATA);
}

HRESULT AcquireContextHandle(_In_ handle_t binding, _Out_ PFOO_CONTEXT context)
{
*context = reinterpret_cast<FOO_CONTEXT>(binding);
return S_OK;
}

HRESULT RpcMethodReturnsHResult(HRESULT toReturn, ULONG toRaise)
{
RpcMethodReturnsVoid(toRaise);
return toReturn;
}

GUID RpcMethodReturnsGuid(ULONG toRaise)
{
RpcMethodReturnsVoid(toRaise);
return __uuidof(IUnknown);
}

TEST_CASE("Rpc::NonThrowing", "[rpc]")
{
SECTION("Success paths")
{
REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, 0UL));
REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, S_OK, 0UL));

GUID tmp{};
REQUIRE_SUCCEEDED(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, 0UL));
REQUIRE(tmp == __uuidof(IUnknown));
}

SECTION("Failures in the method")
{
REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, 0) == E_CHANGED_STATE);
}

SECTION("Failures in the fabric")
{
REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED));
REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED));

GUID tmp{};
REQUIRE(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED));
}

SECTION("Context Handles")
{
using foo_context_t = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseContextHandle), CloseContextHandle>;
foo_context_t ctx;
auto tempBinding = reinterpret_cast<handle_t>(-5);
REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(AcquireContextHandle, tempBinding, ctx.put()));
REQUIRE(ctx.get() == reinterpret_cast<FOO_CONTEXT>(tempBinding));
ctx.reset();
}

SECTION("Context Handles Close Raised")
{
using foo_context_t = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseContextHandleRaise), CloseContextHandleRaise>;
foo_context_t ctx{ reinterpret_cast<FOO_CONTEXT>(42) };
ctx.reset();
}
}

#ifdef WIL_ENABLE_EXCEPTIONS

#include <sstream>

class WilExceptionMatcher : public Catch::MatcherBase<wil::ResultException>
{
HRESULT m_expected;
public:
WilExceptionMatcher(HRESULT ex) : m_expected(ex) { }

bool match(wil::ResultException const& ex) const override {
return ex.GetErrorCode() == m_expected;
}

std::string describe() const override {
std::ostringstream ss;
ss << "wil::ResultException expects code 0x%08lx" << std::hex << m_expected;
return ss.str();
}
};

#define REQUIRE_THROWS_WIL_HR(hr, expr) REQUIRE_THROWS_MATCHES(expr, wil::ResultException, WilExceptionMatcher(hr))

TEST_CASE("Rpc::Throwing", "[rpc]")
{
SECTION("Success paths")
{
REQUIRE_NOTHROW(wil::invoke_rpc(RpcMethodReturnsVoid, 0UL));

GUID tmp{};
REQUIRE_NOTHROW(tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, 0UL));
REQUIRE(tmp == __uuidof(IUnknown));
}

SECTION("Failures in the method")
{
REQUIRE_THROWS_WIL_HR(E_CHANGED_STATE, wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, 0UL));
}

SECTION("Failures in the fabric")
{
REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsVoid, RPC_S_CALL_FAILED));
REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED));

GUID tmp{};
REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, RPC_S_CALL_FAILED));
REQUIRE(tmp == GUID{});
}
}
#endif

Large diffs are not rendered by default.

@@ -0,0 +1,47 @@

#include <wil/stl.h>

#include "common.h"

#ifndef WIL_ENABLE_EXCEPTIONS
#error STL tests require exceptions
#endif

struct dummy
{
char value;
};

// Specialize std::allocator<> so that we don't actually allocate/deallocate memory
dummy g_memoryBuffer[256];
namespace std
{
template <>
struct allocator<dummy>
{
using value_type = dummy;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;

dummy* allocate(std::size_t count)
{
REQUIRE(count <= std::size(g_memoryBuffer));
return g_memoryBuffer;
}

void deallocate(dummy* ptr, std::size_t count)
{
for (std::size_t i = 0; i < count; ++i)
{
REQUIRE(ptr[i].value == 0);
}
}
};
}

TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]")
{
{
wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' });
}
}
@@ -0,0 +1,320 @@

#include <wil/token_helpers.h>

#include "common.h"

TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessTokenNoThrow", "[token_helpers]")
{
// Open current thread/process access token
wil::unique_handle token;
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token));
REQUIRE(token != nullptr);

REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ));
REQUIRE(token != nullptr);

REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Current));
REQUIRE(token != nullptr);

REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Self));
REQUIRE(token != nullptr);
}

#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]")
{
// Open current thread/process access token
wil::unique_handle token(wil::open_current_access_token());
REQUIRE(token != nullptr);

token = wil::open_current_access_token(TOKEN_READ);
REQUIRE(token != nullptr);

token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Current);
REQUIRE(token != nullptr);

token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Self);
REQUIRE(token != nullptr);
}
#endif

TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]")
{
SECTION("Passing a null token")
{
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, nullptr));
REQUIRE(tokenInfo != nullptr);
}

SECTION("Passing a non null token, since it a fake token there is no tokenInfo and hence should fail, code path is correct")
{
HANDLE faketoken = GetStdHandle(STD_INPUT_HANDLE);
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, faketoken));
}
}

// Pseudo tokens can be passed to token APIs and avoid the handle allocations
// making use more efficient.
TEST_CASE("TokenHelpersTests::DemonstrateUseWithPseudoTokens", "[token_helpers]")
{
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentProcessToken()));
REQUIRE(tokenInfo != nullptr);

REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadEffectiveToken()));
REQUIRE(tokenInfo != nullptr);

// No thread token by default, this should fail
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadToken()));
REQUIRE(tokenInfo == nullptr);
}

#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation", "[token_helpers]")
{
// Passing a null token
wistd::unique_ptr<TOKEN_USER> tokenInfo(wil::get_token_information<TOKEN_USER>(nullptr));
REQUIRE(tokenInfo != nullptr);
}
#endif

// This fails with 'ERROR_NO_SUCH_LOGON_SESSION' on the CI machines, so disable
#ifndef WIL_FAST_BUILD
TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]")
{
wil::unique_token_linked_token theToken;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(theToken, nullptr));

#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE_NOTHROW(wil::get_linked_token_information());
#endif
}
#endif

bool IsImpersonating()
{
wil::unique_handle token;
if (!::OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token))
{
WI_ASSERT(::GetLastError() == ERROR_NO_TOKEN);
return false;
}

return true;
}

wil::unique_handle GetTokenToImpersonate()
{
wil::unique_handle processToken;
FAIL_FAST_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken));

wil::unique_handle impersonateToken;
FAIL_FAST_IF_WIN32_BOOL_FALSE(::DuplicateToken(processToken.get(), SecurityImpersonation, &impersonateToken));

return impersonateToken;
}

TEST_CASE("TokenHelpersTests::VerifyResetThreadTokenNoThrow", "[token_helpers]")
{
auto impersonateToken = GetTokenToImpersonate();

// Set the thread into a known state - no token.
wil::unique_token_reverter clearThreadToken;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadToken));
REQUIRE_FALSE(IsImpersonating());

// Set a token on the thread - the process token, guaranteed to be friendly
wil::unique_token_reverter setThreadToken1;
REQUIRE_SUCCEEDED(wil::impersonate_token_nothrow(impersonateToken.get(), setThreadToken1));
REQUIRE(IsImpersonating());

SECTION("Clear the token again, should be not impersonating, explicit reset")
{
wil::unique_token_reverter clearThreadAgain;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
REQUIRE_FALSE(IsImpersonating());
clearThreadAgain.reset();
REQUIRE(IsImpersonating());
}

SECTION("Clear the token again, should be not impersonating, dtor reset")
{
wil::unique_token_reverter clearThreadAgain;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
REQUIRE_FALSE(IsImpersonating());
}
REQUIRE(IsImpersonating());

// Clear what we were impersonating
setThreadToken1.reset();
REQUIRE_FALSE(IsImpersonating());
}

#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]")
{
auto impersonateToken = GetTokenToImpersonate();

// Set the thread into a known state - no token.
auto clearThreadToken = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());

// Set a token on the thread - the process token, guaranteed to be friendly
auto setThreadToken1 = wil::impersonate_token(impersonateToken.get());
REQUIRE(IsImpersonating());

SECTION("Clear the token again, should be not impersonating, explicit reset")
{
auto clearThreadAgain = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
clearThreadAgain.reset();
REQUIRE(IsImpersonating());
}

SECTION("Clear the token again, should be not impersonating, dtor reset")
{
auto clearThreadAgain = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
}
REQUIRE(IsImpersonating());

// Clear what we were impersonating
setThreadToken1.reset();
REQUIRE_FALSE(IsImpersonating());
}
#endif // WIL_ENABLE_EXCEPTIONS

template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
void TestGetTokenInfoForCurrentThread()
{
wistd::unique_ptr<T> tokenInfo;
const auto hr = wil::get_token_information_nothrow(tokenInfo, nullptr);
REQUIRE(S_OK == hr);
}

template <typename T, wistd::enable_if_t<wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
void TestGetTokenInfoForCurrentThread()
{
T tokenInfo{};
const auto hr = wil::get_token_information_nothrow(&tokenInfo, nullptr);
REQUIRE(S_OK == hr);
}

TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation2", "[token_helpers]")
{
// Variable sized cases
TestGetTokenInfoForCurrentThread<TOKEN_ACCESS_INFORMATION>();
TestGetTokenInfoForCurrentThread<TOKEN_APPCONTAINER_INFORMATION>();
TestGetTokenInfoForCurrentThread<TOKEN_DEFAULT_DACL>();
TestGetTokenInfoForCurrentThread<TOKEN_GROUPS_AND_PRIVILEGES>();
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_LABEL>();
TestGetTokenInfoForCurrentThread<TOKEN_OWNER>();
TestGetTokenInfoForCurrentThread<TOKEN_PRIMARY_GROUP>();
TestGetTokenInfoForCurrentThread<TOKEN_PRIVILEGES>();
TestGetTokenInfoForCurrentThread<TOKEN_USER>();

// Fixed size and reports size using ERROR_INSUFFICIENT_BUFFER (perf opportunity, ignore second allocation)
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION_TYPE>();
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_POLICY>();
TestGetTokenInfoForCurrentThread<TOKEN_ORIGIN>();
TestGetTokenInfoForCurrentThread<TOKEN_SOURCE>();
TestGetTokenInfoForCurrentThread<TOKEN_STATISTICS>();
TestGetTokenInfoForCurrentThread<TOKEN_TYPE>();
}

TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationBadLength", "[token_helpers]")
{
// Fixed size and reports size using ERROR_BAD_LENGTH (bug)
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION>();
}

TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLevelErrorCases", "[token_helpers]")
{
SECURITY_IMPERSONATION_LEVEL tokenInfo{};

// SECURITY_IMPERSONATION_LEVEL does not support the effective token when it is implicit.
// Demonstrate the error return in that case.
REQUIRE(E_INVALIDARG == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadEffectiveToken()));

// Using an explicit token is supported but returns ERROR_NO_TOKEN when there is no
// impersonation token be sure to use RETURN_IF_FAILED_EXPECTED() and don't use
// the exception forms if this case is not expected.
REQUIRE(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));

// Setup the impersonation token that SECURITY_IMPERSONATION_LEVEL requires.
FAIL_FAST_IF_WIN32_BOOL_FALSE(ImpersonateSelf(SecurityIdentification));
TestGetTokenInfoForCurrentThread<SECURITY_IMPERSONATION_LEVEL>();

REQUIRE(S_OK == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));

RevertToSelf();
}

bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right)
{
return memcmp(&left, &right, sizeof(left)) == 0;
}

TEST_CASE("TokenHelpersTests::StaticSid", "[token_helpers]")
{
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
auto staticSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS);
auto largerSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, DOMAIN_ALIAS_RID_BACKUP_OPS);

largerSid = staticSid;
largerSid = largerSid;
// staticSid = largerSid; // Uncommenting this correctly fails to compile.

REQUIRE(IsValidSid(staticSid.get()));
REQUIRE(*GetSidSubAuthorityCount(staticSid.get()) == 2);
REQUIRE(*GetSidIdentifierAuthority(staticSid.get()) == ntAuthority);
REQUIRE(*GetSidSubAuthority(staticSid.get(), 0) == SECURITY_BUILTIN_DOMAIN_RID);
REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS);
}

TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]")
{
bool member;
REQUIRE_SUCCEEDED(wil::test_token_membership_nothrow(
&member,
GetCurrentThreadEffectiveToken(),
SECURITY_NT_AUTHORITY,
SECURITY_AUTHENTICATED_USER_RID));
}

#ifdef WIL_ENABLE_EXCEPTIONS

TEST_CASE("TokenHelpersTests::VerifyGetTokenInfo", "[token_helpers]")
{
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_APPCONTAINER_INFORMATION>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION_TYPE>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_MANDATORY_POLICY>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ORIGIN>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_STATISTICS>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION>());

// check a non-pointer size value to make sure the whole struct is returned.
DWORD resultSize{};
TOKEN_SOURCE ts{};
auto tokenSource = wil::get_token_information<TOKEN_SOURCE>();
GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenSource, &ts, sizeof(ts), &resultSize);
REQUIRE(memcmp(&ts, &tokenSource, sizeof(ts)) == 0);
}

TEST_CASE("TokenHelpersTests::VerifyGetTokenInfoFailFast", "[token_helpers]")
{
// fixed size
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_APPCONTAINER_INFORMATION>());
// variable size
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_OWNER>());
}

TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]")
{
auto impersonationToken = wil::impersonate_token();
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
}
#endif // WIL_ENABLE_EXCEPTIONS
@@ -0,0 +1,266 @@

#include <wil/winrt.h>
#include <wrl/implements.h>

#include "common.h"

using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;

namespace wiltest
{
class AbiTestEventSender WrlFinal : public RuntimeClass<
RuntimeClassFlags<RuntimeClassType::WinRtClassicComMix>,
IClosable,
IMemoryBufferReference,
FtmBase>
{
public:

// IMemoryBufferReference
IFACEMETHODIMP get_Capacity(_Out_ UINT32* value)
{
*value = 0;
return S_OK;
}

IFACEMETHODIMP add_Closed(
_In_ ITypedEventHandler<IMemoryBufferReference*, IInspectable*>* handler,
_Out_ ::EventRegistrationToken* token)
{
return m_closedEvent.Add(handler, token);
}

IFACEMETHODIMP remove_Closed(::EventRegistrationToken token)
{
return m_closedEvent.Remove(token);
}

// IClosable
IFACEMETHODIMP Close()
{
RETURN_IF_FAILED(m_closedEvent.InvokeAll(this, nullptr));
return S_OK;
}

private:
Microsoft::WRL::EventSource<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>> m_closedEvent;
};
}

TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEventSubscribe", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);

int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);

{
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}

REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}

TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEarlyReset", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);

int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);

wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);

token.reset();

REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}

TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveTokenToDifferentScope", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);

int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);

wil::unique_winrt_event_token<IMemoryBufferReference> outerToken;
REQUIRE_FALSE(static_cast<bool>(outerToken));
{
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);

outerToken = std::move(token);
REQUIRE_FALSE(static_cast<bool>(token));
REQUIRE(static_cast<bool>(outerToken));
}

REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 2);
}

TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveConstructor", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);

int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);

wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
REQUIRE(static_cast<bool>(firstToken));
closable->Close();
REQUIRE(timesInvoked == 1);

wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(std::move(firstToken));
REQUIRE_FALSE(static_cast<bool>(firstToken));
REQUIRE(static_cast<bool>(secondToken));

closable->Close();
REQUIRE(timesInvoked == 2);

firstToken.reset();
closable->Close();
REQUIRE(timesInvoked == 3);

secondToken.reset();
closable->Close();
REQUIRE(timesInvoked == 3);
}

TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenReleaseAndReattachToNewWrapper", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);

int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);

wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
REQUIRE(static_cast<bool>(firstToken));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);

::EventRegistrationToken rawToken = firstToken.release();
REQUIRE_FALSE(static_cast<bool>(firstToken));
REQUIRE(rawToken.value != 0);

REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 2);

wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(
rawToken, testEventSender.Get(), &IMemoryBufferReference::remove_Closed);

REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 3);

secondToken.reset();
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 3);
}

TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenPolicyVariants", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);

int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);

{
#ifdef WIL_ENABLE_EXCEPTIONS
auto exceptionPolicyToken = WI_MakeUniqueWinRtEventToken(Closed, testEventSender.Get(), handler.Get());
REQUIRE(static_cast<bool>(exceptionPolicyToken));
#endif

auto failFastPolicyToken = WI_MakeUniqueWinRtEventTokenFailFast(Closed, testEventSender.Get(), handler.Get());
REQUIRE(static_cast<bool>(failFastPolicyToken));

REQUIRE_SUCCEEDED(closable->Close());

#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE(timesInvoked == 2);
#else
REQUIRE(timesInvoked == 1);
#endif
}

REQUIRE_SUCCEEDED(closable->Close());

#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE(timesInvoked == 2);
#else
REQUIRE(timesInvoked == 1);
#endif
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,209 @@

#include <wil/wistd_functional.h>

#include "common.h"
#include "test_objects.h"

// Test methods/objects
int GetValue()
{
return 42;
}

int GetOtherValue()
{
return 8;
}

int Negate(int value)
{
return -value;
}

int Add(int lhs, int rhs)
{
return lhs + rhs;
}

TEST_CASE("WistdFunctionTests::CallOperatorTest", "[wistd]")
{
wistd::function<int()> getValue = GetValue;
REQUIRE(GetValue() == getValue());

wistd::function<int(int)> negate = Negate;
REQUIRE(Negate(42) == negate(42));

wistd::function<int(int, int)> add = Add;
REQUIRE(Add(42, 8) == add(42, 8));
}

TEST_CASE("WistdFunctionTests::AssignmentOperatorTest", "[wistd]")
{
wistd::function<int()> fn = GetValue;
REQUIRE(GetValue() == fn());

fn = GetOtherValue;
REQUIRE(GetOtherValue() == fn());
}

#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("WistdFunctionTests::StdFunctionConstructionTest", "[wistd]")
{
// We should be able to capture a std::function in a wistd::function
wistd::function<int()> fn;

{
value_holder holder{ 42 };
std::function<int()> stdFn = [holder]()
{
return holder.value;
};

fn = stdFn;
}

REQUIRE(42 == fn());
}
#endif

TEST_CASE("WistdFunctionTests::CopyConstructionTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == copyFrom());

auto copyTo = copyFrom;
REQUIRE(1 == copyTo());
}

REQUIRE(0 == state.instance_count());
}

TEST_CASE("WistdFunctionTests::CopyAssignmentTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> copyTo;
{
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == copyFrom());

copyTo = copyFrom;
}

REQUIRE(1 == copyTo());
}

REQUIRE(0 == state.instance_count());
}

TEST_CASE("WistdFunctionTests::MoveConstructionTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == moveFrom());

auto moveTo = std::move(moveFrom);
REQUIRE(0 == moveTo());

// Because we move the underlying function object, we _must_ invalidate the moved from function
REQUIRE_FALSE(moveFrom != nullptr);
}

REQUIRE(0 == state.instance_count());
}

TEST_CASE("WistdFunctionTests::MoveAssignmentTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> moveTo;
{
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == moveFrom());

moveTo = std::move(moveFrom);
}

REQUIRE(0 == moveTo());
}

REQUIRE(0 == state.instance_count());
}

TEST_CASE("WistdFunctionTests::SwapTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> first;
wistd::function<int()> second;

first.swap(second);
REQUIRE_FALSE(first != nullptr);
REQUIRE_FALSE(second != nullptr);

first = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};

first.swap(second);
REQUIRE_FALSE(first != nullptr);
REQUIRE(second != nullptr);
REQUIRE(0 == second());

first.swap(second);
REQUIRE(first != nullptr);
REQUIRE_FALSE(second != nullptr);
REQUIRE(0 == first());

second = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};

first.swap(second);
REQUIRE(first != nullptr);
REQUIRE(second != nullptr);
REQUIRE(0 == first());
}

REQUIRE(0 == state.instance_count());
}

// MSVC's optimizer has had issues with wistd::function in the past when forwarding wistd::function objects to a
// function that accepts the arguments by value. This test exercises the workaround that we have in place. Note
// that this of course requires building with optimizations enabled
void ForwardingTest(wistd::function<int()> getValue, wistd::function<int(int)> negate, wistd::function<int(int, int)> add)
{
// Previously, this would cause a runtime crash
REQUIRE(Add(GetValue(), Negate(8)) == add(getValue(), negate(8)));
}

template <typename... Args>
void CallForwardingTest(Args&&... args)
{
ForwardingTest(wistd::forward<Args>(args)...);
}

TEST_CASE("WistdFunctionTests::OptimizationRegressionTest", "[wistd]")
{
CallForwardingTest(
wistd::function<int()>(GetValue),
wistd::function<int(int)>(Negate),
wistd::function<int(int, int)>(Add));
}
@@ -0,0 +1,21 @@

project(witest.app)
add_executable(witest.app)

add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)

target_sources(witest.app PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,31 @@

# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned
# on compiler) as new standards are ratified/support is available
set(CMAKE_CXX_STANDARD 17)

project(witest.cpplatest)
add_executable(witest.cpplatest)

# Semi-arbitrary insiders SDK version selected that uses C++/WinRT "2.0"
if ("${WIL_WINDOWS_SDK_VERSION}" VERSION_GREATER_EQUAL "10.0.18878.0")
target_sources(witest.cpplatest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp)
endif()

target_sources(witest.cpplatest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)
@@ -0,0 +1,8 @@

#pragma comment(lib, "Pathcch.lib")
#pragma comment(lib, "RuntimeObject.lib")
#pragma comment(lib, "Synchronization.lib")
#pragma comment(lib, "RpcRt4.lib")

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
@@ -0,0 +1,27 @@

project(witest.noexcept)
add_executable(witest.noexcept)

# Turn off exceptions for this test
replace_cxx_flag("/EHsc" "")
add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS)

# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch
# statements... Thankfully MSVC just gives us a warning that we can disable
append_cxx_flag("/wd4530")

target_sources(witest.noexcept PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp)
@@ -0,0 +1,21 @@

project(witest)
add_executable(witest)

target_sources(witest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)
@@ -0,0 +1,108 @@
#pragma once

#include "catch.hpp"

// Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that
// the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that
// require CopyConstructible (e.g. for wistd::function)
struct fail_on_copy
{
fail_on_copy() = default;

fail_on_copy(const fail_on_copy&)
{
FAIL("Copy constructor invoked for fail_on_copy type");
}

fail_on_copy(fail_on_copy&&) = default;

fail_on_copy& operator=(const fail_on_copy&)
{
FAIL("Copy assignment operator invoked for fail_on_copy type");
return *this;
}

fail_on_copy& operator=(fail_on_copy&&) = default;
};

// Useful for validating that objects get copied e.g. as opposed to capturing a reference
struct value_holder
{
int value = 0xbadf00d;

~value_holder()
{
value = 0xbadf00d;
}
};

// Useful for validating that functions, etc. are callable with move-only types
// Example real type that is move only is Microsoft::WRL::Wrappers::HString
struct cannot_copy
{
cannot_copy() = default;
cannot_copy(const cannot_copy&) = delete;
cannot_copy& operator=(const cannot_copy&) = delete;

cannot_copy(cannot_copy&&) = default;
cannot_copy& operator=(cannot_copy&&) = default;
};

// State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in
// contexts that require a default constructible type, but has the nice property that it allows for tests to run
// concurrently
struct object_counter_state
{
volatile LONG constructed_count = 0;
volatile LONG destructed_count = 0;
volatile LONG copy_count = 0;
volatile LONG move_count = 0;

LONG instance_count()
{
return constructed_count - destructed_count;
}
};

struct object_counter
{
object_counter_state* state;

object_counter(object_counter_state& s) :
state(&s)
{
::InterlockedIncrement(&state->constructed_count);
}

object_counter(const object_counter& other) :
state(other.state)
{
::InterlockedIncrement(&state->constructed_count);
::InterlockedIncrement(&state->copy_count);
}

object_counter(object_counter&& other) WI_NOEXCEPT :
state(other.state)
{
::InterlockedIncrement(&state->constructed_count);
::InterlockedIncrement(&state->move_count);
}

~object_counter()
{
::InterlockedIncrement(&state->destructed_count);
state = nullptr;
}

object_counter& operator=(const object_counter&)
{
::InterlockedIncrement(&state->copy_count);
return *this;
}

object_counter& operator=(object_counter&&) WI_NOEXCEPT
{
::InterlockedIncrement(&state->move_count);
return *this;
}
};

Large diffs are not rendered by default.

@@ -0,0 +1,2 @@

We try and be as conformant as possible, but sometimes dependencies make that difficult. For example, WRL has had a number of conformance issues that keep getting uncovered. The files here are fixed up copies of those files and the include path is modified such that these directories appear first.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -60,6 +60,8 @@ Dolphin includes or links code of the following third-party software projects:
[LGPLv2+](http://www.surina.net/soundtouch/license.html)
- [TAP-Windows](https://openvpn.net/):
header only
- [Windows Implementation Libraries](https://github.com/microsoft/wil):
[MIT](https://github.com/microsoft/wil/blob/master/LICENSE)
- [xxHash](https://github.com/Cyan4973/xxHash):
[2-clause BSD](https://github.com/Cyan4973/xxHash/blob/master/LICENSE)
- [zlib](http://www.zlib.net/):
@@ -53,6 +53,7 @@
<AdditionalIncludeDirectories>$(ExternalsDir)pugixml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)SFML\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)Vulkan\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)WIL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)xxhash;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>FMT_HEADER_ONLY=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -62,6 +63,7 @@
<PreprocessorDefinitions>USE_ANALYTICS=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USE_DISCORD_PRESENCE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>CURL_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIL_SUPPRESS_EXCEPTIONS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">_ARCH_64=1;_M_X86=1;_M_X86_64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">_ARCH_64=1;_M_ARM_64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">HAVE_FFMPEG;%(PreprocessorDefinitions)</PreprocessorDefinitions>