Initial Commit
This commit is contained in:
parent
6368329a17
commit
6bb2d8a488
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -32,3 +32,5 @@
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
.vs/*
|
||||||
|
|
||||||
|
|
78
ApplicationLoopback.cpp
Normal file
78
ApplicationLoopback.cpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include "LoopbackCapture.h" // Assuming this is where CLoopbackCapture is defined
|
||||||
|
|
||||||
|
void usage() {
|
||||||
|
std::wcout << L"Usage: AudioCapture <ProcessId> <includetree|excludetree> <OutputFile>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile bool keepRunning = true;
|
||||||
|
|
||||||
|
void signalHandler(int signum) {
|
||||||
|
keepRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wmain(int argc, wchar_t* argv[])
|
||||||
|
{
|
||||||
|
if (argc != 4)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD processId = wcstoul(argv[1], nullptr, 0);
|
||||||
|
if (processId == 0)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool includeProcessTree;
|
||||||
|
if (wcscmp(argv[2], L"includetree") == 0)
|
||||||
|
{
|
||||||
|
includeProcessTree = true;
|
||||||
|
}
|
||||||
|
else if (wcscmp(argv[2], L"excludetree") == 0)
|
||||||
|
{
|
||||||
|
includeProcessTree = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCWSTR outputFile = argv[3];
|
||||||
|
|
||||||
|
// Set up signal handling
|
||||||
|
signal(SIGINT, signalHandler);
|
||||||
|
signal(SIGTERM, signalHandler);
|
||||||
|
|
||||||
|
CLoopbackCapture loopbackCapture;
|
||||||
|
HRESULT hr = loopbackCapture.StartCaptureAsync(processId, includeProcessTree, outputFile);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
wil::unique_hlocal_string message;
|
||||||
|
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, hr,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (PWSTR)&message, 0, nullptr);
|
||||||
|
std::wcout << L"Failed to start capture\n0x" << std::hex << hr << L": " << message.get() << L"\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::wcout << L"Capturing audio. Terminate the process to stop." << std::endl;
|
||||||
|
|
||||||
|
// Run until a termination signal is received
|
||||||
|
while (keepRunning)
|
||||||
|
{
|
||||||
|
Sleep(100); // Sleep to reduce CPU usage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the capture
|
||||||
|
loopbackCapture.StopCaptureAsync();
|
||||||
|
|
||||||
|
std::wcout << L"Capture stopped." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
31
ApplicationLoopback.sln
Normal file
31
ApplicationLoopback.sln
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30204.135
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ApplicationLoopback", "ApplicationLoopback.vcxproj", "{6E745655-513E-4713-B3AB-D6D3F62D7734}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x64.Build.0 = Release|x64
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {64406148-8C03-42D2-8A2A-C14A03CFF8E3}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
169
ApplicationLoopback.vcxproj
Normal file
169
ApplicationLoopback.vcxproj
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{6e745655-513e-4713-b3ab-d6d3f62d7734}</ProjectGuid>
|
||||||
|
<RootNamespace>ApplicationLoopback</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
<CLRSupport>false</CLRSupport>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<EnableManagedIncrementalBuild>false</EnableManagedIncrementalBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="ApplicationLoopback.cpp" />
|
||||||
|
<ClCompile Include="LoopbackCapture.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="Common.h" />
|
||||||
|
<ClInclude Include="LoopbackCapture.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
36
ApplicationLoopback.vcxproj.filters
Normal file
36
ApplicationLoopback.vcxproj.filters
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="ApplicationLoopback.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="LoopbackCapture.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="LoopbackCapture.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Common.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
4
ApplicationLoopback.vcxproj.user
Normal file
4
ApplicationLoopback.vcxproj.user
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup />
|
||||||
|
</Project>
|
58
Common.h
Normal file
58
Common.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mfidl.h>
|
||||||
|
#include <mfapi.h>
|
||||||
|
#include <mfobjects.h>
|
||||||
|
|
||||||
|
#ifndef METHODASYNCCALLBACK
|
||||||
|
#define METHODASYNCCALLBACK(Parent, AsyncCallback, pfnCallback) \
|
||||||
|
class Callback##AsyncCallback :\
|
||||||
|
public IMFAsyncCallback \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
Callback##AsyncCallback() : \
|
||||||
|
_parent(((Parent*)((BYTE*)this - offsetof(Parent, m_x##AsyncCallback)))), \
|
||||||
|
_dwQueueID( MFASYNC_CALLBACK_QUEUE_MULTITHREADED ) \
|
||||||
|
{ \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
STDMETHOD_( ULONG, AddRef )() \
|
||||||
|
{ \
|
||||||
|
return _parent->AddRef(); \
|
||||||
|
} \
|
||||||
|
STDMETHOD_( ULONG, Release )() \
|
||||||
|
{ \
|
||||||
|
return _parent->Release(); \
|
||||||
|
} \
|
||||||
|
STDMETHOD( QueryInterface )( REFIID riid, void **ppvObject ) \
|
||||||
|
{ \
|
||||||
|
if (riid == IID_IMFAsyncCallback || riid == IID_IUnknown) \
|
||||||
|
{ \
|
||||||
|
(*ppvObject) = this; \
|
||||||
|
AddRef(); \
|
||||||
|
return S_OK; \
|
||||||
|
} \
|
||||||
|
*ppvObject = NULL; \
|
||||||
|
return E_NOINTERFACE; \
|
||||||
|
} \
|
||||||
|
STDMETHOD( GetParameters )( \
|
||||||
|
/* [out] */ __RPC__out DWORD *pdwFlags, \
|
||||||
|
/* [out] */ __RPC__out DWORD *pdwQueue) \
|
||||||
|
{ \
|
||||||
|
*pdwFlags = 0; \
|
||||||
|
*pdwQueue = _dwQueueID; \
|
||||||
|
return S_OK; \
|
||||||
|
} \
|
||||||
|
STDMETHOD( Invoke )( /* [out] */ __RPC__out IMFAsyncResult * pResult ) \
|
||||||
|
{ \
|
||||||
|
_parent->pfnCallback( pResult ); \
|
||||||
|
return S_OK; \
|
||||||
|
} \
|
||||||
|
void SetQueueID( DWORD dwQueueID ) { _dwQueueID = dwQueueID; } \
|
||||||
|
\
|
||||||
|
protected: \
|
||||||
|
Parent* _parent; \
|
||||||
|
DWORD _dwQueueID; \
|
||||||
|
\
|
||||||
|
} m_x##AsyncCallback;
|
||||||
|
#endif
|
417
LoopbackCapture.cpp
Normal file
417
LoopbackCapture.cpp
Normal file
|
@ -0,0 +1,417 @@
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <audioclientactivationparams.h>
|
||||||
|
|
||||||
|
#include "LoopbackCapture.h"
|
||||||
|
|
||||||
|
#define BITS_PER_BYTE 8
|
||||||
|
|
||||||
|
HRESULT CLoopbackCapture::SetDeviceStateErrorIfFailed(HRESULT hr)
|
||||||
|
{
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
m_DeviceState = DeviceState::Error;
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CLoopbackCapture::InitializeLoopbackCapture()
|
||||||
|
{
|
||||||
|
// Create events for sample ready or user stop
|
||||||
|
RETURN_IF_FAILED(m_SampleReadyEvent.create(wil::EventOptions::None));
|
||||||
|
|
||||||
|
// Initialize MF
|
||||||
|
RETURN_IF_FAILED(MFStartup(MF_VERSION, MFSTARTUP_LITE));
|
||||||
|
|
||||||
|
// Register MMCSS work queue
|
||||||
|
DWORD dwTaskID = 0;
|
||||||
|
RETURN_IF_FAILED(MFLockSharedWorkQueue(L"Capture", 0, &dwTaskID, &m_dwQueueID));
|
||||||
|
|
||||||
|
// Set the capture event work queue to use the MMCSS queue
|
||||||
|
m_xSampleReady.SetQueueID(m_dwQueueID);
|
||||||
|
|
||||||
|
// Create the completion event as auto-reset
|
||||||
|
RETURN_IF_FAILED(m_hActivateCompleted.create(wil::EventOptions::None));
|
||||||
|
|
||||||
|
// Create the capture-stopped event as auto-reset
|
||||||
|
RETURN_IF_FAILED(m_hCaptureStopped.create(wil::EventOptions::None));
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLoopbackCapture::~CLoopbackCapture()
|
||||||
|
{
|
||||||
|
if (m_dwQueueID != 0)
|
||||||
|
{
|
||||||
|
MFUnlockWorkQueue(m_dwQueueID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CLoopbackCapture::ActivateAudioInterface(DWORD processId, bool includeProcessTree)
|
||||||
|
{
|
||||||
|
return SetDeviceStateErrorIfFailed([&]() -> HRESULT
|
||||||
|
{
|
||||||
|
AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams = {};
|
||||||
|
audioclientActivationParams.ActivationType = AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK;
|
||||||
|
audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = includeProcessTree ?
|
||||||
|
PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE : PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE;
|
||||||
|
audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = processId;
|
||||||
|
|
||||||
|
PROPVARIANT activateParams = {};
|
||||||
|
activateParams.vt = VT_BLOB;
|
||||||
|
activateParams.blob.cbSize = sizeof(audioclientActivationParams);
|
||||||
|
activateParams.blob.pBlobData = (BYTE*)&audioclientActivationParams;
|
||||||
|
|
||||||
|
wil::com_ptr_nothrow<IActivateAudioInterfaceAsyncOperation> asyncOp;
|
||||||
|
RETURN_IF_FAILED(ActivateAudioInterfaceAsync(VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, __uuidof(IAudioClient), &activateParams, this, &asyncOp));
|
||||||
|
|
||||||
|
// Wait for activation completion
|
||||||
|
m_hActivateCompleted.wait();
|
||||||
|
|
||||||
|
return m_activateResult;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ActivateCompleted()
|
||||||
|
//
|
||||||
|
// Callback implementation of ActivateAudioInterfaceAsync function. This will be called on MTA thread
|
||||||
|
// when results of the activation are available.
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::ActivateCompleted(IActivateAudioInterfaceAsyncOperation* operation)
|
||||||
|
{
|
||||||
|
m_activateResult = SetDeviceStateErrorIfFailed([&]()->HRESULT
|
||||||
|
{
|
||||||
|
// Check for a successful activation result
|
||||||
|
HRESULT hrActivateResult = E_UNEXPECTED;
|
||||||
|
wil::com_ptr_nothrow<IUnknown> punkAudioInterface;
|
||||||
|
RETURN_IF_FAILED(operation->GetActivateResult(&hrActivateResult, &punkAudioInterface));
|
||||||
|
RETURN_IF_FAILED(hrActivateResult);
|
||||||
|
|
||||||
|
// Get the pointer for the Audio Client
|
||||||
|
RETURN_IF_FAILED(punkAudioInterface.copy_to(&m_AudioClient));
|
||||||
|
|
||||||
|
// The app can also call m_AudioClient->GetMixFormat instead to get the capture format.
|
||||||
|
// 16 - bit PCM format.
|
||||||
|
m_CaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
m_CaptureFormat.nChannels = 2;
|
||||||
|
m_CaptureFormat.nSamplesPerSec = 44100;
|
||||||
|
m_CaptureFormat.wBitsPerSample = 16;
|
||||||
|
m_CaptureFormat.nBlockAlign = m_CaptureFormat.nChannels * m_CaptureFormat.wBitsPerSample / BITS_PER_BYTE;
|
||||||
|
m_CaptureFormat.nAvgBytesPerSec = m_CaptureFormat.nSamplesPerSec * m_CaptureFormat.nBlockAlign;
|
||||||
|
|
||||||
|
// Initialize the AudioClient in Shared Mode with the user specified buffer
|
||||||
|
RETURN_IF_FAILED(m_AudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
|
||||||
|
AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||||
|
200000,
|
||||||
|
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM,
|
||||||
|
&m_CaptureFormat,
|
||||||
|
nullptr));
|
||||||
|
|
||||||
|
// Get the maximum size of the AudioClient Buffer
|
||||||
|
RETURN_IF_FAILED(m_AudioClient->GetBufferSize(&m_BufferFrames));
|
||||||
|
|
||||||
|
// Get the capture client
|
||||||
|
RETURN_IF_FAILED(m_AudioClient->GetService(IID_PPV_ARGS(&m_AudioCaptureClient)));
|
||||||
|
|
||||||
|
// Create Async callback for sample events
|
||||||
|
RETURN_IF_FAILED(MFCreateAsyncResult(nullptr, &m_xSampleReady, nullptr, &m_SampleReadyAsyncResult));
|
||||||
|
|
||||||
|
// Tell the system which event handle it should signal when an audio buffer is ready to be processed by the client
|
||||||
|
RETURN_IF_FAILED(m_AudioClient->SetEventHandle(m_SampleReadyEvent.get()));
|
||||||
|
|
||||||
|
// Creates the WAV file.
|
||||||
|
RETURN_IF_FAILED(CreateWAVFile());
|
||||||
|
|
||||||
|
// Everything is ready.
|
||||||
|
m_DeviceState = DeviceState::Initialized;
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}());
|
||||||
|
|
||||||
|
// Let ActivateAudioInterface know that m_activateResult has the result of the activation attempt.
|
||||||
|
m_hActivateCompleted.SetEvent();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CreateWAVFile()
|
||||||
|
//
|
||||||
|
// Creates a WAV file in music folder
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::CreateWAVFile()
|
||||||
|
{
|
||||||
|
return SetDeviceStateErrorIfFailed([&]()->HRESULT
|
||||||
|
{
|
||||||
|
m_hFile.reset(CreateFile(m_outputFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||||
|
RETURN_LAST_ERROR_IF(!m_hFile);
|
||||||
|
|
||||||
|
// Create and write the WAV header
|
||||||
|
|
||||||
|
// 1. RIFF chunk descriptor
|
||||||
|
DWORD header[] = {
|
||||||
|
FCC('RIFF'), // RIFF header
|
||||||
|
0, // Total size of WAV (will be filled in later)
|
||||||
|
FCC('WAVE'), // WAVE FourCC
|
||||||
|
FCC('fmt '), // Start of 'fmt ' chunk
|
||||||
|
sizeof(m_CaptureFormat) // Size of fmt chunk
|
||||||
|
};
|
||||||
|
DWORD dwBytesWritten = 0;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), header, sizeof(header), &dwBytesWritten, NULL));
|
||||||
|
|
||||||
|
m_cbHeaderSize += dwBytesWritten;
|
||||||
|
|
||||||
|
// 2. The fmt sub-chunk
|
||||||
|
WI_ASSERT(m_CaptureFormat.cbSize == 0);
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &m_CaptureFormat, sizeof(m_CaptureFormat), &dwBytesWritten, NULL));
|
||||||
|
m_cbHeaderSize += dwBytesWritten;
|
||||||
|
|
||||||
|
// 3. The data sub-chunk
|
||||||
|
DWORD data[] = { FCC('data'), 0 }; // Start of 'data' chunk
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), data, sizeof(data), &dwBytesWritten, NULL));
|
||||||
|
m_cbHeaderSize += dwBytesWritten;
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// FixWAVHeader()
|
||||||
|
//
|
||||||
|
// The size values were not known when we originally wrote the header, so now go through and fix the values
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::FixWAVHeader()
|
||||||
|
{
|
||||||
|
// Write the size of the 'data' chunk first
|
||||||
|
DWORD dwPtr = SetFilePointer(m_hFile.get(), m_cbHeaderSize - sizeof(DWORD), NULL, FILE_BEGIN);
|
||||||
|
RETURN_LAST_ERROR_IF(INVALID_SET_FILE_POINTER == dwPtr);
|
||||||
|
|
||||||
|
DWORD dwBytesWritten = 0;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &m_cbDataSize, sizeof(DWORD), &dwBytesWritten, NULL));
|
||||||
|
|
||||||
|
// Write the total file size, minus RIFF chunk and size
|
||||||
|
// sizeof(DWORD) == sizeof(FOURCC)
|
||||||
|
RETURN_LAST_ERROR_IF(INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile.get(), sizeof(DWORD), NULL, FILE_BEGIN));
|
||||||
|
|
||||||
|
DWORD cbTotalSize = m_cbDataSize + m_cbHeaderSize - 8;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &cbTotalSize, sizeof(DWORD), &dwBytesWritten, NULL));
|
||||||
|
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(m_hFile.get()));
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT CLoopbackCapture::StartCaptureAsync(DWORD processId, bool includeProcessTree, PCWSTR outputFileName)
|
||||||
|
{
|
||||||
|
m_outputFileName = outputFileName;
|
||||||
|
auto resetOutputFileName = wil::scope_exit([&] { m_outputFileName = nullptr; });
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(InitializeLoopbackCapture());
|
||||||
|
RETURN_IF_FAILED(ActivateAudioInterface(processId, includeProcessTree));
|
||||||
|
|
||||||
|
// We should be in the initialzied state if this is the first time through getting ready to capture.
|
||||||
|
if (m_DeviceState == DeviceState::Initialized)
|
||||||
|
{
|
||||||
|
m_DeviceState = DeviceState::Starting;
|
||||||
|
return MFPutWorkItem2(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, 0, &m_xStartCapture, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// OnStartCapture()
|
||||||
|
//
|
||||||
|
// Callback method to start capture
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::OnStartCapture(IMFAsyncResult* pResult)
|
||||||
|
{
|
||||||
|
return SetDeviceStateErrorIfFailed([&]()->HRESULT
|
||||||
|
{
|
||||||
|
// Start the capture
|
||||||
|
RETURN_IF_FAILED(m_AudioClient->Start());
|
||||||
|
|
||||||
|
m_DeviceState = DeviceState::Capturing;
|
||||||
|
MFPutWaitingWorkItem(m_SampleReadyEvent.get(), 0, m_SampleReadyAsyncResult.get(), &m_SampleReadyKey);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// StopCaptureAsync()
|
||||||
|
//
|
||||||
|
// Stop capture asynchronously via MF Work Item
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::StopCaptureAsync()
|
||||||
|
{
|
||||||
|
RETURN_HR_IF(E_NOT_VALID_STATE, (m_DeviceState != DeviceState::Capturing) &&
|
||||||
|
(m_DeviceState != DeviceState::Error));
|
||||||
|
|
||||||
|
m_DeviceState = DeviceState::Stopping;
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(MFPutWorkItem2(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, 0, &m_xStopCapture, nullptr));
|
||||||
|
|
||||||
|
// Wait for capture to stop
|
||||||
|
m_hCaptureStopped.wait();
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// OnStopCapture()
|
||||||
|
//
|
||||||
|
// Callback method to stop capture
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::OnStopCapture(IMFAsyncResult* pResult)
|
||||||
|
{
|
||||||
|
// Stop capture by cancelling Work Item
|
||||||
|
// Cancel the queued work item (if any)
|
||||||
|
if (0 != m_SampleReadyKey)
|
||||||
|
{
|
||||||
|
MFCancelWorkItem(m_SampleReadyKey);
|
||||||
|
m_SampleReadyKey = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_AudioClient->Stop();
|
||||||
|
m_SampleReadyAsyncResult.reset();
|
||||||
|
|
||||||
|
return FinishCaptureAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// FinishCaptureAsync()
|
||||||
|
//
|
||||||
|
// Finalizes WAV file on a separate thread via MF Work Item
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::FinishCaptureAsync()
|
||||||
|
{
|
||||||
|
// We should be flushing when this is called
|
||||||
|
return MFPutWorkItem2(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, 0, &m_xFinishCapture, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// OnFinishCapture()
|
||||||
|
//
|
||||||
|
// Because of the asynchronous nature of the MF Work Queues and the DataWriter, there could still be
|
||||||
|
// a sample processing. So this will get called to finalize the WAV header.
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::OnFinishCapture(IMFAsyncResult* pResult)
|
||||||
|
{
|
||||||
|
// FixWAVHeader will set the DeviceStateStopped when all async tasks are complete
|
||||||
|
HRESULT hr = FixWAVHeader();
|
||||||
|
|
||||||
|
m_DeviceState = DeviceState::Stopped;
|
||||||
|
|
||||||
|
m_hCaptureStopped.SetEvent();
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// OnSampleReady()
|
||||||
|
//
|
||||||
|
// Callback method when ready to fill sample buffer
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::OnSampleReady(IMFAsyncResult* pResult)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(OnAudioSampleRequested()))
|
||||||
|
{
|
||||||
|
// Re-queue work item for next sample
|
||||||
|
if (m_DeviceState == DeviceState::Capturing)
|
||||||
|
{
|
||||||
|
// Re-queue work item for next sample
|
||||||
|
return MFPutWaitingWorkItem(m_SampleReadyEvent.get(), 0, m_SampleReadyAsyncResult.get(), &m_SampleReadyKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_DeviceState = DeviceState::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// OnAudioSampleRequested()
|
||||||
|
//
|
||||||
|
// Called when audio device fires m_SampleReadyEvent
|
||||||
|
//
|
||||||
|
HRESULT CLoopbackCapture::OnAudioSampleRequested()
|
||||||
|
{
|
||||||
|
UINT32 FramesAvailable = 0;
|
||||||
|
BYTE* Data = nullptr;
|
||||||
|
DWORD dwCaptureFlags;
|
||||||
|
UINT64 u64DevicePosition = 0;
|
||||||
|
UINT64 u64QPCPosition = 0;
|
||||||
|
DWORD cbBytesToCapture = 0;
|
||||||
|
|
||||||
|
auto lock = m_CritSec.lock();
|
||||||
|
|
||||||
|
// If this flag is set, we have already queued up the async call to finialize the WAV header
|
||||||
|
// So we don't want to grab or write any more data that would possibly give us an invalid size
|
||||||
|
if (m_DeviceState == DeviceState::Stopping)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A word on why we have a loop here;
|
||||||
|
// Suppose it has been 10 milliseconds or so since the last time
|
||||||
|
// this routine was invoked, and that we're capturing 48000 samples per second.
|
||||||
|
//
|
||||||
|
// The audio engine can be reasonably expected to have accumulated about that much
|
||||||
|
// audio data - that is, about 480 samples.
|
||||||
|
//
|
||||||
|
// However, the audio engine is free to accumulate this in various ways:
|
||||||
|
// a. as a single packet of 480 samples, OR
|
||||||
|
// b. as a packet of 80 samples plus a packet of 400 samples, OR
|
||||||
|
// c. as 48 packets of 10 samples each.
|
||||||
|
//
|
||||||
|
// In particular, there is no guarantee that this routine will be
|
||||||
|
// run once for each packet.
|
||||||
|
//
|
||||||
|
// So every time this routine runs, we need to read ALL the packets
|
||||||
|
// that are now available;
|
||||||
|
//
|
||||||
|
// We do this by calling IAudioCaptureClient::GetNextPacketSize
|
||||||
|
// over and over again until it indicates there are no more packets remaining.
|
||||||
|
while (SUCCEEDED(m_AudioCaptureClient->GetNextPacketSize(&FramesAvailable)) && FramesAvailable > 0)
|
||||||
|
{
|
||||||
|
cbBytesToCapture = FramesAvailable * m_CaptureFormat.nBlockAlign;
|
||||||
|
|
||||||
|
// WAV files have a 4GB (0xFFFFFFFF) size limit, so likely we have hit that limit when we
|
||||||
|
// overflow here. Time to stop the capture
|
||||||
|
if ((m_cbDataSize + cbBytesToCapture) < m_cbDataSize)
|
||||||
|
{
|
||||||
|
StopCaptureAsync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get sample buffer
|
||||||
|
RETURN_IF_FAILED(m_AudioCaptureClient->GetBuffer(&Data, &FramesAvailable, &dwCaptureFlags, &u64DevicePosition, &u64QPCPosition));
|
||||||
|
|
||||||
|
|
||||||
|
// Write File
|
||||||
|
if (m_DeviceState != DeviceState::Stopping)
|
||||||
|
{
|
||||||
|
DWORD dwBytesWritten = 0;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(
|
||||||
|
m_hFile.get(),
|
||||||
|
Data,
|
||||||
|
cbBytesToCapture,
|
||||||
|
&dwBytesWritten,
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release buffer back
|
||||||
|
m_AudioCaptureClient->ReleaseBuffer(FramesAvailable);
|
||||||
|
|
||||||
|
// Increase the size of our 'data' chunk. m_cbDataSize needs to be accurate
|
||||||
|
m_cbDataSize += cbBytesToCapture;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
86
LoopbackCapture.h
Normal file
86
LoopbackCapture.h
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AudioClient.h>
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <guiddef.h>
|
||||||
|
#include <mfapi.h>
|
||||||
|
|
||||||
|
#include <wrl\implements.h>
|
||||||
|
#include <wil\com.h>
|
||||||
|
#include <wil\result.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
using namespace Microsoft::WRL;
|
||||||
|
|
||||||
|
class CLoopbackCapture :
|
||||||
|
public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CLoopbackCapture() = default;
|
||||||
|
~CLoopbackCapture();
|
||||||
|
|
||||||
|
HRESULT StartCaptureAsync(DWORD processId, bool includeProcessTree, PCWSTR outputFileName);
|
||||||
|
HRESULT StopCaptureAsync();
|
||||||
|
|
||||||
|
METHODASYNCCALLBACK(CLoopbackCapture, StartCapture, OnStartCapture);
|
||||||
|
METHODASYNCCALLBACK(CLoopbackCapture, StopCapture, OnStopCapture);
|
||||||
|
METHODASYNCCALLBACK(CLoopbackCapture, SampleReady, OnSampleReady);
|
||||||
|
METHODASYNCCALLBACK(CLoopbackCapture, FinishCapture, OnFinishCapture);
|
||||||
|
|
||||||
|
// IActivateAudioInterfaceCompletionHandler
|
||||||
|
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation* operation);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// NB: All states >= Initialized will allow some methods
|
||||||
|
// to be called successfully on the Audio Client
|
||||||
|
enum class DeviceState
|
||||||
|
{
|
||||||
|
Uninitialized,
|
||||||
|
Error,
|
||||||
|
Initialized,
|
||||||
|
Starting,
|
||||||
|
Capturing,
|
||||||
|
Stopping,
|
||||||
|
Stopped,
|
||||||
|
};
|
||||||
|
|
||||||
|
HRESULT OnStartCapture(IMFAsyncResult* pResult);
|
||||||
|
HRESULT OnStopCapture(IMFAsyncResult* pResult);
|
||||||
|
HRESULT OnFinishCapture(IMFAsyncResult* pResult);
|
||||||
|
HRESULT OnSampleReady(IMFAsyncResult* pResult);
|
||||||
|
|
||||||
|
HRESULT InitializeLoopbackCapture();
|
||||||
|
HRESULT CreateWAVFile();
|
||||||
|
HRESULT FixWAVHeader();
|
||||||
|
HRESULT OnAudioSampleRequested();
|
||||||
|
|
||||||
|
HRESULT ActivateAudioInterface(DWORD processId, bool includeProcessTree);
|
||||||
|
HRESULT FinishCaptureAsync();
|
||||||
|
|
||||||
|
HRESULT SetDeviceStateErrorIfFailed(HRESULT hr);
|
||||||
|
|
||||||
|
wil::com_ptr_nothrow<IAudioClient> m_AudioClient;
|
||||||
|
WAVEFORMATEX m_CaptureFormat{};
|
||||||
|
UINT32 m_BufferFrames = 0;
|
||||||
|
wil::com_ptr_nothrow<IAudioCaptureClient> m_AudioCaptureClient;
|
||||||
|
wil::com_ptr_nothrow<IMFAsyncResult> m_SampleReadyAsyncResult;
|
||||||
|
|
||||||
|
wil::unique_event_nothrow m_SampleReadyEvent;
|
||||||
|
MFWORKITEM_KEY m_SampleReadyKey = 0;
|
||||||
|
wil::unique_hfile m_hFile;
|
||||||
|
wil::critical_section m_CritSec;
|
||||||
|
DWORD m_dwQueueID = 0;
|
||||||
|
DWORD m_cbHeaderSize = 0;
|
||||||
|
DWORD m_cbDataSize = 0;
|
||||||
|
|
||||||
|
// These two members are used to communicate between the main thread
|
||||||
|
// and the ActivateCompleted callback.
|
||||||
|
PCWSTR m_outputFileName = nullptr;
|
||||||
|
HRESULT m_activateResult = E_UNEXPECTED;
|
||||||
|
|
||||||
|
DeviceState m_DeviceState{ DeviceState::Uninitialized };
|
||||||
|
wil::unique_event_nothrow m_hActivateCompleted;
|
||||||
|
wil::unique_event_nothrow m_hCaptureStopped;
|
||||||
|
};
|
4
packages.config
Normal file
4
packages.config
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
|
||||||
|
</packages>
|
BIN
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/.signature.p7s
vendored
Normal file
BIN
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/.signature.p7s
vendored
Normal file
Binary file not shown.
21
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/LICENSE
vendored
Normal file
21
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE
|
Binary file not shown.
60
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/ThirdPartyNotices.txt
vendored
Normal file
60
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/ThirdPartyNotices.txt
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
THIRD PARTY SOFTWARE NOTICES AND INFORMATION
|
||||||
|
Do Not Translate or Localize
|
||||||
|
|
||||||
|
This software incorporates material from third parties. Microsoft makes certain open source code available at http://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to:
|
||||||
|
|
||||||
|
Source Code Compliance Team
|
||||||
|
Microsoft Corporation
|
||||||
|
One Microsoft Way
|
||||||
|
Redmond, WA 98052
|
||||||
|
USA
|
||||||
|
|
||||||
|
Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
Libc++
|
||||||
|
|
||||||
|
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
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. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
Catch2
|
||||||
|
|
||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
</Project>
|
2830
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/com.h
vendored
Normal file
2830
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/com.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
778
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/common.h
vendored
Normal file
778
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/common.h
vendored
Normal file
|
@ -0,0 +1,778 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_COMMON_INCLUDED
|
||||||
|
#define __WIL_COMMON_INCLUDED
|
||||||
|
|
||||||
|
#if defined(_KERNEL_MODE ) && !defined(__WIL_MIN_KERNEL)
|
||||||
|
// This define indicates that the WIL usage is in a kernel mode context where
|
||||||
|
// a high degree of WIL functionality is desired.
|
||||||
|
//
|
||||||
|
// Use (sparingly) to change behavior based on whether WIL is being used in kernel
|
||||||
|
// mode or user mode.
|
||||||
|
#define WIL_KERNEL_MODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Defining WIL_HIDE_DEPRECATED will hide everything deprecated.
|
||||||
|
// Each wave of deprecation will add a new WIL_HIDE_DEPRECATED_YYMM number that can be used to lock deprecation at
|
||||||
|
// a particular point, allowing components to avoid backslide and catch up to the current independently.
|
||||||
|
#ifdef WIL_HIDE_DEPRECATED
|
||||||
|
#define WIL_HIDE_DEPRECATED_1809
|
||||||
|
#endif
|
||||||
|
#ifdef WIL_HIDE_DEPRECATED_1809
|
||||||
|
#define WIL_HIDE_DEPRECATED_1612
|
||||||
|
#endif
|
||||||
|
#ifdef WIL_HIDE_DEPRECATED_1612
|
||||||
|
#define WIL_HIDE_DEPRECATED_1611
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Implementation side note: ideally the deprecation would be done with the function-level declspec
|
||||||
|
// as it allows you to utter the error text when used. The declspec works, but doing it selectively with
|
||||||
|
// a macro makes intellisense deprecation comments not work. So we just use the #pragma deprecation.
|
||||||
|
#ifdef WIL_WARN_DEPRECATED
|
||||||
|
#define WIL_WARN_DEPRECATED_1809
|
||||||
|
#endif
|
||||||
|
#ifdef WIL_WARN_DEPRECATED_1809
|
||||||
|
#define WIL_WARN_DEPRECATED_1612
|
||||||
|
#endif
|
||||||
|
#ifdef WIL_WARN_DEPRECATED_1612
|
||||||
|
#define WIL_WARN_DEPRECATED_1611
|
||||||
|
#endif
|
||||||
|
#ifdef WIL_WARN_DEPRECATED_1809
|
||||||
|
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...)
|
||||||
|
#endif
|
||||||
|
#ifdef WIL_WARN_DEPRECATED_1611
|
||||||
|
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...)
|
||||||
|
#endif
|
||||||
|
#ifdef WIL_WARN_DEPRECATED_1612
|
||||||
|
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSVC_LANG)
|
||||||
|
#define __WI_SUPPRESS_4127_S __pragma(warning(push)) __pragma(warning(disable:4127)) __pragma(warning(disable:26498)) __pragma(warning(disable:4245))
|
||||||
|
#define __WI_SUPPRESS_4127_E __pragma(warning(pop))
|
||||||
|
#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress:28285)) __pragma(warning(suppress:6504))
|
||||||
|
#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
|
||||||
|
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
|
||||||
|
#else
|
||||||
|
#define __WI_SUPPRESS_4127_S
|
||||||
|
#define __WI_SUPPRESS_4127_E
|
||||||
|
#define __WI_SUPPRESS_NULLPTR_ANALYSIS
|
||||||
|
#define __WI_SUPPRESS_NONINIT_ANALYSIS
|
||||||
|
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sal.h>
|
||||||
|
|
||||||
|
// Some SAL remapping / decoration to better support Doxygen. Macros that look like function calls can
|
||||||
|
// confuse Doxygen when they are used to decorate a function or variable. We simplify some of these to
|
||||||
|
// basic macros without the function for common use cases.
|
||||||
|
/// @cond
|
||||||
|
#define _Success_return_ _Success_(return)
|
||||||
|
#define _Success_true_ _Success_(true)
|
||||||
|
#define __declspec_noinline_ __declspec(noinline)
|
||||||
|
#define __declspec_selectany_ __declspec(selectany)
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
//! @defgroup macrobuilding Macro Composition
|
||||||
|
//! The following macros are building blocks primarily intended for authoring other macros.
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Re-state a macro value (indirection for composition)
|
||||||
|
#define WI_FLATTEN(...) __VA_ARGS__
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
#define __WI_PASTE_imp(a, b) a##b
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro.
|
||||||
|
#define WI_PASTE(a, b) __WI_PASTE_imp(a, b)
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
#define __WI_HAS_VA_OPT_IMPL(F, T, ...) T
|
||||||
|
#define __WI_HAS_VA_OPT_(...) __WI_HAS_VA_OPT_IMPL(__VA_OPT__(0,) 1, 0)
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
//! Evaluates to '1' when support for '__VA_OPT__' is available, else '0'
|
||||||
|
#define WI_HAS_VA_OPT __WI_HAS_VA_OPT_(unused)
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \
|
||||||
|
A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \
|
||||||
|
A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, \
|
||||||
|
A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, count, ...) count
|
||||||
|
#define __WI_ARGS_COUNT0(...) WI_FLATTEN(__WI_ARGS_COUNT1(__VA_ARGS__, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \
|
||||||
|
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
|
||||||
|
39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
|
||||||
|
#define __WI_ARGS_COUNT_PREFIX(...) 0, __VA_ARGS__
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
//! This variadic macro returns the number of arguments passed to it (up to 99).
|
||||||
|
#if WI_HAS_VA_OPT
|
||||||
|
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(0 __VA_OPT__(, __VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(__WI_ARGS_COUNT_PREFIX(__VA_ARGS__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
#define __WI_FOR_imp0( fn)
|
||||||
|
#define __WI_FOR_imp1( fn, arg) fn(arg)
|
||||||
|
#define __WI_FOR_imp2( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp1(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp3( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp2(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp4( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp3(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp5( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp4(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp6( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp5(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp7( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp6(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp8( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp7(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp9( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp8(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp10(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp9(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp11(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp10(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp12(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp11(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp13(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp12(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp14(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp13(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp15(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp14(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp16(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp15(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp17(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp16(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp18(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp17(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp19(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp18(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp20(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp19(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp21(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp20(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp22(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp21(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp23(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp22(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp24(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp23(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp25(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp24(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp26(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp25(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp27(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp26(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp28(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp27(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp29(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp28(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp30(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp29(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp31(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp30(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp32(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp31(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp33(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp32(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp34(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp33(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp35(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp34(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp36(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp35(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp37(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp36(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp38(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp37(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp39(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp38(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp40(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp39(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp41(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp40(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp42(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp41(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp43(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp42(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp44(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp43(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp45(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp44(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp46(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp45(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp47(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp46(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp48(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp47(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp49(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp48(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp50(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp49(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp51(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp50(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp52(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp51(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp53(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp52(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp54(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp53(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp55(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp54(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp56(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp55(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp57(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp56(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp58(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp57(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp59(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp58(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp60(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp59(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp61(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp60(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp62(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp61(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp63(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp62(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp64(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp63(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp65(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp64(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp66(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp65(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp67(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp66(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp68(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp67(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp69(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp68(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp70(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp69(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp71(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp70(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp72(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp71(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp73(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp72(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp74(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp73(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp75(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp74(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp76(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp75(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp77(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp76(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp78(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp77(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp79(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp78(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp80(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp79(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp81(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp80(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp82(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp81(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp83(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp82(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp84(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp83(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp85(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp84(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp86(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp85(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp87(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp86(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp88(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp87(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp89(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp88(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp90(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp89(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp91(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp90(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp92(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp91(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp93(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp92(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp94(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp93(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp95(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp94(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp96(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp95(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp97(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp96(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp98(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp97(fn, __VA_ARGS__))
|
||||||
|
#define __WI_FOR_imp99(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp98(fn, __VA_ARGS__))
|
||||||
|
|
||||||
|
#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
//! Iterates through each of the given arguments invoking the specified macro against each one.
|
||||||
|
#define WI_FOREACH(fn, ...) __WI_FOR_imp(WI_ARGS_COUNT(__VA_ARGS__), (fn, ##__VA_ARGS__))
|
||||||
|
|
||||||
|
//! Dispatches a single macro name to separate macros based on the number of arguments passed to it.
|
||||||
|
#define WI_MACRO_DISPATCH(name, ...) WI_PASTE(WI_PASTE(name, WI_ARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))
|
||||||
|
|
||||||
|
//! @} // Macro composition helpers
|
||||||
|
|
||||||
|
#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL)
|
||||||
|
|
||||||
|
#define WI_ODR_PRAGMA(NAME, TOKEN)
|
||||||
|
#define WI_NOEXCEPT
|
||||||
|
|
||||||
|
#else
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4714) // __forceinline not honored
|
||||||
|
|
||||||
|
// DO NOT add *any* further includes to this file -- there should be no dependencies from its usage
|
||||||
|
#include "wistd_type_traits.h"
|
||||||
|
|
||||||
|
//! This macro inserts ODR violation protection; the macro allows it to be compatible with straight "C" code
|
||||||
|
#define WI_ODR_PRAGMA(NAME, TOKEN) __pragma(detect_mismatch("ODR_violation_" NAME "_mismatch", TOKEN))
|
||||||
|
|
||||||
|
#ifdef WIL_KERNEL_MODE
|
||||||
|
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "1")
|
||||||
|
#else
|
||||||
|
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_CPPUNWIND) && !defined(WIL_SUPPRESS_EXCEPTIONS)
|
||||||
|
/** This define is automatically set when exceptions are enabled within wil.
|
||||||
|
It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in
|
||||||
|
_CPPUNWIND flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil
|
||||||
|
header. All exception-based WIL methods and classes are included behind:
|
||||||
|
~~~~
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
// code
|
||||||
|
#endif
|
||||||
|
~~~~
|
||||||
|
This enables exception-free code to directly include WIL headers without worrying about exception-based
|
||||||
|
routines suddenly becoming available. */
|
||||||
|
#define WIL_ENABLE_EXCEPTIONS
|
||||||
|
#endif
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
#if defined(WIL_EXCEPTION_MODE)
|
||||||
|
static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode");
|
||||||
|
#elif !defined(WIL_LOCK_EXCEPTION_MODE)
|
||||||
|
#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together
|
||||||
|
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0")
|
||||||
|
#elif defined(WIL_ENABLE_EXCEPTIONS)
|
||||||
|
#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled
|
||||||
|
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1")
|
||||||
|
#else
|
||||||
|
#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions
|
||||||
|
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS)
|
||||||
|
#error Must enable exceptions when WIL_EXCEPTION_MODE == 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// block for documentation only
|
||||||
|
#if defined(WIL_DOXYGEN)
|
||||||
|
/** This define can be explicitly set to disable exception usage within wil.
|
||||||
|
Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking
|
||||||
|
at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based
|
||||||
|
classes and methods from WIL, define this macro ahead of including the first WIL header. */
|
||||||
|
#define WIL_SUPPRESS_EXCEPTIONS
|
||||||
|
|
||||||
|
/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS.
|
||||||
|
Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to
|
||||||
|
do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations
|
||||||
|
when linking libraries together with different exception handling semantics. */
|
||||||
|
#define WIL_LOCK_EXCEPTION_MODE
|
||||||
|
|
||||||
|
/** This define explicit sets the exception mode for the process to control optimizations.
|
||||||
|
Three exception modes are available:
|
||||||
|
0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that
|
||||||
|
use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR
|
||||||
|
violations when linking libraries together with different exception handling semantics.
|
||||||
|
1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled.
|
||||||
|
2) This locks the binary to libraries built without exceptions. */
|
||||||
|
#define WIL_EXCEPTION_MODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703)
|
||||||
|
#define WIL_HAS_CXX_17 1
|
||||||
|
#else
|
||||||
|
#define WIL_HAS_CXX_17 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Until we'll have C++17 enabled in our code base, we're falling back to SAL
|
||||||
|
#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE
|
||||||
|
|
||||||
|
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = (void*)0
|
||||||
|
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = (void*)0
|
||||||
|
|
||||||
|
//! @defgroup bitwise Bitwise Inspection and Manipulation
|
||||||
|
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
|
||||||
|
//! Several macros have been constructed to assist with bitwise inspection and manipulation. These macros exist
|
||||||
|
//! for two primary purposes:
|
||||||
|
//!
|
||||||
|
//! 1. To improve the readability of bitwise comparisons and manipulation.
|
||||||
|
//!
|
||||||
|
//! The macro names are the more concise, readable form of what's being done and do not require that any flags
|
||||||
|
//! or variables be specified multiple times for the comparisons.
|
||||||
|
//!
|
||||||
|
//! 2. To reduce the error rate associated with bitwise operations.
|
||||||
|
//!
|
||||||
|
//! The readability improvements naturally lend themselves to this by cutting down the number of concepts.
|
||||||
|
//! Using `WI_IsFlagSet(var, MyEnum::Flag)` rather than `((var & MyEnum::Flag) == MyEnum::Flag)` removes the comparison
|
||||||
|
//! operator and repetition in the flag value.
|
||||||
|
//!
|
||||||
|
//! Additionally, these macros separate single flag operations (which tend to be the most common) from multi-flag
|
||||||
|
//! operations so that compile-time errors are generated for bitwise operations which are likely incorrect,
|
||||||
|
//! such as: `WI_IsFlagSet(var, MyEnum::None)` or `WI_IsFlagSet(var, MyEnum::ValidMask)`.
|
||||||
|
//!
|
||||||
|
//! Note that the single flag helpers should be used when a compile-time constant single flag is being manipulated. These
|
||||||
|
//! helpers provide compile-time errors on misuse and should be preferred over the multi-flag helpers. The multi-flag helpers
|
||||||
|
//! should be used when multiple flags are being used simultaneously or when the flag values are not compile-time constants.
|
||||||
|
//!
|
||||||
|
//! Common example usage (manipulation of flag variables):
|
||||||
|
//! ~~~~
|
||||||
|
//! WI_SetFlag(m_flags, MyFlags::Foo); // Set a single flag in the given variable
|
||||||
|
//! WI_SetAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Set one or more flags
|
||||||
|
//! WI_ClearFlagIf(m_flags, MyFlags::Bar, isBarClosed); // Conditionally clear a single flag based upon a bool
|
||||||
|
//! WI_ClearAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Clear one or more flags from the given variable
|
||||||
|
//! WI_ToggleFlag(m_flags, MyFlags::Foo); // Toggle (change to the opposite value) a single flag
|
||||||
|
//! WI_UpdateFlag(m_flags, MyFlags::Bar, isBarClosed); // Sets or Clears a single flag from the given variable based upon a bool value
|
||||||
|
//! WI_UpdateFlagsInMask(m_flags, flagsMask, newFlagValues); // Sets or Clears the flags in flagsMask to the masked values from newFlagValues
|
||||||
|
//! ~~~~
|
||||||
|
//! Common example usage (inspection of flag variables):
|
||||||
|
//! ~~~~
|
||||||
|
//! if (WI_IsFlagSet(m_flags, MyFlags::Foo)) // Is a single flag set in the given variable?
|
||||||
|
//! if (WI_IsAnyFlagSet(m_flags, MyFlags::Foo | MyFlags::Bar)) // Is at least one flag from the given mask set?
|
||||||
|
//! if (WI_AreAllFlagsClear(m_flags, MyFlags::Foo | MyFlags::Bar)) // Are all flags in the given list clear?
|
||||||
|
//! if (WI_IsSingleFlagSet(m_flags)) // Is *exactly* one flag set in the given variable?
|
||||||
|
//! ~~~~
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Returns the unsigned type of the same width and numeric value as the given enum
|
||||||
|
#define WI_EnumValue(val) static_cast<::wil::integral_from_enum<decltype(val)>>(val)
|
||||||
|
//! Validates that exactly ONE bit is set in compile-time constant `flag`
|
||||||
|
#define WI_StaticAssertSingleBitSet(flag) static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(WI_EnumValue(flag))>::value)
|
||||||
|
|
||||||
|
//! @name Bitwise manipulation macros
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Set zero or more bitflags specified by `flags` in the variable `var`.
|
||||||
|
#define WI_SetAllFlags(var, flags) ((var) |= (flags))
|
||||||
|
//! Set a single compile-time constant `flag` in the variable `var`.
|
||||||
|
#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||||
|
//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
|
||||||
|
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0)
|
||||||
|
|
||||||
|
//! Clear zero or more bitflags specified by `flags` from the variable `var`.
|
||||||
|
#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
|
||||||
|
//! Clear a single compile-time constant `flag` from the variable `var`.
|
||||||
|
#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||||
|
//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true.
|
||||||
|
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0)
|
||||||
|
|
||||||
|
//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false.
|
||||||
|
#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag))
|
||||||
|
//! Changes only the flags specified by `flagsMask` in the variable `var` to match the corresponding flags in `newFlags`.
|
||||||
|
#define WI_UpdateFlagsInMask(var, flagsMask, newFlags) wil::details::UpdateFlagsInMaskHelper(var, flagsMask, newFlags)
|
||||||
|
|
||||||
|
//! Toggles (XOR the value) of multiple bitflags specified by `flags` in the variable `var`.
|
||||||
|
#define WI_ToggleAllFlags(var, flags) ((var) ^= (flags))
|
||||||
|
//! Toggles (XOR the value) of a single compile-time constant `flag` in the variable `var`.
|
||||||
|
#define WI_ToggleFlag(var, flag) WI_ToggleAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||||
|
//! @} // bitwise manipulation macros
|
||||||
|
|
||||||
|
//! @name Bitwise inspection macros
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
//! Evaluates as true if every bitflag specified in `flags` is set within `val`.
|
||||||
|
#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags)
|
||||||
|
//! Evaluates as true if one or more bitflags specified in `flags` are set within `val`.
|
||||||
|
#define WI_IsAnyFlagSet(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(0))
|
||||||
|
//! Evaluates as true if a single compile-time constant `flag` is set within `val`.
|
||||||
|
#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag))
|
||||||
|
|
||||||
|
//! Evaluates as true if every bitflag specified in `flags` is clear within `val`.
|
||||||
|
#define WI_AreAllFlagsClear(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(0))
|
||||||
|
//! Evaluates as true if one or more bitflags specified in `flags` are clear within `val`.
|
||||||
|
#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags))
|
||||||
|
//! Evaluates as true if a single compile-time constant `flag` is clear within `val`.
|
||||||
|
#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag))
|
||||||
|
|
||||||
|
//! Evaluates as true if exactly one bit (any bit) is set within `val`.
|
||||||
|
#define WI_IsSingleFlagSet(val) wil::details::IsSingleFlagSetHelper(val)
|
||||||
|
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val`.
|
||||||
|
#define WI_IsSingleFlagSetInMask(val, mask) wil::details::IsSingleFlagSetHelper((val) & (mask))
|
||||||
|
//! Evaluates as true if exactly one bit (any bit) is set within `val` or if there are no bits set within `val`.
|
||||||
|
#define WI_IsClearOrSingleFlagSet(val) wil::details::IsClearOrSingleFlagSetHelper(val)
|
||||||
|
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val` or if there are no bits from `mask` set within `val`.
|
||||||
|
#define WI_IsClearOrSingleFlagSetInMask(val, mask) wil::details::IsClearOrSingleFlagSetHelper((val) & (mask))
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
#if defined(WIL_DOXYGEN)
|
||||||
|
/** This macro provides a C++ header with a guaranteed initialization function.
|
||||||
|
Normally, were a global object's constructor used for this purpose, the optimizer/linker might throw
|
||||||
|
the object away if it's unreferenced (which throws away the side-effects that the initialization function
|
||||||
|
was trying to achieve). Using this macro forces linker inclusion of a variable that's initialized by the
|
||||||
|
provided function to elide that optimization.
|
||||||
|
//!
|
||||||
|
This functionality is primarily provided as a building block for header-based libraries (such as WIL)
|
||||||
|
to be able to layer additional functionality into other libraries by their mere inclusion. Alternative models
|
||||||
|
of initialization should be used whenever they are available.
|
||||||
|
~~~~
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, []
|
||||||
|
{
|
||||||
|
g_pfnGetModuleName = GetCurrentModuleName;
|
||||||
|
g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout;
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
~~~~
|
||||||
|
The above example is used within WIL to decide whether or not the library containing WIL is allowed to use
|
||||||
|
desktop APIs. Building this functionality as #IFDEFs within functions would create ODR violations, whereas
|
||||||
|
doing it with global function pointers and header initialization allows a runtime determination. */
|
||||||
|
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn)
|
||||||
|
#elif defined(_M_IX86)
|
||||||
|
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
|
||||||
|
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
|
||||||
|
__pragma(comment(linker, "/INCLUDE:_g_header_init_" #name))
|
||||||
|
#elif defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64)
|
||||||
|
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
|
||||||
|
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
|
||||||
|
__pragma(comment(linker, "/INCLUDE:g_header_init_" #name))
|
||||||
|
#else
|
||||||
|
#error linker pragma must include g_header_init variation
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** All Windows Implementation Library classes and functions are located within the "wil" namespace.
|
||||||
|
The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference
|
||||||
|
the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using
|
||||||
|
statement for wil to avoid introducing potential name collisions between wil and other namespaces. */
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class pointer_range
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_) {}
|
||||||
|
T begin() const { return m_begin; }
|
||||||
|
T end() const { return m_end; }
|
||||||
|
private:
|
||||||
|
T m_begin;
|
||||||
|
T m_end;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/** Enables using range-based for between a begin and end object pointer.
|
||||||
|
~~~~
|
||||||
|
for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { }
|
||||||
|
~~~~ */
|
||||||
|
template <typename T>
|
||||||
|
details::pointer_range<T> make_range(T begin, T end)
|
||||||
|
{
|
||||||
|
return details::pointer_range<T>(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Enables using range-based for on a range when given the base pointer and the number of objects in the range.
|
||||||
|
~~~~
|
||||||
|
for (auto& obj : make_range(objPointer, objCount)) { }
|
||||||
|
~~~~ */
|
||||||
|
template <typename T>
|
||||||
|
details::pointer_range<T> make_range(T begin, size_t count)
|
||||||
|
{
|
||||||
|
return details::pointer_range<T>(begin, begin + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! @defgroup outparam Output Parameters
|
||||||
|
//! Improve the conciseness of assigning values to optional output parameters.
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
/** Assign the given value to an optional output parameter.
|
||||||
|
Makes code more concise by removing trivial `if (outParam)` blocks. */
|
||||||
|
template <typename T>
|
||||||
|
inline void assign_to_opt_param(_Out_opt_ T *outParam, T val)
|
||||||
|
{
|
||||||
|
if (outParam != nullptr)
|
||||||
|
{
|
||||||
|
*outParam = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Assign NULL to an optional output pointer parameter.
|
||||||
|
Makes code more concise by removing trivial `if (outParam)` blocks. */
|
||||||
|
template <typename T>
|
||||||
|
inline void assign_null_to_opt_param(_Out_opt_ T *outParam)
|
||||||
|
{
|
||||||
|
if (outParam != nullptr)
|
||||||
|
{
|
||||||
|
*outParam = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! @} // end output parameter helpers
|
||||||
|
|
||||||
|
/** Performs a logical or of the given variadic template parameters allowing indirect compile-time boolean evaluation.
|
||||||
|
Example usage:
|
||||||
|
~~~~
|
||||||
|
template <unsigned int... Rest>
|
||||||
|
struct FeatureRequiredBy
|
||||||
|
{
|
||||||
|
static const bool enabled = wil::variadic_logical_or<WilFeature<Rest>::enabled...>::value;
|
||||||
|
};
|
||||||
|
~~~~ */
|
||||||
|
template <bool...> struct variadic_logical_or;
|
||||||
|
/// @cond
|
||||||
|
template <> struct variadic_logical_or<> : wistd::false_type { };
|
||||||
|
template <bool... Rest> struct variadic_logical_or<true, Rest...> : wistd::true_type { };
|
||||||
|
template <bool... Rest> struct variadic_logical_or<false, Rest...> : variadic_logical_or<Rest...>::type { };
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template <unsigned long long flag>
|
||||||
|
struct verify_single_flag_helper
|
||||||
|
{
|
||||||
|
static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found");
|
||||||
|
static const unsigned long long value = flag;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
|
||||||
|
//! @defgroup typesafety Type Validation
|
||||||
|
//! Helpers to validate variable types to prevent accidental, but allowed type conversions.
|
||||||
|
//! These helpers are most useful when building macros that accept a particular type. Putting these functions around the types accepted
|
||||||
|
//! prior to pushing that type through to a function (or using it within the macro) allows the macro to add an additional layer of type
|
||||||
|
//! safety that would ordinarily be stripped away by C++ implicit conversions. This system is extensively used in the error handling helper
|
||||||
|
//! macros to validate the types given to various macro parameters.
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
/** Verify that `val` can be evaluated as a logical bool.
|
||||||
|
Other types will generate an intentional compilation error. Allowed types for a logical bool are bool, BOOL,
|
||||||
|
boolean, BOOLEAN, and classes with an explicit bool cast.
|
||||||
|
@param val The logical bool expression
|
||||||
|
@return A C++ bool representing the evaluation of `val`. */
|
||||||
|
template <typename T, __R_ENABLE_IF_IS_CLASS(T)>
|
||||||
|
_Post_satisfies_(return == static_cast<bool>(val))
|
||||||
|
__forceinline constexpr bool verify_bool(const T& val)
|
||||||
|
{
|
||||||
|
return static_cast<bool>(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
|
||||||
|
__forceinline constexpr bool verify_bool(T /*val*/)
|
||||||
|
{
|
||||||
|
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
_Post_satisfies_(return == val)
|
||||||
|
__forceinline constexpr bool verify_bool<bool>(bool val)
|
||||||
|
{
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
_Post_satisfies_(return == (val != 0))
|
||||||
|
__forceinline constexpr bool verify_bool<int>(int val)
|
||||||
|
{
|
||||||
|
return (val != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
_Post_satisfies_(return == !!val)
|
||||||
|
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
|
||||||
|
{
|
||||||
|
return !!val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify that `val` is a Win32 BOOL value.
|
||||||
|
Other types (including other logical bool expressions) will generate an intentional compilation error. Note that this will
|
||||||
|
accept any `int` value as long as that is the underlying typedef behind `BOOL`.
|
||||||
|
@param val The Win32 BOOL returning expression
|
||||||
|
@return A Win32 BOOL representing the evaluation of `val`. */
|
||||||
|
template <typename T>
|
||||||
|
_Post_satisfies_(return == val)
|
||||||
|
__forceinline constexpr int verify_BOOL(T val)
|
||||||
|
{
|
||||||
|
// Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL;
|
||||||
|
static_assert((wistd::is_same<T, int>::value), "Wrong Type: BOOL expected");
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify that `hr` is an HRESULT value.
|
||||||
|
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
|
||||||
|
underlying typedef behind HRESULT.
|
||||||
|
//!
|
||||||
|
Note that occasionally you might run into an HRESULT which is directly defined with a #define, such as:
|
||||||
|
~~~~
|
||||||
|
#define UIA_E_NOTSUPPORTED 0x80040204
|
||||||
|
~~~~
|
||||||
|
Though this looks like an `HRESULT`, this is actually an `unsigned long` (the hex specification forces this). When
|
||||||
|
these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change
|
||||||
|
their definition to match the manner in which `HRESULT` constants are defined in winerror.h:
|
||||||
|
~~~~
|
||||||
|
#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L)
|
||||||
|
~~~~
|
||||||
|
When these are encountered in the public SDK, their type should not be changed and you should use a static_cast
|
||||||
|
to use this value in a macro that utilizes `verify_hresult`, for example:
|
||||||
|
~~~~
|
||||||
|
RETURN_HR_IF(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId));
|
||||||
|
~~~~
|
||||||
|
@param val The HRESULT returning expression
|
||||||
|
@return An HRESULT representing the evaluation of `val`. */
|
||||||
|
template <typename T>
|
||||||
|
_Post_satisfies_(return == hr)
|
||||||
|
inline constexpr long verify_hresult(T hr)
|
||||||
|
{
|
||||||
|
// Note: Written in terms of 'long' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT
|
||||||
|
static_assert(wistd::is_same<T, long>::value, "Wrong Type: HRESULT expected");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify that `status` is an NTSTATUS value.
|
||||||
|
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
|
||||||
|
underlying typedef behind NTSTATUS.
|
||||||
|
//!
|
||||||
|
Note that occasionally you might run into an NTSTATUS which is directly defined with a #define, such as:
|
||||||
|
~~~~
|
||||||
|
#define STATUS_NOT_SUPPORTED 0x1
|
||||||
|
~~~~
|
||||||
|
Though this looks like an `NTSTATUS`, this is actually an `unsigned long` (the hex specification forces this). When
|
||||||
|
these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change
|
||||||
|
their definition to match the manner in which `NTSTATUS` constants are defined in ntstatus.h:
|
||||||
|
~~~~
|
||||||
|
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
||||||
|
~~~~
|
||||||
|
When these are encountered in the public SDK, their type should not be changed and you should use a static_cast
|
||||||
|
to use this value in a macro that utilizes `verify_ntstatus`, for example:
|
||||||
|
~~~~
|
||||||
|
NT_RETURN_IF_FALSE(static_cast<NTSTATUS>(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0));
|
||||||
|
~~~~
|
||||||
|
@param val The NTSTATUS returning expression
|
||||||
|
@return An NTSTATUS representing the evaluation of `val`. */
|
||||||
|
template <typename T>
|
||||||
|
_Post_satisfies_(return == status)
|
||||||
|
inline long verify_ntstatus(T status)
|
||||||
|
{
|
||||||
|
// Note: Written in terms of 'long' as NTSTATUS is actually: typedef _Return_type_success_(return >= 0) long NTSTATUS
|
||||||
|
static_assert(wistd::is_same<T, long>::value, "Wrong Type: NTSTATUS expected");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
/// @} // end type validation routines
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
// Implementation details for macros and helper functions... do not use directly.
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value
|
||||||
|
#define __WI_MAKE_UNSIGNED(val) \
|
||||||
|
(__pragma(warning(push)) __pragma(warning(disable: 4310 4309)) (sizeof(val) == 1 ? static_cast<unsigned char>(val) : \
|
||||||
|
sizeof(val) == 2 ? static_cast<unsigned short>(val) : \
|
||||||
|
sizeof(val) == 4 ? static_cast<unsigned long>(val) : \
|
||||||
|
static_cast<unsigned long long>(val)) __pragma(warning(pop)))
|
||||||
|
#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
|
||||||
|
#define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))
|
||||||
|
|
||||||
|
template <typename TVal, typename TFlags>
|
||||||
|
__forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
|
||||||
|
{
|
||||||
|
return ((val & flags) == static_cast<decltype(val & flags)>(flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TVal>
|
||||||
|
__forceinline constexpr bool IsSingleFlagSetHelper(TVal val)
|
||||||
|
{
|
||||||
|
return __WI_IS_SINGLE_FLAG_SET(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TVal>
|
||||||
|
__forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)
|
||||||
|
{
|
||||||
|
return ((val == static_cast<wistd::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TVal, typename TMask, typename TFlags>
|
||||||
|
__forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags)
|
||||||
|
{
|
||||||
|
val = static_cast<wistd::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <long>
|
||||||
|
struct variable_size;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct variable_size<1>
|
||||||
|
{
|
||||||
|
typedef unsigned char type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct variable_size<2>
|
||||||
|
{
|
||||||
|
typedef unsigned short type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct variable_size<4>
|
||||||
|
{
|
||||||
|
typedef unsigned long type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct variable_size<8>
|
||||||
|
{
|
||||||
|
typedef unsigned long long type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct variable_size_mapping
|
||||||
|
{
|
||||||
|
typedef typename variable_size<sizeof(T)>::type type;
|
||||||
|
};
|
||||||
|
} // details
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type.
|
||||||
|
This allows code to generically convert any enum class to it's corresponding underlying type. */
|
||||||
|
template <typename T>
|
||||||
|
using integral_from_enum = typename details::variable_size_mapping<T>::type;
|
||||||
|
} // wil
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
#endif // __cplusplus
|
||||||
|
#endif // __WIL_COMMON_INCLUDED
|
319
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt.h
vendored
Normal file
319
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt.h
vendored
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_CPPWINRT_INCLUDED
|
||||||
|
#define __WIL_CPPWINRT_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <unknwn.h>
|
||||||
|
#include <inspectable.h>
|
||||||
|
#include <hstring.h>
|
||||||
|
|
||||||
|
// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
|
||||||
|
// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to
|
||||||
|
// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below -
|
||||||
|
// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global
|
||||||
|
// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and
|
||||||
|
// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined.
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace wil::details
|
||||||
|
{
|
||||||
|
// Since the C++/WinRT version macro is a string...
|
||||||
|
// For example: "2.0.210122.3"
|
||||||
|
inline constexpr int version_from_string(const char* versionString)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
auto str = versionString;
|
||||||
|
while ((*str >= '0') && (*str <= '9'))
|
||||||
|
{
|
||||||
|
result = result * 10 + (*str - '0');
|
||||||
|
++str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr int major_version_from_string(const char* versionString)
|
||||||
|
{
|
||||||
|
return version_from_string(versionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr int minor_version_from_string(const char* versionString)
|
||||||
|
{
|
||||||
|
auto str = versionString;
|
||||||
|
int dotCount = 0;
|
||||||
|
while ((*str != '\0'))
|
||||||
|
{
|
||||||
|
if (*str == '.')
|
||||||
|
{
|
||||||
|
++dotCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
++str;
|
||||||
|
if (dotCount == 2)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*str == '\0')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return version_from_string(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
#ifdef CPPWINRT_VERSION
|
||||||
|
// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of
|
||||||
|
// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer
|
||||||
|
// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0
|
||||||
|
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
|
||||||
|
"Please include wil/cppwinrt.h before including any C++/WinRT headers");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed
|
||||||
|
#ifdef WINRT_EXTERNAL_CATCH_CLAUSE
|
||||||
|
#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1
|
||||||
|
#else
|
||||||
|
#define WINRT_EXTERNAL_CATCH_CLAUSE \
|
||||||
|
catch (const wil::ResultException& e) \
|
||||||
|
{ \
|
||||||
|
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "result_macros.h"
|
||||||
|
#include <winrt/base.h>
|
||||||
|
|
||||||
|
#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE
|
||||||
|
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
|
||||||
|
"C++/WinRT external catch clause already defined outside of WIL");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid
|
||||||
|
// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to
|
||||||
|
// use it unless the version of C++/WinRT is high enough
|
||||||
|
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;
|
||||||
|
|
||||||
|
// The same is true with this function pointer as well, except that the version must be 2.X or higher.
|
||||||
|
extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept;
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace wil::details
|
||||||
|
{
|
||||||
|
inline void MaybeGetExceptionString(
|
||||||
|
const winrt::hresult_error& exception,
|
||||||
|
_Out_writes_opt_(debugStringChars) PWSTR debugString,
|
||||||
|
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars)
|
||||||
|
{
|
||||||
|
if (debugString)
|
||||||
|
{
|
||||||
|
StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline HRESULT __stdcall ResultFromCaughtException_CppWinRt(
|
||||||
|
_Inout_updates_opt_(debugStringChars) PWSTR debugString,
|
||||||
|
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars,
|
||||||
|
_Inout_ bool* isNormalized) noexcept
|
||||||
|
{
|
||||||
|
if (g_pfnResultFromCaughtException)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (const ResultException& exception)
|
||||||
|
{
|
||||||
|
*isNormalized = true;
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return exception.GetErrorCode();
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return exception.to_abi();
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
catch (const std::out_of_range& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return E_BOUNDS;
|
||||||
|
}
|
||||||
|
catch (const std::invalid_argument& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (const ResultException& exception)
|
||||||
|
{
|
||||||
|
*isNormalized = true;
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return exception.GetErrorCode();
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return exception.to_abi();
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
catch (const std::out_of_range& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return E_BOUNDS;
|
||||||
|
}
|
||||||
|
catch (const std::invalid_argument& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return E_INVALIDARG;
|
||||||
|
}
|
||||||
|
catch (const std::exception& exception)
|
||||||
|
{
|
||||||
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// Fall through to returning 'S_OK' below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the caller that we were unable to map the exception by succeeding...
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept
|
||||||
|
{
|
||||||
|
// C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't
|
||||||
|
// have accurate file/line/etc. information
|
||||||
|
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void __stdcall winrt_throw_hresult(uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept
|
||||||
|
{
|
||||||
|
void* callerReturnAddress{nullptr}; PCSTR code{nullptr};
|
||||||
|
wil::details::ReportFailure_Hr<FailureType::Log>(__R_FN_CALL_FULL __R_COMMA result);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void WilInitialize_CppWinRT()
|
||||||
|
{
|
||||||
|
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
|
||||||
|
if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2)
|
||||||
|
{
|
||||||
|
WI_ASSERT(winrt_to_hresult_handler == nullptr);
|
||||||
|
winrt_to_hresult_handler = winrt_to_hresult;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr ((details::major_version_from_string(CPPWINRT_VERSION) >= 2) && (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122))
|
||||||
|
{
|
||||||
|
WI_ASSERT(winrt_throw_hresult_handler == nullptr);
|
||||||
|
winrt_throw_hresult_handler = winrt_throw_hresult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
|
||||||
|
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
|
||||||
|
WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, []
|
||||||
|
{
|
||||||
|
::wil::WilInitialize_CppWinRT();
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
// Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type.
|
||||||
|
inline long verify_hresult(winrt::hresult hr) noexcept
|
||||||
|
{
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience.
|
||||||
|
template <typename T>
|
||||||
|
auto get_abi(T const& object) noexcept
|
||||||
|
{
|
||||||
|
return winrt::get_abi(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto get_abi(winrt::hstring const& object) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<HSTRING>(winrt::get_abi(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto put_abi(T& object) noexcept
|
||||||
|
{
|
||||||
|
return winrt::put_abi(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto put_abi(winrt::hstring& object) noexcept
|
||||||
|
{
|
||||||
|
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<::IUnknown*>(winrt::get_abi(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed to power wil::cx_object_from_abi that requires IInspectable
|
||||||
|
inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<::IInspectable*>(winrt::get_abi(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from the docs.microsoft.com article
|
||||||
|
template <typename T>
|
||||||
|
T convert_from_abi(::IUnknown* from)
|
||||||
|
{
|
||||||
|
T to{ nullptr }; // `T` is a projected type.
|
||||||
|
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __WIL_CPPWINRT_INCLUDED
|
74
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt_wrl.h
vendored
Normal file
74
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt_wrl.h
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_CPPWINRT_WRL_INCLUDED
|
||||||
|
#define __WIL_CPPWINRT_WRL_INCLUDED
|
||||||
|
|
||||||
|
#include "cppwinrt.h"
|
||||||
|
#include <winrt\base.h>
|
||||||
|
|
||||||
|
#include "result_macros.h"
|
||||||
|
#include <wrl\module.h>
|
||||||
|
|
||||||
|
// wil::wrl_factory_for_winrt_com_class provides interopability between a
|
||||||
|
// C++/WinRT class and the WRL Module system, allowing the winrt class to be
|
||||||
|
// CoCreatable.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// - In your cpp, add:
|
||||||
|
// CoCreatableCppWinRtClass(className)
|
||||||
|
//
|
||||||
|
// - In the dll.cpp (or equivalent) for the module containing your class, add:
|
||||||
|
// CoCreatableClassWrlCreatorMapInclude(className)
|
||||||
|
//
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template <typename TCppWinRTClass>
|
||||||
|
class module_count_wrapper : public TCppWinRTClass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
module_count_wrapper()
|
||||||
|
{
|
||||||
|
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||||
|
{
|
||||||
|
modulePtr->IncrementObjectCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~module_count_wrapper()
|
||||||
|
{
|
||||||
|
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||||
|
{
|
||||||
|
modulePtr->DecrementObjectCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TCppWinRTClass>
|
||||||
|
class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try
|
||||||
|
{
|
||||||
|
*object = nullptr;
|
||||||
|
RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr);
|
||||||
|
|
||||||
|
return winrt::make<details::module_count_wrapper<TCppWinRTClass>>().as(riid, object);
|
||||||
|
}
|
||||||
|
CATCH_RETURN()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class<className>)
|
||||||
|
|
||||||
|
#endif // __WIL_CPPWINRT_WRL_INCLUDED
|
1016
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/filesystem.h
vendored
Normal file
1016
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/filesystem.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
168
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/nt_result_macros.h
vendored
Normal file
168
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/nt_result_macros.h
vendored
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_NT_RESULTMACROS_INCLUDED
|
||||||
|
#define __WIL_NT_RESULTMACROS_INCLUDED
|
||||||
|
|
||||||
|
#include "result_macros.h"
|
||||||
|
|
||||||
|
// Helpers for return macros
|
||||||
|
#define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } while ((void)0, 0)
|
||||||
|
#define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return __status; } while ((void)0, 0)
|
||||||
|
|
||||||
|
//*****************************************************************************
|
||||||
|
// Macros for returning failures as NTSTATUS
|
||||||
|
//*****************************************************************************
|
||||||
|
|
||||||
|
// Always returns a known result (NTSTATUS) - always logs failures
|
||||||
|
#define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status)
|
||||||
|
|
||||||
|
// Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure
|
||||||
|
#define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
// Conditionally returns failures (NTSTATUS) - always logs failures
|
||||||
|
#define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
|
||||||
|
// Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure
|
||||||
|
#define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0)
|
||||||
|
|
||||||
|
//*****************************************************************************
|
||||||
|
// Macros to catch and convert exceptions on failure
|
||||||
|
//*****************************************************************************
|
||||||
|
|
||||||
|
// Use these macros *within* a catch (...) block to handle exceptions
|
||||||
|
#define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr))
|
||||||
|
#define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
// Use these macros in place of a catch block to handle exceptions
|
||||||
|
#define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); }
|
||||||
|
#define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); }
|
||||||
|
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
//*****************************************************************************
|
||||||
|
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
|
||||||
|
//*****************************************************************************
|
||||||
|
|
||||||
|
// StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally
|
||||||
|
// it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type
|
||||||
|
// the function will fail fast.
|
||||||
|
//
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// // Code
|
||||||
|
// }
|
||||||
|
// catch (...)
|
||||||
|
// {
|
||||||
|
// status = wil::StatusFromCaughtException();
|
||||||
|
// }
|
||||||
|
_Always_(_Post_satisfies_(return < 0))
|
||||||
|
__declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
bool isNormalized = false;
|
||||||
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
|
if (details::g_pfnResultFromCaughtExceptionInternal)
|
||||||
|
{
|
||||||
|
status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status;
|
||||||
|
}
|
||||||
|
if (FAILED_NTSTATUS(status))
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caller bug: an unknown exception was thrown
|
||||||
|
__WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions);
|
||||||
|
return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template<FailureType>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default);
|
||||||
|
template<FailureType>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList);
|
||||||
|
|
||||||
|
namespace __R_NS_NAME
|
||||||
|
{
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
__R_FN_LOCALS;
|
||||||
|
return wil::details::ReportStatus_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
va_list argList;
|
||||||
|
va_start(argList, formatString);
|
||||||
|
__R_FN_LOCALS;
|
||||||
|
return wil::details::ReportStatus_CaughtExceptionMsg<FailureType::Return>(__R_DIRECT_FN_CALL formatString, argList);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<FailureType T>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||||
|
{
|
||||||
|
wchar_t message[2048];
|
||||||
|
message[0] = L'\0';
|
||||||
|
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::FailFast>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||||
|
{
|
||||||
|
wchar_t message[2048];
|
||||||
|
message[0] = L'\0';
|
||||||
|
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::Exception>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||||
|
{
|
||||||
|
wchar_t message[2048];
|
||||||
|
message[0] = L'\0';
|
||||||
|
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<FailureType T>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
|
||||||
|
{
|
||||||
|
// Pre-populate the buffer with our message, the exception message will be added to it...
|
||||||
|
wchar_t message[2048];
|
||||||
|
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
|
||||||
|
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
|
||||||
|
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::FailFast>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
|
||||||
|
{
|
||||||
|
// Pre-populate the buffer with our message, the exception message will be added to it...
|
||||||
|
wchar_t message[2048];
|
||||||
|
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
|
||||||
|
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
|
||||||
|
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::Exception>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
|
||||||
|
{
|
||||||
|
// Pre-populate the buffer with our message, the exception message will be added to it...
|
||||||
|
wchar_t message[2048];
|
||||||
|
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
|
||||||
|
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
|
||||||
|
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __WIL_NT_RESULTMACROS_INCLUDED
|
277
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/registry.h
vendored
Normal file
277
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/registry.h
vendored
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_REGISTRY_INCLUDED
|
||||||
|
#define __WIL_REGISTRY_INCLUDED
|
||||||
|
|
||||||
|
#ifdef _KERNEL_MODE
|
||||||
|
#error This header is not supported in kernel-mode.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <winreg.h>
|
||||||
|
#include <new.h> // new(std::nothrow)
|
||||||
|
#include "resource.h" // unique_hkey
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
//! The key name includes the absolute path of the key in the registry, always starting at a
|
||||||
|
//! base key, for example, HKEY_LOCAL_MACHINE.
|
||||||
|
size_t const max_registry_key_name_length = 255;
|
||||||
|
|
||||||
|
//! The maximum number of characters allowed in a registry value's name.
|
||||||
|
size_t const max_registry_value_name_length = 16383;
|
||||||
|
|
||||||
|
// unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast
|
||||||
|
// These classes make it easy to execute a provided function when a
|
||||||
|
// registry key changes (optionally recursively). Specify the key
|
||||||
|
// either as a root key + path, or an open registry handle as wil::unique_hkey
|
||||||
|
// or a raw HKEY value (that will be duplicated).
|
||||||
|
//
|
||||||
|
// Example use with exceptions base error handling:
|
||||||
|
// auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[]
|
||||||
|
// {
|
||||||
|
// if (changeKind == RegistryChangeKind::Delete)
|
||||||
|
// {
|
||||||
|
// watcher.reset();
|
||||||
|
// }
|
||||||
|
// // invalidate cached registry data here
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// Example use with error code base error handling:
|
||||||
|
// auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[]
|
||||||
|
// {
|
||||||
|
// // invalidate cached registry data here
|
||||||
|
// });
|
||||||
|
// RETURN_IF_NULL_ALLOC(watcher);
|
||||||
|
|
||||||
|
enum class RegistryChangeKind
|
||||||
|
{
|
||||||
|
Modify = 0,
|
||||||
|
Delete = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
struct registry_watcher_state
|
||||||
|
{
|
||||||
|
registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
: m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
wistd::function<void(RegistryChangeKind)> m_callback;
|
||||||
|
unique_hkey m_keyToWatch;
|
||||||
|
unique_event_nothrow m_eventHandle;
|
||||||
|
|
||||||
|
// While not strictly needed since this is ref counted the thread pool wait
|
||||||
|
// should be last to ensure that the other members are valid
|
||||||
|
// when it is destructed as it will reference them.
|
||||||
|
unique_threadpool_wait m_threadPoolWait;
|
||||||
|
bool m_isRecursive;
|
||||||
|
|
||||||
|
volatile long m_refCount = 1;
|
||||||
|
srwlock m_lock;
|
||||||
|
|
||||||
|
// Returns true if the refcount can be increased from a non zero value,
|
||||||
|
// false it was zero impling that the object is in or on the way to the destructor.
|
||||||
|
// In this case ReleaseFromCallback() should not be called.
|
||||||
|
bool TryAddRef()
|
||||||
|
{
|
||||||
|
return ::InterlockedIncrement(&m_refCount) > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Release()
|
||||||
|
{
|
||||||
|
auto lock = m_lock.lock_exclusive();
|
||||||
|
if (0 == ::InterlockedDecrement(&m_refCount))
|
||||||
|
{
|
||||||
|
lock.reset(); // leave the lock before deleting it.
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseFromCallback(bool rearm)
|
||||||
|
{
|
||||||
|
auto lock = m_lock.lock_exclusive();
|
||||||
|
if (0 == ::InterlockedDecrement(&m_refCount))
|
||||||
|
{
|
||||||
|
// Destroy the thread pool wait now to avoid the wait that would occur in the
|
||||||
|
// destructor. That wait would cause a deadlock since we are doing this from the callback.
|
||||||
|
::CloseThreadpoolWait(m_threadPoolWait.release());
|
||||||
|
lock.reset(); // leave the lock before deleting it.
|
||||||
|
delete this;
|
||||||
|
// Sleep(1); // Enable for testing to find use after free bugs.
|
||||||
|
}
|
||||||
|
else if (rearm)
|
||||||
|
{
|
||||||
|
::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); }
|
||||||
|
|
||||||
|
typedef resource_policy<registry_watcher_state *, decltype(&details::delete_registry_watcher_state),
|
||||||
|
details::delete_registry_watcher_state, details::pointer_access_none> registry_watcher_state_resource_policy;
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
template <typename storage_t, typename err_policy = err_exception_policy>
|
||||||
|
class registry_watcher_t : public storage_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// forward all base class constructors...
|
||||||
|
template <typename... args_t>
|
||||||
|
explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
|
||||||
|
|
||||||
|
// HRESULT or void error handling...
|
||||||
|
typedef typename err_policy::result result;
|
||||||
|
|
||||||
|
// Exception-based constructors
|
||||||
|
registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||||
|
create(rootKey, subKey, isRecursive, wistd::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||||
|
create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.
|
||||||
|
result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
// Most use will want to create the key, consider adding an option for open as a future design change.
|
||||||
|
unique_hkey keyToWatch;
|
||||||
|
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
return err_policy::HResult(hr);
|
||||||
|
}
|
||||||
|
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
|
||||||
|
}
|
||||||
|
|
||||||
|
result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
|
||||||
|
// to __stdcall
|
||||||
|
static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT)
|
||||||
|
{
|
||||||
|
#ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST
|
||||||
|
#define __WIL_REGISTRY_CHANGE_CALLBACK_TEST
|
||||||
|
#endif
|
||||||
|
__WIL_REGISTRY_CHANGE_CALLBACK_TEST
|
||||||
|
auto watcherState = static_cast<details::registry_watcher_state *>(context);
|
||||||
|
if (watcherState->TryAddRef())
|
||||||
|
{
|
||||||
|
// using auto reset event so don't need to manually reset.
|
||||||
|
|
||||||
|
// failure here is a programming error.
|
||||||
|
const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive,
|
||||||
|
REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
|
||||||
|
watcherState->m_eventHandle.get(), TRUE);
|
||||||
|
|
||||||
|
// Call the client before re-arming to ensure that multiple callbacks don't
|
||||||
|
// run concurrently.
|
||||||
|
switch (error)
|
||||||
|
{
|
||||||
|
case ERROR_SUCCESS:
|
||||||
|
case ERROR_ACCESS_DENIED:
|
||||||
|
// Normal modification: send RegistryChangeKind::Modify and re-arm.
|
||||||
|
watcherState->m_callback(RegistryChangeKind::Modify);
|
||||||
|
watcherState->ReleaseFromCallback(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ERROR_KEY_DELETED:
|
||||||
|
// Key deleted, send RegistryChangeKind::Delete, do not re-arm.
|
||||||
|
watcherState->m_callback(RegistryChangeKind::Delete);
|
||||||
|
watcherState->ReleaseFromCallback(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ERROR_HANDLE_REVOKED:
|
||||||
|
// Handle revoked. This can occur if the user session ends before
|
||||||
|
// the watcher shuts-down. Disarm silently since there is generally no way to respond.
|
||||||
|
watcherState->ReleaseFromCallback(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
FAIL_FAST_HR(HRESULT_FROM_WIN32(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function exists to avoid template expansion of this code based on err_policy.
|
||||||
|
HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
wistd::unique_ptr<details::registry_watcher_state> watcherState(new(std::nothrow) details::registry_watcher_state(
|
||||||
|
wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
|
||||||
|
RETURN_IF_NULL_ALLOC(watcherState);
|
||||||
|
RETURN_IF_FAILED(watcherState->m_eventHandle.create());
|
||||||
|
RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(),
|
||||||
|
watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
|
||||||
|
watcherState->m_eventHandle.get(), TRUE));
|
||||||
|
|
||||||
|
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(®istry_watcher_t::callback, watcherState.get(), nullptr));
|
||||||
|
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
|
||||||
|
storage_t::reset(watcherState.release()); // no more failures after this, pass ownership
|
||||||
|
SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_returncode_policy>> unique_registry_watcher_nothrow;
|
||||||
|
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_failfast_policy>> unique_registry_watcher_failfast;
|
||||||
|
|
||||||
|
inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
unique_registry_watcher_nothrow watcher;
|
||||||
|
watcher.create(rootKey, subKey, isRecursive, wistd::move(callback));
|
||||||
|
return watcher; // caller must test for success using if (watcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
unique_registry_watcher_nothrow watcher;
|
||||||
|
watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
|
||||||
|
return watcher; // caller must test for success using if (watcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_exception_policy >> unique_registry_watcher;
|
||||||
|
|
||||||
|
inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||||
|
{
|
||||||
|
return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
|
||||||
|
}
|
||||||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
} // namespace wil
|
||||||
|
|
||||||
|
#endif
|
6473
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/resource.h
vendored
Normal file
6473
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/resource.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
1274
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result.h
vendored
Normal file
1274
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
6147
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_macros.h
vendored
Normal file
6147
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_macros.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
125
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_originate.h
vendored
Normal file
125
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_originate.h
vendored
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//*********************************************************
|
||||||
|
|
||||||
|
// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
|
||||||
|
// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match,
|
||||||
|
// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the
|
||||||
|
// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors
|
||||||
|
// simply because the HRESULTs match.
|
||||||
|
//
|
||||||
|
// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is
|
||||||
|
// caught and re-thrown.
|
||||||
|
//
|
||||||
|
// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions
|
||||||
|
// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must
|
||||||
|
// capture the entire stack and some additional data.
|
||||||
|
|
||||||
|
#ifndef __WIL_RESULT_ORIGINATE_INCLUDED
|
||||||
|
#define __WIL_RESULT_ORIGINATE_INCLUDED
|
||||||
|
|
||||||
|
#include "result.h"
|
||||||
|
#include <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
|
||||||
|
#include "resource.h"
|
||||||
|
#include "com.h"
|
||||||
|
#include <roerrorapi.h>
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame.
|
||||||
|
inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception))
|
||||||
|
{
|
||||||
|
bool shouldOriginate = true;
|
||||||
|
|
||||||
|
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
|
||||||
|
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
|
||||||
|
{
|
||||||
|
// This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are
|
||||||
|
// observing right now.
|
||||||
|
wil::unique_bstr descriptionUnused;
|
||||||
|
HRESULT existingHr = failure.hr;
|
||||||
|
wil::unique_bstr restrictedDescriptionUnused;
|
||||||
|
wil::unique_bstr capabilitySidUnused;
|
||||||
|
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)))
|
||||||
|
{
|
||||||
|
shouldOriginate = (failure.hr != existingHr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldOriginate)
|
||||||
|
{
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||||
|
wil::unique_hmodule errorModule;
|
||||||
|
if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule))
|
||||||
|
{
|
||||||
|
auto pfn = reinterpret_cast<decltype(&::RoOriginateError)>(GetProcAddress(errorModule.get(), "RoOriginateError"));
|
||||||
|
if (pfn != nullptr)
|
||||||
|
{
|
||||||
|
pfn(failure.hr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else // DESKTOP | SYSTEM
|
||||||
|
::RoOriginateError(failure.hr, nullptr);
|
||||||
|
#endif // DESKTOP | SYSTEM
|
||||||
|
}
|
||||||
|
else if (restrictedErrorInformation)
|
||||||
|
{
|
||||||
|
// GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present,
|
||||||
|
// then we need to restore the error information for later observation.
|
||||||
|
SetRestrictedErrorInfo(restrictedErrorInformation.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT
|
||||||
|
// matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will
|
||||||
|
// result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and
|
||||||
|
// the calling method fails fast the same way it always has.
|
||||||
|
inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
|
||||||
|
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
|
||||||
|
{
|
||||||
|
wil::unique_bstr descriptionUnused;
|
||||||
|
HRESULT existingHr = failure.hr;
|
||||||
|
wil::unique_bstr restrictedDescriptionUnused;
|
||||||
|
wil::unique_bstr capabilitySidUnused;
|
||||||
|
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) &&
|
||||||
|
(existingHr == failure.hr))
|
||||||
|
{
|
||||||
|
// GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext
|
||||||
|
// so we must restore it via SetRestrictedErrorInfo first.
|
||||||
|
SetRestrictedErrorInfo(restrictedErrorInformation.get());
|
||||||
|
RoFailFastWithErrorContext(existingHr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast
|
||||||
|
// in this method, so it is available in the debugger just-in-case.
|
||||||
|
SetRestrictedErrorInfo(restrictedErrorInformation.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
} // namespace wil
|
||||||
|
|
||||||
|
// Automatically call RoOriginateError upon error origination by including this file
|
||||||
|
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
|
||||||
|
{
|
||||||
|
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
|
||||||
|
::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback);
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
#endif // __WIL_RESULT_ORIGINATE_INCLUDED
|
206
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/rpc_helpers.h
vendored
Normal file
206
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/rpc_helpers.h
vendored
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_RPC_HELPERS_INCLUDED
|
||||||
|
#define __WIL_RPC_HELPERS_INCLUDED
|
||||||
|
|
||||||
|
#include "result.h"
|
||||||
|
#include "resource.h"
|
||||||
|
#include "wistd_functional.h"
|
||||||
|
#include "wistd_type_traits.h"
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// This call-adapter template converts a void-returning 'wistd::invoke' into
|
||||||
|
// an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
|
||||||
|
// with 'if constexpr' when C++17 is in wide use.
|
||||||
|
template<typename TReturnType> struct call_adapter
|
||||||
|
{
|
||||||
|
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
|
||||||
|
{
|
||||||
|
return wistd::invoke(wistd::forward<TArgs>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct call_adapter<void>
|
||||||
|
{
|
||||||
|
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
|
||||||
|
{
|
||||||
|
wistd::invoke(wistd::forward<TArgs>(args)...);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Some RPC exceptions are already HRESULTs. Others are in the regular Win32
|
||||||
|
// error space. If the incoming exception code isn't an HRESULT, wrap it.
|
||||||
|
constexpr HRESULT map_rpc_exception(DWORD code)
|
||||||
|
{
|
||||||
|
return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
|
||||||
|
Failures encountered by the RPC infrastructure (such as server crashes, authentication
|
||||||
|
errors, client parameter issues, etc.) are emitted by raising a structured exception from
|
||||||
|
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
|
||||||
|
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
|
||||||
|
flow control machinery to use.
|
||||||
|
|
||||||
|
Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
|
||||||
|
the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
|
||||||
|
returned as-is.
|
||||||
|
|
||||||
|
RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
|
||||||
|
completes successfully.
|
||||||
|
|
||||||
|
For example, consider an RPC interface method defined in idl as:
|
||||||
|
~~~
|
||||||
|
HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
|
||||||
|
~~~
|
||||||
|
To call this method, use:
|
||||||
|
~~~
|
||||||
|
wil::unique_rpc_binding binding = // typically gotten elsewhere;
|
||||||
|
wil::unique_midl_ptr<KittenState> state;
|
||||||
|
HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
|
||||||
|
RETURN_IF_FAILED(hr);
|
||||||
|
~~~
|
||||||
|
*/
|
||||||
|
template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
RpcTryExcept
|
||||||
|
{
|
||||||
|
// Note: this helper type can be removed with C++17 enabled via
|
||||||
|
// 'if constexpr(wistd::is_same_v<void, result_t>)'
|
||||||
|
using result_t = typename wistd::__invoke_of<TCall...>::type;
|
||||||
|
RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
|
||||||
|
{
|
||||||
|
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
|
||||||
|
}
|
||||||
|
RpcEndExcept
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
|
||||||
|
Failures encountered by the RPC infrastructure (such as server crashes, authentication
|
||||||
|
errors, client parameter issues, etc.) are emitted by raising a structured exception from
|
||||||
|
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
|
||||||
|
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
|
||||||
|
flow control machinery to use.
|
||||||
|
|
||||||
|
Some RPC methods return results (such as a state enumeration or other value) directly in
|
||||||
|
their signature. This adapter writes that result into a caller-provided object then
|
||||||
|
returns S_OK.
|
||||||
|
|
||||||
|
For example, consider an RPC interface method defined in idl as:
|
||||||
|
~~~
|
||||||
|
GUID GetKittenId([in, ref, string] const wchar_t* name);
|
||||||
|
~~~
|
||||||
|
To call this method, use:
|
||||||
|
~~~
|
||||||
|
wil::unique_rpc_binding binding = // typically gotten elsewhere;
|
||||||
|
GUID id;
|
||||||
|
HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
|
||||||
|
RETURN_IF_FAILED(hr);
|
||||||
|
~~~
|
||||||
|
*/
|
||||||
|
template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
RpcTryExcept
|
||||||
|
{
|
||||||
|
result = wistd::invoke(wistd::forward<TCall>(args)...);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
|
||||||
|
{
|
||||||
|
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
|
||||||
|
}
|
||||||
|
RpcEndExcept
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// Provides an adapter around calling the context-handle-close method on an
|
||||||
|
// RPC interface, which itself is an RPC call.
|
||||||
|
template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
|
||||||
|
struct rpc_closer_t
|
||||||
|
{
|
||||||
|
static void Close(TStorage arg) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Manages explicit RPC context handles
|
||||||
|
Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
|
||||||
|
context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
|
||||||
|
the server close out the context handle. As the close method itself is an RPC call,
|
||||||
|
it can fail and raise a structured exception.
|
||||||
|
|
||||||
|
This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
|
||||||
|
helper, ensuring correct cleanup and lifecycle management.
|
||||||
|
~~~
|
||||||
|
// Assume the interface has two methods:
|
||||||
|
// HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
|
||||||
|
// HRESULT UseFoo([in] FOO_CONTEXT context;
|
||||||
|
// void CloseFoo([in, out] PFOO_CONTEXT);
|
||||||
|
using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
|
||||||
|
unique_foo_context context;
|
||||||
|
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
|
||||||
|
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
|
||||||
|
context.reset();
|
||||||
|
~~~
|
||||||
|
*/
|
||||||
|
template<typename TContext, typename close_fn_t, close_fn_t close_fn>
|
||||||
|
using unique_rpc_context_handle = unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close>;
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
|
||||||
|
See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
|
||||||
|
and those returned by the _method_ are mapped to HRESULTs and thrown inside a
|
||||||
|
wil::ResultException. Using the example RPC method provided above:
|
||||||
|
~~~
|
||||||
|
wil::unique_midl_ptr<KittenState> state;
|
||||||
|
wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
|
||||||
|
// use 'state'
|
||||||
|
~~~
|
||||||
|
*/
|
||||||
|
template<typename... TCall> void invoke_rpc(TCall&& ... args)
|
||||||
|
{
|
||||||
|
THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
|
||||||
|
See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
|
||||||
|
_call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
|
||||||
|
example RPC method provided above:
|
||||||
|
~~~
|
||||||
|
GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
|
||||||
|
// use 'id'
|
||||||
|
~~~
|
||||||
|
*/
|
||||||
|
template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
|
||||||
|
{
|
||||||
|
using result_t = typename wistd::__invoke_of<TCall...>::type;
|
||||||
|
result_t result{};
|
||||||
|
THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
369
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/safecast.h
vendored
Normal file
369
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/safecast.h
vendored
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_SAFECAST_INCLUDED
|
||||||
|
#define __WIL_SAFECAST_INCLUDED
|
||||||
|
|
||||||
|
#include "result_macros.h"
|
||||||
|
#include <intsafe.h>
|
||||||
|
#include "wistd_config.h"
|
||||||
|
#include "wistd_type_traits.h"
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// Default error case for undefined conversions in intsafe.h
|
||||||
|
template<typename OldT, typename NewT> constexpr wistd::nullptr_t intsafe_conversion = nullptr;
|
||||||
|
|
||||||
|
// is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known
|
||||||
|
// safe conversions can be handled by static_cast, this includes conversions between the same
|
||||||
|
// type, when the new type is larger than the old type but is not a signed to unsigned
|
||||||
|
// conversion, and when the two types are the same size and signed/unsigned. All other
|
||||||
|
// conversions will be assumed to be potentially unsafe, and the conversion must be handled
|
||||||
|
// by intsafe and checked.
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_known_safe_static_cast_v =
|
||||||
|
(sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v<OldT> && wistd::is_unsigned_v<NewT>)) ||
|
||||||
|
(sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v<NewT> && wistd::is_signed_v<OldT>) || (wistd::is_unsigned_v<NewT> && wistd::is_unsigned_v<OldT>)));
|
||||||
|
|
||||||
|
// Helper template to determine that NewT and OldT are both integral types. The safe_cast
|
||||||
|
// operation only supports conversions between integral types.
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool both_integral_v = wistd::is_integral<NewT>::value && wistd::is_integral<OldT>::value;
|
||||||
|
|
||||||
|
// Note on native wchar_t (__wchar_t):
|
||||||
|
// Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is
|
||||||
|
// typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of
|
||||||
|
// support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an
|
||||||
|
// unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and
|
||||||
|
// share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast
|
||||||
|
// to a native wchar_t.
|
||||||
|
|
||||||
|
// Intsafe does not have a defined conversion for native wchar_t
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool neither_native_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
|
||||||
|
|
||||||
|
// Check to see if the cast is a conversion to native wchar_t
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_cast_to_wchar_v = wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
|
||||||
|
|
||||||
|
// Check to see if the cast is a conversion from native wchar_t
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_cast_from_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && wistd::is_same<OldT, __wchar_t>::value;
|
||||||
|
|
||||||
|
// Validate the conversion to be performed has a defined mapping to an intsafe conversion
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_supported_intsafe_cast_v = intsafe_conversion<OldT, NewT> != nullptr;
|
||||||
|
|
||||||
|
// True when the conversion is between integral types and can be handled by static_cast
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_supported_safe_static_cast_v = both_integral_v<NewT, OldT> && is_known_safe_static_cast_v<NewT, OldT>;
|
||||||
|
|
||||||
|
// True when the conversion is between integral types, does not involve native wchar, has
|
||||||
|
// a mapped intsafe conversion, and is unsafe.
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_supported_unsafe_cast_no_wchar_v =
|
||||||
|
both_integral_v<NewT, OldT> &&
|
||||||
|
!is_known_safe_static_cast_v<NewT, OldT> &&
|
||||||
|
neither_native_wchar_v<NewT, OldT> &&
|
||||||
|
is_supported_intsafe_cast_v<NewT, OldT>;
|
||||||
|
|
||||||
|
// True when the conversion is between integral types, is a cast to native wchar_t, has
|
||||||
|
// a mapped intsafe conversion, and is unsafe.
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_supported_unsafe_cast_to_wchar_v =
|
||||||
|
both_integral_v<NewT, OldT> &&
|
||||||
|
!is_known_safe_static_cast_v<NewT, OldT> &&
|
||||||
|
is_cast_to_wchar_v<NewT, OldT> &&
|
||||||
|
is_supported_intsafe_cast_v<unsigned short, OldT>;
|
||||||
|
|
||||||
|
// True when the conversion is between integral types, is a cast from native wchar_t, has
|
||||||
|
// a mapped intsafe conversion, and is unsafe.
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_supported_unsafe_cast_from_wchar_v =
|
||||||
|
both_integral_v<NewT, OldT> &&
|
||||||
|
!is_known_safe_static_cast_v<NewT, OldT> &&
|
||||||
|
is_cast_from_wchar_v<NewT, OldT> &&
|
||||||
|
is_supported_intsafe_cast_v<NewT, unsigned short>;
|
||||||
|
|
||||||
|
// True when the conversion is supported and unsafe, and may or may not involve
|
||||||
|
// native wchar_t.
|
||||||
|
template <typename NewT, typename OldT>
|
||||||
|
constexpr bool is_supported_unsafe_cast_v =
|
||||||
|
is_supported_unsafe_cast_no_wchar_v<NewT, OldT> ||
|
||||||
|
is_supported_unsafe_cast_to_wchar_v<NewT, OldT> ||
|
||||||
|
is_supported_unsafe_cast_from_wchar_v<NewT, OldT>;
|
||||||
|
|
||||||
|
// True when T is any one of the primitive types that the variably sized types are defined as.
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_potentially_variably_sized_type_v =
|
||||||
|
wistd::is_same<T, int>::value ||
|
||||||
|
wistd::is_same<T, unsigned int>::value ||
|
||||||
|
wistd::is_same<T, long>::value ||
|
||||||
|
wistd::is_same<T, unsigned long>::value ||
|
||||||
|
wistd::is_same<T, __int64>::value ||
|
||||||
|
wistd::is_same<T, unsigned __int64>::value;
|
||||||
|
|
||||||
|
// True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t)
|
||||||
|
template <typename OldT, typename NewT>
|
||||||
|
constexpr bool is_potentially_variably_sized_cast_v =
|
||||||
|
is_potentially_variably_sized_type_v<OldT> ||
|
||||||
|
is_potentially_variably_sized_type_v<NewT>;
|
||||||
|
|
||||||
|
// Mappings of all conversions defined in intsafe.h to intsafe_conversion
|
||||||
|
// Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve
|
||||||
|
// to the base types. The base types are used since they do not vary based on architecture.
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
|
||||||
|
template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, char> = IntToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, short> = IntToShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
|
||||||
|
template<> constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, char> = LongToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, int> = LongToInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, short> = LongToShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
|
||||||
|
template<> constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<short, char> = ShortToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
|
||||||
|
template<> constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
|
||||||
|
template<> constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
|
||||||
|
template<> constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where failure results in fail fast.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast_failfast(const OldT var)
|
||||||
|
{
|
||||||
|
NewT newVar;
|
||||||
|
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
|
||||||
|
return newVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where failure results in fail fast.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast_failfast(const OldT var)
|
||||||
|
{
|
||||||
|
NewT newVar;
|
||||||
|
FAIL_FAST_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
|
||||||
|
return newVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where failure results in fail fast.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast_failfast(const OldT var)
|
||||||
|
{
|
||||||
|
unsigned short newVar;
|
||||||
|
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
|
||||||
|
return static_cast<__wchar_t>(newVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This conversion is always safe, therefore a static_cast is fine.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast_failfast(const OldT var)
|
||||||
|
{
|
||||||
|
return static_cast<NewT>(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
// Unsafe conversion where failure results in a thrown exception.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast(const OldT var)
|
||||||
|
{
|
||||||
|
NewT newVar;
|
||||||
|
THROW_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
|
||||||
|
return newVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where failure results in a thrown exception.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast(const OldT var)
|
||||||
|
{
|
||||||
|
NewT newVar;
|
||||||
|
THROW_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
|
||||||
|
return newVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where failure results in a thrown exception.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast(const OldT var)
|
||||||
|
{
|
||||||
|
unsigned short newVar;
|
||||||
|
THROW_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
|
||||||
|
return static_cast<__wchar_t>(newVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This conversion is always safe, therefore a static_cast is fine.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast(const OldT var)
|
||||||
|
{
|
||||||
|
return static_cast<NewT>(var);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast_nothrow(const OldT /*var*/)
|
||||||
|
{
|
||||||
|
static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This conversion is always safe, therefore a static_cast is fine.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
NewT safe_cast_nothrow(const OldT var)
|
||||||
|
{
|
||||||
|
return static_cast<NewT>(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||||
|
{
|
||||||
|
return details::intsafe_conversion<OldT, NewT>(var, newTResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||||
|
{
|
||||||
|
return details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), newTResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||||
|
{
|
||||||
|
return details::intsafe_conversion<OldT, unsigned short>(var, reinterpret_cast<unsigned short *>(newTResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion
|
||||||
|
// does not involve a variably sized type, then the compilation will fail and say the single parameter version
|
||||||
|
// of safe_cast_nothrow should be used instead.
|
||||||
|
template <
|
||||||
|
typename NewT,
|
||||||
|
typename OldT,
|
||||||
|
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||||
|
>
|
||||||
|
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||||
|
{
|
||||||
|
static_assert(details::is_potentially_variably_sized_cast_v<OldT, NewT>, "This cast is always safe; use safe_cast_nothrow<T>(value) to avoid unnecessary error handling.");
|
||||||
|
*newTResult = static_cast<NewT>(var);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __WIL_SAFECAST_INCLUDED
|
116
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/stl.h
vendored
Normal file
116
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/stl.h
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_STL_INCLUDED
|
||||||
|
#define __WIL_STL_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "resource.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(WIL_ENABLE_EXCEPTIONS)
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
/** Secure allocator for STL containers.
|
||||||
|
The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating
|
||||||
|
memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`,
|
||||||
|
`wil::secure_string`, and `wil::secure_wstring`. */
|
||||||
|
template <typename T>
|
||||||
|
struct secure_allocator
|
||||||
|
: public std::allocator<T>
|
||||||
|
{
|
||||||
|
template<typename Other>
|
||||||
|
struct rebind
|
||||||
|
{
|
||||||
|
typedef secure_allocator<Other> other;
|
||||||
|
};
|
||||||
|
|
||||||
|
secure_allocator()
|
||||||
|
: std::allocator<T>()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~secure_allocator() = default;
|
||||||
|
|
||||||
|
secure_allocator(const secure_allocator& a)
|
||||||
|
: std::allocator<T>(a)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
secure_allocator(const secure_allocator<U>& a)
|
||||||
|
: std::allocator<T>(a)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T* allocate(size_t n)
|
||||||
|
{
|
||||||
|
return std::allocator<T>::allocate(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(T* p, size_t n)
|
||||||
|
{
|
||||||
|
SecureZeroMemory(p, sizeof(T) * n);
|
||||||
|
std::allocator<T>::deallocate(p, n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! `wil::secure_vector` will be securely zeroed before deallocation.
|
||||||
|
template <typename Type>
|
||||||
|
using secure_vector = std::vector<Type, secure_allocator<Type>>;
|
||||||
|
//! `wil::secure_wstring` will be securely zeroed before deallocation.
|
||||||
|
using secure_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, wil::secure_allocator<wchar_t>>;
|
||||||
|
//! `wil::secure_string` will be securely zeroed before deallocation.
|
||||||
|
using secure_string = std::basic_string<char, std::char_traits<char>, wil::secure_allocator<char>>;
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template<> struct string_maker<std::wstring>
|
||||||
|
{
|
||||||
|
HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try
|
||||||
|
{
|
||||||
|
m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0');
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t* buffer() { return &m_value[0]; }
|
||||||
|
|
||||||
|
HRESULT trim_at_existing_null(size_t length) { m_value.erase(length); return S_OK; }
|
||||||
|
|
||||||
|
std::wstring release() { return std::wstring(std::move(m_value)); }
|
||||||
|
|
||||||
|
static PCWSTR get(const std::wstring& value) { return value.c_str(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::wstring m_value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
// str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer.
|
||||||
|
// This is the overload for std::wstring. Other overloads available in resource.h.
|
||||||
|
inline PCWSTR str_raw_ptr(const std::wstring& str)
|
||||||
|
{
|
||||||
|
return str.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wil
|
||||||
|
|
||||||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
||||||
|
#endif // __WIL_STL_INCLUDED
|
601
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/token_helpers.h
vendored
Normal file
601
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/token_helpers.h
vendored
Normal file
|
@ -0,0 +1,601 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_TOKEN_HELPERS_INCLUDED
|
||||||
|
#define __WIL_TOKEN_HELPERS_INCLUDED
|
||||||
|
|
||||||
|
#ifdef _KERNEL_MODE
|
||||||
|
#error This header is not supported in kernel-mode.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "resource.h"
|
||||||
|
#include <new>
|
||||||
|
#include <lmcons.h> // for UNLEN and DNLEN
|
||||||
|
#include <processthreadsapi.h>
|
||||||
|
|
||||||
|
// for GetUserNameEx()
|
||||||
|
#define SECURITY_WIN32
|
||||||
|
#include <Security.h>
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed
|
||||||
|
// TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may
|
||||||
|
// be an info class value that uses the same structure. That is the case for the file
|
||||||
|
// system information.
|
||||||
|
template<typename T> struct MapTokenStructToInfoClass;
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; };
|
||||||
|
|
||||||
|
// fixed size cases
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; };
|
||||||
|
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; };
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
enum class OpenThreadTokenAs
|
||||||
|
{
|
||||||
|
Current,
|
||||||
|
Self
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Open the active token.
|
||||||
|
Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller
|
||||||
|
can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the
|
||||||
|
effective user.
|
||||||
|
|
||||||
|
Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information.
|
||||||
|
This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle
|
||||||
|
and much easier to manage.
|
||||||
|
~~~~
|
||||||
|
wil::unique_handle theToken;
|
||||||
|
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken));
|
||||||
|
~~~~
|
||||||
|
Callers who want more access to the token (such as to duplicate or modify the token) can pass
|
||||||
|
any mask of the token rights.
|
||||||
|
~~~~
|
||||||
|
wil::unique_handle theToken;
|
||||||
|
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES));
|
||||||
|
~~~~
|
||||||
|
Services impersonating their clients may need to request that the active token is opened on the
|
||||||
|
behalf of the service process to perform certain operations. Opening a token for impersonation access
|
||||||
|
or privilege-adjustment are examples of uses.
|
||||||
|
~~~~
|
||||||
|
wil::unique_handle callerToken;
|
||||||
|
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true));
|
||||||
|
~~~~
|
||||||
|
@param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or
|
||||||
|
(preferably) stored in a wil::unique_handle
|
||||||
|
@param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken
|
||||||
|
@param asSelf When true, and if the thread is impersonating, the thread token is opened using the
|
||||||
|
process token's rights.
|
||||||
|
*/
|
||||||
|
inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
||||||
|
{
|
||||||
|
HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
|
||||||
|
if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
|
||||||
|
{
|
||||||
|
hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
|
||||||
|
inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
||||||
|
{
|
||||||
|
HANDLE rawTokenHandle;
|
||||||
|
FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
|
||||||
|
return wil::unique_handle(rawTokenHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception based function to open current thread/process access token and acquire pointer to it
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
|
||||||
|
inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
||||||
|
{
|
||||||
|
HANDLE rawTokenHandle;
|
||||||
|
THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
|
||||||
|
return wil::unique_handle(rawTokenHandle);
|
||||||
|
}
|
||||||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
|
// Returns tokenHandle or the effective thread token if tokenHandle is null.
|
||||||
|
// Note, this returns an token handle who's lifetime is managed independently
|
||||||
|
// and it may be a pseudo token, don't free it!
|
||||||
|
inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle)
|
||||||
|
{
|
||||||
|
return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fetches information about a token.
|
||||||
|
See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information
|
||||||
|
is returned to the caller as a wistd::unique_ptr<T> (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For
|
||||||
|
fixed sized, the struct is returned directly.
|
||||||
|
The caller must have access to read the information from the provided token. This method works with both real
|
||||||
|
(e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles.
|
||||||
|
~~~~
|
||||||
|
// Retrieve the TOKEN_USER structure for the current process
|
||||||
|
wistd::unique_ptr<TOKEN_USER> user;
|
||||||
|
RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken()));
|
||||||
|
RETURN_IF_FAILED(ConsumeSid(user->User.Sid));
|
||||||
|
~~~~
|
||||||
|
Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token.
|
||||||
|
~~~~
|
||||||
|
wistd::unique_ptr<TOKEN_PRIVILEGES> privileges;
|
||||||
|
RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges));
|
||||||
|
for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount))
|
||||||
|
{
|
||||||
|
RETURN_IF_FAILED(ConsumePrivilege(privilege));
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
@param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested
|
||||||
|
type. The type of <T> selects which TOKEN_INFORMATION_CLASS will be used.
|
||||||
|
@param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used.
|
||||||
|
@return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename T, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||||
|
inline HRESULT get_token_information_nothrow(wistd::unique_ptr<T>& tokenInfo, HANDLE tokenHandle = nullptr)
|
||||||
|
{
|
||||||
|
tokenInfo.reset();
|
||||||
|
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
|
||||||
|
|
||||||
|
DWORD tokenInfoSize = 0;
|
||||||
|
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
|
||||||
|
RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) &&
|
||||||
|
(::GetLastError() == ERROR_INSUFFICIENT_BUFFER)));
|
||||||
|
wistd::unique_ptr<char> tokenInfoClose(
|
||||||
|
static_cast<char*>(operator new(tokenInfoSize, std::nothrow)));
|
||||||
|
RETURN_IF_NULL_ALLOC(tokenInfoClose.get());
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize));
|
||||||
|
tokenInfo.reset(reinterpret_cast<T *>(tokenInfoClose.release()));
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||||
|
inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr)
|
||||||
|
{
|
||||||
|
*tokenInfo = {};
|
||||||
|
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
|
||||||
|
|
||||||
|
DWORD tokenInfoSize = sizeof(T);
|
||||||
|
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize));
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template<typename T, typename policy, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||||
|
wistd::unique_ptr<T> GetTokenInfoWrap(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
wistd::unique_ptr<T> temp;
|
||||||
|
policy::HResult(get_token_information_nothrow(temp, token));
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename policy, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||||
|
T GetTokenInfoWrap(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
T temp{};
|
||||||
|
policy::HResult(get_token_information_nothrow(&temp, token));
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! A variant of get_token_information<T> that fails-fast on errors retrieving the token
|
||||||
|
template <typename T>
|
||||||
|
inline auto get_token_information_failfast(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
return details::GetTokenInfoWrap<T, err_failfast_policy>(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token
|
||||||
|
inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch");
|
||||||
|
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
|
||||||
|
|
||||||
|
DWORD tokenInfoSize = 0;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken,
|
||||||
|
tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize));
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Retrieves the linked-token information for a token.
|
||||||
|
Fails-fast if the link information cannot be retrieved.
|
||||||
|
~~~~
|
||||||
|
auto link = get_linked_token_information_failfast(GetCurrentThreadToken());
|
||||||
|
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
|
||||||
|
~~~~
|
||||||
|
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
|
||||||
|
@return unique_token_linked_token containing a handle to the linked token
|
||||||
|
*/
|
||||||
|
inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
unique_token_linked_token tokenInfo;
|
||||||
|
FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
|
||||||
|
return tokenInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
/** Fetches information about a token.
|
||||||
|
See get_token_information_nothrow for full details.
|
||||||
|
~~~~
|
||||||
|
auto user = wil::get_token_information<TOKEN_USER>(GetCurrentProcessToken());
|
||||||
|
ConsumeSid(user->User.Sid);
|
||||||
|
~~~~
|
||||||
|
Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token.
|
||||||
|
~~~~
|
||||||
|
auto privs = wil::get_token_information<TOKEN_PRIVILEGES>(privileges);
|
||||||
|
for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount))
|
||||||
|
{
|
||||||
|
if (priv.Attributes & SE_PRIVILEGE_ENABLED)
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
@return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of
|
||||||
|
<T> selects which TOKEN_INFORMATION_CLASS will be used.
|
||||||
|
@param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
inline auto get_token_information(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
return details::GetTokenInfoWrap<T, err_exception_policy>(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Retrieves the linked-token information for a token.
|
||||||
|
Throws an exception if the link information cannot be retrieved.
|
||||||
|
~~~~
|
||||||
|
auto link = get_linked_token_information(GetCurrentThreadToken());
|
||||||
|
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
|
||||||
|
~~~~
|
||||||
|
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
|
||||||
|
@return unique_token_linked_token containing a handle to the linked token
|
||||||
|
*/
|
||||||
|
inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
unique_token_linked_token tokenInfo;
|
||||||
|
THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
|
||||||
|
return tokenInfo;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
inline void RevertImpersonateToken(_In_ _Post_ptr_invalid_ HANDLE oldToken)
|
||||||
|
{
|
||||||
|
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
|
||||||
|
|
||||||
|
if (oldToken)
|
||||||
|
{
|
||||||
|
::CloseHandle(oldToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
using unique_token_reverter = wil::unique_any<
|
||||||
|
HANDLE,
|
||||||
|
decltype(&details::RevertImpersonateToken),
|
||||||
|
details::RevertImpersonateToken,
|
||||||
|
details::pointer_access_none,
|
||||||
|
HANDLE,
|
||||||
|
INT_PTR,
|
||||||
|
-1,
|
||||||
|
HANDLE>;
|
||||||
|
|
||||||
|
/** Temporarily impersonates a token on this thread.
|
||||||
|
This method sets a new token on a thread, restoring the current token when the returned object
|
||||||
|
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
|
||||||
|
~~~~
|
||||||
|
HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened)
|
||||||
|
{
|
||||||
|
wil::unique_handle userToken;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
|
||||||
|
|
||||||
|
wil::unique_token_reverter reverter;
|
||||||
|
RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter));
|
||||||
|
|
||||||
|
wil::unique_hfile userFile(::CreateFile(filePath, ...));
|
||||||
|
RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND));
|
||||||
|
|
||||||
|
*opened = userFile.release();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
@param token A token to impersonate, or 'nullptr' to run as the process identity.
|
||||||
|
*/
|
||||||
|
inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter)
|
||||||
|
{
|
||||||
|
wil::unique_handle currentToken;
|
||||||
|
|
||||||
|
// Get the token for the current thread. If there wasn't one, the reset will clear it as well
|
||||||
|
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, ¤tToken))
|
||||||
|
{
|
||||||
|
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the current token
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token));
|
||||||
|
|
||||||
|
reverter.reset(currentToken.release()); // Ownership passed
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Temporarily clears any impersonation on this thread.
|
||||||
|
This method resets the current thread's token to nullptr, indicating that it is not impersonating
|
||||||
|
any user. Useful for elevating to whatever identity a service or higher-privilege process might
|
||||||
|
be capable of running under.
|
||||||
|
~~~~
|
||||||
|
HRESULT DeleteFileRetryAsSelf(PCWSTR filePath)
|
||||||
|
{
|
||||||
|
if (!::DeleteFile(filePath))
|
||||||
|
{
|
||||||
|
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED);
|
||||||
|
wil::unique_token_reverter reverter;
|
||||||
|
RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter));
|
||||||
|
RETURN_IF_FAILED(TakeOwnershipOfFile(filePath));
|
||||||
|
RETURN_IF_FAILED(GrantDeleteAccess(filePath));
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath));
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
*/
|
||||||
|
inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter)
|
||||||
|
{
|
||||||
|
return impersonate_token_nothrow(nullptr, reverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unique_token_reverter impersonate_token_failfast(HANDLE token)
|
||||||
|
{
|
||||||
|
unique_token_reverter oldToken;
|
||||||
|
FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken));
|
||||||
|
return oldToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unique_token_reverter run_as_self_failfast()
|
||||||
|
{
|
||||||
|
return impersonate_token_failfast(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
/** Temporarily impersonates a token on this thread.
|
||||||
|
This method sets a new token on a thread, restoring the current token when the returned object
|
||||||
|
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
|
||||||
|
~~~~
|
||||||
|
wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session)
|
||||||
|
{
|
||||||
|
wil::unique_handle userToken;
|
||||||
|
THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
|
||||||
|
|
||||||
|
auto priorToken = wil::impersonate_token(userToken.get());
|
||||||
|
|
||||||
|
wil::unique_hfile userFile(::CreateFile(filePath, ...));
|
||||||
|
THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND);
|
||||||
|
|
||||||
|
return userFile;
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
@param token A token to impersonate, or 'nullptr' to run as the process identity.
|
||||||
|
*/
|
||||||
|
inline unique_token_reverter impersonate_token(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
unique_token_reverter oldToken;
|
||||||
|
THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken));
|
||||||
|
return oldToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Temporarily clears any impersonation on this thread.
|
||||||
|
This method resets the current thread's token to nullptr, indicating that it is not impersonating
|
||||||
|
any user. Useful for elevating to whatever identity a service or higher-privilege process might
|
||||||
|
be capable of running under.
|
||||||
|
~~~~
|
||||||
|
void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath)
|
||||||
|
{
|
||||||
|
if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED))
|
||||||
|
{
|
||||||
|
auto priorToken = wil::run_as_self();
|
||||||
|
TakeOwnershipOfFile(filePath);
|
||||||
|
GrantDeleteAccess(filePath);
|
||||||
|
::DeleteFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
*/
|
||||||
|
inline unique_token_reverter run_as_self()
|
||||||
|
{
|
||||||
|
return impersonate_token(nullptr);
|
||||||
|
}
|
||||||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template<size_t AuthorityCount> struct static_sid_t
|
||||||
|
{
|
||||||
|
BYTE Revision;
|
||||||
|
BYTE SubAuthorityCount;
|
||||||
|
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
|
||||||
|
DWORD SubAuthority[AuthorityCount];
|
||||||
|
|
||||||
|
PSID get()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<PSID>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t other> static_sid_t& operator=(const static_sid_t<other>& source)
|
||||||
|
{
|
||||||
|
static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one");
|
||||||
|
|
||||||
|
if (&this->Revision != &source.Revision)
|
||||||
|
{
|
||||||
|
memcpy(this, &source, sizeof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a structure containing a Revision 1 SID initialized with the authorities provided
|
||||||
|
Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but
|
||||||
|
returned like a value. The resulting object is suitable for use with any method taking PSID,
|
||||||
|
passed by "&the_sid" or via "the_sid.get()"
|
||||||
|
~~~~
|
||||||
|
// Change the owner of the key to administrators
|
||||||
|
auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
|
||||||
|
RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr));
|
||||||
|
~~~~
|
||||||
|
*/
|
||||||
|
template<typename... Ts> constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities)
|
||||||
|
{
|
||||||
|
using sid_t = details::static_sid_t<sizeof...(subAuthorities)>;
|
||||||
|
|
||||||
|
static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities");
|
||||||
|
static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch");
|
||||||
|
static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch");
|
||||||
|
static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch");
|
||||||
|
static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch");
|
||||||
|
|
||||||
|
return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast<DWORD>(subAuthorities)... } };
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Variant of static_sid that defaults to the NT authority
|
||||||
|
template<typename... Ts> constexpr auto make_static_nt_sid(Ts&& ... subAuthorities)
|
||||||
|
{
|
||||||
|
return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward<Ts>(subAuthorities)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Determines whether a specified security identifier (SID) is enabled in an access token.
|
||||||
|
This function determines whether a security identifier, described by a given set of subauthorities, is enabled
|
||||||
|
in the given access token. Note that only up to eight subauthorities can be passed to this function.
|
||||||
|
~~~~
|
||||||
|
bool IsGuest()
|
||||||
|
{
|
||||||
|
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS));
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
@param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token.
|
||||||
|
@param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership
|
||||||
|
uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token.
|
||||||
|
@param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID.
|
||||||
|
@param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit)
|
||||||
|
@return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise.
|
||||||
|
*/
|
||||||
|
template<typename... Ts> HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token,
|
||||||
|
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
|
||||||
|
{
|
||||||
|
*result = false;
|
||||||
|
auto tempSid = make_static_sid(sidAuthority, wistd::forward<Ts>(subAuthorities)...);
|
||||||
|
BOOL isMember;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember));
|
||||||
|
|
||||||
|
*result = (isMember != FALSE);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
|
/** Determine whether a token represents an app container
|
||||||
|
This method uses the passed in token and emits a boolean indicating that
|
||||||
|
whether TokenIsAppContainer is true.
|
||||||
|
~~~~
|
||||||
|
HRESULT OnlyIfAppContainer()
|
||||||
|
{
|
||||||
|
bool isAppContainer;
|
||||||
|
RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer));
|
||||||
|
RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer);
|
||||||
|
RETURN_HR(...);
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
@param token A token to get info about, or 'nullptr' to run as the current thread.
|
||||||
|
*/
|
||||||
|
inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value)
|
||||||
|
{
|
||||||
|
DWORD isAppContainer = 0;
|
||||||
|
DWORD returnLength = 0;
|
||||||
|
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(
|
||||||
|
token ? token : GetCurrentThreadEffectiveToken(),
|
||||||
|
TokenIsAppContainer,
|
||||||
|
&isAppContainer,
|
||||||
|
sizeof(isAppContainer),
|
||||||
|
&returnLength));
|
||||||
|
|
||||||
|
value = (isAppContainer != 0);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information
|
||||||
|
inline bool get_token_is_app_container_failfast(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
bool value = false;
|
||||||
|
FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value));
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
//! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information
|
||||||
|
inline bool get_token_is_app_container(HANDLE token = nullptr)
|
||||||
|
{
|
||||||
|
bool value = false;
|
||||||
|
THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value));
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||||
|
|
||||||
|
template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
|
||||||
|
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
template<typename... Ts> bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority,
|
||||||
|
Ts&&... subAuthorities)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} //namespace wil
|
||||||
|
|
||||||
|
#endif // __WIL_TOKEN_HELPERS_INCLUDED
|
606
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/win32_helpers.h
vendored
Normal file
606
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/win32_helpers.h
vendored
Normal file
|
@ -0,0 +1,606 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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 <objbase.h>
|
||||||
|
|
||||||
|
// detect std::bit_cast
|
||||||
|
#ifdef __has_include
|
||||||
|
# if __has_include(<bit>)
|
||||||
|
# include <bit>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cpp_lib_bit_cast >= 201806L
|
||||||
|
# define __WI_CONSTEXPR_BIT_CAST constexpr
|
||||||
|
#else
|
||||||
|
# define __WI_CONSTEXPR_BIT_CAST inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if __cpp_lib_bit_cast >= 201806L
|
||||||
|
return std::bit_cast<unsigned long long>(ft);
|
||||||
|
#else
|
||||||
|
// Cannot reinterpret_cast FILETIME* to unsigned long long*
|
||||||
|
// due to alignment differences.
|
||||||
|
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__WI_CONSTEXPR_BIT_CAST FILETIME from_int64(unsigned long long i64) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if __cpp_lib_bit_cast >= 201806L
|
||||||
|
return std::bit_cast<FILETIME>(i64);
|
||||||
|
#else
|
||||||
|
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);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const &ft, long long delta100ns) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
return from_int64(to_int64(ft) + delta100ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool is_empty(const FILETIME &ft) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FILETIME get_system_time() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
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) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
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) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
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() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
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() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
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) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 success, return the amount used; on failure, try doubling
|
||||||
|
*valueLengthNeededWithNul = success ? (lengthToUse + 1) : (lengthToUse * 2);
|
||||||
|
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, size_t initialBufferLength = 128>
|
||||||
|
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(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, size_t initialBufferLength = 128>
|
||||||
|
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
const auto hr = wil::GetEnvironmentVariableW<string_type, initialBufferLength>(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) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||||||
|
{
|
||||||
|
DWORD copiedCount;
|
||||||
|
size_t valueUsedWithNul;
|
||||||
|
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. The
|
||||||
|
// count copied to the buffer includes the nul-character as well.
|
||||||
|
copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast<DWORD>(valueLength));
|
||||||
|
valueUsedWithNul = copiedCount + 1;
|
||||||
|
copyFailed = (0 == copiedCount);
|
||||||
|
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
|
||||||
|
// and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include
|
||||||
|
// the nul-character
|
||||||
|
copiedCount = ::GetModuleFileNameW(module, value, static_cast<DWORD>(valueLength));
|
||||||
|
valueUsedWithNul = copiedCount + 1;
|
||||||
|
copyFailed = (0 == copiedCount);
|
||||||
|
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_LAST_ERROR_IF(copyFailed);
|
||||||
|
|
||||||
|
// When the copy truncated, request another try with more space.
|
||||||
|
*valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(path, wistd::move(adapter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
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, size_t initialBufferLength = 128>
|
||||||
|
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
|
||||||
|
{
|
||||||
|
string_type result;
|
||||||
|
THROW_IF_FAILED((wil::GetEnvironmentVariableW<string_type, initialBufferLength>(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, size_t initialBufferLength = 128>
|
||||||
|
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
|
||||||
|
{
|
||||||
|
string_type result;
|
||||||
|
THROW_IF_FAILED((wil::TryGetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||||
|
string_type GetModuleFileNameW(HMODULE module)
|
||||||
|
{
|
||||||
|
string_type result;
|
||||||
|
THROW_IF_FAILED((wil::GetModuleFileNameW<string_type, initialBufferLength>(module, result)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||||
|
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
|
||||||
|
{
|
||||||
|
string_type result;
|
||||||
|
THROW_IF_FAILED((wil::GetModuleFileNameExW<string_type, initialBufferLength>(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() WI_NOEXCEPT { 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) WI_NOEXCEPT : m_once(once)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||||
|
void success() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
m_flags = 0;
|
||||||
|
}
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
~init_once_completer() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
::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
|
2293
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/winrt.h
vendored
Normal file
2293
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/winrt.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
548
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_config.h
vendored
Normal file
548
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_config.h
vendored
Normal file
|
@ -0,0 +1,548 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===--------------------------- __config ---------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// STL common functionality
|
||||||
|
//
|
||||||
|
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
|
||||||
|
// of whether exceptions are enabled in the component. Common library code that expects to be used
|
||||||
|
// from exception-free components want these concepts, but including STL headers directly introduces
|
||||||
|
// friction as it requires components not using STL to declare their STL version. Doing so creates
|
||||||
|
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
|
||||||
|
// a long list of headers (including <new>) which can create further ambiguity around throwing new
|
||||||
|
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
|
||||||
|
// the potential to create naming conflicts or other implied dependencies.
|
||||||
|
//
|
||||||
|
// To promote the use of these core language concepts outside of STL-based binaries, this file is
|
||||||
|
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
|
||||||
|
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
|
||||||
|
// STL. The implementation and naming of all functions are taken directly from STL, instead using
|
||||||
|
// "wistd" (Windows Implementation std) as the namespace.
|
||||||
|
//
|
||||||
|
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
|
||||||
|
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
|
||||||
|
//
|
||||||
|
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
|
||||||
|
// Only code that is not exception-based and libraries that expect to be utilized across both exception
|
||||||
|
// and non-exception based code should utilize this functionality.
|
||||||
|
|
||||||
|
// This header mimics libc++'s '__config' header to the extent necessary to get the wistd::* definitions compiling. Note
|
||||||
|
// that this has a few key differences since libc++'s MSVC compatability is currently not functional and a bit behind
|
||||||
|
|
||||||
|
#ifndef _WISTD_CONFIG_H_
|
||||||
|
#define _WISTD_CONFIG_H_
|
||||||
|
|
||||||
|
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
|
||||||
|
#include <cstddef> // For size_t and other necessary types
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__)
|
||||||
|
# if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||||
|
# define __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
|
||||||
|
#pragma GCC system_header
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
# define __WI_GNUC_VER (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||||
|
// The __WI_GNUC_VER_NEW macro better represents the new GCC versioning scheme
|
||||||
|
// introduced in GCC 5.0.
|
||||||
|
# define __WI_GNUC_VER_NEW (__WI_GNUC_VER * 10 + __GNUC_PATCHLEVEL__)
|
||||||
|
#else
|
||||||
|
# define __WI_GNUC_VER 0
|
||||||
|
# define __WI_GNUC_VER_NEW 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// _MSVC_LANG is the more accurate way to get the C++ version in MSVC
|
||||||
|
#if defined(_MSVC_LANG) && (_MSVC_LANG > __cplusplus)
|
||||||
|
#define __WI_CPLUSPLUS _MSVC_LANG
|
||||||
|
#else
|
||||||
|
#define __WI_CPLUSPLUS __cplusplus
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __WI_LIBCPP_STD_VER
|
||||||
|
# if __WI_CPLUSPLUS <= 201103L
|
||||||
|
# define __WI_LIBCPP_STD_VER 11
|
||||||
|
# elif __WI_CPLUSPLUS <= 201402L
|
||||||
|
# define __WI_LIBCPP_STD_VER 14
|
||||||
|
# elif __WI_CPLUSPLUS <= 201703L
|
||||||
|
# define __WI_LIBCPP_STD_VER 17
|
||||||
|
# else
|
||||||
|
# define __WI_LIBCPP_STD_VER 18 // current year, or date of c++2a ratification
|
||||||
|
# endif
|
||||||
|
#endif // __WI_LIBCPP_STD_VER
|
||||||
|
|
||||||
|
#if __WI_CPLUSPLUS < 201103L
|
||||||
|
#define __WI_LIBCPP_CXX03_LANG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ELF__)
|
||||||
|
# define __WI_LIBCPP_OBJECT_FORMAT_ELF 1
|
||||||
|
#elif defined(__MACH__)
|
||||||
|
# define __WI_LIBCPP_OBJECT_FORMAT_MACHO 1
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
# define __WI_LIBCPP_OBJECT_FORMAT_COFF 1
|
||||||
|
#elif defined(__wasm__)
|
||||||
|
# define __WI_LIBCPP_OBJECT_FORMAT_WASM 1
|
||||||
|
#else
|
||||||
|
# error Unknown object file format
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# define __WI_LIBCPP_COMPILER_CLANG
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# define __WI_LIBCPP_COMPILER_GCC
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# define __WI_LIBCPP_COMPILER_MSVC
|
||||||
|
#elif defined(__IBMCPP__)
|
||||||
|
# define __WI_LIBCPP_COMPILER_IBM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as
|
||||||
|
// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we
|
||||||
|
// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls
|
||||||
|
// back to the __has_feature(...), etc. value otherwise. We intentionally leave '__has_feature', etc. undefined for MSVC
|
||||||
|
// so that we don't accidentally use the incorrect behavior
|
||||||
|
#ifndef __WI_LIBCPP_COMPILER_MSVC
|
||||||
|
|
||||||
|
#ifndef __has_feature
|
||||||
|
#define __has_feature(__x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// '__is_identifier' returns '0' if '__x' is a reserved identifier provided by
|
||||||
|
// the compiler and '1' otherwise.
|
||||||
|
#ifndef __is_identifier
|
||||||
|
#define __is_identifier(__x) 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __has_cpp_attribute
|
||||||
|
#define __has_cpp_attribute(__x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __has_attribute
|
||||||
|
#define __has_attribute(__x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __has_builtin
|
||||||
|
#define __has_builtin(__x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(cxx_alignas)
|
||||||
|
# define __WI_ALIGNAS_TYPE(x) alignas(x)
|
||||||
|
# define __WI_ALIGNAS(x) alignas(x)
|
||||||
|
#else
|
||||||
|
# define __WI_ALIGNAS_TYPE(x) __attribute__((__aligned__(__alignof(x))))
|
||||||
|
# define __WI_ALIGNAS(x) __attribute__((__aligned__(x)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(cxx_explicit_conversions) || defined(__IBMCPP__) || \
|
||||||
|
(!defined(__WI_LIBCPP_CXX03_LANG) && defined(__GNUC__)) // All supported GCC versions
|
||||||
|
# define __WI_LIBCPP_EXPLICIT explicit
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_EXPLICIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(cxx_attributes)
|
||||||
|
# define __WI_LIBCPP_NORETURN [[noreturn]]
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_NORETURN __attribute__ ((noreturn))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
|
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||||
|
|
||||||
|
// The __WI_LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other
|
||||||
|
// NODISCARD macros to the correct attribute.
|
||||||
|
#if __has_cpp_attribute(nodiscard)
|
||||||
|
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
|
||||||
|
#elif defined(__WI_LIBCPP_COMPILER_CLANG) && !defined(__WI_LIBCPP_CXX03_LANG)
|
||||||
|
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[clang::warn_unused_result]]
|
||||||
|
#else
|
||||||
|
// We can't use GCC's [[gnu::warn_unused_result]] and
|
||||||
|
// __attribute__((warn_unused_result)), because GCC does not silence them via
|
||||||
|
// (void) cast.
|
||||||
|
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __WI_HAS_FEATURE_IS_UNION __has_feature(is_union)
|
||||||
|
#define __WI_HAS_FEATURE_IS_CLASS __has_feature(is_class)
|
||||||
|
#define __WI_HAS_FEATURE_IS_ENUM __has_feature(is_enum)
|
||||||
|
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO __has_feature(is_convertible_to)
|
||||||
|
#define __WI_HAS_FEATURE_IS_EMPTY __has_feature(is_empty)
|
||||||
|
#define __WI_HAS_FEATURE_IS_POLYMORPHIC __has_feature(is_polymorphic)
|
||||||
|
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR __has_feature(has_virtual_destructor)
|
||||||
|
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions)
|
||||||
|
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE __has_feature(is_constructible)
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE __has_feature(is_trivially_constructible)
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE __has_feature(is_trivially_assignable)
|
||||||
|
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR __has_feature(has_trivial_destructor)
|
||||||
|
#define __WI_HAS_FEATURE_NOEXCEPT __has_feature(cxx_noexcept)
|
||||||
|
#define __WI_HAS_FEATURE_IS_POD __has_feature(is_pod)
|
||||||
|
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT __has_feature(is_standard_layout)
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE __has_feature(is_trivially_copyable)
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIAL __has_feature(is_trivial)
|
||||||
|
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR __has_feature(has_trivial_constructor) || (__WI_GNUC_VER >= 403)
|
||||||
|
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR __has_feature(has_nothrow_constructor) || (__WI_GNUC_VER >= 403)
|
||||||
|
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY __has_feature(has_nothrow_copy) || (__WI_GNUC_VER >= 403)
|
||||||
|
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN __has_feature(has_nothrow_assign) || (__WI_GNUC_VER >= 403)
|
||||||
|
|
||||||
|
#if !(__has_feature(cxx_noexcept))
|
||||||
|
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !__is_identifier(__has_unique_object_representations) || __WI_GNUC_VER >= 700
|
||||||
|
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(__has_feature(cxx_variadic_templates))
|
||||||
|
#define __WI_LIBCPP_HAS_NO_VARIADICS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(is_literal) || __WI_GNUC_VER >= 407
|
||||||
|
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal(T)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(underlying_type) || __WI_GNUC_VER >= 407
|
||||||
|
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(is_final) || __WI_GNUC_VER >= 407
|
||||||
|
#define __WI_LIBCPP_HAS_IS_FINAL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(is_base_of) || defined(__GNUC__) && __WI_GNUC_VER >= 403
|
||||||
|
#define __WI_LIBCPP_HAS_IS_BASE_OF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __is_identifier(__is_aggregate) && (__WI_GNUC_VER_NEW < 7001)
|
||||||
|
#define __WI_LIBCPP_HAS_NO_IS_AGGREGATE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(__has_feature(cxx_rtti)) && !defined(__WI_LIBCPP_NO_RTTI)
|
||||||
|
#define __WI_LIBCPP_NO_RTTI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(__has_feature(cxx_variable_templates))
|
||||||
|
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(__has_feature(cxx_relaxed_constexpr))
|
||||||
|
#define __WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !__has_builtin(__builtin_addressof) && _GNUC_VER < 700
|
||||||
|
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_attribute(__no_sanitize__) && !defined(__WI_LIBCPP_COMPILER_GCC)
|
||||||
|
# define __WI_LIBCPP_NO_CFI __attribute__((__no_sanitize__("cfi")))
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_NO_CFI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_ALWAYS_INLINE __attribute__ ((__always_inline__))
|
||||||
|
|
||||||
|
#if __has_attribute(internal_linkage)
|
||||||
|
# define __WI_LIBCPP_INTERNAL_LINKAGE __attribute__ ((internal_linkage))
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_INTERNAL_LINKAGE __WI_LIBCPP_ALWAYS_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// NOTE: Much of the following assumes a decently recent version of MSVC. Past versions can be supported, but will need
|
||||||
|
// to be updated to contain the proper _MSC_VER check
|
||||||
|
#define __WI_ALIGNAS_TYPE(x) alignas(x)
|
||||||
|
#define __WI_ALIGNAS(x) alignas(x)
|
||||||
|
#define __alignof__ __alignof
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_EXPLICIT explicit
|
||||||
|
#define __WI_LIBCPP_NORETURN [[noreturn]]
|
||||||
|
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
|
||||||
|
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
|
||||||
|
|
||||||
|
|
||||||
|
#if __WI_LIBCPP_STD_VER > 14
|
||||||
|
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
|
||||||
|
#else
|
||||||
|
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE _Check_return_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __WI_HAS_FEATURE_IS_UNION 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_CLASS 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_ENUM 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_EMPTY 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_POLYMORPHIC 1
|
||||||
|
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR 1
|
||||||
|
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
|
||||||
|
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE 1
|
||||||
|
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR 1
|
||||||
|
#define __WI_HAS_FEATURE_NOEXCEPT 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_POD 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_TRIVIAL 1
|
||||||
|
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR 1
|
||||||
|
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR 1
|
||||||
|
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY 1
|
||||||
|
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN 1
|
||||||
|
#define __WI_HAS_FEATURE_IS_DESTRUCTIBLE 1
|
||||||
|
|
||||||
|
#if !defined(_CPPRTTI) && !defined(__WI_LIBCPP_NO_RTTI)
|
||||||
|
#define __WI_LIBCPP_NO_RTTI
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal_type(T)
|
||||||
|
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
|
||||||
|
#define __WI_LIBCPP_HAS_IS_FINAL
|
||||||
|
#define __WI_LIBCPP_HAS_IS_BASE_OF
|
||||||
|
|
||||||
|
#if __WI_LIBCPP_STD_VER < 14
|
||||||
|
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
|
||||||
|
#define __WI_LIBCPP_NO_CFI
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_ALWAYS_INLINE __forceinline
|
||||||
|
#define __WI_LIBCPP_INTERNAL_LINKAGE
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
#ifdef __LITTLE_ENDIAN__
|
||||||
|
# if __LITTLE_ENDIAN__
|
||||||
|
# define __WI_LIBCPP_LITTLE_ENDIAN
|
||||||
|
# endif // __LITTLE_ENDIAN__
|
||||||
|
#endif // __LITTLE_ENDIAN__
|
||||||
|
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
# if __BIG_ENDIAN__
|
||||||
|
# define __WI_LIBCPP_BIG_ENDIAN
|
||||||
|
# endif // __BIG_ENDIAN__
|
||||||
|
#endif // __BIG_ENDIAN__
|
||||||
|
|
||||||
|
#ifdef __BYTE_ORDER__
|
||||||
|
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
# define __WI_LIBCPP_LITTLE_ENDIAN
|
||||||
|
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
# define __WI_LIBCPP_BIG_ENDIAN
|
||||||
|
# endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
#endif // __BYTE_ORDER__
|
||||||
|
|
||||||
|
#if !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
|
||||||
|
# include <endian.h>
|
||||||
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
# define __WI_LIBCPP_LITTLE_ENDIAN
|
||||||
|
# elif __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
# define __WI_LIBCPP_BIG_ENDIAN
|
||||||
|
# else // __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
# error unable to determine endian
|
||||||
|
# endif
|
||||||
|
#endif // !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
|
||||||
|
|
||||||
|
#else // _WIN32
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_LITTLE_ENDIAN
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#ifdef __WI_LIBCPP_HAS_NO_CONSTEXPR
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR constexpr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 constexpr
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 constexpr
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __WI_LIBCPP_STD_VER > 17 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 constexpr
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__WI_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17) && \
|
||||||
|
(__WI_LIBCPP_STD_VER > 17 || defined(__WI_LIBCPP_ENABLE_NODISCARD))
|
||||||
|
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 __WI_LIBCPP_NODISCARD_ATTRIBUTE
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __WI_LIBCPP_STD_VER > 14 && defined(__cpp_inline_variables) && (__cpp_inline_variables >= 201606L)
|
||||||
|
# define __WI_LIBCPP_INLINE_VAR inline
|
||||||
|
#else
|
||||||
|
# define __WI_LIBCPP_INLINE_VAR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WI_LIBCPP_CXX03_LANG
|
||||||
|
#define __WI_LIBCPP_HAS_NO_UNICODE_CHARS
|
||||||
|
#define __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __SIZEOF_INT128__
|
||||||
|
#define __WI_LIBCPP_HAS_NO_INT128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !__WI_HAS_FEATURE_NOEXCEPT && !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT)
|
||||||
|
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT
|
||||||
|
# define WI_NOEXCEPT noexcept
|
||||||
|
# define __WI_NOEXCEPT_(x) noexcept(x)
|
||||||
|
#else
|
||||||
|
# define WI_NOEXCEPT throw()
|
||||||
|
# define __WI_NOEXCEPT_(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
|
||||||
|
#define __WI_LIBCPP_HIDDEN
|
||||||
|
#define __WI_LIBCPP_TEMPLATE_VIS
|
||||||
|
#endif // defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
|
||||||
|
|
||||||
|
#ifndef __WI_LIBCPP_HIDDEN
|
||||||
|
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS)
|
||||||
|
# define __WI_LIBCPP_HIDDEN __attribute__ ((__visibility__("hidden")))
|
||||||
|
# else
|
||||||
|
# define __WI_LIBCPP_HIDDEN
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __WI_LIBCPP_TEMPLATE_VIS
|
||||||
|
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) && !defined(__WI_LIBCPP_COMPILER_MSVC)
|
||||||
|
# if __has_attribute(__type_visibility__)
|
||||||
|
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__type_visibility__("default")))
|
||||||
|
# else
|
||||||
|
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__visibility__("default")))
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# define __WI_LIBCPP_TEMPLATE_VIS
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_HIDDEN __WI_LIBCPP_INTERNAL_LINKAGE
|
||||||
|
|
||||||
|
namespace wistd // ("Windows Implementation" std)
|
||||||
|
{
|
||||||
|
typedef decltype(__nullptr) nullptr_t;
|
||||||
|
|
||||||
|
template <class _T1, class _T2 = _T1>
|
||||||
|
struct __less
|
||||||
|
{
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||||
|
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
bool operator()(const _T1& __x, const _T2& __y) const {return __x < __y;}
|
||||||
|
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
bool operator()(const _T2& __x, const _T1& __y) const {return __x < __y;}
|
||||||
|
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
bool operator()(const _T2& __x, const _T2& __y) const {return __x < __y;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _T1>
|
||||||
|
struct __less<_T1, _T1>
|
||||||
|
{
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _T1>
|
||||||
|
struct __less<const _T1, _T1>
|
||||||
|
{
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _T1>
|
||||||
|
struct __less<_T1, const _T1>
|
||||||
|
{
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||||
|
};
|
||||||
|
|
||||||
|
// These are added to wistd to enable use of min/max without having to use the windows.h min/max
|
||||||
|
// macros that some clients might not have access to. Note: the STL versions of these have debug
|
||||||
|
// checking for the less than operator and support for iterators that these implementations lack.
|
||||||
|
// Use the STL versions when you require use of those features.
|
||||||
|
|
||||||
|
// min
|
||||||
|
|
||||||
|
template <class _Tp, class _Compare>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
const _Tp&
|
||||||
|
(min)(const _Tp& __a, const _Tp& __b, _Compare __comp)
|
||||||
|
{
|
||||||
|
return __comp(__b, __a) ? __b : __a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Tp>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
const _Tp&
|
||||||
|
(min)(const _Tp& __a, const _Tp& __b)
|
||||||
|
{
|
||||||
|
return (min)(__a, __b, __less<_Tp>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// max
|
||||||
|
|
||||||
|
template <class _Tp, class _Compare>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
const _Tp&
|
||||||
|
(max)(const _Tp& __a, const _Tp& __b, _Compare __comp)
|
||||||
|
{
|
||||||
|
return __comp(__a, __b) ? __b : __a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Tp>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||||
|
const _Tp&
|
||||||
|
(max)(const _Tp& __a, const _Tp& __b)
|
||||||
|
{
|
||||||
|
return (max)(__a, __b, __less<_Tp>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Arg, class _Result>
|
||||||
|
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
|
||||||
|
{
|
||||||
|
typedef _Arg argument_type;
|
||||||
|
typedef _Result result_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _Arg1, class _Arg2, class _Result>
|
||||||
|
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
|
||||||
|
{
|
||||||
|
typedef _Arg1 first_argument_type;
|
||||||
|
typedef _Arg2 second_argument_type;
|
||||||
|
typedef _Result result_type;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
#endif // _WISTD_CONFIG_H_
|
544
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_functional.h
vendored
Normal file
544
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_functional.h
vendored
Normal file
|
@ -0,0 +1,544 @@
|
||||||
|
// -*- C++ -*-
|
||||||
|
//===------------------------ functional ----------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||||
|
// Source Licenses. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// STL common functionality
|
||||||
|
//
|
||||||
|
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
|
||||||
|
// of whether exceptions are enabled in the component. Common library code that expects to be used
|
||||||
|
// from exception-free components want these concepts, but including STL headers directly introduces
|
||||||
|
// friction as it requires components not using STL to declare their STL version. Doing so creates
|
||||||
|
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
|
||||||
|
// a long list of headers (including <new>) which can create further ambiguity around throwing new
|
||||||
|
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
|
||||||
|
// the potential to create naming conflicts or other implied dependencies.
|
||||||
|
//
|
||||||
|
// To promote the use of these core language concepts outside of STL-based binaries, this file is
|
||||||
|
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
|
||||||
|
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
|
||||||
|
// STL. The implementation and naming of all functions are taken directly from STL, instead using
|
||||||
|
// "wistd" (Windows Implementation std) as the namespace.
|
||||||
|
//
|
||||||
|
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
|
||||||
|
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
|
||||||
|
//
|
||||||
|
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
|
||||||
|
// Only code that is not exception-based and libraries that expect to be utilized across both exception
|
||||||
|
// and non-exception based code should utilize this functionality.
|
||||||
|
|
||||||
|
#ifndef _WISTD_FUNCTIONAL_H_
|
||||||
|
#define _WISTD_FUNCTIONAL_H_
|
||||||
|
|
||||||
|
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
|
||||||
|
#include "wistd_memory.h"
|
||||||
|
#include <intrin.h> // For __fastfail
|
||||||
|
#include <new.h> // For placement new
|
||||||
|
|
||||||
|
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||||
|
#pragma GCC system_header
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4324)
|
||||||
|
#pragma warning(disable: 4800)
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
namespace wistd // ("Windows Implementation" std)
|
||||||
|
{
|
||||||
|
// wistd::function
|
||||||
|
//
|
||||||
|
// All of the code below is in direct support of wistd::function. This class is identical to std::function
|
||||||
|
// with the following exceptions:
|
||||||
|
//
|
||||||
|
// 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported)
|
||||||
|
// 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit)
|
||||||
|
// 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation)
|
||||||
|
|
||||||
|
template <class _Ret>
|
||||||
|
struct __invoke_void_return_wrapper
|
||||||
|
{
|
||||||
|
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||||
|
template <class ..._Args>
|
||||||
|
static _Ret __call(_Args&&... __args) {
|
||||||
|
return __invoke(wistd::forward<_Args>(__args)...);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <class _Fn>
|
||||||
|
static _Ret __call(_Fn __f) {
|
||||||
|
return __invoke(__f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fn, class _A0>
|
||||||
|
static _Ret __call(_Fn __f, _A0& __a0) {
|
||||||
|
return __invoke(__f, __a0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fn, class _A0, class _A1>
|
||||||
|
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) {
|
||||||
|
return __invoke(__f, __a0, __a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fn, class _A0, class _A1, class _A2>
|
||||||
|
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){
|
||||||
|
return __invoke(__f, __a0, __a1, __a2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct __invoke_void_return_wrapper<void>
|
||||||
|
{
|
||||||
|
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||||
|
template <class ..._Args>
|
||||||
|
static void __call(_Args&&... __args) {
|
||||||
|
(void)__invoke(wistd::forward<_Args>(__args)...);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <class _Fn>
|
||||||
|
static void __call(_Fn __f) {
|
||||||
|
__invoke(__f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fn, class _A0>
|
||||||
|
static void __call(_Fn __f, _A0& __a0) {
|
||||||
|
__invoke(__f, __a0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fn, class _A0, class _A1>
|
||||||
|
static void __call(_Fn __f, _A0& __a0, _A1& __a1) {
|
||||||
|
__invoke(__f, __a0, __a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fn, class _A0, class _A1, class _A2>
|
||||||
|
static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) {
|
||||||
|
__invoke(__f, __a0, __a1, __a2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// FUNCTION
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
// bad_function_call
|
||||||
|
|
||||||
|
__WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
void __throw_bad_function_call()
|
||||||
|
{
|
||||||
|
__fastfail(7); // FAST_FAIL_FATAL_APP_EXIT
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Fp> class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
|
||||||
|
|
||||||
|
namespace __function
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class _Rp>
|
||||||
|
struct __maybe_derive_from_unary_function
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _Rp, class _A1>
|
||||||
|
struct __maybe_derive_from_unary_function<_Rp(_A1)>
|
||||||
|
: public unary_function<_A1, _Rp>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _Rp>
|
||||||
|
struct __maybe_derive_from_binary_function
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _Rp, class _A1, class _A2>
|
||||||
|
struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)>
|
||||||
|
: public binary_function<_A1, _A2, _Rp>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _Fp>
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool __not_null(_Fp const&) { return true; }
|
||||||
|
|
||||||
|
template <class _Fp>
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool __not_null(_Fp* __ptr) { return __ptr; }
|
||||||
|
|
||||||
|
template <class _Ret, class _Class>
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool __not_null(_Ret _Class::*__ptr) { return __ptr; }
|
||||||
|
|
||||||
|
template <class _Fp>
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool __not_null(function<_Fp> const& __f) { return !!__f; }
|
||||||
|
|
||||||
|
} // namespace __function
|
||||||
|
|
||||||
|
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||||
|
|
||||||
|
namespace __function {
|
||||||
|
|
||||||
|
template<class _Fp> class __base;
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
class __base<_Rp(_ArgTypes...)>
|
||||||
|
{
|
||||||
|
__base(const __base&);
|
||||||
|
__base& operator=(const __base&);
|
||||||
|
public:
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __base() {}
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
|
||||||
|
virtual void __clone(__base*) const = 0;
|
||||||
|
virtual void __move(__base*) = 0;
|
||||||
|
virtual void destroy() WI_NOEXCEPT = 0;
|
||||||
|
virtual _Rp operator()(_ArgTypes&& ...) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _FD, class _FB> class __func;
|
||||||
|
|
||||||
|
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||||
|
class __func<_Fp, _Rp(_ArgTypes...)>
|
||||||
|
: public __base<_Rp(_ArgTypes...)>
|
||||||
|
{
|
||||||
|
_Fp __f_;
|
||||||
|
public:
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
explicit __func(_Fp&& __f)
|
||||||
|
: __f_(wistd::move(__f)) {}
|
||||||
|
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
explicit __func(const _Fp& __f)
|
||||||
|
: __f_(__f) {}
|
||||||
|
|
||||||
|
virtual void __clone(__base<_Rp(_ArgTypes...)>*) const;
|
||||||
|
virtual void __move(__base<_Rp(_ArgTypes...)>*);
|
||||||
|
virtual void destroy() WI_NOEXCEPT;
|
||||||
|
virtual _Rp operator()(_ArgTypes&& ... __arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||||
|
void
|
||||||
|
__func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const
|
||||||
|
{
|
||||||
|
::new (__p) __func(__f_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||||
|
void
|
||||||
|
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
|
||||||
|
{
|
||||||
|
::new (__p) __func(wistd::move(__f_));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||||
|
void
|
||||||
|
__func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
__f_.~_Fp();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||||
|
_Rp
|
||||||
|
__func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg)
|
||||||
|
{
|
||||||
|
typedef __invoke_void_return_wrapper<_Rp> _Invoker;
|
||||||
|
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // __function
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
|
||||||
|
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
|
||||||
|
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
|
||||||
|
{
|
||||||
|
// 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects
|
||||||
|
// that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12
|
||||||
|
// pointers (__base vtable takes an additional one).
|
||||||
|
static constexpr size_t __buffer_size = 13 * sizeof(void*);
|
||||||
|
|
||||||
|
typedef __function::__base<_Rp(_ArgTypes...)> __base;
|
||||||
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
|
typename aligned_storage<__buffer_size>::type __buf_;
|
||||||
|
__base* __f_;
|
||||||
|
|
||||||
|
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
|
||||||
|
return reinterpret_cast<__base*>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fp, bool>
|
||||||
|
struct __callable_imp
|
||||||
|
{
|
||||||
|
static const bool value = is_same<void, _Rp>::value ||
|
||||||
|
is_convertible<typename __invoke_of<_Fp&, _ArgTypes...>::type,
|
||||||
|
_Rp>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _Fp>
|
||||||
|
struct __callable_imp<_Fp, false>
|
||||||
|
{
|
||||||
|
static const bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _Fp>
|
||||||
|
struct __callable
|
||||||
|
{
|
||||||
|
static const bool value = __callable_imp<_Fp, __lazy_and<
|
||||||
|
integral_constant<bool, !is_same<__uncvref_t<_Fp>, function>::value>,
|
||||||
|
__invokable<_Fp&, _ArgTypes...>
|
||||||
|
>::value>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class _Fp>
|
||||||
|
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
|
||||||
|
public:
|
||||||
|
typedef _Rp result_type;
|
||||||
|
|
||||||
|
// construct/copy/destroy:
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
|
function() WI_NOEXCEPT : __f_(0) {}
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
|
||||||
|
function(const function&);
|
||||||
|
function(function&&);
|
||||||
|
template<class _Fp, class = _EnableIfCallable<_Fp>>
|
||||||
|
function(_Fp);
|
||||||
|
|
||||||
|
function& operator=(const function&);
|
||||||
|
function& operator=(function&&);
|
||||||
|
function& operator=(nullptr_t) WI_NOEXCEPT;
|
||||||
|
template<class _Fp, class = _EnableIfCallable<_Fp>>
|
||||||
|
function& operator=(_Fp&&);
|
||||||
|
|
||||||
|
~function();
|
||||||
|
|
||||||
|
// function modifiers:
|
||||||
|
void swap(function&);
|
||||||
|
|
||||||
|
// function capacity:
|
||||||
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
__WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;}
|
||||||
|
|
||||||
|
// deleted overloads close possible hole in the type system
|
||||||
|
template<class _R2, class... _ArgTypes2>
|
||||||
|
bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete;
|
||||||
|
template<class _R2, class... _ArgTypes2>
|
||||||
|
bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete;
|
||||||
|
public:
|
||||||
|
// function invocation:
|
||||||
|
_Rp operator()(_ArgTypes...) const;
|
||||||
|
|
||||||
|
// NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than
|
||||||
|
// 'std' so all functions requiring RTTI have been removed
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
|
function<_Rp(_ArgTypes...)>::function(const function& __f)
|
||||||
|
{
|
||||||
|
if (__f.__f_ == 0)
|
||||||
|
__f_ = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__f_ = __as_base(&__buf_);
|
||||||
|
__f.__f_->__clone(__f_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||||
|
function<_Rp(_ArgTypes...)>::function(function&& __f)
|
||||||
|
{
|
||||||
|
if (__f.__f_ == 0)
|
||||||
|
__f_ = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__f_ = __as_base(&__buf_);
|
||||||
|
__f.__f_->__move(__f_);
|
||||||
|
__f.__f_->destroy();
|
||||||
|
__f.__f_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
template <class _Fp, class>
|
||||||
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
|
function<_Rp(_ArgTypes...)>::function(_Fp __f)
|
||||||
|
: __f_(0)
|
||||||
|
{
|
||||||
|
if (__function::__not_null(__f))
|
||||||
|
{
|
||||||
|
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
|
||||||
|
static_assert(sizeof(_FF) <= sizeof(__buf_),
|
||||||
|
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
|
||||||
|
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
function<_Rp(_ArgTypes...)>&
|
||||||
|
function<_Rp(_ArgTypes...)>::operator=(const function& __f)
|
||||||
|
{
|
||||||
|
*this = nullptr;
|
||||||
|
if (__f.__f_)
|
||||||
|
{
|
||||||
|
__f_ = __as_base(&__buf_);
|
||||||
|
__f.__f_->__clone(__f_);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
function<_Rp(_ArgTypes...)>&
|
||||||
|
function<_Rp(_ArgTypes...)>::operator=(function&& __f)
|
||||||
|
{
|
||||||
|
*this = nullptr;
|
||||||
|
if (__f.__f_)
|
||||||
|
{
|
||||||
|
__f_ = __as_base(&__buf_);
|
||||||
|
__f.__f_->__move(__f_);
|
||||||
|
__f.__f_->destroy();
|
||||||
|
__f.__f_ = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
function<_Rp(_ArgTypes...)>&
|
||||||
|
function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
__base* __t = __f_;
|
||||||
|
__f_ = 0;
|
||||||
|
if (__t)
|
||||||
|
__t->destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
template <class _Fp, class>
|
||||||
|
function<_Rp(_ArgTypes...)>&
|
||||||
|
function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f)
|
||||||
|
{
|
||||||
|
*this = nullptr;
|
||||||
|
if (__function::__not_null(__f))
|
||||||
|
{
|
||||||
|
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
|
||||||
|
static_assert(sizeof(_FF) <= sizeof(__buf_),
|
||||||
|
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
|
||||||
|
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
function<_Rp(_ArgTypes...)>::~function()
|
||||||
|
{
|
||||||
|
if (__f_)
|
||||||
|
__f_->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
void
|
||||||
|
function<_Rp(_ArgTypes...)>::swap(function& __f)
|
||||||
|
{
|
||||||
|
if (wistd::addressof(__f) == this)
|
||||||
|
return;
|
||||||
|
if (__f_ && __f.__f_)
|
||||||
|
{
|
||||||
|
typename aligned_storage<sizeof(__buf_)>::type __tempbuf;
|
||||||
|
__base* __t = __as_base(&__tempbuf);
|
||||||
|
__f_->__move(__t);
|
||||||
|
__f_->destroy();
|
||||||
|
__f_ = 0;
|
||||||
|
__f.__f_->__move(__as_base(&__buf_));
|
||||||
|
__f.__f_->destroy();
|
||||||
|
__f.__f_ = 0;
|
||||||
|
__f_ = __as_base(&__buf_);
|
||||||
|
__t->__move(__as_base(&__f.__buf_));
|
||||||
|
__t->destroy();
|
||||||
|
__f.__f_ = __as_base(&__f.__buf_);
|
||||||
|
}
|
||||||
|
else if (__f_)
|
||||||
|
{
|
||||||
|
__f_->__move(__as_base(&__f.__buf_));
|
||||||
|
__f_->destroy();
|
||||||
|
__f_ = 0;
|
||||||
|
__f.__f_ = __as_base(&__f.__buf_);
|
||||||
|
}
|
||||||
|
else if (__f.__f_)
|
||||||
|
{
|
||||||
|
__f.__f_->__move(__as_base(&__buf_));
|
||||||
|
__f.__f_->destroy();
|
||||||
|
__f.__f_ = 0;
|
||||||
|
__f_ = __as_base(&__buf_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
_Rp
|
||||||
|
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
|
||||||
|
{
|
||||||
|
if (__f_ == 0)
|
||||||
|
__throw_bad_function_call();
|
||||||
|
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Rp, class... _ArgTypes>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool
|
||||||
|
operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;}
|
||||||
|
|
||||||
|
template <class _Rp, class... _ArgTypes>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool
|
||||||
|
operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;}
|
||||||
|
|
||||||
|
template <class _Rp, class... _ArgTypes>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool
|
||||||
|
operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;}
|
||||||
|
|
||||||
|
template <class _Rp, class... _ArgTypes>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
bool
|
||||||
|
operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;}
|
||||||
|
|
||||||
|
// Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work
|
||||||
|
template <class _Rp, class... _ArgTypes>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
void
|
||||||
|
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||||
|
{return __x.swap(__y);}
|
||||||
|
|
||||||
|
template <class _Rp, class... _ArgTypes>
|
||||||
|
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
void
|
||||||
|
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||||
|
{return __x.swap(__y);}
|
||||||
|
|
||||||
|
// std::invoke
|
||||||
|
template <class _Fn, class ..._Args>
|
||||||
|
typename __invoke_of<_Fn, _Args...>::type
|
||||||
|
invoke(_Fn&& __f, _Args&&... __args)
|
||||||
|
__WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value))
|
||||||
|
{
|
||||||
|
return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // __WI_LIBCPP_CXX03_LANG
|
||||||
|
|
||||||
|
#error wistd::function and wistd::invoke not implemented for pre-C++11
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
#endif // _WISTD_FUNCTIONAL_H_
|
1038
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_memory.h
vendored
Normal file
1038
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_memory.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
4504
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_type_traits.h
vendored
Normal file
4504
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_type_traits.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
84
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wrl.h
vendored
Normal file
84
packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wrl.h
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_WRL_INCLUDED
|
||||||
|
#define __WIL_WRL_INCLUDED
|
||||||
|
|
||||||
|
#include <wrl.h>
|
||||||
|
#include "result.h"
|
||||||
|
#include "common.h" // wistd type_traits helpers
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
#pragma region Object construction helpers that throw exceptions
|
||||||
|
|
||||||
|
/** Used to construct a RuntimeClass based object that uses 2 phase construction.
|
||||||
|
Construct a RuntimeClass based object that uses 2 phase construction (by implementing
|
||||||
|
RuntimeClassInitialize() and returning error codes for failures.
|
||||||
|
~~~~
|
||||||
|
// SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize()
|
||||||
|
auto someClass = MakeAndInitializeOrThrow<SomeClass>(L"input", true);
|
||||||
|
~~~~ */
|
||||||
|
|
||||||
|
template <typename T, typename... TArgs>
|
||||||
|
Microsoft::WRL::ComPtr<T> MakeAndInitializeOrThrow(TArgs&&... args)
|
||||||
|
{
|
||||||
|
Microsoft::WRL::ComPtr<T> obj;
|
||||||
|
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<T>(&obj, Microsoft::WRL::Details::Forward<TArgs>(args)...));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does
|
||||||
|
not require 2 phase construction).
|
||||||
|
~~~~
|
||||||
|
// SomeClass uses exceptions for error handling in its constructor.
|
||||||
|
auto someClass = MakeOrThrow<SomeClass>(L"input", true);
|
||||||
|
~~~~ */
|
||||||
|
|
||||||
|
template <typename T, typename... TArgs>
|
||||||
|
Microsoft::WRL::ComPtr<T> MakeOrThrow(TArgs&&... args)
|
||||||
|
{
|
||||||
|
// This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use.
|
||||||
|
// Unfortunately this produces false positives as all RuntimeClass derived classes have
|
||||||
|
// a RuntimeClassInitialize() method from their base class.
|
||||||
|
// static_assert(!std::is_member_function_pointer<decltype(&T::RuntimeClassInitialize)>::value,
|
||||||
|
// "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead");
|
||||||
|
auto obj = Microsoft::WRL::Make<T>(Microsoft::WRL::Details::Forward<TArgs>(args)...);
|
||||||
|
THROW_IF_NULL_ALLOC(obj.Get());
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
||||||
|
/** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>.
|
||||||
|
Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC()
|
||||||
|
from wil\result.h to test the result. */
|
||||||
|
template<typename TDelegateInterface, typename ...Args>
|
||||||
|
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
using namespace Microsoft::WRL;
|
||||||
|
return Callback<Implements<RuntimeClassFlags<ClassicCom>, TDelegateInterface, FtmBase>>(wistd::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
template<typename TDelegateInterface, typename ...Args>
|
||||||
|
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallback(Args&&... args)
|
||||||
|
{
|
||||||
|
auto result = MakeAgileCallbackNoThrow<TDelegateInterface, Args...>(wistd::forward<Args>(args)...);
|
||||||
|
THROW_IF_NULL_ALLOC(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
} // namespace wil
|
||||||
|
|
||||||
|
#endif // __WIL_WRL_INCLUDED
|
Loading…
Reference in a new issue