├── .clang-format ├── .gitattributes ├── .gitignore ├── .vsconfig ├── CMakeLists.txt ├── CMakePresets.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── ThirdPartyNotices.txt ├── cmake └── common_build_flags.cmake ├── docs ├── CMakeLists.txt ├── Doxyfile ├── WilDocs.md └── nosal2.doxyfile ├── include └── wil │ ├── Tracelogging.h │ ├── com.h │ ├── com_apartment_variable.h │ ├── common.h │ ├── coroutine.h │ ├── cppwinrt.h │ ├── cppwinrt_authoring.h │ ├── cppwinrt_helpers.h │ ├── cppwinrt_notifiable_server_lock.h │ ├── cppwinrt_wrl.h │ ├── filesystem.h │ ├── network.h │ ├── nt_result_macros.h │ ├── registry.h │ ├── registry_helpers.h │ ├── resource.h │ ├── result.h │ ├── result_macros.h │ ├── result_originate.h │ ├── rpc_helpers.h │ ├── safecast.h │ ├── stl.h │ ├── token_helpers.h │ ├── traceloggingconfig.h │ ├── win32_helpers.h │ ├── win32_result_macros.h │ ├── windowing.h │ ├── winrt.h │ ├── wistd_config.h │ ├── wistd_functional.h │ ├── wistd_memory.h │ ├── wistd_type_traits.h │ └── wrl.h ├── natvis ├── wil.natstepfilter └── wil.natvis ├── packaging ├── CMakeLists.txt └── nuget │ ├── CMakeLists.txt │ ├── Microsoft.Windows.ImplementationLibrary.nuspec │ └── Microsoft.Windows.ImplementationLibrary.targets ├── scripts ├── azure-pipelines.yml ├── build_all.cmd ├── call-vcvars.cmd ├── enable-appverifier.cmd ├── find-clang-format.cmd ├── format-changes.cmd ├── init.cmd ├── init_all.cmd ├── run-clang-format.cmd ├── runtests.cmd └── stress-test.cmd ├── tests ├── CMakeLists.txt ├── ComApartmentVariableTests.cpp ├── ComTests.cpp ├── CommonTests.cpp ├── CoroutineTests.cpp ├── CppWinRT20Tests.cpp ├── CppWinRTAuthoringTests.cpp ├── CppWinRTNotifiableServerLockTests.cpp ├── CppWinRTTests.cpp ├── FakeWinRTTypes.h ├── FileSystemTests.cpp ├── MockingTests.cpp ├── NetworkTests.cpp ├── NtResultTests.cpp ├── RegistryTests.cpp ├── ResourceTests.cpp ├── ResultTests.cpp ├── Rpc.cpp ├── SafeCastTests.cpp ├── StlTests.cpp ├── TokenHelpersTests.cpp ├── TraceLoggingTests.cpp ├── TraceLoggingTests.h ├── TraceLoggingTests_PartB.cpp ├── UniqueWinRTEventTokenTests.cpp ├── WatcherTests.cpp ├── WinRTTests.cpp ├── WinVerifyTrustTest.cpp ├── WindowingTests.cpp ├── WistdTests.cpp ├── app.manifest ├── app │ └── CMakeLists.txt ├── common.h ├── cpplatest │ └── CMakeLists.txt ├── cppwinrt-notifiable-server-lock │ └── CMakeLists.txt ├── cppwinrt_threadpool_guard.h ├── main.cpp ├── mocking.h ├── noexcept │ └── CMakeLists.txt ├── normal │ └── CMakeLists.txt ├── pch.h ├── sanitize-address │ └── CMakeLists.txt ├── sanitize-undefined-behavior │ └── CMakeLists.txt ├── test_objects.h ├── wiTest.cpp └── win7 │ └── CMakeLists.txt ├── vcpkg-configuration.json ├── vcpkg-overlay ├── catch2 │ ├── except.patch │ ├── fix-install-path.patch │ ├── portfile.cmake │ ├── synch.patch │ └── vcpkg.json └── detours │ ├── portfile.cmake │ ├── usage │ └── vcpkg.json └── vcpkg.json /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | 5 | AccessModifierOffset: -4 6 | AlignAfterOpenBracket: AlwaysBreak 7 | AlignConsecutiveAssignments: false 8 | AlignEscapedNewlines: DontAlign 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLambdasOnASingleLine: Empty 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakAfterDefinitionReturnType: None 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakBeforeMultilineStrings: true 21 | AlwaysBreakTemplateDeclarations: true 22 | BinPackArguments: false 23 | BinPackParameters: false 24 | BraceWrapping: 25 | AfterCaseLabel: true 26 | AfterClass: true 27 | AfterControlStatement: true 28 | AfterEnum: true 29 | AfterFunction: true 30 | AfterNamespace: true 31 | AfterStruct: true 32 | AfterUnion: true 33 | AfterExternBlock: true 34 | BeforeCatch: true 35 | BeforeElse: true 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Custom 41 | BreakBeforeTernaryOperators: true 42 | BreakConstructorInitializers: AfterColon 43 | BreakStringLiterals: false 44 | ColumnLimit: 130 45 | CommentPragmas: '^ IWYU pragma:' 46 | CompactNamespaces: true 47 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 48 | ConstructorInitializerIndentWidth: 4 49 | ContinuationIndentWidth: 4 50 | Cpp11BracedListStyle: true 51 | DerivePointerAlignment: false 52 | DisableFormat: false 53 | FixNamespaceComments: true 54 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 55 | IncludeBlocks: Regroup 56 | IndentCaseLabels: false 57 | IncludeCategories: 58 | - Regex: '^"(stdafx.h|pch.h|precomp.h)"$' 59 | Priority: -1 60 | IndentWidth: 4 61 | IndentWrappedFunctionNames: false 62 | KeepEmptyLinesAtTheStartOfBlocks: true 63 | MacroBlockBegin: '^BEGIN_COM_MAP$|^BEGIN_CONNECTION_POINT_MAP$|^BEGIN_HELPER_NODEMAP$|^BEGIN_MODULE$|^BEGIN_MSG_MAP$|^BEGIN_OBJECT_MAP$|^BEGIN_TEST_CLASS$|^BEGIN_TEST_METHOD$|^BEGIN_TEST_METHOD_PROPERTIES$' 64 | MacroBlockEnd: '^END_COM_MAP$|^END_CONNECTION_POINT_MAP$|^END_HELPER_NODEMAP$|^END_MODULE$|^END_MSG_MAP$|^END_OBJECT_MAP$|^END_TEST_CLASS$|^END_TEST_METHOD$|^END_TEST_METHOD_PROPERTIES$' 65 | MaxEmptyLinesToKeep: 1 66 | NamespaceIndentation: Inner 67 | ObjCBlockIndentWidth: 2 68 | ObjCSpaceAfterProperty: false 69 | ObjCSpaceBeforeProtocolList: true 70 | PenaltyBreakBeforeFirstCallParameter: 19 71 | PenaltyBreakComment: 300 72 | PenaltyBreakFirstLessLess: 120 73 | PenaltyBreakString: 2000 74 | PenaltyExcessCharacter: 2 75 | PenaltyReturnTypeOnItsOwnLine: 1000 76 | PointerAlignment: Left 77 | SortIncludes: false 78 | SpaceAfterCStyleCast: false 79 | SpaceBeforeAssignmentOperators: true 80 | SpaceBeforeParens: ControlStatements 81 | SpaceInEmptyParentheses: false 82 | SpacesBeforeTrailingComments: 1 83 | SpacesInAngles: Never 84 | SpacesInContainerLiterals: true 85 | SpacesInCStyleCastParentheses: false 86 | SpacesInParentheses: false 87 | SpacesInSquareBrackets: false 88 | Standard: Latest 89 | TabWidth: 4 90 | UseTab: Never 91 | 92 | AttributeMacros: [ 93 | CALLBACK, 94 | ] 95 | 96 | StatementMacros: [ 97 | _Acquires_exclusive_lock_, 98 | _Acquires_lock_, 99 | _Acquires_nonreentrant_lock_, 100 | _Acquires_shared_lock_, 101 | _Analysis_assume_smart_lock_acquired_, 102 | _Analysis_assume_smart_lock_released_, 103 | _Create_lock_level_, 104 | _Detaches_lock_, 105 | _Function_class_, 106 | _Global_cancel_spin_lock_, 107 | _Global_critical_region_, 108 | _Global_interlock_, 109 | _Global_priority_region_, 110 | _Has_lock_kind_, 111 | _Has_lock_level_, 112 | _IRQL_always_function_max_, 113 | _IRQL_always_function_min_, 114 | _IRQL_raises_, 115 | _IRQL_requires_, 116 | _IRQL_requires_max_, 117 | _IRQL_requires_min_, 118 | _IRQL_requires_same_, 119 | _IRQL_restores_, 120 | _IRQL_restores_global_, 121 | _IRQL_saves_, 122 | _IRQL_saves_global_, 123 | _Lock_level_order_, 124 | _Moves_lock_, 125 | _Must_inspect_result_, 126 | _No_competing_thread_, 127 | _Post_same_lock_, 128 | _Post_writable_byte_size_, 129 | _Pre_satisfies_, 130 | _Releases_exclusive_lock_, 131 | _Releases_lock_, 132 | _Releases_nonreentrant_lock_, 133 | _Releases_shared_lock_, 134 | _Replaces_lock_, 135 | _Requires_exclusive_lock_held_, 136 | _Requires_lock_held_, 137 | _Requires_lock_not_held_, 138 | _Requires_no_locks_held_, 139 | _Requires_shared_lock_held_, 140 | _Ret_maybenull_, 141 | _Ret_range_, 142 | _Struct_size_bytes_, 143 | _Success_, 144 | _Swaps_locks_, 145 | _Use_decl_annotations_, 146 | _When_, 147 | 148 | DECLARE_ORDINAL_MAP, 149 | DECLARE_PROCNAME_MAP, 150 | DEFINE_ORDINAL_ENTRIES, 151 | DEFINE_ORDINAL_ENTRIES_ALTNAME, 152 | DEFINE_ORDINAL_ENTRIES_APISET, 153 | DEFINE_ORDINAL_MAP, 154 | DEFINE_PROCNAME_ENTRIES, 155 | DEFINE_PROCNAME_ENTRIES_ALTNAME, 156 | DEFINE_PROCNAME_ENTRIES_APISET, 157 | DEFINE_PROCNAME_MAP, 158 | DLOENTRY, 159 | DLOENTRY_APISET, 160 | DLPENTRY, 161 | DLPENTRY_APISET, 162 | 163 | RpcEndExcept, 164 | 165 | ActivatableClass, 166 | ActivatableClassWithFactory, 167 | ActivatableClassWithFactoryEx, 168 | ActivatableStaticOnlyFactory, 169 | ActivatableStaticOnlyFactoryEx, 170 | CoCreatableClass, 171 | CoCreatableClassWithFactory, 172 | CoCreatableClassWithFactoryEx, 173 | 174 | # Additional definitions for the WIL repo 175 | _Translates_NTSTATUS_to_HRESULT_, 176 | _Translates_Win32_to_HRESULT_, 177 | _Translates_last_error_to_HRESULT_, 178 | 179 | TEST_CASE, 180 | SECTION, 181 | ] 182 | 183 | TypenameMacros: [ 184 | IFACEMETHOD, 185 | STDMETHOD, 186 | STDAPI_, 187 | ] 188 | 189 | # Additional definitions for the WIL repo 190 | WhitespaceSensitiveMacros: [ 191 | __WI_CLANG_DISABLE_WARNING, 192 | ] 193 | 194 | # TODO: Only supported by clang-format version 17+ 195 | # Macros: 196 | # - WI_NOEXCEPT=noexcept 197 | # - __R_FN_PARAMS_FULL=int x 198 | # - RESULT_NORETURN=[[noreturn]] 199 | --- 200 | Language: CSharp 201 | AlignAfterOpenBracket: AlwaysBreak 202 | AllowShortBlocksOnASingleLine: false 203 | AllowShortCaseLabelsOnASingleLine: false 204 | AllowShortFunctionsOnASingleLine: None 205 | AllowShortIfStatementsOnASingleLine: false 206 | AllowShortLoopsOnASingleLine: false 207 | BraceWrapping: 208 | AfterCaseLabel: true 209 | AfterClass: true 210 | AfterControlStatement: true 211 | AfterEnum: true 212 | AfterFunction: true 213 | AfterNamespace: true 214 | AfterStruct: true 215 | BeforeCatch: true 216 | BeforeElse: true 217 | SplitEmptyFunction: true 218 | SplitEmptyRecord: true 219 | SplitEmptyNamespace: true 220 | BreakBeforeBraces: Custom 221 | ColumnLimit: 130 222 | DerivePointerAlignment: false 223 | IndentWidth: 4 224 | PenaltyBreakBeforeFirstCallParameter: 19 225 | PenaltyBreakComment: 300 226 | PenaltyBreakFirstLessLess: 120 227 | PenaltyBreakString: 2000 228 | PenaltyExcessCharacter: 2 229 | PenaltyReturnTypeOnItsOwnLine: 1000 230 | PointerAlignment: Left 231 | TabWidth: 4 232 | UseTab: Never 233 | ... 234 | 235 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Disable CRLF-mapping for all files in the depot. 2 | * -text 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | 332 | # Visual Studio Code directory 333 | .vscode/ 334 | 335 | # CMake/Build output 336 | build/ 337 | -------------------------------------------------------------------------------- /.vsconfig: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "components": [ 4 | "Microsoft.VisualStudio.Component.CoreEditor", 5 | "Microsoft.VisualStudio.Workload.CoreEditor", 6 | "Microsoft.VisualStudio.Component.Roslyn.Compiler", 7 | "Microsoft.Component.MSBuild", 8 | "Microsoft.VisualStudio.Component.TextTemplating", 9 | "Microsoft.VisualStudio.Component.NuGet", 10 | "Microsoft.VisualStudio.Component.IntelliCode", 11 | "Microsoft.VisualStudio.Component.VC.CoreIde", 12 | "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", 13 | "Microsoft.VisualStudio.Component.Windows11SDK.22621", 14 | "Microsoft.VisualStudio.Component.VC.Redist.14.Latest", 15 | "Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core", 16 | "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake", 17 | "Microsoft.VisualStudio.Component.VC.CMake.Project", 18 | "Microsoft.VisualStudio.Component.VC.TestAdapterForBoostTest", 19 | "Microsoft.VisualStudio.Component.VC.TestAdapterForGoogleTest", 20 | "Microsoft.VisualStudio.Component.VC.ASAN", 21 | "Microsoft.VisualStudio.Component.Vcpkg", 22 | "Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", 23 | "Microsoft.VisualStudio.Component.VC.Llvm.Clang", 24 | "Microsoft.VisualStudio.Component.VC.Tools.ARM64EC", 25 | "Microsoft.VisualStudio.Component.VC.Tools.ARM64", 26 | "Microsoft.VisualStudio.Workload.NativeDesktop", 27 | "Microsoft.VisualStudio.Component.VC.ATL.Spectre", 28 | "Microsoft.VisualStudio.Component.VC.ATL.ARM64.Spectre", 29 | "Microsoft.VisualStudio.Component.VC.ATLMFC.Spectre", 30 | "Microsoft.VisualStudio.Component.VC.MFC.ARM64.Spectre", 31 | "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64EC.Spectre", 32 | "Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre", 33 | "Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre", 34 | "Microsoft.NetCore.Component.Runtime.6.0" 35 | ], 36 | "extensions": [] 37 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.29) 2 | project(WIL) 3 | 4 | include(GNUInstallDirs) 5 | 6 | # Set by build server to speed up build/reduce file/object size 7 | option(FAST_BUILD "Sets options to speed up build/reduce obj/executable size" OFF) 8 | option(WIL_BUILD_PACKAGING "Sets option to build the packaging, default on" ON) 9 | option(WIL_BUILD_TESTS "Sets option to build the unit tests, default on" ON) 10 | 11 | if (NOT DEFINED WIL_BUILD_VERSION) 12 | set(WIL_BUILD_VERSION "0.0.0") 13 | endif() 14 | 15 | if (NOT DEFINED CPPWINRT_VERSION) 16 | set(CPPWINRT_VERSION "2.0.240405.15") 17 | endif() 18 | 19 | # Detect the Windows SDK version. If we're using the Visual Studio generator, this will be provided for us. Otherwise 20 | # we'll need to assume that this value comes from the command line (e.g. through the VS command prompt) 21 | if (DEFINED CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) 22 | set(WIL_WINDOWS_SDK_VERSION ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}) 23 | else() 24 | # This has a trailing backslash for whatever reason... 25 | string(REGEX REPLACE "\\\\$" "" WIL_WINDOWS_SDK_VERSION "$ENV{WindowsSDKVersion}") 26 | endif() 27 | 28 | if (${WIL_BUILD_PACKAGING}) 29 | add_subdirectory(packaging) 30 | endif() 31 | 32 | if (${WIL_BUILD_TESTS}) 33 | add_subdirectory(docs) 34 | add_subdirectory(tests) 35 | 36 | enable_testing() 37 | 38 | # Custom target for running clang-format 39 | add_custom_target(format 40 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 41 | COMMAND scripts/run-clang-format.cmd) 42 | endif() 43 | 44 | # Gather headers into an interface library. 45 | file(GLOB_RECURSE HEADER_FILES "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/*.h") 46 | add_library(${PROJECT_NAME} INTERFACE) 47 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 48 | 49 | # The interface's include directory. 50 | target_include_directories(${PROJECT_NAME} INTERFACE 51 | $ 52 | $ 53 | ) 54 | 55 | # Include the .natvis files 56 | if (MSVC) 57 | target_sources(${PROJECT_NAME} INTERFACE 58 | "$") 59 | endif() 60 | 61 | # Install Package Configuration 62 | string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER) 63 | install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME_LOWER}_targets) 64 | install(EXPORT ${PROJECT_NAME_LOWER}_targets 65 | NAMESPACE ${PROJECT_NAME}:: 66 | FILE ${PROJECT_NAME_LOWER}Config.cmake 67 | DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}" 68 | ) 69 | 70 | # Install the headers at a standard cmake location. 71 | install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/wil" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 72 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 29, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "msvc", 11 | "hidden": false, 12 | "binaryDir": "${sourceDir}/build/${presetName}", 13 | "generator": "Ninja Multi-Config", 14 | "installDir": "${sourceDir}/build/install/${presetName}", 15 | "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", 16 | "cacheVariables": { 17 | "CMAKE_EXPORT_COMPILE_COMMANDS": true, 18 | "CMAKE_CONFIGURATION_TYPES": "Debug;RelWithDebInfo;Release;MinSizeRel", 19 | "CMAKE_CXX_COMPILER": "cl", 20 | "CMAKE_C_COMPILER": "cl" 21 | } 22 | }, 23 | { 24 | "name": "clang", 25 | "inherits": "msvc", 26 | "hidden": false, 27 | "vendor": { 28 | "microsoft.com/VisualStudioSettings/CMake/1.0": { 29 | "intelliSenseMode": "windows-clang-x64" 30 | } 31 | }, 32 | "cacheVariables": { 33 | "CMAKE_CXX_COMPILER": "clang-cl", 34 | "CMAKE_C_COMPILER": "clang-cl" 35 | } 36 | } 37 | ], 38 | "buildPresets": [ 39 | { 40 | "name": "msvc-debug", 41 | "displayName": "MSVC Debug", 42 | "configurePreset": "msvc", 43 | "configuration": "Debug" 44 | }, 45 | { 46 | "name": "msvc-release", 47 | "displayName": "MSVC Release (debuggable)", 48 | "configurePreset": "msvc", 49 | "configuration": "RelWithDebInfo" 50 | }, 51 | { 52 | "name": "clang-debug", 53 | "displayName": "clang Debug", 54 | "configurePreset": "clang", 55 | "configuration": "Debug" 56 | }, 57 | { 58 | "name": "clang-release", 59 | "displayName": "clang Release (debuggable)", 60 | "configurePreset": "clang", 61 | "configuration": "RelWithDebInfo" 62 | } 63 | ], 64 | "testPresets": [ 65 | { 66 | "name": "msvc-debug", 67 | "configurePreset": "msvc", 68 | "configuration": "Debug" 69 | }, 70 | { 71 | "name": "msvc-release", 72 | "configurePreset": "msvc", 73 | "configuration": "RelWithDebInfo" 74 | }, 75 | { 76 | "name": "clang-debug", 77 | "configurePreset": "clang", 78 | "configuration": "Debug" 79 | }, 80 | { 81 | "name": "clang-release", 82 | "configurePreset": "clang", 83 | "configuration": "RelWithDebInfo" 84 | } 85 | ] 86 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 2 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 3 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | THIRD PARTY SOFTWARE NOTICES AND INFORMATION 2 | Do Not Translate or Localize 3 | 4 | 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: 5 | 6 | Source Code Compliance Team 7 | Microsoft Corporation 8 | One Microsoft Way 9 | Redmond, WA 98052 10 | USA 11 | 12 | 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. 13 | 14 | Libc++ 15 | 16 | Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in 26 | all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 34 | THE SOFTWARE. 35 | 36 | Catch2 37 | 38 | Boost Software License - Version 1.0 - August 17th, 2003 39 | 40 | Permission is hereby granted, free of charge, to any person or organization 41 | obtaining a copy of the software and accompanying documentation covered by 42 | this license (the "Software") to use, reproduce, display, distribute, 43 | execute, and transmit the Software, and to prepare derivative works of the 44 | Software, and to permit third-parties to whom the Software is furnished to 45 | do so, all subject to the following: 46 | 47 | The copyright notices in the Software and this entire statement, including 48 | the above license grant, this restriction and the following disclaimer, 49 | must be included in all copies of the Software, in whole or in part, and 50 | all derivative works of the Software, unless such copies or derivative 51 | works are solely in the form of machine-executable object code generated by 52 | a source language processor. 53 | 54 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 55 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 56 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 57 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 58 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 59 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 60 | DEALINGS IN THE SOFTWARE. 61 | 62 | Detours 63 | 64 | Copyright (c) Microsoft Corporation. 65 | 66 | MIT License 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining a copy 69 | of this software and associated documentation files (the "Software"), to deal 70 | in the Software without restriction, including without limitation the rights 71 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 72 | copies of the Software, and to permit persons to whom the Software is 73 | furnished to do so, subject to the following conditions: 74 | 75 | The above copyright notice and this permission notice shall be included in all 76 | copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 79 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 80 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 81 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 82 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 83 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 84 | SOFTWARE. 85 | -------------------------------------------------------------------------------- /cmake/common_build_flags.cmake: -------------------------------------------------------------------------------- 1 | 2 | # This is unfortunately still needed to disable exceptions/RTTI since modern CMake still has no builtin support... 3 | # E.g. replace_cxx_flag("/EHsc", "/EHs-c-") 4 | macro(replace_cxx_flag pattern text) 5 | foreach (flag 6 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 7 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) 8 | 9 | string(REGEX REPLACE "${pattern}" "${text}" ${flag} "${${flag}}") 10 | 11 | endforeach() 12 | endmacro() 13 | 14 | # Fixup default compiler settings 15 | if (MSVC) 16 | add_compile_options( 17 | # Be as strict as reasonably possible, since we want to support consumers using strict warning levels 18 | /W4 /WX 19 | ) 20 | else() 21 | # Clang with non-MSVC commandline syntax 22 | add_compile_options( 23 | # Effectively the same as /W4 /WX 24 | -Wall -Werror 25 | ) 26 | endif() 27 | 28 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 29 | add_compile_options( 30 | # Ignore some pedantic warnings enabled by '-Wextra' 31 | -Wno-missing-field-initializers 32 | 33 | # Ignore some pedantic warnings enabled by '-Wpedantic' 34 | -Wno-language-extension-token 35 | -Wno-c++17-attribute-extensions 36 | -Wno-gnu-zero-variadic-macro-arguments 37 | -Wno-extra-semi 38 | 39 | # For tests, we want to be able to test self assignment, so disable this warning 40 | -Wno-self-assign-overloaded 41 | -Wno-self-move 42 | 43 | # We don't want legacy MSVC conformance 44 | -fno-delayed-template-parsing 45 | ) 46 | if (NOT $ENV{Platform} STREQUAL "arm64") 47 | add_compile_options( 48 | # clang needs this to enable _InterlockedCompareExchange128 49 | -mcx16 50 | ) 51 | endif() 52 | else() 53 | add_compile_options( 54 | # We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl) 55 | /permissive- 56 | 57 | # wistd::function has padding due to alignment. This is expected 58 | /wd4324 59 | 60 | # Some tests have a LOT of template instantiations 61 | /bigobj 62 | ) 63 | endif() 64 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # If doxygen can't be found, don't error out as this is not a hard requirement. We will instead create a dummy target that prints 3 | # an error message to the user 4 | find_program(DOXYGEN doxygen) 5 | 6 | if (DOXYGEN) 7 | # Add a custom target to generate the documentation 8 | add_custom_target(docs 9 | COMMAND ${CMAKE_COMMAND} -E env "DOXYGEN_OUTPUT_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}" "WIL_BUILD_VERSION=${WIL_BUILD_VERSION}" ${DOXYGEN} Doxyfile 10 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 11 | COMMENT "Generating API documentation with Doxygen" 12 | VERBATIM 13 | ) 14 | else() 15 | message(WARNING "Doxygen not found. The 'docs' target will not be available. If you would like to generate documentation, " 16 | "please install Doxygen and ensure that it is available in your PATH and re-run CMake.") 17 | endif() 18 | -------------------------------------------------------------------------------- /include/wil/cppwinrt_notifiable_server_lock.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | //! @file 12 | //! A server lock that runs a callback once all references are released. 13 | 14 | #ifndef __WIL_CPPWINRT_NOTIFIABLE_MODULE_LOCK_INCLUDED 15 | #define __WIL_CPPWINRT_NOTIFIABLE_MODULE_LOCK_INCLUDED 16 | 17 | #ifdef WINRT_BASE_H 18 | #error You must include this header before including any winrt header 19 | #endif 20 | 21 | #ifndef WINRT_CUSTOM_MODULE_LOCK 22 | #error You must define 'WINRT_CUSTOM_MODULE_LOCK' at the project level 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace wil 30 | { 31 | struct notifiable_server_lock 32 | { 33 | uint32_t operator++() noexcept 34 | { 35 | return CoAddRefServerProcess(); 36 | } 37 | 38 | uint32_t operator--() 39 | { 40 | const auto ref = CoReleaseServerProcess(); 41 | if (ref == 0 && notifier) 42 | { 43 | notifier(); 44 | } 45 | 46 | return ref; 47 | } 48 | 49 | template 50 | void set_notifier(Func&& func) 51 | { 52 | notifier = std::forward(func); 53 | } 54 | 55 | void set_notifier(std::nullptr_t) noexcept 56 | { 57 | notifier = nullptr; 58 | } 59 | 60 | static notifiable_server_lock& instance() noexcept 61 | { 62 | static notifiable_server_lock lock; 63 | return lock; 64 | } 65 | 66 | private: 67 | notifiable_server_lock() = default; 68 | 69 | std::function notifier; 70 | }; 71 | } // namespace wil 72 | 73 | namespace winrt 74 | { 75 | inline auto& get_module_lock() noexcept 76 | { 77 | return wil::notifiable_server_lock::instance(); 78 | } 79 | } // namespace winrt 80 | 81 | #endif -------------------------------------------------------------------------------- /include/wil/cppwinrt_wrl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | //! @file 12 | //! Provides interoperability between C++/WinRT types and the WRL Module system. 13 | #ifndef __WIL_CPPWINRT_WRL_INCLUDED 14 | #define __WIL_CPPWINRT_WRL_INCLUDED 15 | 16 | #include "cppwinrt.h" 17 | #include 18 | 19 | #include "result_macros.h" 20 | #include 21 | 22 | // wil::wrl_factory_for_winrt_com_class provides interopability between a 23 | // C++/WinRT class and the WRL Module system, allowing the winrt class to be 24 | // CoCreatable. 25 | // 26 | // Usage: 27 | // - In your cpp, add: 28 | // CoCreatableCppWinRtClass(className) 29 | // 30 | // - In the dll.cpp (or equivalent) for the module containing your class, add: 31 | // CoCreatableClassWrlCreatorMapInclude(className) 32 | // 33 | namespace wil 34 | { 35 | /// @cond 36 | namespace details 37 | { 38 | template 39 | class module_count_wrapper : public TCppWinRTClass 40 | { 41 | public: 42 | module_count_wrapper() 43 | { 44 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 45 | { 46 | modulePtr->IncrementObjectCount(); 47 | } 48 | } 49 | 50 | virtual ~module_count_wrapper() 51 | { 52 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 53 | { 54 | modulePtr->DecrementObjectCount(); 55 | } 56 | } 57 | }; 58 | } // namespace details 59 | /// @endcond 60 | 61 | template 62 | class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> 63 | { 64 | public: 65 | IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void** object) noexcept 66 | try 67 | { 68 | *object = nullptr; 69 | RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr); 70 | 71 | return winrt::make>().as(riid, object); 72 | } 73 | CATCH_RETURN() 74 | }; 75 | } // namespace wil 76 | 77 | #define CoCreatableCppWinRtClass(className) \ 78 | CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) 79 | 80 | #endif // __WIL_CPPWINRT_WRL_INCLUDED 81 | -------------------------------------------------------------------------------- /include/wil/result_originate.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | //! @file 12 | //! WIL Error Handling Helpers: supporting file enabling the originating of errors to produce better crash dumps 13 | 14 | // Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. 15 | // Before originating a new error we will observe whether there is already an error payload associated with the current thread. If 16 | // there is, and the HRESULTs match, then a new error will not be originated. Otherwise we will overwrite it with a new 17 | // origination. The ABI boundary for WinRT APIs will check the per-thread error information. The act of checking the error 18 | // clears it, so there should be minimal risk of failing to originate distinct errors simply because the HRESULTs match. 19 | // 20 | // For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if 21 | // the exception is caught and re-thrown. 22 | // 23 | // For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because 24 | // error conditions -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is 25 | // originating the error because it must capture the entire stack and some additional data. 26 | 27 | #ifndef __WIL_RESULT_ORIGINATE_INCLUDED 28 | #define __WIL_RESULT_ORIGINATE_INCLUDED 29 | 30 | #include "result.h" 31 | #include // RestrictedErrorInfo uses BSTRs :( 32 | #include 33 | #include "resource.h" 34 | #include "com.h" 35 | #include 36 | 37 | namespace wil 38 | { 39 | /// @cond 40 | namespace details 41 | { 42 | // 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. 43 | inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT 44 | { 45 | if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception)) 46 | { 47 | bool shouldOriginate = true; 48 | 49 | wil::com_ptr_nothrow restrictedErrorInformation; 50 | if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) 51 | { 52 | // This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we 53 | // are observing right now. 54 | wil::unique_bstr descriptionUnused; 55 | HRESULT existingHr = failure.hr; 56 | wil::unique_bstr restrictedDescriptionUnused; 57 | wil::unique_bstr capabilitySidUnused; 58 | if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails( 59 | &descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused))) 60 | { 61 | shouldOriginate = (failure.hr != existingHr); 62 | } 63 | } 64 | 65 | if (shouldOriginate) 66 | { 67 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) 68 | wil::unique_hmodule errorModule; 69 | if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule)) 70 | { 71 | auto pfn = details::GetProcAddress(errorModule.get(), "RoOriginateErrorW"); 72 | if (pfn != nullptr) 73 | { 74 | pfn(failure.hr, 0, failure.pszMessage); 75 | } 76 | } 77 | #else // DESKTOP | SYSTEM 78 | ::RoOriginateErrorW(failure.hr, 0, failure.pszMessage); 79 | #endif // DESKTOP | SYSTEM 80 | } 81 | else if (restrictedErrorInformation) 82 | { 83 | // GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was 84 | // already present, then we need to restore the error information for later observation. 85 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 86 | } 87 | } 88 | } 89 | 90 | // This method will check for the presence of stowed exception data on the current thread. If such data exists, and the 91 | // HRESULT matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this 92 | // situation will result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we 93 | // just return and the calling method fails fast the same way it always has. 94 | inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT 95 | { 96 | wil::com_ptr_nothrow restrictedErrorInformation; 97 | if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) 98 | { 99 | wil::unique_bstr descriptionUnused; 100 | HRESULT existingHr = failure.hr; 101 | wil::unique_bstr restrictedDescriptionUnused; 102 | wil::unique_bstr capabilitySidUnused; 103 | if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails( 104 | &descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) && 105 | (existingHr == failure.hr)) 106 | { 107 | // GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for 108 | // RoFailFastWithErrorContext so we must restore it via SetRestrictedErrorInfo first. 109 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 110 | RoFailFastWithErrorContext(existingHr); 111 | } 112 | else 113 | { 114 | // The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing 115 | // fast in this method, so it is available in the debugger just-in-case. 116 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 117 | } 118 | } 119 | } 120 | } // namespace details 121 | /// @endcond 122 | } // namespace wil 123 | 124 | // Automatically call RoOriginateError upon error origination by including this file 125 | WI_HEADER_INITIALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] { 126 | ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); 127 | ::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback); 128 | return 1; 129 | }) 130 | 131 | #endif // __WIL_RESULT_ORIGINATE_INCLUDED 132 | -------------------------------------------------------------------------------- /include/wil/rpc_helpers.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | //! @file 12 | //! Helpers for invoking RPC functions and translating structured exceptions to HRESULTs or C++ exceptions 13 | #ifndef __WIL_RPC_HELPERS_INCLUDED 14 | #define __WIL_RPC_HELPERS_INCLUDED 15 | 16 | #include "result.h" 17 | #include "resource.h" 18 | #include "wistd_functional.h" 19 | #include "wistd_type_traits.h" 20 | 21 | namespace wil 22 | { 23 | 24 | /// @cond 25 | namespace details 26 | { 27 | // This call-adapter template converts a void-returning 'wistd::invoke' into 28 | // an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated 29 | // with 'if constexpr' when C++17 is in wide use. 30 | template 31 | struct call_adapter 32 | { 33 | template 34 | static HRESULT call(TArgs&&... args) 35 | { 36 | return wistd::invoke(wistd::forward(args)...); 37 | } 38 | }; 39 | 40 | template <> 41 | struct call_adapter 42 | { 43 | template 44 | static HRESULT call(TArgs&&... args) 45 | { 46 | wistd::invoke(wistd::forward(args)...); 47 | return S_OK; 48 | } 49 | }; 50 | 51 | // Some RPC exceptions are already HRESULTs. Others are in the regular Win32 52 | // error space. If the incoming exception code isn't an HRESULT, wrap it. 53 | constexpr HRESULT map_rpc_exception(DWORD code) 54 | { 55 | return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code); 56 | } 57 | } // namespace details 58 | /// @endcond 59 | 60 | /** Invokes an RPC method, mapping structured exceptions to HRESULTs 61 | Failures encountered by the RPC infrastructure (such as server crashes, authentication 62 | errors, client parameter issues, etc.) are emitted by raising a structured exception from 63 | within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, 64 | RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual 65 | flow control machinery to use. 66 | 67 | Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates 68 | the result of the _work_. HRESULTs returned by a successful completion of the _call_ are 69 | returned as-is. 70 | 71 | RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_ 72 | completes successfully. 73 | 74 | For example, consider an RPC interface method defined in idl as: 75 | ~~~ 76 | HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state); 77 | ~~~ 78 | To call this method, use: 79 | ~~~ 80 | wil::unique_rpc_binding binding = // typically gotten elsewhere; 81 | wil::unique_midl_ptr state; 82 | HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put()); 83 | RETURN_IF_FAILED(hr); 84 | ~~~ 85 | */ 86 | template 87 | HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT 88 | { 89 | RpcTryExcept 90 | { 91 | // Note: this helper type can be removed with C++17 enabled via 92 | // 'if constexpr(wistd::is_same_v)' 93 | using result_t = typename wistd::__invoke_of::type; 94 | RETURN_IF_FAILED(details::call_adapter::call(wistd::forward(args)...)); 95 | return S_OK; 96 | } 97 | RpcExcept(RpcExceptionFilter(RpcExceptionCode())) 98 | { 99 | RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); 100 | } 101 | RpcEndExcept 102 | } 103 | 104 | /** Invokes an RPC method, mapping structured exceptions to HRESULTs 105 | Failures encountered by the RPC infrastructure (such as server crashes, authentication 106 | errors, client parameter issues, etc.) are emitted by raising a structured exception from 107 | within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, 108 | RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual 109 | flow control machinery to use. 110 | 111 | Some RPC methods return results (such as a state enumeration or other value) directly in 112 | their signature. This adapter writes that result into a caller-provided object then 113 | returns S_OK. 114 | 115 | For example, consider an RPC interface method defined in idl as: 116 | ~~~ 117 | GUID GetKittenId([in, ref, string] const wchar_t* name); 118 | ~~~ 119 | To call this method, use: 120 | ~~~ 121 | wil::unique_rpc_binding binding = // typically gotten elsewhere; 122 | GUID id; 123 | HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy"); 124 | RETURN_IF_FAILED(hr); 125 | ~~~ 126 | */ 127 | template 128 | HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT 129 | { 130 | RpcTryExcept 131 | { 132 | result = wistd::invoke(wistd::forward(args)...); 133 | return S_OK; 134 | } 135 | RpcExcept(RpcExceptionFilter(RpcExceptionCode())) 136 | { 137 | RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); 138 | } 139 | RpcEndExcept 140 | } 141 | 142 | /// @cond 143 | namespace details 144 | { 145 | // Provides an adapter around calling the context-handle-close method on an 146 | // RPC interface, which itself is an RPC call. 147 | template 148 | struct rpc_closer_t 149 | { 150 | static void Close(TStorage arg) WI_NOEXCEPT 151 | { 152 | LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg)); 153 | } 154 | }; 155 | } // namespace details 156 | /// @endcond 157 | 158 | /** Manages explicit RPC context handles 159 | Explicit RPC context handles are used in many RPC interfaces. Most interfaces with 160 | context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets 161 | the server close out the context handle. As the close method itself is an RPC call, 162 | it can fail and raise a structured exception. 163 | 164 | This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow` 165 | helper, ensuring correct cleanup and lifecycle management. 166 | @code 167 | // Assume the interface has two methods: 168 | // HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*); 169 | // HRESULT UseFoo([in] FOO_CONTEXT context; 170 | // void CloseFoo([in, out] PFOO_CONTEXT); 171 | using unique_foo_context = wil::unique_rpc_context_handle; 172 | unique_foo_context context; 173 | RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put())); 174 | RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get())); 175 | context.reset(); 176 | @endcode 177 | */ 178 | template 179 | using unique_rpc_context_handle = 180 | unique_any::Close), details::rpc_closer_t::Close>; 181 | 182 | #ifdef WIL_ENABLE_EXCEPTIONS 183 | /** Invokes an RPC method, mapping structured exceptions to C++ exceptions 184 | See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_ 185 | and those returned by the _method_ are mapped to HRESULTs and thrown inside a 186 | wil::ResultException. Using the example RPC method provided above: 187 | @code 188 | wil::unique_midl_ptr state; 189 | wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put()); 190 | // use 'state' 191 | @endcode 192 | */ 193 | template 194 | void invoke_rpc(TCall&&... args) 195 | { 196 | THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward(args)...)); 197 | } 198 | 199 | /** Invokes an RPC method, mapping structured exceptions to C++ exceptions 200 | See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the 201 | _call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the 202 | example RPC method provided above: 203 | @code 204 | GUID id = wil::invoke_rpc_result(GetKittenId, binding.get()); 205 | // use 'id' 206 | @endcode 207 | */ 208 | template 209 | auto invoke_rpc_result(TCall&&... args) 210 | { 211 | using result_t = typename wistd::__invoke_of::type; 212 | result_t result{}; 213 | THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward(args)...)); 214 | return result; 215 | } 216 | #endif 217 | } // namespace wil 218 | 219 | #endif 220 | -------------------------------------------------------------------------------- /include/wil/stl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | //! @file 12 | //! Windows STL helpers: custom allocators for STL containers 13 | #ifndef __WIL_STL_INCLUDED 14 | #define __WIL_STL_INCLUDED 15 | 16 | #include "common.h" 17 | #include "resource.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #if (__WI_LIBCPP_STD_VER >= 17) && WI_HAS_INCLUDE(, 1) // Assume present if C++17 23 | #include 24 | #endif 25 | 26 | /// @cond 27 | #ifndef WI_STL_FAIL_FAST_IF 28 | #define WI_STL_FAIL_FAST_IF FAIL_FAST_IF 29 | #endif 30 | /// @endcond 31 | 32 | #if defined(WIL_ENABLE_EXCEPTIONS) 33 | 34 | namespace wil 35 | { 36 | /** Secure allocator for STL containers. 37 | The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating 38 | memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`, 39 | `wil::secure_string`, and `wil::secure_wstring`. */ 40 | template 41 | struct secure_allocator : public std::allocator 42 | { 43 | template 44 | struct rebind 45 | { 46 | using other = secure_allocator; 47 | }; 48 | 49 | secure_allocator() : std::allocator() 50 | { 51 | } 52 | 53 | ~secure_allocator() = default; 54 | 55 | secure_allocator(const secure_allocator& a) : std::allocator(a) 56 | { 57 | } 58 | 59 | template 60 | secure_allocator(const secure_allocator& a) : std::allocator(a) 61 | { 62 | } 63 | 64 | T* allocate(size_t n) 65 | { 66 | return std::allocator::allocate(n); 67 | } 68 | 69 | void deallocate(T* p, size_t n) 70 | { 71 | SecureZeroMemory(p, sizeof(T) * n); 72 | std::allocator::deallocate(p, n); 73 | } 74 | }; 75 | 76 | //! `wil::secure_vector` will be securely zeroed before deallocation. 77 | template 78 | using secure_vector = std::vector>; 79 | //! `wil::secure_wstring` will be securely zeroed before deallocation. 80 | using secure_wstring = std::basic_string, wil::secure_allocator>; 81 | //! `wil::secure_string` will be securely zeroed before deallocation. 82 | using secure_string = std::basic_string, wil::secure_allocator>; 83 | 84 | /// @cond 85 | namespace details 86 | { 87 | template <> 88 | struct string_maker 89 | { 90 | HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT 91 | try 92 | { 93 | m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0'); 94 | return S_OK; 95 | } 96 | catch (...) 97 | { 98 | return E_OUTOFMEMORY; 99 | } 100 | 101 | wchar_t* buffer() 102 | { 103 | return &m_value[0]; 104 | } 105 | 106 | HRESULT trim_at_existing_null(size_t length) 107 | { 108 | m_value.erase(length); 109 | return S_OK; 110 | } 111 | 112 | std::wstring release() 113 | { 114 | return std::wstring(std::move(m_value)); 115 | } 116 | 117 | static PCWSTR get(const std::wstring& value) 118 | { 119 | return value.c_str(); 120 | } 121 | 122 | private: 123 | std::wstring m_value; 124 | }; 125 | } // namespace details 126 | /// @endcond 127 | 128 | // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. 129 | // This is the overload for std::wstring. Other overloads available in resource.h. 130 | inline PCWSTR str_raw_ptr(const std::wstring& str) 131 | { 132 | return str.c_str(); 133 | } 134 | 135 | #if __cpp_lib_string_view >= 201606L 136 | /** 137 | zstring_view. A zstring_view is identical to a std::string_view except it is always nul-terminated (unless empty). 138 | * zstring_view can be used for storing string literals without "forgetting" the length or that it is nul-terminated. 139 | * A zstring_view can be converted implicitly to a std::string_view because it is always safe to use a nul-terminated 140 | string_view as a plain string view. 141 | * A zstring_view can be constructed from a std::string because the data in std::string is nul-terminated. 142 | */ 143 | template 144 | class basic_zstring_view : public std::basic_string_view 145 | { 146 | using size_type = typename std::basic_string_view::size_type; 147 | 148 | template 149 | struct has_c_str 150 | { 151 | template 152 | static auto test(int) -> decltype(std::declval().c_str(), std::true_type()); 153 | template 154 | static std::false_type test(...); 155 | static constexpr bool value = decltype(test(0))::value; 156 | }; 157 | 158 | template 159 | struct has_size 160 | { 161 | template 162 | static auto test(int) -> decltype(std::declval().size() == 1, std::true_type()); 163 | template 164 | static std::false_type test(...); 165 | static constexpr bool value = decltype(test(0))::value; 166 | }; 167 | 168 | public: 169 | constexpr basic_zstring_view() noexcept = default; 170 | constexpr basic_zstring_view(const basic_zstring_view&) noexcept = default; 171 | constexpr basic_zstring_view& operator=(const basic_zstring_view&) noexcept = default; 172 | 173 | constexpr basic_zstring_view(const TChar* pStringData, size_type stringLength) noexcept : 174 | std::basic_string_view(pStringData, stringLength) 175 | { 176 | if (pStringData[stringLength] != 0) 177 | { 178 | WI_STL_FAIL_FAST_IF(true); 179 | } 180 | } 181 | 182 | template 183 | constexpr basic_zstring_view(const TChar (&stringArray)[stringArrayLength]) noexcept : 184 | std::basic_string_view(&stringArray[0], length_n(&stringArray[0], stringArrayLength)) 185 | { 186 | } 187 | 188 | // Construct from nul-terminated char ptr. To prevent this from overshadowing array construction, 189 | // we disable this constructor if the value is an array (including string literal). 190 | template ::value && !std::is_array::value>* = nullptr> 191 | constexpr basic_zstring_view(TPtr&& pStr) noexcept : std::basic_string_view(std::forward(pStr)) 192 | { 193 | } 194 | 195 | constexpr basic_zstring_view(const std::basic_string& str) noexcept : 196 | std::basic_string_view(&str[0], str.size()) 197 | { 198 | } 199 | 200 | template ::value && has_size::value>* = nullptr> 201 | constexpr basic_zstring_view(TSrc const& src) noexcept : std::basic_string_view(src.c_str(), src.size()) 202 | { 203 | } 204 | 205 | template ::value && !has_size::value>* = nullptr> 206 | constexpr basic_zstring_view(TSrc const& src) noexcept : std::basic_string_view(src.c_str()) 207 | { 208 | } 209 | 210 | // basic_string_view [] precondition won't let us read view[view.size()]; so we define our own. 211 | WI_NODISCARD constexpr const TChar& operator[](size_type idx) const noexcept 212 | { 213 | WI_ASSERT(idx <= this->size() && this->data() != nullptr); 214 | return this->data()[idx]; 215 | } 216 | 217 | WI_NODISCARD constexpr const TChar* c_str() const noexcept 218 | { 219 | WI_ASSERT(this->data() == nullptr || this->data()[this->size()] == 0); 220 | return this->data(); 221 | } 222 | 223 | private: 224 | // Bounds-checked version of char_traits::length, like strnlen. Requires that the input contains a null terminator. 225 | static constexpr size_type length_n(_In_reads_opt_(buf_size) const TChar* str, size_type buf_size) noexcept 226 | { 227 | const std::basic_string_view view(str, buf_size); 228 | auto pos = view.find_first_of(TChar()); 229 | if (pos == view.npos) 230 | { 231 | WI_STL_FAIL_FAST_IF(true); 232 | } 233 | return pos; 234 | } 235 | 236 | // The following basic_string_view methods must not be allowed because they break the nul-termination. 237 | using std::basic_string_view::swap; 238 | using std::basic_string_view::remove_suffix; 239 | }; 240 | 241 | using zstring_view = basic_zstring_view; 242 | using zwstring_view = basic_zstring_view; 243 | 244 | inline namespace literals 245 | { 246 | constexpr zstring_view operator""_zv(const char* str, std::size_t len) noexcept 247 | { 248 | return {str, len}; 249 | } 250 | 251 | constexpr zwstring_view operator""_zv(const wchar_t* str, std::size_t len) noexcept 252 | { 253 | return {str, len}; 254 | } 255 | } // namespace literals 256 | 257 | #endif // __cpp_lib_string_view >= 201606L 258 | 259 | } // namespace wil 260 | 261 | #if (__WI_LIBCPP_STD_VER >= 20) && WI_HAS_INCLUDE(, 1) // Assume present if C++20 262 | #include 263 | template 264 | struct std::formatter, TChar> : std::formatter, TChar> 265 | { 266 | }; 267 | #endif 268 | 269 | #endif // WIL_ENABLE_EXCEPTIONS 270 | 271 | #endif // __WIL_STL_INCLUDED 272 | -------------------------------------------------------------------------------- /include/wil/traceloggingconfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //********************************************************* 3 | // 4 | // Copyright (c) Microsoft. All rights reserved. 5 | // This code is licensed under the MIT License. 6 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 7 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 8 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 9 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 10 | // 11 | //********************************************************* 12 | //! @file 13 | //! Various definitions for use in conjunction with TraceLogging APIs 14 | 15 | #ifndef __WIL_TRACELOGGING_CONFIG_H 16 | /// @cond 17 | #define __WIL_TRACELOGGING_CONFIG_H 18 | /// @endcond 19 | 20 | // Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition 21 | // in this file configures the provider as a normal (non-telemetry) provider. 22 | #define TraceLoggingOptionMicrosoftTelemetry() // Empty definition for TraceLoggingOptionMicrosoftTelemetry 23 | 24 | // Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition 25 | // in this file configures the provider as a normal (non-telemetry) provider. 26 | #define TraceLoggingOptionWindowsCoreTelemetry() // Empty definition for TraceLoggingOptionWindowsCoreTelemetry 27 | 28 | // Event privacy tags. Use the PDT macro values for the tag parameter, e.g.: 29 | // TraceLoggingWrite(..., 30 | // TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage), 31 | // ...); 32 | #define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags") 33 | #define PDT_BrowsingHistory 0x0000000000000002u 34 | #define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u 35 | #define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u 36 | #define PDT_ProductAndServicePerformance 0x0000000001000000u 37 | #define PDT_ProductAndServiceUsage 0x0000000002000000u 38 | #define PDT_SoftwareSetupAndInventory 0x0000000080000000u 39 | 40 | // Event categories specified via keywords, e.g.: 41 | // TraceLoggingWrite(..., 42 | // TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), 43 | // ...); 44 | #define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47 45 | #define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46 46 | #define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45 47 | #define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment) 48 | 49 | // Event categories specified via event tags, e.g.: 50 | // TraceLoggingWrite(..., 51 | // TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY), 52 | // ...); 53 | #define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000 54 | #define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000 55 | #define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000 56 | #define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000 57 | #define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000 58 | #define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000 59 | #define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000 60 | #define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000 61 | #define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000 62 | #define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000 63 | #define MICROSOFT_EVENTTAG_DROP_PII 0x02000000 64 | #define MICROSOFT_EVENTTAG_HASH_PII 0x04000000 65 | #define MICROSOFT_EVENTTAG_MARK_PII 0x08000000 66 | 67 | // Field categories specified via field tags, e.g.: 68 | // TraceLoggingWrite(..., 69 | // TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII), 70 | // ...); 71 | #define MICROSOFT_FIELDTAG_DROP_PII 0x04000000 72 | #define MICROSOFT_FIELDTAG_HASH_PII 0x08000000 73 | #endif // __WIL_TRACELOGGING_CONFIG_H -------------------------------------------------------------------------------- /include/wil/win32_result_macros.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | //! @file 12 | //! WIL Error Handling Helpers: supporting file defining a family of macros and functions designed to uniformly handle errors 13 | //! across return codes, fail fast, exceptions and logging for Win32 error codes. 14 | #ifndef __WIL_WIN32_RESULTMACROS_INCLUDED 15 | #define __WIL_WIN32_RESULTMACROS_INCLUDED 16 | 17 | #include "result_macros.h" 18 | 19 | // Helpers for return macros 20 | /// @cond 21 | #define __WIN32_RETURN_WIN32(error, str) \ 22 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 23 | { \ 24 | const auto __error = (error); \ 25 | if (FAILED_WIN32(__error)) \ 26 | { \ 27 | __R_FN(Return_Win32)(__R_INFO(str) __error); \ 28 | } \ 29 | return __error; \ 30 | } \ 31 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 32 | #define __WIN32_RETURN_GLE_FAIL(str) return __R_FN(Win32_Return_GetLastError)(__R_INFO_ONLY(str)) 33 | 34 | FORCEINLINE long __WIN32_FROM_HRESULT(HRESULT hr) 35 | { 36 | if (SUCCEEDED(hr)) 37 | { 38 | return ERROR_SUCCESS; 39 | } 40 | return HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr; 41 | } 42 | /// @endcond 43 | 44 | //***************************************************************************** 45 | // Macros for returning failures as WIN32 error codes 46 | //***************************************************************************** 47 | 48 | // Always returns a known result (WIN32 error code) - always logs failures 49 | #define WIN32_RETURN_WIN32(error) __WIN32_RETURN_WIN32(wil::verify_win32(error), #error) 50 | #define WIN32_RETURN_LAST_ERROR() __WIN32_RETURN_GLE_FAIL(nullptr) 51 | 52 | // Conditionally returns failures (WIN32 error code) - always logs failures 53 | #define WIN32_RETURN_IF_WIN32_ERROR(error) \ 54 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 55 | { \ 56 | const auto __errorRet = wil::verify_win32(error); \ 57 | if (FAILED_WIN32(__errorRet)) \ 58 | { \ 59 | __WIN32_RETURN_WIN32(__errorRet, #error); \ 60 | } \ 61 | } \ 62 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 63 | #define WIN32_RETURN_WIN32_IF(error, condition) \ 64 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 65 | { \ 66 | if (wil::verify_bool(condition)) \ 67 | { \ 68 | __WIN32_RETURN_WIN32(wil::verify_win32(error), #condition); \ 69 | } \ 70 | } \ 71 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 72 | #define WIN32_RETURN_WIN32_IF_NULL(error, ptr) \ 73 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 74 | { \ 75 | if ((ptr) == nullptr) \ 76 | { \ 77 | __WIN32_RETURN_WIN32(wil::verify_win32(error), #ptr); \ 78 | } \ 79 | } \ 80 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 81 | #define WIN32_RETURN_LAST_ERROR_IF(condition) \ 82 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 83 | { \ 84 | if (wil::verify_bool(condition)) \ 85 | { \ 86 | __WIN32_RETURN_GLE_FAIL(#condition); \ 87 | } \ 88 | } \ 89 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 90 | #define WIN32_RETURN_LAST_ERROR_IF_NULL(ptr) \ 91 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 92 | { \ 93 | if ((ptr) == nullptr) \ 94 | { \ 95 | __WIN32_RETURN_GLE_FAIL(#ptr); \ 96 | } \ 97 | } \ 98 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 99 | 100 | // Conditionally returns failures (WIN32 error code) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern 101 | #define WIN32_RETURN_IF_WIN32_ERROR_EXPECTED(error) \ 102 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 103 | { \ 104 | const auto __errorRet = wil::verify_win32(error); \ 105 | if (FAILED_WIN32(__errorRet)) \ 106 | { \ 107 | return __errorRet; \ 108 | } \ 109 | } \ 110 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 111 | #define WIN32_RETURN_WIN32_IF_EXPECTED(error, condition) \ 112 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 113 | { \ 114 | if (wil::verify_bool(condition)) \ 115 | { \ 116 | return wil::verify_win32(error); \ 117 | } \ 118 | } \ 119 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 120 | #define WIN32_RETURN_WIN32_IF_NULL_EXPECTED(error, ptr) \ 121 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 122 | { \ 123 | if ((ptr) == nullptr) \ 124 | { \ 125 | return wil::verify_win32(error); \ 126 | } \ 127 | } \ 128 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 129 | #define WIN32_RETURN_LAST_ERROR_IF_EXPECTED(condition) \ 130 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 131 | { \ 132 | if (wil::verify_bool(condition)) \ 133 | { \ 134 | return wil::verify_win32(wil::details::GetLastErrorFail()); \ 135 | } \ 136 | } \ 137 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 138 | #define WIN32_RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) \ 139 | __WI_SUPPRESS_BREAKING_WARNINGS_S do \ 140 | { \ 141 | if ((ptr) == nullptr) \ 142 | { \ 143 | return wil::verify_win32(wil::details::GetLastErrorFail()); \ 144 | } \ 145 | } \ 146 | __WI_SUPPRESS_BREAKING_WARNINGS_E while ((void)0, 0) 147 | 148 | //***************************************************************************** 149 | // Macros to catch and convert exceptions on failure 150 | //***************************************************************************** 151 | 152 | // Use these macros *within* a catch (...) block to handle exceptions 153 | #define WIN32_RETURN_CAUGHT_EXCEPTION() return __R_FN(Win32_Return_CaughtException)(__R_INFO_ONLY(nullptr)) 154 | 155 | // Use these macros in place of a catch block to handle exceptions 156 | #define WIN32_CATCH_RETURN() \ 157 | catch (...) \ 158 | { \ 159 | WIN32_RETURN_CAUGHT_EXCEPTION(); \ 160 | } 161 | 162 | namespace wil 163 | { 164 | //***************************************************************************** 165 | // Public Helpers that catch -- mostly only enabled when exceptions are enabled 166 | //***************************************************************************** 167 | 168 | // Win32ErrorFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally 169 | // it re-throws and catches the exception to convert it to a WIN32 error code. If an exception is of an unrecognized type 170 | // the function will fail fast. 171 | // 172 | // try 173 | // { 174 | // // Code 175 | // } 176 | // catch (...) 177 | // { 178 | // status = wil::Win32ErrorFromCaughtException(); 179 | // } 180 | _Always_(_Post_satisfies_(return > 0)) __declspec(noinline) inline long Win32ErrorFromCaughtException() WI_NOEXCEPT 181 | { 182 | return __WIN32_FROM_HRESULT(ResultFromCaughtException()); 183 | } 184 | 185 | /// @cond 186 | namespace details::__R_NS_NAME 187 | { 188 | #ifdef WIL_ENABLE_EXCEPTIONS 189 | __R_DIRECT_METHOD(long, Win32_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT 190 | { 191 | __R_FN_LOCALS; 192 | return __WIN32_FROM_HRESULT(wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY)); 193 | } 194 | #endif 195 | 196 | __R_DIRECT_METHOD(long, Win32_Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT 197 | { 198 | __R_FN_LOCALS; 199 | return __WIN32_FROM_HRESULT(wil::details::ReportFailure_GetLastErrorHr(__R_DIRECT_FN_CALL_ONLY)); 200 | } 201 | } // namespace details::__R_NS_NAME 202 | /// @endcond 203 | } // namespace wil 204 | 205 | #endif // __WIL_WIN32_RESULTMACROS_INCLUDED 206 | -------------------------------------------------------------------------------- /include/wil/windowing.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_WINDOWING_INCLUDED 12 | #define __WIL_WINDOWING_INCLUDED 13 | 14 | #include 15 | #include 16 | 17 | #include "common.h" 18 | 19 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 20 | namespace wil 21 | { 22 | namespace details 23 | { 24 | template 25 | struct always_false : wistd::false_type 26 | { 27 | }; 28 | 29 | template 30 | BOOL __stdcall EnumWindowsCallbackNoThrow(HWND hwnd, LPARAM lParam) 31 | { 32 | auto pCallback = reinterpret_cast(lParam); 33 | #if __cpp_if_constexpr >= 201606L 34 | using result_t = decltype((*pCallback)(hwnd)); 35 | if constexpr (wistd::is_void_v) 36 | { 37 | (*pCallback)(hwnd); 38 | return TRUE; 39 | } 40 | else if constexpr (wistd::is_same_v) 41 | { 42 | // NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0 43 | return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE; 44 | } 45 | else if constexpr (std::is_same_v) 46 | { 47 | return (*pCallback)(hwnd) ? TRUE : FALSE; 48 | } 49 | else 50 | { 51 | static_assert(details::always_false::value, "Callback must return void, bool, or HRESULT"); 52 | } 53 | #else 54 | return (*pCallback)(hwnd); 55 | #endif 56 | } 57 | 58 | template 59 | void DoEnumWindowsNoThrow(TEnumApi&& enumApi, TCallback&& callback) noexcept 60 | { 61 | enumApi(EnumWindowsCallbackNoThrow, reinterpret_cast(&callback)); 62 | } 63 | 64 | #ifdef WIL_ENABLE_EXCEPTIONS 65 | template 66 | struct EnumWindowsCallbackData 67 | { 68 | std::exception_ptr exception; 69 | TCallback* pCallback; 70 | }; 71 | 72 | template 73 | BOOL __stdcall EnumWindowsCallback(HWND hwnd, LPARAM lParam) 74 | { 75 | auto pCallbackData = reinterpret_cast*>(lParam); 76 | try 77 | { 78 | auto pCallback = pCallbackData->pCallback; 79 | #if __cpp_if_constexpr >= 201606L 80 | using result_t = decltype((*pCallback)(hwnd)); 81 | if constexpr (std::is_void_v) 82 | { 83 | (*pCallback)(hwnd); 84 | return TRUE; 85 | } 86 | else if constexpr (std::is_same_v) 87 | { 88 | // NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0 89 | return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE; 90 | } 91 | else if constexpr (std::is_same_v) 92 | { 93 | return (*pCallback)(hwnd) ? TRUE : FALSE; 94 | } 95 | else 96 | { 97 | static_assert(details::always_false::value, "Callback must return void, bool, or HRESULT"); 98 | } 99 | #else 100 | return (*pCallback)(hwnd); 101 | #endif 102 | } 103 | catch (...) 104 | { 105 | pCallbackData->exception = std::current_exception(); 106 | return FALSE; 107 | } 108 | }; 109 | 110 | template 111 | void DoEnumWindows(TEnumApi&& enumApi, TCallback&& callback) 112 | { 113 | EnumWindowsCallbackData callbackData = {nullptr, &callback}; 114 | enumApi(EnumWindowsCallback, reinterpret_cast(&callbackData)); 115 | if (callbackData.exception) 116 | { 117 | std::rethrow_exception(callbackData.exception); 118 | } 119 | } 120 | #endif 121 | } // namespace details 122 | 123 | template 124 | void for_each_window_nothrow(TCallback&& callback) noexcept 125 | { 126 | details::DoEnumWindowsNoThrow(&EnumWindows, wistd::forward(callback)); 127 | } 128 | 129 | template 130 | void for_each_thread_window_nothrow(_In_ DWORD threadId, TCallback&& callback) noexcept 131 | { 132 | auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { 133 | return EnumThreadWindows(threadId, enumproc, lParam); 134 | }; 135 | details::DoEnumWindowsNoThrow(boundEnumThreadWindows, wistd::forward(callback)); 136 | } 137 | 138 | template 139 | void for_each_child_window_nothrow(_In_ HWND hwndParent, TCallback&& callback) noexcept 140 | { 141 | auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { 142 | return EnumChildWindows(hwndParent, enumproc, lParam); 143 | }; 144 | details::DoEnumWindowsNoThrow(boundEnumChildWindows, wistd::forward(callback)); 145 | } 146 | 147 | #ifdef WIL_ENABLE_EXCEPTIONS 148 | template 149 | void for_each_window(TCallback&& callback) 150 | { 151 | details::DoEnumWindows(&EnumWindows, wistd::forward(callback)); 152 | } 153 | 154 | template 155 | void for_each_thread_window(_In_ DWORD threadId, TCallback&& callback) 156 | { 157 | auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { 158 | return EnumThreadWindows(threadId, enumproc, lParam); 159 | }; 160 | details::DoEnumWindows(boundEnumThreadWindows, wistd::forward(callback)); 161 | } 162 | 163 | template 164 | void for_each_child_window(_In_ HWND hwndParent, TCallback&& callback) 165 | { 166 | auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { 167 | return EnumChildWindows(hwndParent, enumproc, lParam); 168 | }; 169 | details::DoEnumWindows(boundEnumChildWindows, wistd::forward(callback)); 170 | } 171 | #endif 172 | 173 | } // namespace wil 174 | #endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 175 | #endif // __WIL_WINDOWING_INCLUDED -------------------------------------------------------------------------------- /include/wil/wrl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | //! @file 12 | //! Windows Runtime Library Helpers: helpers for constructing RuntimeClass based objects and agile WRL Callback objects 13 | #ifndef __WIL_WRL_INCLUDED 14 | #define __WIL_WRL_INCLUDED 15 | 16 | #include 17 | #include "result.h" 18 | #include "common.h" // wistd type_traits helpers 19 | #include // GetModuleHandleW 20 | 21 | /// @cond 22 | EXTERN_C IMAGE_DOS_HEADER __ImageBase; 23 | /// @endcond 24 | 25 | namespace wil 26 | { 27 | 28 | #ifdef WIL_ENABLE_EXCEPTIONS 29 | #pragma region Object construction helpers that throw exceptions 30 | 31 | /** Used to construct a RuntimeClass based object that uses 2 phase construction. 32 | Construct a RuntimeClass based object that uses 2 phase construction (by implementing 33 | RuntimeClassInitialize() and returning error codes for failures. 34 | @code 35 | // SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize() 36 | auto someClass = MakeAndInitializeOrThrow(L"input", true); 37 | @endcode 38 | */ 39 | template 40 | Microsoft::WRL::ComPtr MakeAndInitializeOrThrow(TArgs&&... args) 41 | { 42 | Microsoft::WRL::ComPtr obj; 43 | THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&obj, Microsoft::WRL::Details::Forward(args)...)); 44 | return obj; 45 | } 46 | 47 | /** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does 48 | not require 2 phase construction). 49 | @code 50 | // SomeClass uses exceptions for error handling in its constructor. 51 | auto someClass = MakeOrThrow(L"input", true); 52 | @endcode 53 | */ 54 | template 55 | Microsoft::WRL::ComPtr MakeOrThrow(TArgs&&... args) 56 | { 57 | // This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use. 58 | // Unfortunately this produces false positives as all RuntimeClass derived classes have 59 | // a RuntimeClassInitialize() method from their base class. 60 | // static_assert(!std::is_member_function_pointer::value, 61 | // "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead"); 62 | auto obj = Microsoft::WRL::Make(Microsoft::WRL::Details::Forward(args)...); 63 | THROW_IF_NULL_ALLOC(obj.Get()); 64 | return obj; 65 | } 66 | #pragma endregion 67 | 68 | #endif // WIL_ENABLE_EXCEPTIONS 69 | 70 | /** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with 71 | MakeAgileCallback<>. Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC() from wil/result.h 72 | to test the result. */ 73 | template 74 | ::Microsoft::WRL::ComPtr MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT 75 | { 76 | using namespace Microsoft::WRL; 77 | return Callback, TDelegateInterface, FtmBase>>(wistd::forward(args)...); 78 | } 79 | 80 | #ifdef WIL_ENABLE_EXCEPTIONS 81 | template 82 | ::Microsoft::WRL::ComPtr MakeAgileCallback(Args&&... args) 83 | { 84 | auto result = MakeAgileCallbackNoThrow(wistd::forward(args)...); 85 | THROW_IF_NULL_ALLOC(result); 86 | return result; 87 | } 88 | #endif // WIL_ENABLE_EXCEPTIONS 89 | 90 | /** Holds a reference to the host WRL module to prevent it from being unloaded. 91 | Normally, the reference is held implicitly because you are a member function 92 | of a DLL-hosted COM object, or because you retain a strong reference 93 | to some DLL-hosted COM object, but if those do not apply to you, then you 94 | will need to hold a reference explicitly. For examples (and for the C++/WinRT 95 | equivalent), see winrt_module_reference. 96 | */ 97 | struct [[nodiscard]] wrl_module_reference 98 | { 99 | wrl_module_reference() 100 | { 101 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 102 | { 103 | modulePtr->IncrementObjectCount(); 104 | } 105 | else 106 | { 107 | #ifdef GET_MODULE_HANDLE_EX_FLAG_PIN 108 | // If this assertion fails, then you are using wrl_module_reference 109 | // from a DLL that does not host WRL objects, and the module reference 110 | // has no effect. 111 | WI_ASSERT(reinterpret_cast(&__ImageBase) == GetModuleHandleW(nullptr)); 112 | #endif 113 | } 114 | } 115 | 116 | wrl_module_reference(wrl_module_reference const&) : wrl_module_reference() 117 | { 118 | } 119 | 120 | ~wrl_module_reference() 121 | { 122 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 123 | { 124 | modulePtr->DecrementObjectCount(); 125 | } 126 | } 127 | }; 128 | 129 | } // namespace wil 130 | 131 | #endif // __WIL_WRL_INCLUDED 132 | -------------------------------------------------------------------------------- /natvis/wil.natstepfilter: -------------------------------------------------------------------------------- 1 | 2 | 3 | wil::unique_any_t<.*>::getNoStepInto 4 | wil::unique_any_t<.*>::operator.*NoStepInto 5 | wistd::unique_ptr<.*>::getNoStepInto 6 | wistd::forward<.*NoStepInto 7 | wistd::move<.*NoStepInto 8 | wistd::unique_ptr<.*>::operator.*NoStepInto 9 | wil::com_ptr_t<.*>::operator.*NoStepInto 10 | wil::com_ptr_t<.*>::getNoStepInto 11 | 12 | -------------------------------------------------------------------------------- /natvis/wil.natvis: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | default_delete 13 | 14 | 15 | 16 | 17 | 18 | {get()} 19 | 20 | get() 21 | 22 | 23 | 24 | 25 | 26 | {get()} 27 | 28 | get() 29 | 30 | 31 | 32 | 33 | 34 | 35 | ({first()}, {second()}) 36 | 37 | first() 38 | second() 39 | 40 | 41 | 42 | 43 | {__f_} 44 | 45 | 46 | 47 | 48 | 49 | empty 50 | {*__f_} 51 | 52 | *__f_ 53 | 54 | 55 | 56 | 57 | __ptr_.first() 58 | empty 59 | {*__ptr_.first()} 60 | 61 | __ptr_.first() 62 | __ptr_.second() 63 | 64 | 65 | 66 | 67 | empty 68 | {__ptr_.first(),su} 69 | __ptr_.first() 70 | 71 | __ptr_.first() 72 | wcslen(__ptr_.first()) 73 | __ptr_.second() 74 | 75 | 76 | 77 | 78 | empty 79 | {__ptr_.first(),su} 80 | __ptr_.first() 81 | 82 | __ptr_.first() 83 | wcslen(__ptr_.first()) 84 | __ptr_.second() 85 | 86 | 87 | 88 | 89 | empty 90 | {__ptr_.first(),s} 91 | __ptr_.first() 92 | 93 | __ptr_.first() 94 | strlen(__ptr_.first()) 95 | __ptr_.second() 96 | 97 | 98 | 99 | 100 | empty 101 | {__ptr_.first(),s} 102 | __ptr_.first() 103 | 104 | __ptr_.first() 105 | strlen(__ptr_.first()) 106 | __ptr_.second() 107 | 108 | 109 | 110 | 111 | empty 112 | {*m_ptr} 113 | m_ptr 114 | 115 | m_ptr 116 | 117 | 118 | 119 | 120 | empty 121 | {m_ptr} 122 | 123 | 124 | 125 | 126 | empty 127 | {m_ptr} 128 | 129 | 130 | 131 | 132 | empty 133 | {m_ptr} 134 | m_ptr 135 | 136 | 137 | 138 | 139 | empty 140 | {m_ptr,su} 141 | m_ptr 142 | 143 | wcslen(m_ptr) 144 | 145 | 146 | 147 | empty 148 | {m_ptr,s} 149 | m_ptr 150 | 151 | strlen(m_ptr) 152 | 153 | 154 | 155 | 156 | {m_ptr} 157 | 158 | m_ptr 159 | 160 | 161 | 162 | 163 | 164 | 165 | {_Mydata,[_Mysize]} 166 | 167 | size() 168 | 169 | 170 | -------------------------------------------------------------------------------- /packaging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_subdirectory(nuget) 3 | -------------------------------------------------------------------------------- /packaging/nuget/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/build_tools/nuget.exe" nuget_exe) 3 | file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" nupkg_dir) 4 | file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_BUILD_VERSION}.nupkg" wil_nupkg) 5 | 6 | # The build servers don't have an up-to-date version of nuget, so pull it down ourselves... 7 | file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe}) 8 | 9 | file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h) 10 | file(GLOB_RECURSE wil_natvis ${CMAKE_SOURCE_DIR}/natvis/*.natvis) 11 | 12 | add_custom_command(OUTPUT ${wil_nupkg} 13 | COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive 14 | DEPENDS 15 | ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec 16 | ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets 17 | ${wil_headers} 18 | ${wil_natvis} 19 | ${CMAKE_SOURCE_DIR}/LICENSE 20 | ${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt) 21 | 22 | add_custom_target(make_wil_nupkg DEPENDS ${wil_nupkg}) 23 | -------------------------------------------------------------------------------- /packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.Windows.ImplementationLibrary 5 | 0.0.0 6 | Windows Implementation Library 7 | Microsoft 8 | false 9 | The Windows Implementation Libraries (wil) were created to improve productivity and solve problems commonly seen by Windows developers. 10 | windows utility wil native 11 | © Microsoft Corporation. All rights reserved. 12 | LICENSE 13 | https://github.com/Microsoft/wil 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /scripts/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Windows Implementation Library Pipeline 2 | 3 | trigger: 4 | - master 5 | 6 | jobs: 7 | - job: CheckFormatting 8 | 9 | pool: 10 | vmImage: 'windows-2022' 11 | 12 | steps: 13 | - script: | 14 | :: The architecture is not important; we just need VCINSTALLDIR set 15 | call scripts\call-vcvars.cmd x64 16 | call scripts\format-changes.cmd origin/master 17 | if %ERRORLEVEL% neq 0 ( 18 | echo ##vso[task.logissue type=error]ERROR: This branch contains changes that have not been formatted with 'clang-format' 19 | echo NOTE: To resolve this issue, you can run 'clang-format' in the following ways: 20 | echo * Run `scripts/format-changes.cmd ^` where '^' is either 'origin/master' or 'upstream/master' 21 | echo depending on whether or not this is a fork. This will only format the changes you made relative to the 22 | echo master branch in the 'microsoft/wil' repo. 23 | echo * Run `scripts/run-clang-format.cmd` which will run 'clang-format' on _all_ source files. This script is 24 | echo simpler to run, however there's a chance it may touch additional files you never changed due to you having 25 | echo a mis-matched version of 'clang-format'. This may require you to manually revert changes made by 26 | echo 'clang-format' to the locations where you made no code changes. 27 | echo * Build the 'format' target ^(e.g. `ninja format`^). This is equivalent to running the second option above. 28 | echo. 29 | echo For more information, please see https://github.com/microsoft/wil?tab=readme-ov-file#formatting 30 | echo. 31 | echo NOTE: As an additional note, given that different versions of 'clang-format' may have different behaviors, this 32 | echo may be a false positive. If you believe that to be the case ^(e.g. none of the above resulted in modifications 33 | echo to the code you have changed^), please note this in your PR. 34 | echo ---------------------------------------------- 35 | echo See below for the file^(s^) that have changed: 36 | git diff 37 | exit /b 1 38 | ) 39 | displayName: 'Check Formatting of Changes' 40 | 41 | - job: BuildAndTest 42 | timeoutInMinutes: 90 43 | 44 | variables: 45 | compilers: 'clang,msvc' 46 | architectures: 'x86,x64' 47 | buildTypes: 'debug,relwithdebinfo' 48 | 49 | strategy: 50 | matrix: 51 | ${{each compiler in split(variables.compilers, ',')}}: 52 | ${{each arch in split(variables.architectures, ',')}}: 53 | ${{each buildType in split(variables.buildTypes, ',')}}: 54 | ${{compiler}}-${{arch}}-${{buildType}}: 55 | compiler: ${{compiler}} 56 | arch: ${{arch}} 57 | build-type: ${{buildType}} 58 | 59 | pool: 60 | vmImage: 'windows-2022' 61 | 62 | steps: 63 | - script: | 64 | choco upgrade llvm -y 65 | if %ERRORLEVEL% NEQ 0 goto :eof 66 | echo ##vso[task.setvariable variable=PATH]%PATH%;C:\Program Files\LLVM\bin 67 | displayName: 'Install Clang' 68 | condition: eq(variables['compiler'], 'clang') 69 | 70 | - script: | 71 | call scripts\call-vcvars.cmd $(arch) 72 | if %ERRORLEVEL% NEQ 0 goto :eof 73 | 74 | call scripts\init.cmd -c $(compiler) -b $(build-type) --fast 75 | if %ERRORLEVEL% NEQ 0 goto :eof 76 | 77 | call scripts\build_all.cmd 78 | displayName: 'Build $(compiler) $(arch)$(build-type)' 79 | 80 | - script: | 81 | call scripts\call-vcvars.cmd $(arch) 82 | set ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE=1 83 | REM NOTE: You can add '-s' to display detailed information about passing assertions, which can be helpful for diagnosing 84 | REM hanging tests, however the callstack logic has mostly rendered this unnecssary for most scenarios. 85 | REM That said, we do pass '-d yes' so that it's easier to see which tests were run immediately before a hanging/crashing test 86 | call scripts\runtests.cmd ~[LocalOnly] -d yes 87 | displayName: 'Test $(compiler) $(arch)$(build-type)' 88 | -------------------------------------------------------------------------------- /scripts/build_all.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | set BUILD_ROOT=%~dp0\..\build 6 | 7 | if NOT defined Platform ( 8 | echo ERROR: The build_all.cmd script must be run from a Visual Studio command window 9 | exit /B 1 10 | ) 11 | 12 | set COMPILERS=clang msvc 13 | set BUILD_TYPES=debug release relwithdebinfo minsizerel 14 | 15 | for %%c in (%COMPILERS%) do ( 16 | for %%b in (%BUILD_TYPES%) do ( 17 | call :build %%c %%b 18 | if !ERRORLEVEL! NEQ 0 ( goto :eof ) 19 | ) 20 | ) 21 | 22 | echo All build completed successfully! 23 | 24 | goto :eof 25 | 26 | :: build [compiler] [type] 27 | :build 28 | set BUILD_DIR=%BUILD_ROOT%\%1-%Platform%-%2 29 | if not exist %BUILD_DIR% ( 30 | goto :eof 31 | ) 32 | 33 | pushd %BUILD_DIR% 34 | echo Building from %CD% 35 | ninja 36 | set EXIT_CODE=%ERRORLEVEL% 37 | popd 38 | exit /B %EXIT_CODE% 39 | -------------------------------------------------------------------------------- /scripts/call-vcvars.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: NOTE: Intentionally not specifying 'setlocal' as we want the side-effects of calling 'vcvars' to affect the caller 4 | 5 | :: NOTE: This is primarily intended to be used by the build pipelines, hence the hard-coded paths, and might not be 6 | :: generally useful. The following check is intended to help diagnose such possible issues 7 | if NOT EXIST "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" ( 8 | echo ERROR: Could not locate 'vcvars' batch file. This script is intended to be run from a build machine & exit /B 1 9 | ) 10 | 11 | if /I "%1"=="x86" ( 12 | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64_x86 13 | ) else if /I "%1"=="x64" ( 14 | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 15 | ) else if /I "%1"=="arm64" ( 16 | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" arm64 17 | ) else echo ERROR: Requires one of 'x86', 'x64', or 'arm64' & exit /B 1 18 | -------------------------------------------------------------------------------- /scripts/enable-appverifier.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | REM NOTE: AppVerifier & ASan don't mix well, so leaving it out 6 | set EXES=witest.app witest.cpplatest witest.cppwinrt-notifiable-server-lock witest.noexcept witest witest.win7 7 | set LAYERS=Exceptions Handles Heaps Locks Memory SRWLock Threadpool TLS 8 | 9 | for %%e in (%EXES%) do ( 10 | appverif /enable %LAYERS% /for %%e.exe 11 | ) 12 | -------------------------------------------------------------------------------- /scripts/find-clang-format.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: NOTE: No 'setlocal' since we're "returning" the definition of "CLANG_FORMAT" to the caller 3 | 4 | :: Clang format's behavior has changed over time, meaning that different machines with different versions of LLVM 5 | :: installed may get different formatting behavior. In an attempt to ensure consistency, we use the clang-format.exe 6 | :: that ships with Visual Studio. There may still be issues if two different machines have different versions of Visual 7 | :: Studio installed, however this will hopefully improve things 8 | set CLANG_FORMAT=%VCINSTALLDIR%\Tools\Llvm\bin\clang-format.exe 9 | if not exist "%CLANG_FORMAT%" ( 10 | set CLANG_FORMAT= 11 | echo ERROR: clang-format.exe not found at %%VCINSTALLDIR%%\Tools\Llvm\bin\clang-format.exe 12 | echo ERROR: Ensure that this script is being run from a Visual Studio command prompt 13 | exit /B 1 14 | ) 15 | -------------------------------------------------------------------------------- /scripts/format-changes.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | set PROJECT_ROOT=%~dp0\.. 6 | 7 | set BRANCH=%1 8 | if "%BRANCH%"=="" ( 9 | echo ERROR: Missing commit/branch argument. If this is a fork of the microsoft/wil repo, you likely want to specify 10 | echo 'upstream/master'. If this is not a fork, you likely want to specify 'origin/master'. Examples: 11 | echo. 12 | echo format-changes.cmd origin/master 13 | echo format-changes.cmd upstream/master 14 | exit /b 1 15 | ) 16 | 17 | call "%~dp0/find-clang-format.cmd" 18 | if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL% 19 | 20 | pushd %PROJECT_ROOT% > NUL 21 | git clang-format %BRANCH% --binary "%CLANG_FORMAT%" --style file -- include/wil/*.h tests/*.h tests/*.cpp 22 | popd > NUL 23 | -------------------------------------------------------------------------------- /scripts/init.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | :: Globals 6 | set BUILD_ROOT=%~dp0\..\build 7 | 8 | goto :init 9 | 10 | :usage 11 | echo USAGE: 12 | echo init.cmd [--help] [-c^|--compiler ^] [-g^|--generator ^] 13 | echo [-b^|--build-type ^] [-p^|--vcpkg path/to/vcpkg/root] 14 | echo [-v^|--version X.Y.Z] [--cppwinrt ^] [--fast] 15 | echo. 16 | echo ARGUMENTS 17 | echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc' 18 | echo -g^|--generator Controls the CMake generator used, either 'ninja' (the default) or 'msbuild' 19 | echo -b^|--build-type Controls the value of 'CMAKE_BUILD_TYPE', either 'debug' (the default), 'release', 20 | echo 'relwithdebinfo', or 'minsizerel' 21 | echo -p^|--vcpkg Specifies the path to the root of your local vcpkg clone. If this value is not 22 | echo specified, then several attempts will be made to try and deduce it. The first attempt 23 | echo will be to check for the presence of the %%VCPKG_ROOT%% environment variable. If that 24 | echo variable does not exist, the 'where' command will be used to try and locate the 25 | echo vcpkg.exe executable. If that check fails, the path '\vcpkg' will be used to try and 26 | echo locate the vcpkg clone. If all those checks fail, initialization will fail. 27 | echo -v^|--version Specifies the version of the NuGet package produced. Primarily only used by the CI 28 | echo build and is typically not necessary when building locally 29 | echo --cppwinrt Manually specifies the version of C++/WinRT to use for generating headers 30 | echo --fast Used to (slightly) reduce compile times and build output size. This is primarily 31 | echo used by the CI build machines where resources are more constrained. This switch is 32 | echo temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed 33 | goto :eof 34 | 35 | :init 36 | :: Initialize values as empty so that we can identify if we are using defaults later for error purposes 37 | set COMPILER= 38 | set GENERATOR= 39 | set BUILD_TYPE= 40 | set CMAKE_ARGS= 41 | set VERSION= 42 | set VCPKG_ROOT_PATH= 43 | set CPPWINRT_VERSION= 44 | set FAST_BUILD=0 45 | 46 | :parse 47 | if /I "%~1"=="" goto :execute 48 | 49 | if /I "%~1"=="--help" call :usage & goto :eof 50 | 51 | set COMPILER_SET=0 52 | if /I "%~1"=="-c" set COMPILER_SET=1 53 | if /I "%~1"=="--compiler" set COMPILER_SET=1 54 | if %COMPILER_SET%==1 ( 55 | if "%COMPILER%" NEQ "" echo ERROR: Compiler already specified & call :usage & exit /B 1 56 | 57 | if /I "%~2"=="clang" set COMPILER=clang 58 | if /I "%~2"=="msvc" set COMPILER=msvc 59 | if "!COMPILER!"=="" echo ERROR: Unrecognized/missing compiler %~2 & call :usage & exit /B 1 60 | 61 | shift 62 | shift 63 | goto :parse 64 | ) 65 | 66 | set GENERATOR_SET=0 67 | if /I "%~1"=="-g" set GENERATOR_SET=1 68 | if /I "%~1"=="--generator" set GENERATOR_SET=1 69 | if %GENERATOR_SET%==1 ( 70 | if "%GENERATOR%" NEQ "" echo ERROR: Generator already specified & call :usage & exit /B 1 71 | 72 | if /I "%~2"=="ninja" set GENERATOR=ninja 73 | if /I "%~2"=="msbuild" set GENERATOR=msbuild 74 | if "!GENERATOR!"=="" echo ERROR: Unrecognized/missing generator %~2 & call :usage & exit /B 1 75 | 76 | shift 77 | shift 78 | goto :parse 79 | ) 80 | 81 | set BUILD_TYPE_SET=0 82 | if /I "%~1"=="-b" set BUILD_TYPE_SET=1 83 | if /I "%~1"=="--build-type" set BUILD_TYPE_SET=1 84 | if %BUILD_TYPE_SET%==1 ( 85 | if "%BUILD_TYPE%" NEQ "" echo ERROR: Build type already specified & call :usage & exit /B 1 86 | 87 | if /I "%~2"=="debug" set BUILD_TYPE=debug 88 | if /I "%~2"=="release" set BUILD_TYPE=release 89 | if /I "%~2"=="relwithdebinfo" set BUILD_TYPE=relwithdebinfo 90 | if /I "%~2"=="minsizerel" set BUILD_TYPE=minsizerel 91 | if "!BUILD_TYPE!"=="" echo ERROR: Unrecognized/missing build type %~2 & call :usage & exit /B 1 92 | 93 | shift 94 | shift 95 | goto :parse 96 | ) 97 | 98 | set VCPKG_ROOT_SET=0 99 | if /I "%~1"=="-p" set VCPKG_ROOT_SET=1 100 | if /I "%~1"=="--vcpkg" set VCPKG_ROOT_SET=1 101 | if %VCPKG_ROOT_SET%==1 ( 102 | if "%VCPKG_ROOT_PATH%" NEQ "" echo ERROR: vcpkg root path already specified & call :usage & exit /B 1 103 | if /I "%~2"=="" echo ERROR: Path to vcpkg root missing & call :usage & exit /B 1 104 | 105 | set VCPKG_ROOT_PATH=%~2 106 | 107 | shift 108 | shift 109 | goto :parse 110 | ) 111 | 112 | set VERSION_SET=0 113 | if /I "%~1"=="-v" set VERSION_SET=1 114 | if /I "%~1"=="--version" set VERSION_SET=1 115 | if %VERSION_SET%==1 ( 116 | if "%VERSION%" NEQ "" echo ERROR: Version already specified & call :usage & exit /B 1 117 | if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1 118 | 119 | set VERSION=%~2 120 | 121 | shift 122 | shift 123 | goto :parse 124 | ) 125 | 126 | if /I "%~1"=="--cppwinrt" ( 127 | if "%CPPWINRT_VERSION%" NEQ "" echo ERROR: C++/WinRT version already specified & call :usage & exit /B 1 128 | if /I "%~2"=="" echo ERROR: C++/WinRT version string missing & call :usage & exit /B 1 129 | 130 | set CPPWINRT_VERSION=%~2 131 | 132 | shift 133 | shift 134 | goto :parse 135 | ) 136 | 137 | if /I "%~1"=="--fast" ( 138 | if %FAST_BUILD% NEQ 0 echo ERROR: Fast build already specified 139 | set FAST_BUILD=1 140 | shift 141 | goto :parse 142 | ) 143 | 144 | echo ERROR: Unrecognized argument %~1 145 | call :usage 146 | exit /B 1 147 | 148 | :execute 149 | :: Check for conflicting arguments 150 | if "%GENERATOR%"=="msbuild" ( 151 | if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1 152 | ) 153 | 154 | :: Select defaults 155 | if "%GENERATOR%"=="" set GENERATOR=ninja 156 | if %GENERATOR%==msbuild set COMPILER=msvc 157 | 158 | if "%COMPILER%"=="" set COMPILER=clang 159 | 160 | if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug 161 | 162 | if "%VCPKG_ROOT_PATH%"=="" ( 163 | :: First check for %VCPKG_ROOT% variable 164 | if defined VCPKG_ROOT ( 165 | set "VCPKG_ROOT_PATH=%VCPKG_ROOT%" 166 | ) else ( 167 | :: Next check the PATH for vcpkg.exe 168 | for %%i in (vcpkg.exe) do set VCPKG_ROOT_PATH=%%~dp$PATH:i 169 | 170 | if "!VCPKG_ROOT_PATH!"=="" ( 171 | :: Finally, check the root of the drive for a clone of the name 'vcpkg' 172 | if exist \vcpkg\vcpkg.exe ( 173 | for %%i in (%cd%) do set VCPKG_ROOT_PATH=%%~di\vcpkg 174 | ) 175 | ) 176 | ) 177 | ) 178 | if "%VCPKG_ROOT_PATH%"=="" ( 179 | echo ERROR: Unable to locate the root path of your local vcpkg installation. 180 | :: TODO: Better messaging 181 | exit /B 1 182 | ) 183 | 184 | :: Formulate CMake arguments 185 | if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja 186 | 187 | :: NOTE: clang++ seems to currently have an issue handling SEH & destructors, so use clang-cl for now which, for 188 | :: some reason, doesn't seem to have the same issue 189 | if %COMPILER%==clang set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl 190 | if %COMPILER%==msvc set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl 191 | 192 | if %GENERATOR% NEQ msbuild ( 193 | if %BUILD_TYPE%==debug set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Debug 194 | if %BUILD_TYPE%==release set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Release 195 | if %BUILD_TYPE%==relwithdebinfo set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=RelWithDebInfo 196 | if %BUILD_TYPE%==minsizerel set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=MinSizeRel 197 | ) else ( 198 | :: The Visual Studio generator, by default, will use the most recent Windows SDK version installed that is not 199 | :: greater than the host OS version. This decision is to ensure that anything built will be able to run on the 200 | :: machine that built it. This experience is generally okay, if not desired, but affects us since we build with 201 | :: '/permissive-' etc. and older versions of the SDK are typically not as clean as the most recent versions. 202 | :: This flag will force the generator to select the most recent SDK version independent of host OS version. 203 | set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0 204 | ) 205 | 206 | if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION% 207 | 208 | if "%CPPWINRT_VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DCPPWINRT_VERSION=%CPPWINRT_VERSION% 209 | 210 | if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON 211 | 212 | set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_TOOLCHAIN_FILE="%VCPKG_ROOT_PATH%\scripts\buildsystems\vcpkg.cmake" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 213 | 214 | :: Figure out the platform 215 | if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1 216 | if "%Platform%"=="x86" ( 217 | if %COMPILER%==clang set CFLAGS=-target i386-win32-msvc & set CXXFLAGS=-target i386-win32-msvc 218 | ) else if "%Platform%"=="x64" ( 219 | if %COMPILER%==clang set CFLAGS=-target x86_64-win32-msvc & set CXXFLAGS=-target x86_64-win32-msvc 220 | ) else if "%Platform%"=="arm64" ( 221 | if "%COMPILER%"=="clang" set CFLAGS=-target aarch64-win32-msvc & set CXXFLAGS=-target aarch64-win32-msvc 222 | ) 223 | 224 | :: Set up the build directory 225 | set BUILD_DIR=%BUILD_ROOT%\%COMPILER%-%Platform%-%BUILD_TYPE% 226 | mkdir %BUILD_DIR% > NUL 2>&1 227 | 228 | :: Run CMake 229 | pushd %BUILD_DIR% 230 | echo Using compiler....... %COMPILER% 231 | echo Using architecture... %Platform% 232 | echo Using build type..... %BUILD_TYPE% 233 | echo Using build root..... %CD% 234 | echo. 235 | cmake %CMAKE_ARGS% ..\.. 236 | popd 237 | 238 | goto :eof 239 | -------------------------------------------------------------------------------- /scripts/init_all.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | :: NOTE: Architecture is picked up from the command window, so we can't control that here :( 6 | set COMPILERS=clang msvc 7 | set BUILD_TYPES=debug relwithdebinfo 8 | 9 | for %%c in (%COMPILERS%) do ( 10 | for %%b in (%BUILD_TYPES%) do ( 11 | call %~dp0\init.cmd -c %%c -g ninja -b %%b %* 12 | if !ERRORLEVEL! NEQ 0 ( goto :eof ) 13 | ) 14 | ) 15 | -------------------------------------------------------------------------------- /scripts/run-clang-format.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | set ROOT_DIR=%~dp0\.. 6 | 7 | set DIRS=include/wil tests 8 | set EXTS=.cpp .h 9 | 10 | call "%~dp0/find-clang-format.cmd" 11 | if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL% 12 | 13 | for %%d in (%DIRS%) do call :format_files %ROOT_DIR%\%%d 14 | goto :eof 15 | 16 | :format_files 17 | :: Format all desired files 18 | for %%e in (%EXTS%) do ( 19 | for %%f in (%1\*%%e) do call :run_clang_format %%f 20 | ) 21 | goto :eof 22 | 23 | :run_clang_format 24 | "%CLANG_FORMAT%" -style=file -i %1 25 | goto :eof 26 | -------------------------------------------------------------------------------- /scripts/runtests.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | set TEST_ARGS=%* 6 | 7 | :: For some reason, '__asan_default_options' seems to have no effect under some unknown circumstances (despite the 8 | :: function being called), so set the environment variable as a workaround. This ensures that we get the correct 9 | :: behavior at least when this script is being used, which should cover most developer scenarios as well as the CI 10 | set ASAN_OPTIONS=allocator_may_return_null=1:new_delete_type_mismatch=0 11 | 12 | set BUILD_ROOT=%~dp0\..\build 13 | 14 | set COMPILERS=clang msvc 15 | set BUILD_TYPES=debug release relwithdebinfo minsizerel 16 | 17 | :: The asan binaries are architecture specific, so we unfortunately must limit the tests we run by the architecture of 18 | :: the command window. 19 | if "%Platform%"=="x64" ( 20 | set ARCHITECTURES=x64 21 | ) else if "%Platform%"=="x86" ( 22 | set ARCHITECTURES=x86 23 | ) else if "%Platform%"=="arm64" ( 24 | set ARCHITECTURES=arm64 25 | ) 26 | 27 | for %%c in (%COMPILERS%) do ( 28 | for %%a in (%ARCHITECTURES%) do ( 29 | for %%b in (%BUILD_TYPES%) do ( 30 | call :execute_tests %%c-%%a-%%b 31 | if !ERRORLEVEL! NEQ 0 ( goto :eof ) 32 | ) 33 | ) 34 | ) 35 | 36 | goto :eof 37 | 38 | :execute_tests 39 | set BUILD_DIR=%BUILD_ROOT%\%1 40 | if not exist %BUILD_DIR% ( goto :eof ) 41 | 42 | pushd %BUILD_DIR% 43 | echo Running tests from %CD% 44 | call :execute_test app witest.app.exe 45 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 46 | call :execute_test cpplatest witest.cpplatest.exe 47 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 48 | call :execute_test cppwinrt-notifiable-server-lock witest.cppwinrt-notifiable-server-lock.exe 49 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 50 | call :execute_test noexcept witest.noexcept.exe 51 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 52 | call :execute_test normal witest.exe 53 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 54 | call :execute_test sanitize-address witest.asan.exe 55 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 56 | call :execute_test sanitize-undefined-behavior witest.ubsan.exe 57 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 58 | call :execute_test win7 witest.win7.exe 59 | if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) 60 | :: Fall through 61 | 62 | :execute_tests_done 63 | set EXIT_CODE=%ERRORLEVEL% 64 | popd 65 | exit /B %EXIT_CODE% 66 | 67 | :execute_test 68 | if not exist tests\%1\%2 ( goto :eof ) 69 | echo Running %1 tests... 70 | tests\%1\%2 %TEST_ARGS% 71 | goto :eof 72 | -------------------------------------------------------------------------------- /scripts/stress-test.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | setlocal EnableDelayedExpansion 4 | 5 | set THIS_DIR=%~dp0\ 6 | 7 | :repeat 8 | REM If you're running this test, you probably don't want it constantly clearing out your clipboard... 9 | call %THIS_DIR%\runtests.cmd ~UniqueCloseClipboardCall %* 10 | if %ERRORLEVEL%==0 goto :repeat 11 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | include(${PROJECT_SOURCE_DIR}/cmake/common_build_flags.cmake) 3 | 4 | # All projects need to reference the WIL headers 5 | include_directories(${PROJECT_SOURCE_DIR}/include) 6 | 7 | # Because we don't always use msbuild, we need to run nuget manually 8 | find_program(NUGET nuget) 9 | if (NOT NUGET) 10 | message(FATAL_ERROR "Unable to find the nuget CLI tool. Please install it from https://www.nuget.org/downloads and ensure it has been added to the PATH") 11 | endif() 12 | 13 | execute_process(COMMAND 14 | ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages 15 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 16 | RESULT_VARIABLE ret) 17 | if (NOT ret EQUAL 0) 18 | message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}") 19 | endif() 20 | 21 | set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) 22 | execute_process(COMMAND 23 | ${CPPWINRT} -input sdk+ -output include 24 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 25 | RESULT_VARIABLE ret) 26 | if (NOT ret EQUAL 0) 27 | message(FATAL_ERROR "Failed to run cppwinrt.exe") 28 | endif() 29 | 30 | # In general, we've supported C++14 as the minimum standard, however the C++17 standard is already a couple releases old 31 | # and the Windows OS build has been on C++17 for some time. For the time being, we will still try and guard uses of 32 | # C++17+ features, however we've started to take dependencies on C++17 features in the tests and therefore no longer 33 | # build them with older standards 34 | set(CMAKE_CXX_STANDARD 17) 35 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 36 | 37 | # Turn on CTest support 38 | enable_testing() 39 | 40 | include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) 41 | 42 | # The build pipelines have limitations that local development environments do not, so turn a few knobs 43 | if (${FAST_BUILD}) 44 | if (MSVC) 45 | replace_cxx_flag("/GR" "/GR-") # Disables RTTI 46 | else() 47 | add_compile_options(-fno-rtti) 48 | endif() 49 | 50 | add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD) 51 | endif() 52 | 53 | # For some unknown reason, 'RelWithDebInfo' compiles with '/Ob1' as opposed to '/Ob2' which prevents inlining of 54 | # functions not marked 'inline'. The reason we prefer 'RelWithDebInfo' over 'Release' is to get debug info, so manually 55 | # revert to the desired (and default) inlining behavior as that exercises more interesting code paths 56 | if (MSVC AND "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") 57 | # TODO: This is currently blocked by an apparent Clang bug: https://github.com/llvm/llvm-project/issues/59690 58 | # replace_cxx_flag("/Ob1" "/Ob2") 59 | endif() 60 | 61 | set(COMMON_SOURCES 62 | ${CMAKE_CURRENT_SOURCE_DIR}/CommonTests.cpp 63 | ${CMAKE_CURRENT_SOURCE_DIR}/ComTests.cpp 64 | ${CMAKE_CURRENT_SOURCE_DIR}/FileSystemTests.cpp 65 | ${CMAKE_CURRENT_SOURCE_DIR}/MockingTests.cpp 66 | ${CMAKE_CURRENT_SOURCE_DIR}/NTResultTests.cpp 67 | ${CMAKE_CURRENT_SOURCE_DIR}/ResourceTests.cpp 68 | ${CMAKE_CURRENT_SOURCE_DIR}/ResultTests.cpp 69 | ${CMAKE_CURRENT_SOURCE_DIR}/Rpc.cpp 70 | ${CMAKE_CURRENT_SOURCE_DIR}/SafeCastTests.cpp 71 | ${CMAKE_CURRENT_SOURCE_DIR}/StlTests.cpp 72 | ${CMAKE_CURRENT_SOURCE_DIR}/TraceLoggingTests.cpp 73 | ${CMAKE_CURRENT_SOURCE_DIR}/TraceLoggingTests_PartB.cpp 74 | ${CMAKE_CURRENT_SOURCE_DIR}/WindowingTests.cpp 75 | ${CMAKE_CURRENT_SOURCE_DIR}/WistdTests.cpp 76 | ${CMAKE_CURRENT_SOURCE_DIR}/wiTest.cpp 77 | ${CMAKE_CURRENT_SOURCE_DIR}/../natvis/wil.natvis 78 | ) 79 | 80 | # Source files that can be compiled downlevel to at least Win7 81 | set(DOWNLEVEL_SOURCES 82 | ${CMAKE_CURRENT_SOURCE_DIR}/NetworkTests.cpp 83 | ${CMAKE_CURRENT_SOURCE_DIR}/RegistryTests.cpp 84 | ${CMAKE_CURRENT_SOURCE_DIR}/TokenHelpersTests.cpp 85 | ${CMAKE_CURRENT_SOURCE_DIR}/WatcherTests.cpp 86 | ) 87 | 88 | # Source files that require WinRT (Win8+ & can compile with App partition) 89 | set(WINRT_SOURCES 90 | ${CMAKE_CURRENT_SOURCE_DIR}/UniqueWinRTEventTokenTests.cpp 91 | ${CMAKE_CURRENT_SOURCE_DIR}/WinRTTests.cpp 92 | ) 93 | 94 | # Source files that can compile on Win11+, but only on the Destkop partition 95 | set(WIN11_DESTKTOP_SOURCES 96 | ${CMAKE_CURRENT_SOURCE_DIR}/WinVerifyTrustTest.cpp 97 | ) 98 | 99 | # Source files that require C++20 100 | set(CPP20_SOURCES 101 | ${CMAKE_CURRENT_SOURCE_DIR}/CoroutineTests.cpp 102 | ${CMAKE_CURRENT_SOURCE_DIR}/CppWinRT20Tests.cpp 103 | ${CMAKE_CURRENT_SOURCE_DIR}/CppWinRTAuthoringTests.cpp 104 | ${CMAKE_CURRENT_SOURCE_DIR}/CppWinRTTests.cpp 105 | ${CMAKE_CURRENT_SOURCE_DIR}/app.manifest 106 | ) 107 | 108 | # It appears as though Clang has some issues with exception handling inside of coroutines, which causes issues when 109 | # trying to run the ComApartmentVariableTests.cpp tests, so disable on Clang for now 110 | if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") 111 | set(CPP20_SOURCES 112 | ${CPP20_SOURCES} 113 | ${CMAKE_CURRENT_SOURCE_DIR}/ComApartmentVariableTests.cpp 114 | ) 115 | endif() 116 | 117 | if (MSVC) 118 | add_link_options(/NATVIS:${CMAKE_SOURCE_DIR}/natvis/wil.natvis) 119 | endif() 120 | 121 | find_path(DETOURS_INCLUDE_DIRS detours/detours.h) 122 | find_library(DETOURS_LIBRARY detours REQUIRED) 123 | 124 | find_package(Catch2 CONFIG REQUIRED) 125 | 126 | include_directories(${DETOURS_INCLUDE_DIRS}) 127 | add_definitions(-DNOMINMAX) 128 | 129 | # We build the 'app' tests just to ensure various things can build for the App partition. We don't actually run them in 130 | # an app container or anything. By building 'main.cpp' separately and linking to it, we allow the app tests to get the 131 | # same callstack printing logic as the rest of the tests. 132 | add_library(witest.main STATIC) 133 | target_sources(witest.main 134 | PRIVATE 135 | main.cpp 136 | ) 137 | target_link_libraries(witest.main 138 | INTERFACE 139 | ${DETOURS_LIBRARY} 140 | Catch2::Catch2 141 | ntdll.lib 142 | dbghelp.lib 143 | ws2_32.lib 144 | ) 145 | 146 | link_libraries(witest.main) 147 | 148 | add_subdirectory(app) 149 | add_subdirectory(cpplatest) 150 | add_subdirectory(cppwinrt-notifiable-server-lock) 151 | add_subdirectory(noexcept) 152 | add_subdirectory(normal) 153 | add_subdirectory(win7) 154 | 155 | add_test(NAME app COMMAND $) 156 | add_test(NAME cpplatest COMMAND $) 157 | add_test(NAME cppwinrt-notifiable-server-lock COMMAND $) 158 | add_test(NAME noexcept COMMAND $) 159 | add_test(NAME normal COMMAND $) 160 | add_test(NAME win7 COMMAND $) 161 | 162 | set(DEBUG_BUILD FALSE) 163 | set(HAS_DEBUG_INFO FALSE) 164 | 165 | if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") 166 | set(DEBUG_BUILD TRUE) 167 | set(HAS_DEBUG_INFO TRUE) 168 | elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") 169 | set(HAS_DEBUG_INFO TRUE) 170 | endif() 171 | # Release & MinSizeRel => keep all false 172 | 173 | set(ASAN_AVAILABLE FALSE) 174 | set(UBSAN_AVAILABLE FALSE) 175 | 176 | # Neither Clang nor MSVC currently ship with ARM64 ASan/UBSan libraries/binaries, so disable until support is added. 177 | if ((NOT defined $ENV{Platform}) OR (NOT $ENV{Platform} STREQUAL "arm64")) 178 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 179 | # Address Sanitizer is available for all architectures and build types, but warns/errors if debug info is not enabled 180 | set(ASAN_AVAILABLE ${HAS_DEBUG_INFO}) 181 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 182 | # Sanitizers are not available with debug libraries 183 | # Starting with Clang 17, there appears to be some issue where linking to the dynamic asan libraries seems to think that 184 | # things like 'operator new' are present in the executable, but only for x64 for some reason. As there's no way to 185 | # detect the target system architecture, use a good proxy 186 | set(ASAN_AVAILABLE NOT ${DEBUG_BUILD} AND ${CMAKE_SIZEOF_VOID_P} EQUAL 4) 187 | set(UBSAN_AVAILABLE NOT ${DEBUG_BUILD}) 188 | endif() 189 | endif() 190 | 191 | if (${ASAN_AVAILABLE}) 192 | add_subdirectory(sanitize-address) 193 | add_test(NAME witest.asan COMMAND $) 194 | endif() 195 | 196 | if (${UBSAN_AVAILABLE}) 197 | # TODO: Disabled until https://github.com/microsoft/STL/issues/3568 is resolved 198 | # add_subdirectory(sanitize-undefined-behavior) 199 | endif() 200 | -------------------------------------------------------------------------------- /tests/CommonTests.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common.h" 8 | 9 | TEST_CASE("CommonTests::OutParamHelpers", "[common]") 10 | { 11 | int i = 2; 12 | int* pOutTest = &i; 13 | int* pNullTest = nullptr; 14 | 15 | SECTION("Value type") 16 | { 17 | wil::assign_to_opt_param(pNullTest, 3); 18 | wil::assign_to_opt_param(pOutTest, 3); 19 | REQUIRE(*pOutTest == 3); 20 | } 21 | 22 | SECTION("Pointer to value type") 23 | { 24 | int** ppOutTest = &pOutTest; 25 | int** ppNullTest = nullptr; 26 | wil::assign_null_to_opt_param(ppNullTest); 27 | wil::assign_null_to_opt_param(ppOutTest); 28 | REQUIRE(*ppOutTest == nullptr); 29 | } 30 | 31 | SECTION("COM out pointer") 32 | { 33 | Microsoft::WRL::ComPtr spUnk; 34 | IUnknown** ppunkNull = nullptr; 35 | IUnknown* pUnk = reinterpret_cast(1); 36 | IUnknown** ppUnkValid = &pUnk; 37 | 38 | wil::detach_to_opt_param(ppunkNull, spUnk); 39 | wil::detach_to_opt_param(ppUnkValid, spUnk); 40 | REQUIRE(*ppUnkValid == nullptr); 41 | } 42 | } 43 | 44 | TEST_CASE("CommonTests::TypeValidation", "[common]") 45 | { 46 | std::unique_ptr boolCastClass; 47 | std::vector noBoolCastClass; 48 | HRESULT hr = S_OK; 49 | BOOL bigBool = true; 50 | bool smallBool = true; 51 | DWORD dword = 1; 52 | Microsoft::WRL::ComPtr comPtr; 53 | (void)dword; 54 | 55 | // NOTE: The commented out verify* calls should give compilation errors 56 | SECTION("verify_bool") 57 | { 58 | REQUIRE(wil::verify_bool(smallBool)); 59 | REQUIRE(wil::verify_bool(bigBool)); 60 | REQUIRE_FALSE(wil::verify_bool(boolCastClass)); 61 | REQUIRE_FALSE(wil::verify_bool(comPtr)); 62 | // wil::verify_bool(noBoolCastClass); 63 | // wil::verify_bool(dword); 64 | // wil::verify_bool(hr); 65 | } 66 | 67 | SECTION("verify_hresult") 68 | { 69 | // wil::verify_hresult(smallBool); 70 | // wil::verify_hresult(bigBool); 71 | // wil::verify_hresult(boolCastClass); 72 | // wil::verify_hresult(noBoolCastClass); 73 | // wil::verify_hresult(dword); 74 | // wil::verify_hresult(comPtr); 75 | REQUIRE(wil::verify_hresult(hr) == S_OK); 76 | } 77 | 78 | SECTION("verify_BOOL") 79 | { 80 | // wil::verify_BOOL(smallBool); 81 | REQUIRE(wil::verify_BOOL(bigBool)); 82 | // wil::verify_BOOL(boolCastClass); 83 | // wil::verify_BOOL(noBoolCastClass); 84 | // wil::verify_BOOL(dword); 85 | // wil::verify_BOOL(comPtr); 86 | // wil::verify_BOOL(hr); 87 | } 88 | } 89 | 90 | template 91 | static void FlagsMacrosNonStatic(T none, T one, T two, T three, T four) 92 | { 93 | T eval = one | four; 94 | 95 | REQUIRE(WI_AreAllFlagsSet(MDEC(eval), MDEC(one | four))); 96 | REQUIRE_FALSE(WI_AreAllFlagsSet(eval, one | three)); 97 | REQUIRE_FALSE(WI_AreAllFlagsSet(eval, three | two)); 98 | REQUIRE(WI_AreAllFlagsSet(eval, none)); 99 | 100 | REQUIRE(WI_IsAnyFlagSet(MDEC(eval), MDEC(one))); 101 | REQUIRE(WI_IsAnyFlagSet(eval, one | four | three)); 102 | REQUIRE_FALSE(WI_IsAnyFlagSet(eval, two)); 103 | 104 | REQUIRE(WI_AreAllFlagsClear(MDEC(eval), MDEC(three))); 105 | REQUIRE(WI_AreAllFlagsClear(eval, three | two)); 106 | REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | four)); 107 | REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | three)); 108 | 109 | REQUIRE(WI_IsAnyFlagClear(MDEC(eval), MDEC(three))); 110 | REQUIRE(WI_IsAnyFlagClear(eval, three | two)); 111 | REQUIRE(WI_IsAnyFlagClear(eval, four | three)); 112 | REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one)); 113 | REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one | four)); 114 | 115 | REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eval))); 116 | REQUIRE(WI_IsSingleFlagSet(eval & one)); 117 | 118 | REQUIRE(WI_IsSingleFlagSetInMask(MDEC(eval), MDEC(one))); 119 | REQUIRE(WI_IsSingleFlagSetInMask(eval, one | three)); 120 | REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, three)); 121 | REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, one | four)); 122 | 123 | REQUIRE_FALSE(WI_IsClearOrSingleFlagSet(MDEC(eval))); 124 | REQUIRE(WI_IsClearOrSingleFlagSet(eval & one)); 125 | REQUIRE(WI_IsClearOrSingleFlagSet(none)); 126 | 127 | REQUIRE(WI_IsClearOrSingleFlagSetInMask(MDEC(eval), MDEC(one))); 128 | REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, one | three)); 129 | REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, three)); 130 | REQUIRE_FALSE(WI_IsClearOrSingleFlagSetInMask(eval, one | four)); 131 | 132 | eval = none; 133 | WI_SetAllFlags(MDEC(eval), MDEC(one)); 134 | REQUIRE(eval == one); 135 | WI_SetAllFlags(eval, one | two); 136 | REQUIRE(eval == (one | two)); 137 | 138 | eval = one | two; 139 | WI_ClearAllFlags(MDEC(eval), one); 140 | REQUIRE(eval == two); 141 | WI_ClearAllFlags(eval, two); 142 | REQUIRE(eval == none); 143 | 144 | eval = one | two; 145 | WI_UpdateFlagsInMask(MDEC(eval), MDEC(two | three), MDEC(three | four)); 146 | REQUIRE(eval == (one | three)); 147 | 148 | eval = one; 149 | WI_ToggleAllFlags(MDEC(eval), MDEC(one | two)); 150 | REQUIRE(eval == two); 151 | } 152 | 153 | enum class EClassTest 154 | { 155 | None = 0x0, 156 | One = 0x1, 157 | Two = 0x2, 158 | Three = 0x4, 159 | Four = 0x8, 160 | }; 161 | DEFINE_ENUM_FLAG_OPERATORS(EClassTest); 162 | 163 | enum ERawTest : unsigned int 164 | { 165 | ER_None = 0x0, 166 | ER_One = 0x1, 167 | ER_Two = 0x2, 168 | ER_Three = 0x4, 169 | ER_Four = 0x8, 170 | }; 171 | DEFINE_ENUM_FLAG_OPERATORS(ERawTest); 172 | 173 | TEST_CASE("CommonTests::FlagsMacros", "[common]") 174 | { 175 | SECTION("Integral types") 176 | { 177 | FlagsMacrosNonStatic( 178 | static_cast(0), static_cast(0x1), static_cast(0x2), static_cast(0x4), static_cast(0x40)); 179 | FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x80u); 180 | FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x4000); 181 | FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x8000u); 182 | FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x80000000ul); 183 | FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x80000000ul); 184 | FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x8000000000000000ull); 185 | FlagsMacrosNonStatic(0, 0x1, 0x2, 0x4, 0x8000000000000000ull); 186 | } 187 | 188 | SECTION("Raw enum") 189 | { 190 | FlagsMacrosNonStatic(ER_None, ER_One, ER_Two, ER_Three, ER_Four); 191 | } 192 | 193 | SECTION("Enum class") 194 | { 195 | FlagsMacrosNonStatic(EClassTest::None, EClassTest::One, EClassTest::Two, EClassTest::Three, EClassTest::Four); 196 | 197 | EClassTest eclass = EClassTest::One | EClassTest::Two; 198 | REQUIRE(WI_IsFlagSet(MDEC(eclass), EClassTest::One)); 199 | REQUIRE(WI_IsFlagSet(eclass, EClassTest::Two)); 200 | REQUIRE_FALSE(WI_IsFlagSet(eclass, EClassTest::Three)); 201 | 202 | REQUIRE(WI_IsFlagClear(MDEC(eclass), EClassTest::Three)); 203 | REQUIRE_FALSE(WI_IsFlagClear(eclass, EClassTest::One)); 204 | 205 | REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eclass))); 206 | REQUIRE(WI_IsSingleFlagSet(eclass & EClassTest::One)); 207 | 208 | eclass = EClassTest::None; 209 | WI_SetFlag(MDEC(eclass), EClassTest::One); 210 | REQUIRE(eclass == EClassTest::One); 211 | 212 | eclass = EClassTest::None; 213 | WI_SetFlagIf(eclass, EClassTest::One, false); 214 | REQUIRE(eclass == EClassTest::None); 215 | WI_SetFlagIf(eclass, EClassTest::One, true); 216 | REQUIRE(eclass == EClassTest::One); 217 | 218 | eclass = EClassTest::None; 219 | WI_SetFlagIf(eclass, EClassTest::One, false); 220 | REQUIRE(eclass == EClassTest::None); 221 | WI_SetFlagIf(eclass, EClassTest::One, true); 222 | REQUIRE(eclass == EClassTest::One); 223 | 224 | eclass = EClassTest::One | EClassTest::Two; 225 | WI_ClearFlag(eclass, EClassTest::Two); 226 | REQUIRE(eclass == EClassTest::One); 227 | 228 | eclass = EClassTest::One | EClassTest::Two; 229 | WI_ClearFlagIf(eclass, EClassTest::One, false); 230 | REQUIRE(eclass == (EClassTest::One | EClassTest::Two)); 231 | WI_ClearFlagIf(eclass, EClassTest::One, true); 232 | REQUIRE(eclass == EClassTest::Two); 233 | 234 | eclass = EClassTest::None; 235 | WI_UpdateFlag(eclass, EClassTest::One, true); 236 | REQUIRE(eclass == EClassTest::One); 237 | WI_UpdateFlag(eclass, EClassTest::One, false); 238 | REQUIRE(eclass == EClassTest::None); 239 | 240 | eclass = EClassTest::One; 241 | WI_ToggleFlag(eclass, EClassTest::One); 242 | WI_ToggleFlag(eclass, EClassTest::Two); 243 | REQUIRE(eclass == EClassTest::Two); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /tests/CoroutineTests.cpp: -------------------------------------------------------------------------------- 1 | // This file aims to test with the COM support disabled. However, COM 2 | // headers are included by and an include of is unavoidable in 3 | // this project. The best we can do is to define WIN32_LEAN_AND_MEAN to prevent windows.h 4 | // from including (which in turn includes ). 5 | // 6 | // We also avoid including "common.h" from the current directory because it pulls in COM too. 7 | #include "pch.h" 8 | 9 | #define WIN32_LEAN_AND_MEAN 10 | 11 | #if defined(__cpp_impl_coroutine) || defined(__cpp_coroutines) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED) 12 | #include 13 | #include 14 | #endif 15 | 16 | #include "catch.hpp" 17 | 18 | // We are intentionally not including any COM headers so that wil::task<> and 19 | // wil::com_task<> do not light up full COM support. 20 | #ifdef _COMBASEAPI_H_ 21 | #error "COM headers are included in a file that is testing non-COM support." 22 | #endif 23 | 24 | #if (!defined(__clang__) && defined(__cpp_impl_coroutine) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) || \ 25 | defined(_RESUMABLE_FUNCTIONS_SUPPORTED) 26 | 27 | namespace 28 | { 29 | wil::task void_task(std::shared_ptr value) 30 | { 31 | ++*value; 32 | co_return; 33 | } 34 | } // namespace 35 | 36 | TEST_CASE("CppWinRTTests::SimpleNoCOMTaskTest", "[cppwinrt]") 37 | { 38 | std::thread([] { 39 | auto value = std::make_shared(0); 40 | void_task(value).get(); 41 | REQUIRE(*value == 1); 42 | }).join(); 43 | } 44 | 45 | #endif // coroutines 46 | -------------------------------------------------------------------------------- /tests/CppWinRT20Tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Prior to C++/WinRT 2.0 this would cause issues since we're not including wil/cppwinrt.h in this translation unit. 3 | // However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler' 4 | // global function pointer should be set, so these should all run successfully 5 | #include "pch.h" 6 | 7 | #include // Must be included before base.h 8 | 9 | #include 10 | #include 11 | 12 | #include "common.h" 13 | 14 | TEST_CASE("CppWinRTTests::CppWinRT20Test", "[cppwinrt]") 15 | { 16 | auto test = [](HRESULT hr) { 17 | try 18 | { 19 | THROW_HR(hr); 20 | } 21 | catch (...) 22 | { 23 | REQUIRE(hr == winrt::to_hresult()); 24 | } 25 | }; 26 | 27 | test(E_OUTOFMEMORY); 28 | test(E_INVALIDARG); 29 | test(E_UNEXPECTED); 30 | } 31 | -------------------------------------------------------------------------------- /tests/CppWinRTNotifiableServerLockTests.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "common.h" 4 | #undef GetCurrentTime 5 | #define WINRT_CUSTOM_MODULE_LOCK 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std::string_view_literals; 14 | 15 | TEST_CASE("CppWinRTComServerTests::NotifiableServerLock", "[cppwinrt_com_server]") 16 | { 17 | struct Test : winrt::implements 18 | { 19 | }; 20 | 21 | bool released = false; 22 | 23 | wil::notifiable_server_lock::instance().set_notifier([&] { 24 | released = true; 25 | }); 26 | auto resetOnExit = wil::scope_exit([] { 27 | wil::notifiable_server_lock::instance().set_notifier(nullptr); 28 | }); 29 | 30 | winrt::init_apartment(); 31 | 32 | { 33 | auto server{winrt::make()}; 34 | } 35 | 36 | REQUIRE(released); 37 | } 38 | -------------------------------------------------------------------------------- /tests/NtResultTests.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | 8 | #define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS)0xC000003AL) 9 | #define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L) 10 | #define STATUS_INVALID_CONNECTION ((NTSTATUS)0xC0000140L) 11 | #define E_LOAD_NAMESERVICE_FAILED ((HRESULT)0x80000140L) 12 | 13 | TEST_CASE("NtResultTests::NtReturn", "[result]") 14 | { 15 | auto status = []() { 16 | NT_RETURN_NTSTATUS(STATUS_INVALID_CONNECTION); 17 | }(); 18 | REQUIRE(status == STATUS_INVALID_CONNECTION); 19 | 20 | status = []() { 21 | NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG"); 22 | }(); 23 | REQUIRE(status == STATUS_INVALID_CONNECTION); 24 | 25 | status = []() { 26 | NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter"); 27 | }(); 28 | REQUIRE(status == STATUS_INVALID_CONNECTION); 29 | 30 | status = []() { 31 | NT_RETURN_IF_NTSTATUS_FAILED(STATUS_INVALID_CONNECTION); 32 | return STATUS_SUCCESS; 33 | }(); 34 | REQUIRE(status == STATUS_INVALID_CONNECTION); 35 | 36 | status = []() { 37 | NT_RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter"); 38 | return STATUS_SUCCESS; 39 | }(); 40 | REQUIRE(status == STATUS_INVALID_CONNECTION); 41 | 42 | status = []() { 43 | NT_RETURN_IF_NTSTATUS_FAILED(STATUS_SUCCESS); 44 | return STATUS_INVALID_CONNECTION; 45 | }(); 46 | REQUIRE(status == STATUS_INVALID_CONNECTION); 47 | } 48 | 49 | #ifdef WIL_ENABLE_EXCEPTIONS 50 | TEST_CASE("NtResultTests::NtThrowCatch", "[result]") 51 | { 52 | // Throw NTSTATUS with immediate conversion to HRESULT. HRESULT would appear in the logs. 53 | auto hr = []() { 54 | try 55 | { 56 | THROW_NTSTATUS(STATUS_INVALID_CONNECTION); 57 | } 58 | CATCH_RETURN(); 59 | return S_OK; 60 | }(); 61 | // THROW_NTSTATUS converts NTSTATUS to HRESULT through WIN32 error code. 62 | REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION)); 63 | 64 | // Verify that conversion NTSTATUS -> HRESULT -> NTSTATUS is not 1:1. 65 | auto status = []() { 66 | try 67 | { 68 | THROW_HR(wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION)); 69 | } 70 | NT_CATCH_RETURN(); 71 | return STATUS_SUCCESS; 72 | }(); 73 | if (wil::details::g_pfnRtlNtStatusToDosErrorNoTeb) 74 | { 75 | REQUIRE(status != STATUS_INVALID_CONNECTION); 76 | } 77 | else 78 | { 79 | REQUIRE(status == STATUS_INVALID_CONNECTION); 80 | } 81 | 82 | // Throw HRESULT with conversion to NTSTATUS on a best effort. NTSTATUS would appear in the logs. 83 | status = []() { 84 | try 85 | { 86 | THROW_HR(__HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)); 87 | } 88 | NT_CATCH_RETURN(); 89 | return STATUS_SUCCESS; 90 | }(); 91 | REQUIRE(status == STATUS_OBJECT_PATH_NOT_FOUND); 92 | 93 | // Throw HRESULT with conversion to NTSTATUS on a best effort that maps to generic error. NTSTATUS would appear in the logs. 94 | status = []() { 95 | try 96 | { 97 | THROW_HR(E_LOAD_NAMESERVICE_FAILED); 98 | } 99 | NT_CATCH_RETURN(); 100 | return STATUS_SUCCESS; 101 | }(); 102 | REQUIRE(status == STATUS_INTERNAL_ERROR); 103 | 104 | // Throw NTSTATUS without conversion. NTSTATUS would appear in the logs. 105 | status = []() { 106 | try 107 | { 108 | THROW_NTSTATUS(STATUS_INVALID_CONNECTION); 109 | } 110 | NT_CATCH_RETURN(); 111 | return STATUS_SUCCESS; 112 | }(); 113 | REQUIRE(status == STATUS_INVALID_CONNECTION); 114 | 115 | status = []() { 116 | try 117 | { 118 | THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS"); 119 | } 120 | NT_CATCH_RETURN(); 121 | return STATUS_SUCCESS; 122 | }(); 123 | REQUIRE(status == STATUS_INVALID_CONNECTION); 124 | 125 | status = []() { 126 | try 127 | { 128 | THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS with custom catch"); 129 | } 130 | catch (...) 131 | { 132 | LOG_CAUGHT_EXCEPTION(); 133 | 134 | return wil::StatusFromCaughtException(); 135 | } 136 | return STATUS_SUCCESS; 137 | }(); 138 | REQUIRE(status == STATUS_INVALID_CONNECTION); 139 | 140 | hr = []() { 141 | try 142 | { 143 | THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS"); 144 | } 145 | CATCH_RETURN(); 146 | return S_OK; 147 | }(); 148 | REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION)); 149 | 150 | status = []() { 151 | try 152 | { 153 | THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS"); 154 | } 155 | NT_CATCH_RETURN_MSG("Catching STATUS_INVALID_CONNECTION thrown by NT_THROW_NTSTATUS_MSG"); 156 | return STATUS_SUCCESS; 157 | }(); 158 | REQUIRE(status == STATUS_INVALID_CONNECTION); 159 | } 160 | #endif 161 | -------------------------------------------------------------------------------- /tests/Rpc.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "common.h" 4 | 5 | #include 6 | 7 | void RpcMethodReturnsVoid(ULONG toRaise) 8 | { 9 | if (toRaise) 10 | { 11 | RaiseException(toRaise, 0, 0, nullptr); 12 | } 13 | } 14 | 15 | struct FOO_CONTEXT_T 16 | { 17 | }; 18 | using FOO_CONTEXT = FOO_CONTEXT_T*; 19 | using PFOO_CONTEXT = FOO_CONTEXT*; 20 | 21 | void CloseContextHandle(_Inout_ PFOO_CONTEXT) 22 | { 23 | } 24 | 25 | void CloseContextHandleRaise(_Inout_ PFOO_CONTEXT) 26 | { 27 | return RpcMethodReturnsVoid(RPC_X_BAD_STUB_DATA); 28 | } 29 | 30 | HRESULT AcquireContextHandle(_In_ handle_t binding, _Out_ PFOO_CONTEXT context) 31 | { 32 | *context = reinterpret_cast(binding); 33 | return S_OK; 34 | } 35 | 36 | HRESULT RpcMethodReturnsHResult(HRESULT toReturn, ULONG toRaise) 37 | { 38 | RpcMethodReturnsVoid(toRaise); 39 | return toReturn; 40 | } 41 | 42 | GUID RpcMethodReturnsGuid(ULONG toRaise) 43 | { 44 | RpcMethodReturnsVoid(toRaise); 45 | return __uuidof(IUnknown); 46 | } 47 | 48 | TEST_CASE("Rpc::NonThrowing", "[rpc]") 49 | { 50 | SECTION("Success paths") 51 | { 52 | REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, 0UL)); 53 | REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, S_OK, 0UL)); 54 | 55 | GUID tmp{}; 56 | REQUIRE_SUCCEEDED(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, 0UL)); 57 | REQUIRE(tmp == __uuidof(IUnknown)); 58 | } 59 | 60 | SECTION("Failures in the method") 61 | { 62 | REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, 0) == E_CHANGED_STATE); 63 | } 64 | 65 | SECTION("Failures in the fabric") 66 | { 67 | REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); 68 | REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); 69 | 70 | GUID tmp{}; 71 | REQUIRE(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); 72 | } 73 | 74 | SECTION("Context Handles") 75 | { 76 | using foo_context_t = wil::unique_rpc_context_handle; 77 | foo_context_t ctx; 78 | auto tempBinding = reinterpret_cast(-5); 79 | REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(AcquireContextHandle, tempBinding, ctx.put())); 80 | REQUIRE(ctx.get() == reinterpret_cast(tempBinding)); 81 | ctx.reset(); 82 | } 83 | 84 | SECTION("Context Handles Close Raised") 85 | { 86 | using foo_context_t = wil::unique_rpc_context_handle; 87 | foo_context_t ctx{reinterpret_cast(42)}; 88 | ctx.reset(); 89 | } 90 | } 91 | 92 | #ifdef WIL_ENABLE_EXCEPTIONS 93 | 94 | #include 95 | 96 | class WilExceptionMatcher : public Catch::Matchers::MatcherBase 97 | { 98 | HRESULT m_expected; 99 | 100 | public: 101 | WilExceptionMatcher(HRESULT ex) : m_expected(ex) 102 | { 103 | } 104 | 105 | bool match(wil::ResultException const& ex) const override 106 | { 107 | return ex.GetErrorCode() == m_expected; 108 | } 109 | 110 | std::string describe() const override 111 | { 112 | std::ostringstream ss; 113 | ss << "wil::ResultException expects code 0x%08lx" << std::hex << m_expected; 114 | return ss.str(); 115 | } 116 | }; 117 | 118 | #define REQUIRE_THROWS_WIL_HR(hr, expr) REQUIRE_THROWS_MATCHES(expr, wil::ResultException, WilExceptionMatcher(hr)) 119 | 120 | TEST_CASE("Rpc::Throwing", "[rpc]") 121 | { 122 | SECTION("Success paths") 123 | { 124 | REQUIRE_NOTHROW(wil::invoke_rpc(RpcMethodReturnsVoid, 0UL)); 125 | 126 | GUID tmp{}; 127 | REQUIRE_NOTHROW(tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, 0UL)); 128 | REQUIRE(tmp == __uuidof(IUnknown)); 129 | } 130 | 131 | SECTION("Failures in the method") 132 | { 133 | REQUIRE_THROWS_WIL_HR(E_CHANGED_STATE, wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, 0UL)); 134 | } 135 | 136 | SECTION("Failures in the fabric") 137 | { 138 | REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsVoid, RPC_S_CALL_FAILED)); 139 | REQUIRE_THROWS_WIL_HR( 140 | HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED)); 141 | 142 | GUID tmp{}; 143 | REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, RPC_S_CALL_FAILED)); 144 | REQUIRE(tmp == GUID{}); 145 | } 146 | } 147 | #endif 148 | -------------------------------------------------------------------------------- /tests/StlTests.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "common.h" 4 | 5 | // Disable tests if we're not using exceptions. This is simpler than conditionally compiling this file 6 | #ifdef WIL_ENABLE_EXCEPTIONS 7 | 8 | #include 9 | 10 | struct dummy 11 | { 12 | char value; 13 | }; 14 | 15 | using namespace wil::literals; 16 | 17 | // Specialize std::allocator<> so that we don't actually allocate/deallocate memory 18 | dummy g_memoryBuffer[256]; 19 | namespace std 20 | { 21 | template <> 22 | struct allocator 23 | { 24 | using value_type = dummy; 25 | using size_type = std::size_t; 26 | using difference_type = std::ptrdiff_t; 27 | 28 | dummy* allocate(std::size_t count) 29 | { 30 | REQUIRE(count <= std::size(g_memoryBuffer)); 31 | return g_memoryBuffer; 32 | } 33 | 34 | void deallocate(dummy* ptr, std::size_t count) 35 | { 36 | for (std::size_t i = 0; i < count; ++i) 37 | { 38 | REQUIRE(ptr[i].value == 0); 39 | } 40 | } 41 | }; 42 | } // namespace std 43 | 44 | TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]") 45 | { 46 | { 47 | wil::secure_vector sensitiveBytes(32, dummy{'a'}); 48 | } 49 | } 50 | 51 | struct CustomNoncopyableString 52 | { 53 | CustomNoncopyableString() = default; 54 | CustomNoncopyableString(const CustomNoncopyableString&) = delete; 55 | void operator=(const CustomNoncopyableString&) = delete; 56 | 57 | constexpr operator PCSTR() const 58 | { 59 | return "hello"; 60 | } 61 | constexpr operator PCWSTR() const 62 | { 63 | return L"w-hello"; 64 | } 65 | }; 66 | 67 | TEST_CASE("StlTests::TestZStringView", "[stl][zstring_view]") 68 | { 69 | // Test empty cases 70 | REQUIRE(wil::zstring_view{}.length() == (size_t)0u); 71 | REQUIRE(wil::zstring_view{}.data() == nullptr); 72 | REQUIRE(wil::zstring_view{}.c_str() == nullptr); 73 | 74 | // Test empty string cases 75 | REQUIRE(wil::zstring_view{""}[0] == '\0'); 76 | REQUIRE(wil::zstring_view{""}.c_str()[0] == '\0'); 77 | REQUIRE(wil::zstring_view{""}.length() == 0); 78 | 79 | // Test different constructor equality 80 | constexpr wil::zstring_view fromLiteral = "abc"; 81 | REQUIRE(fromLiteral.length() == strlen("abc")); 82 | 83 | std::string stlString = "abc"; 84 | wil::zstring_view fromString(stlString); 85 | wil::zstring_view fromPtr(stlString.data()); 86 | 87 | static constexpr char charArray[] = "abc"; 88 | constexpr wil::zstring_view fromArray(charArray); 89 | 90 | static constexpr char extendedCharArray[] = "abc\0\0\0\0\0"; 91 | constexpr wil::zstring_view fromExtendedArray(extendedCharArray); 92 | 93 | wil::zstring_view copy = fromLiteral; 94 | 95 | REQUIRE(fromLiteral == stlString); 96 | REQUIRE(fromLiteral == fromString); 97 | REQUIRE(fromLiteral == fromArray); 98 | REQUIRE(fromLiteral == fromExtendedArray); 99 | REQUIRE(fromLiteral == copy); 100 | 101 | // Test decay to std::string_view 102 | std::string_view sv = fromLiteral; 103 | REQUIRE(sv == fromLiteral); 104 | 105 | // Test operator[] 106 | REQUIRE(fromLiteral[0] == 'a'); 107 | REQUIRE(fromLiteral[1] == 'b'); 108 | REQUIRE(fromLiteral[2] == 'c'); 109 | REQUIRE(fromLiteral[3] == '\0'); 110 | 111 | // Test constructing with no NULL in range 112 | static constexpr char badCharArray[2][3] = {{'a', 'b', 'c'}, {'a', 'b', 'c'}}; 113 | REQUIRE_ERROR((wil::zstring_view{&badCharArray[0][0], _countof(badCharArray[0])})); 114 | REQUIRE_ERROR((wil::zstring_view{badCharArray[0]})); 115 | 116 | // Test constructing with a NULL one character past the valid range, guarding against off-by-one errors 117 | // Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer 118 | static constexpr char badCharArrayOffByOne[2][3] = {{'a', 'b', 'c'}, {}}; 119 | const wil::zstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0])); 120 | REQUIRE(fromLiteral == fromTerminatedCharArray); 121 | REQUIRE_ERROR((wil::zstring_view{badCharArrayOffByOne[0]})); 122 | 123 | // Test constructing from custom string type 124 | CustomNoncopyableString customString; 125 | wil::zstring_view fromCustomString(customString); 126 | REQUIRE(fromCustomString == (PCSTR)customString); 127 | } 128 | 129 | TEST_CASE("StlTests::TestZWStringView literal", "[stl][zwstring_view]") 130 | { 131 | 132 | SECTION("Literal creates correct zwstring_view") 133 | { 134 | auto str = L"Hello, world!"_zv; 135 | REQUIRE(str.length() == 13); 136 | REQUIRE(str[0] == L'H'); 137 | REQUIRE(str[12] == L'!'); 138 | } 139 | } 140 | 141 | TEST_CASE("StlTests::TestZStringView literal", "[stl][zstring_view]") 142 | { 143 | 144 | SECTION("Literal creates correct zstring_view") 145 | { 146 | auto str = "Hello, world!"_zv; 147 | REQUIRE(str.length() == 13); 148 | REQUIRE(str[0] == 'H'); 149 | REQUIRE(str[12] == '!'); 150 | } 151 | } 152 | 153 | #if __cpp_lib_format >= 201907L 154 | 155 | TEST_CASE("StlTests::TestZStringView formatting", "[stl][zstring_view]") 156 | { 157 | SECTION("zstring_view can be used with std::format(wchar_t const*)") 158 | { 159 | auto str = L"kittens"_zv; 160 | auto f = std::format(L"Hello {}", str); 161 | REQUIRE(f == L"Hello kittens"); 162 | } 163 | 164 | SECTION("zstring_view can be used with std::format(char const*)") 165 | { 166 | auto str = "kittens"_zv; 167 | auto f = std::format("Hello {}", str); 168 | REQUIRE(f == "Hello kittens"); 169 | } 170 | } 171 | 172 | #endif 173 | 174 | TEST_CASE("StlTests::TestZWStringView", "[stl][zstring_view]") 175 | { 176 | // Test empty cases 177 | REQUIRE(wil::zwstring_view{}.length() == (size_t)0u); 178 | REQUIRE(wil::zwstring_view{}.data() == nullptr); 179 | REQUIRE(wil::zwstring_view{}.c_str() == nullptr); 180 | 181 | // Test empty string cases 182 | REQUIRE(wil::zwstring_view{L""}[0] == L'\0'); 183 | REQUIRE(wil::zwstring_view{L""}.c_str()[0] == L'\0'); 184 | REQUIRE(wil::zwstring_view{L""}.length() == 0); 185 | 186 | // Test different constructor equality 187 | constexpr wil::zwstring_view fromLiteral = L"abc"; 188 | REQUIRE(fromLiteral.length() == wcslen(L"abc")); 189 | 190 | std::wstring stlString = L"abc"; 191 | wil::zwstring_view fromString(stlString); 192 | wil::zwstring_view fromPtr(stlString.data()); 193 | 194 | static constexpr wchar_t charArray[] = L"abc"; 195 | constexpr wil::zwstring_view fromArray(charArray); 196 | 197 | static constexpr wchar_t extendedCharArray[] = L"abc\0\0\0\0\0"; 198 | constexpr wil::zwstring_view fromExtendedArray(extendedCharArray); 199 | 200 | wil::zwstring_view copy = fromLiteral; 201 | 202 | REQUIRE(fromLiteral == stlString); 203 | REQUIRE(fromLiteral == fromString); 204 | REQUIRE(fromLiteral == fromArray); 205 | REQUIRE(fromLiteral == fromExtendedArray); 206 | REQUIRE(fromLiteral == copy); 207 | 208 | // Test decay to std::wstring_view 209 | std::wstring_view sv = fromLiteral; 210 | REQUIRE(sv == fromLiteral); 211 | 212 | // Test operator[] 213 | REQUIRE(fromLiteral[0] == L'a'); 214 | REQUIRE(fromLiteral[1] == L'b'); 215 | REQUIRE(fromLiteral[2] == L'c'); 216 | REQUIRE(fromLiteral[3] == L'\0'); 217 | 218 | // Test constructing with no NULL in range 219 | static constexpr wchar_t badCharArray[2][3] = {{L'a', L'b', L'c'}, {L'a', L'b', L'c'}}; 220 | REQUIRE_ERROR((wil::zwstring_view{&badCharArray[0][0], _countof(badCharArray[0])})); 221 | REQUIRE_ERROR((wil::zwstring_view{badCharArray[0]})); 222 | 223 | // Test constructing with a NULL one character past the valid range, guarding against off-by-one errors 224 | // Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer 225 | static constexpr wchar_t badCharArrayOffByOne[2][3] = {{L'a', L'b', L'c'}, {}}; 226 | const wil::zwstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0])); 227 | REQUIRE(fromLiteral == fromTerminatedCharArray); 228 | REQUIRE_ERROR((wil::zwstring_view{badCharArrayOffByOne[0]})); 229 | 230 | // Test constructing from custom string type 231 | CustomNoncopyableString customString; 232 | wil::zwstring_view fromCustomString(customString); 233 | REQUIRE(fromCustomString == (PCWSTR)customString); 234 | 235 | // Test constructing from a type that has a c_str() method only 236 | struct string_with_c_str 237 | { 238 | constexpr PCWSTR c_str() const 239 | { 240 | return L"hello"; 241 | } 242 | }; 243 | string_with_c_str fake_path{}; 244 | REQUIRE(wil::zwstring_view(fake_path) == L"hello"); 245 | } 246 | 247 | #endif 248 | -------------------------------------------------------------------------------- /tests/TraceLoggingTests.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | // Just verify that Tracelogging.h compiles. 4 | #define PROVIDER_CLASS_NAME TestProvider 5 | #include "TraceLoggingTests.h" 6 | -------------------------------------------------------------------------------- /tests/TraceLoggingTests_PartB.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | // Just verify that Tracelogging.h compiles. 4 | #define PROVIDER_CLASS_NAME TestProvider_PartB 5 | 6 | #define _GENERIC_PARTB_FIELDS_ENABLED \ 7 | TraceLoggingWideString(L"1.0.0", "version"), TraceLoggingInt32(1337, "build"), TraceLoggingBool(true, "isInternal") 8 | 9 | #include "TraceLoggingTests.h" 10 | -------------------------------------------------------------------------------- /tests/UniqueWinRTEventTokenTests.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | 8 | using namespace ABI::Windows::Foundation; 9 | using namespace Microsoft::WRL; 10 | 11 | namespace wiltest 12 | { 13 | class AbiTestEventSender WrlFinal 14 | : public RuntimeClass, IClosable, IMemoryBufferReference, FtmBase> 15 | { 16 | public: 17 | // IMemoryBufferReference 18 | IFACEMETHODIMP get_Capacity(_Out_ UINT32* value) 19 | { 20 | *value = 0; 21 | return S_OK; 22 | } 23 | 24 | IFACEMETHODIMP add_Closed(_In_ ITypedEventHandler* handler, _Out_ ::EventRegistrationToken* token) 25 | { 26 | return m_closedEvent.Add(handler, token); 27 | } 28 | 29 | IFACEMETHODIMP remove_Closed(::EventRegistrationToken token) 30 | { 31 | return m_closedEvent.Remove(token); 32 | } 33 | 34 | // IClosable 35 | IFACEMETHODIMP Close() 36 | { 37 | RETURN_IF_FAILED(m_closedEvent.InvokeAll(this, nullptr)); 38 | return S_OK; 39 | } 40 | 41 | private: 42 | Microsoft::WRL::EventSource> m_closedEvent; 43 | }; 44 | } // namespace wiltest 45 | 46 | TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEventSubscribe", "[winrt][unique_winrt_event_token]") 47 | { 48 | ComPtr testEventSender; 49 | REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); 50 | ComPtr closable; 51 | testEventSender.As(&closable); 52 | 53 | int timesInvoked = 0; 54 | auto handler = 55 | Callback, ITypedEventHandler, FtmBase>>( 56 | [×Invoked](IInspectable*, IInspectable*) { 57 | timesInvoked++; 58 | return S_OK; 59 | }); 60 | REQUIRE(timesInvoked == 0); 61 | 62 | { 63 | wil::unique_winrt_event_token token; 64 | REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); 65 | REQUIRE(static_cast(token)); 66 | REQUIRE_SUCCEEDED(closable->Close()); 67 | REQUIRE(timesInvoked == 1); 68 | } 69 | 70 | REQUIRE_SUCCEEDED(closable->Close()); 71 | REQUIRE(timesInvoked == 1); 72 | } 73 | 74 | TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEarlyReset", "[winrt][unique_winrt_event_token]") 75 | { 76 | ComPtr testEventSender; 77 | REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); 78 | ComPtr closable; 79 | testEventSender.As(&closable); 80 | 81 | int timesInvoked = 0; 82 | auto handler = 83 | Callback, ITypedEventHandler, FtmBase>>( 84 | [×Invoked](IInspectable*, IInspectable*) { 85 | timesInvoked++; 86 | return S_OK; 87 | }); 88 | REQUIRE(timesInvoked == 0); 89 | 90 | wil::unique_winrt_event_token token; 91 | REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); 92 | REQUIRE(static_cast(token)); 93 | REQUIRE_SUCCEEDED(closable->Close()); 94 | REQUIRE(timesInvoked == 1); 95 | 96 | token.reset(); 97 | 98 | REQUIRE_SUCCEEDED(closable->Close()); 99 | REQUIRE(timesInvoked == 1); 100 | } 101 | 102 | TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveTokenToDifferentScope", "[winrt][unique_winrt_event_token]") 103 | { 104 | ComPtr testEventSender; 105 | REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); 106 | ComPtr closable; 107 | testEventSender.As(&closable); 108 | 109 | int timesInvoked = 0; 110 | auto handler = 111 | Callback, ITypedEventHandler, FtmBase>>( 112 | [×Invoked](IInspectable*, IInspectable*) { 113 | timesInvoked++; 114 | return S_OK; 115 | }); 116 | REQUIRE(timesInvoked == 0); 117 | 118 | wil::unique_winrt_event_token outerToken; 119 | REQUIRE_FALSE(static_cast(outerToken)); 120 | { 121 | wil::unique_winrt_event_token token; 122 | REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); 123 | REQUIRE(static_cast(token)); 124 | REQUIRE_SUCCEEDED(closable->Close()); 125 | REQUIRE(timesInvoked == 1); 126 | 127 | outerToken = std::move(token); 128 | REQUIRE_FALSE(static_cast(token)); 129 | REQUIRE(static_cast(outerToken)); 130 | } 131 | 132 | REQUIRE_SUCCEEDED(closable->Close()); 133 | REQUIRE(timesInvoked == 2); 134 | } 135 | 136 | TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveConstructor", "[winrt][unique_winrt_event_token]") 137 | { 138 | ComPtr testEventSender; 139 | REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); 140 | ComPtr closable; 141 | testEventSender.As(&closable); 142 | 143 | int timesInvoked = 0; 144 | auto handler = 145 | Callback, ITypedEventHandler, FtmBase>>( 146 | [×Invoked](IInspectable*, IInspectable*) { 147 | timesInvoked++; 148 | return S_OK; 149 | }); 150 | REQUIRE(timesInvoked == 0); 151 | 152 | wil::unique_winrt_event_token firstToken; 153 | REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken)); 154 | REQUIRE(static_cast(firstToken)); 155 | closable->Close(); 156 | REQUIRE(timesInvoked == 1); 157 | 158 | wil::unique_winrt_event_token secondToken(std::move(firstToken)); 159 | REQUIRE_FALSE(static_cast(firstToken)); 160 | REQUIRE(static_cast(secondToken)); 161 | 162 | closable->Close(); 163 | REQUIRE(timesInvoked == 2); 164 | 165 | firstToken.reset(); 166 | closable->Close(); 167 | REQUIRE(timesInvoked == 3); 168 | 169 | secondToken.reset(); 170 | closable->Close(); 171 | REQUIRE(timesInvoked == 3); 172 | } 173 | 174 | TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenReleaseAndReattachToNewWrapper", "[winrt][unique_winrt_event_token]") 175 | { 176 | ComPtr testEventSender; 177 | REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); 178 | ComPtr closable; 179 | testEventSender.As(&closable); 180 | 181 | int timesInvoked = 0; 182 | auto handler = 183 | Callback, ITypedEventHandler, FtmBase>>( 184 | [×Invoked](IInspectable*, IInspectable*) { 185 | timesInvoked++; 186 | return S_OK; 187 | }); 188 | REQUIRE(timesInvoked == 0); 189 | 190 | wil::unique_winrt_event_token firstToken; 191 | REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken)); 192 | REQUIRE(static_cast(firstToken)); 193 | REQUIRE_SUCCEEDED(closable->Close()); 194 | REQUIRE(timesInvoked == 1); 195 | 196 | ::EventRegistrationToken rawToken = firstToken.release(); 197 | REQUIRE_FALSE(static_cast(firstToken)); 198 | REQUIRE(rawToken.value != 0); 199 | 200 | REQUIRE_SUCCEEDED(closable->Close()); 201 | REQUIRE(timesInvoked == 2); 202 | 203 | wil::unique_winrt_event_token secondToken(rawToken, testEventSender.Get(), &IMemoryBufferReference::remove_Closed); 204 | 205 | REQUIRE_SUCCEEDED(closable->Close()); 206 | REQUIRE(timesInvoked == 3); 207 | 208 | secondToken.reset(); 209 | REQUIRE_SUCCEEDED(closable->Close()); 210 | REQUIRE(timesInvoked == 3); 211 | } 212 | 213 | TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenPolicyVariants", "[winrt][unique_winrt_event_token]") 214 | { 215 | ComPtr testEventSender; 216 | REQUIRE_SUCCEEDED(MakeAndInitialize(&testEventSender)); 217 | ComPtr closable; 218 | testEventSender.As(&closable); 219 | 220 | int timesInvoked = 0; 221 | auto handler = 222 | Callback, ITypedEventHandler, FtmBase>>( 223 | [×Invoked](IInspectable*, IInspectable*) { 224 | timesInvoked++; 225 | return S_OK; 226 | }); 227 | REQUIRE(timesInvoked == 0); 228 | 229 | { 230 | #ifdef WIL_ENABLE_EXCEPTIONS 231 | auto exceptionPolicyToken = WI_MakeUniqueWinRtEventToken(Closed, testEventSender.Get(), handler.Get()); 232 | REQUIRE(static_cast(exceptionPolicyToken)); 233 | #endif 234 | 235 | auto failFastPolicyToken = WI_MakeUniqueWinRtEventTokenFailFast(Closed, testEventSender.Get(), handler.Get()); 236 | REQUIRE(static_cast(failFastPolicyToken)); 237 | 238 | REQUIRE_SUCCEEDED(closable->Close()); 239 | 240 | #ifdef WIL_ENABLE_EXCEPTIONS 241 | REQUIRE(timesInvoked == 2); 242 | #else 243 | REQUIRE(timesInvoked == 1); 244 | #endif 245 | } 246 | 247 | REQUIRE_SUCCEEDED(closable->Close()); 248 | 249 | #ifdef WIL_ENABLE_EXCEPTIONS 250 | REQUIRE(timesInvoked == 2); 251 | #else 252 | REQUIRE(timesInvoked == 1); 253 | #endif 254 | } 255 | -------------------------------------------------------------------------------- /tests/WinVerifyTrustTest.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "common.h" 14 | 15 | #pragma comment(lib, "Wintrust.lib") 16 | 17 | TEST_CASE("WilWintrustWrapperTest::VerifyWintrustDataAllocateAndFree", "[resource][wintrust]") 18 | { 19 | wil::unique_wintrust_data uwvtData; 20 | uwvtData.cbStruct = sizeof(WINTRUST_DATA); 21 | DWORD zero = 0; 22 | REQUIRE(sizeof(WINTRUST_DATA) == uwvtData.cbStruct); 23 | 24 | uwvtData.reset(); 25 | REQUIRE(zero == uwvtData.cbStruct); 26 | } 27 | 28 | TEST_CASE("WilWintrustWrapperTest::VerifyUniqueHCATADMINAllocateAndFree", "[resource][wintrust]") 29 | { 30 | wil::unique_hcatadmin hCatAdmin; 31 | 32 | REQUIRE(CryptCATAdminAcquireContext2(hCatAdmin.addressof(), nullptr, BCRYPT_SHA256_ALGORITHM, nullptr, 0)); 33 | 34 | REQUIRE(hCatAdmin.get() != nullptr); 35 | hCatAdmin.reset(); 36 | REQUIRE(hCatAdmin.get() == nullptr); 37 | } 38 | 39 | #ifdef WIL_ENABLE_EXCEPTIONS 40 | TEST_CASE("WilWintrustWrapperTest::VerifyUniqueHCATINFOAllocate", "[resource][wintrust]") 41 | { 42 | wil::shared_hcatadmin hCatAdmin; 43 | HCATINFO hCatInfo = nullptr; 44 | 45 | REQUIRE(CryptCATAdminAcquireContext2(hCatAdmin.addressof(), nullptr, BCRYPT_SHA256_ALGORITHM, nullptr, 0)); 46 | 47 | wil::unique_hcatinfo hCatInfoWrapper(hCatInfo, hCatAdmin); 48 | REQUIRE(hCatInfoWrapper.get() == nullptr); 49 | } 50 | #endif 51 | -------------------------------------------------------------------------------- /tests/WindowingTests.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | 4 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 5 | TEST_CASE("EnumWindows", "[windowing]") 6 | { 7 | // lambda can return a bool 8 | wil::for_each_window_nothrow([](HWND hwnd) { 9 | REQUIRE(IsWindow(hwnd)); 10 | return true; 11 | }); 12 | 13 | // or not return anything (we'll stop if we get an exception) 14 | wil::for_each_window_nothrow([](HWND hwnd) { 15 | REQUIRE(IsWindow(hwnd)); 16 | }); 17 | 18 | // or return an HRESULT and we'll stop if it's not S_OK 19 | wil::for_each_window_nothrow([](HWND hwnd) { 20 | REQUIRE(IsWindow(hwnd)); 21 | return S_FALSE; 22 | }); 23 | 24 | // mutable lambda 25 | std::vector windows; 26 | wil::for_each_window_nothrow([&windows](HWND hwnd) { 27 | windows.push_back(hwnd); 28 | }); 29 | wil::for_each_window_nothrow([windows = std::vector{}](HWND hwnd) mutable { 30 | windows.push_back(hwnd); 31 | }); 32 | 33 | // With captures 34 | const auto pid = GetCurrentProcessId(); 35 | wil::for_each_window_nothrow([pid](HWND hwnd) { 36 | if (pid == GetWindowThreadProcessId(hwnd, nullptr)) 37 | { 38 | REQUIRE(IsWindow(hwnd)); 39 | }; 40 | return true; 41 | }); 42 | 43 | #ifdef WIL_ENABLE_EXCEPTIONS 44 | // throwing version 45 | wil::for_each_window([](HWND hwnd) { 46 | REQUIRE(IsWindow(hwnd)); 47 | return true; 48 | }); 49 | wil::for_each_window([](HWND hwnd) { 50 | REQUIRE(IsWindow(hwnd)); 51 | }); 52 | wil::for_each_window([](HWND hwnd) { 53 | REQUIRE(IsWindow(hwnd)); 54 | return S_FALSE; 55 | }); 56 | windows.clear(); 57 | wil::for_each_window([&windows](HWND hwnd) { 58 | windows.push_back(hwnd); 59 | }); 60 | wil::for_each_window([windows = std::vector{}](HWND hwnd) mutable { 61 | windows.push_back(hwnd); 62 | }); 63 | REQUIRE_THROWS_AS( 64 | wil::for_each_window([](HWND) { 65 | throw std::exception(); 66 | }), 67 | std::exception); 68 | #endif 69 | } 70 | 71 | TEST_CASE("EnumThreadWindows", "[windowing]") 72 | { 73 | // find any window 74 | DWORD thread_id{}; 75 | wil::for_each_window_nothrow([&thread_id](HWND hwnd) { 76 | if (IsWindow(hwnd) && IsWindowVisible(hwnd)) 77 | { 78 | thread_id = GetWindowThreadProcessId(hwnd, nullptr); 79 | return false; 80 | } 81 | return true; 82 | }); 83 | 84 | // nothrow version 85 | { 86 | wil::for_each_thread_window_nothrow(thread_id, [](HWND hwnd) { 87 | REQUIRE(IsWindow(hwnd)); 88 | return true; 89 | }); 90 | 91 | wil::for_each_thread_window_nothrow(thread_id, [](HWND hwnd) { 92 | REQUIRE(IsWindow(hwnd)); 93 | }); 94 | 95 | // lambda can also return an HRESULT and we'll stop if it's not S_OK 96 | wil::for_each_thread_window_nothrow(thread_id, [](HWND hwnd) { 97 | REQUIRE(IsWindow(hwnd)); 98 | return S_FALSE; 99 | }); 100 | 101 | // mutable lambda 102 | std::vector windows; 103 | wil::for_each_thread_window_nothrow(thread_id, [&windows, thread_id](HWND hwnd) { 104 | REQUIRE(GetWindowThreadProcessId(hwnd, nullptr) == thread_id); 105 | windows.push_back(hwnd); 106 | }); 107 | wil::for_each_thread_window_nothrow(thread_id, [windows = std::vector{}](HWND hwnd) mutable { 108 | windows.push_back(hwnd); 109 | }); 110 | } 111 | 112 | #ifdef WIL_ENABLE_EXCEPTIONS 113 | // throwing version 114 | { 115 | wil::for_each_thread_window(thread_id, [](HWND hwnd) { 116 | REQUIRE(IsWindow(hwnd)); 117 | return true; 118 | }); 119 | 120 | wil::for_each_thread_window(thread_id, [](HWND hwnd) { 121 | REQUIRE(IsWindow(hwnd)); 122 | }); 123 | 124 | // lambda can also return an HRESULT and we'll stop if it's not S_OK 125 | wil::for_each_thread_window(thread_id, [](HWND hwnd) { 126 | REQUIRE(IsWindow(hwnd)); 127 | return S_FALSE; 128 | }); 129 | 130 | // mutable lambda 131 | std::vector windows; 132 | wil::for_each_thread_window(thread_id, [&windows, thread_id](HWND hwnd) { 133 | REQUIRE(GetWindowThreadProcessId(hwnd, nullptr) == thread_id); 134 | windows.push_back(hwnd); 135 | }); 136 | wil::for_each_thread_window(thread_id, [windows = std::vector{}](HWND hwnd) mutable { 137 | windows.push_back(hwnd); 138 | }); 139 | 140 | // with exceptions 141 | REQUIRE_THROWS_AS( 142 | wil::for_each_thread_window( 143 | thread_id, 144 | [](HWND) { 145 | throw std::exception(); 146 | }), 147 | std::exception); 148 | } 149 | #endif 150 | } 151 | 152 | bool has_child_window(HWND hwnd) 153 | { 154 | bool hasChildWindow = false; 155 | wil::for_each_child_window_nothrow(hwnd, [&hasChildWindow](HWND) { 156 | hasChildWindow = true; 157 | return false; 158 | }); 159 | return hasChildWindow; 160 | } 161 | 162 | TEST_CASE("EnumChildWindows", "[windowing]") 163 | { 164 | // find any window 165 | HWND parent{}; 166 | 167 | wil::for_each_window_nothrow([&parent](HWND hwnd) { 168 | if (IsWindow(hwnd) && IsWindowVisible(hwnd) && has_child_window(hwnd)) 169 | { 170 | // Make sure we choose a window that has children 171 | bool hasChildren = false; 172 | wil::for_each_child_window_nothrow(hwnd, [&](HWND) { 173 | hasChildren = true; 174 | return false; 175 | }); 176 | 177 | if (hasChildren) 178 | { 179 | parent = hwnd; 180 | return false; 181 | } 182 | } 183 | return true; 184 | }); 185 | 186 | // Avoid confusing issues below 187 | REQUIRE(parent != nullptr); 188 | 189 | // nothrow version 190 | { 191 | wil::for_each_child_window_nothrow(parent, [](HWND hwnd) { 192 | REQUIRE(IsWindow(hwnd)); 193 | return true; 194 | }); 195 | 196 | wil::for_each_child_window_nothrow(parent, [](HWND hwnd) { 197 | REQUIRE(IsWindow(hwnd)); 198 | }); 199 | 200 | // lambda can also return an HRESULT and we'll stop if it's not S_OK 201 | wil::for_each_child_window_nothrow(parent, [](HWND hwnd) { 202 | REQUIRE(IsWindow(hwnd)); 203 | return S_FALSE; 204 | }); 205 | 206 | // mutable lambda 207 | std::vector windows; 208 | wil::for_each_child_window_nothrow(parent, [&windows](HWND hwnd) { 209 | windows.push_back(hwnd); 210 | }); 211 | wil::for_each_child_window_nothrow(parent, [windows = std::vector{}](HWND hwnd) mutable { 212 | windows.push_back(hwnd); 213 | }); 214 | } 215 | 216 | #ifdef WIL_ENABLE_EXCEPTIONS 217 | // throwing version 218 | { 219 | wil::for_each_child_window(parent, [](HWND hwnd) { 220 | REQUIRE(IsWindow(hwnd)); 221 | return true; 222 | }); 223 | 224 | wil::for_each_child_window(parent, [](HWND hwnd) { 225 | REQUIRE(IsWindow(hwnd)); 226 | }); 227 | 228 | // lambda can also return an HRESULT and we'll stop if it's not S_OK 229 | wil::for_each_child_window(parent, [](HWND hwnd) { 230 | REQUIRE(IsWindow(hwnd)); 231 | return S_FALSE; 232 | }); 233 | 234 | // mutable lambda 235 | std::vector windows; 236 | wil::for_each_child_window(parent, [&windows](HWND hwnd) { 237 | windows.push_back(hwnd); 238 | }); 239 | REQUIRE(windows.size() > 0); 240 | wil::for_each_child_window(parent, [windows = std::vector{}](HWND hwnd) mutable { 241 | windows.push_back(hwnd); 242 | }); 243 | 244 | // with exceptions 245 | REQUIRE_THROWS_AS( 246 | wil::for_each_child_window( 247 | parent, 248 | [](HWND) { 249 | throw std::exception(); 250 | }), 251 | std::exception); 252 | } 253 | #endif 254 | } 255 | 256 | #endif -------------------------------------------------------------------------------- /tests/WistdTests.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include 4 | 5 | #include "common.h" 6 | #include "test_objects.h" 7 | 8 | // Test methods/objects 9 | int GetValue() 10 | { 11 | return 42; 12 | } 13 | 14 | int GetOtherValue() 15 | { 16 | return 8; 17 | } 18 | 19 | int Negate(int value) 20 | { 21 | return -value; 22 | } 23 | 24 | int Add(int lhs, int rhs) 25 | { 26 | return lhs + rhs; 27 | } 28 | 29 | TEST_CASE("WistdFunctionTests::CallOperatorTest", "[wistd]") 30 | { 31 | wistd::function getValue = GetValue; 32 | REQUIRE(GetValue() == getValue()); 33 | 34 | wistd::function negate = Negate; 35 | REQUIRE(Negate(42) == negate(42)); 36 | 37 | wistd::function add = Add; 38 | REQUIRE(Add(42, 8) == add(42, 8)); 39 | } 40 | 41 | TEST_CASE("WistdFunctionTests::AssignmentOperatorTest", "[wistd]") 42 | { 43 | wistd::function fn = GetValue; 44 | REQUIRE(GetValue() == fn()); 45 | 46 | fn = GetOtherValue; 47 | REQUIRE(GetOtherValue() == fn()); 48 | } 49 | 50 | #ifdef WIL_ENABLE_EXCEPTIONS 51 | TEST_CASE("WistdFunctionTests::StdFunctionConstructionTest", "[wistd]") 52 | { 53 | // We should be able to capture a std::function in a wistd::function 54 | wistd::function fn; 55 | 56 | { 57 | value_holder holder{42}; 58 | std::function stdFn = [holder]() { 59 | return holder.value; 60 | }; 61 | 62 | fn = stdFn; 63 | } 64 | 65 | REQUIRE(42 == fn()); 66 | } 67 | #endif 68 | 69 | TEST_CASE("WistdFunctionTests::CopyConstructionTest", "[wistd]") 70 | { 71 | object_counter_state state; 72 | { 73 | wistd::function copyFrom = [counter = object_counter{state}]() { 74 | return counter.state->copy_count; 75 | }; 76 | REQUIRE(0 == copyFrom()); 77 | 78 | auto copyTo = copyFrom; 79 | REQUIRE(1 == copyTo()); 80 | } 81 | 82 | REQUIRE(0 == state.instance_count()); 83 | } 84 | 85 | TEST_CASE("WistdFunctionTests::CopyAssignmentTest", "[wistd]") 86 | { 87 | object_counter_state state; 88 | { 89 | wistd::function copyTo; 90 | { 91 | wistd::function copyFrom = [counter = object_counter{state}]() { 92 | return counter.state->copy_count; 93 | }; 94 | REQUIRE(0 == copyFrom()); 95 | 96 | copyTo = copyFrom; 97 | } 98 | 99 | REQUIRE(1 == copyTo()); 100 | } 101 | 102 | REQUIRE(0 == state.instance_count()); 103 | } 104 | 105 | TEST_CASE("WistdFunctionTests::MoveConstructionTest", "[wistd]") 106 | { 107 | object_counter_state state; 108 | { 109 | wistd::function moveFrom = [counter = object_counter{state}]() { 110 | return counter.state->copy_count; 111 | }; 112 | REQUIRE(0 == moveFrom()); 113 | 114 | auto moveTo = std::move(moveFrom); 115 | REQUIRE(0 == moveTo()); 116 | 117 | // Because we move the underlying function object, we _must_ invalidate the moved from function 118 | REQUIRE_FALSE(moveFrom != nullptr); 119 | } 120 | 121 | REQUIRE(0 == state.instance_count()); 122 | } 123 | 124 | TEST_CASE("WistdFunctionTests::MoveAssignmentTest", "[wistd]") 125 | { 126 | object_counter_state state; 127 | { 128 | wistd::function moveTo; 129 | { 130 | wistd::function moveFrom = [counter = object_counter{state}]() { 131 | return counter.state->copy_count; 132 | }; 133 | REQUIRE(0 == moveFrom()); 134 | 135 | moveTo = std::move(moveFrom); 136 | } 137 | 138 | REQUIRE(0 == moveTo()); 139 | } 140 | 141 | REQUIRE(0 == state.instance_count()); 142 | } 143 | 144 | TEST_CASE("WistdFunctionTests::SwapTest", "[wistd]") 145 | { 146 | object_counter_state state; 147 | { 148 | wistd::function first; 149 | wistd::function second; 150 | 151 | first.swap(second); 152 | REQUIRE_FALSE(first != nullptr); 153 | REQUIRE_FALSE(second != nullptr); 154 | 155 | first = [counter = object_counter{state}]() { 156 | return counter.state->copy_count; 157 | }; 158 | 159 | first.swap(second); 160 | REQUIRE_FALSE(first != nullptr); 161 | REQUIRE(second != nullptr); 162 | REQUIRE(0 == second()); 163 | 164 | first.swap(second); 165 | REQUIRE(first != nullptr); 166 | REQUIRE_FALSE(second != nullptr); 167 | REQUIRE(0 == first()); 168 | 169 | second = [counter = object_counter{state}]() { 170 | return counter.state->copy_count; 171 | }; 172 | 173 | first.swap(second); 174 | REQUIRE(first != nullptr); 175 | REQUIRE(second != nullptr); 176 | REQUIRE(0 == first()); 177 | } 178 | 179 | REQUIRE(0 == state.instance_count()); 180 | } 181 | 182 | // MSVC's optimizer has had issues with wistd::function in the past when forwarding wistd::function objects to a 183 | // function that accepts the arguments by value. This test exercises the workaround that we have in place. Note 184 | // that this of course requires building with optimizations enabled 185 | void ForwardingTest(wistd::function getValue, wistd::function negate, wistd::function add) 186 | { 187 | // Previously, this would cause a runtime crash 188 | REQUIRE(Add(GetValue(), Negate(8)) == add(getValue(), negate(8))); 189 | } 190 | 191 | template 192 | void CallForwardingTest(Args&&... args) 193 | { 194 | ForwardingTest(wistd::forward(args)...); 195 | } 196 | 197 | TEST_CASE("WistdFunctionTests::OptimizationRegressionTest", "[wistd]") 198 | { 199 | CallForwardingTest(wistd::function(GetValue), wistd::function(Negate), wistd::function(Add)); 200 | } 201 | -------------------------------------------------------------------------------- /tests/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest.app) 3 | 4 | target_precompile_headers(witest.app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | target_compile_definitions(witest.app PRIVATE 7 | -DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP 8 | ) 9 | 10 | target_sources(witest.app PRIVATE 11 | ${COMMON_SOURCES} 12 | ${WINRT_SOURCES} 13 | ) 14 | -------------------------------------------------------------------------------- /tests/cpplatest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest.cpplatest) 3 | 4 | target_precompile_headers(witest.cpplatest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | # Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned 7 | # on compiler) as new standards are ratified/support is available 8 | target_compile_features(witest.cpplatest PRIVATE cxx_std_23) 9 | 10 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 11 | target_compile_options(witest.cpplatest PRIVATE 12 | -fno-ms-compatibility 13 | ) 14 | else() 15 | target_compile_options(witest.cpplatest PRIVATE 16 | /Zc:preprocessor 17 | ) 18 | endif() 19 | 20 | target_sources(witest.cpplatest PRIVATE 21 | ${COMMON_SOURCES} 22 | ${DOWNLEVEL_SOURCES} 23 | ${WINRT_SOURCES} 24 | ${WIN11_DESTKTOP_SOURCES} 25 | ${CPP20_SOURCES} 26 | ) 27 | -------------------------------------------------------------------------------- /tests/cppwinrt-notifiable-server-lock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest.cppwinrt-notifiable-server-lock) 3 | 4 | target_precompile_headers(witest.cppwinrt-notifiable-server-lock PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | # Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned 7 | # on compiler) as new standards are ratified/support is available 8 | target_compile_features(witest.cppwinrt-notifiable-server-lock PRIVATE cxx_std_20) 9 | 10 | target_sources(witest.cppwinrt-notifiable-server-lock PRIVATE 11 | ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTNotifiableServerLockTests.cpp 13 | ) 14 | -------------------------------------------------------------------------------- /tests/cppwinrt_threadpool_guard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "mocking.h" 6 | 7 | // NOTE: Thanks to https://github.com/microsoft/cppwinrt/issues/1513, we need to ensure that COM remains initialized while 8 | // C++/WinRT is running code on the threadpool. This will block in the destructor until all threadpool callbacks are done 9 | // while keeping COM initialized. 10 | struct cppwinrt_threadpool_guard 11 | { 12 | cppwinrt_threadpool_guard() 13 | { 14 | m_detouredSubmit = [this](PTP_SIMPLE_CALLBACK callback, void* ctxt, PTP_CALLBACK_ENVIRON env) -> BOOL { 15 | auto data = std::make_unique(); 16 | *data = {this, callback, ctxt}; 17 | 18 | auto result = ::TrySubmitThreadpoolCallback(detoured_callback, data.get(), env); 19 | if (result) 20 | { 21 | ++m_callsInFlight; 22 | std::ignore = data.release(); 23 | } 24 | 25 | return result; 26 | }; 27 | } 28 | 29 | ~cppwinrt_threadpool_guard() 30 | { 31 | while (m_callsInFlight > 0) 32 | { 33 | // Could use other synchronization, however this is expected to be very short, so spinning is likely better anyway 34 | std::this_thread::yield(); 35 | } 36 | } 37 | 38 | private: 39 | struct callback_data 40 | { 41 | cppwinrt_threadpool_guard* pThis; 42 | PTP_SIMPLE_CALLBACK callback; 43 | void* ctxt; 44 | }; 45 | 46 | static void __stdcall detoured_callback(PTP_CALLBACK_INSTANCE inst, void* ctxt) 47 | { 48 | std::unique_ptr data(static_cast(ctxt)); 49 | 50 | // Ensure COM is initialized for the entirety of the callback 51 | wil::unique_mta_usage_cookie cookie; 52 | LOG_IF_FAILED(::CoIncrementMTAUsage(cookie.put())); 53 | 54 | data->callback(inst, data->ctxt); 55 | --data->pThis->m_callsInFlight; 56 | } 57 | 58 | std::atomic_size_t m_callsInFlight{0}; 59 | witest::detoured_global_function<&::TrySubmitThreadpoolCallback> m_detouredSubmit; 60 | }; 61 | -------------------------------------------------------------------------------- /tests/noexcept/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest.noexcept) 3 | 4 | target_precompile_headers(witest.noexcept PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | # Turn off exceptions for this test 7 | if (MSVC) 8 | replace_cxx_flag("/EHsc" "/EHs-c-") 9 | else() 10 | target_compile_options(witest.noexcept PRIVATE -fno-exceptions) 11 | endif() 12 | 13 | target_compile_definitions(witest.noexcept PRIVATE 14 | -DCATCH_CONFIG_DISABLE_EXCEPTIONS 15 | # No exceptions means we will deduce STL usage to false, however the tests still want to test things that use the STL 16 | -DWIL_USE_STL=1 17 | ) 18 | 19 | # Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch 20 | # statements... Thankfully MSVC just gives us a warning that we can disable 21 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 22 | target_compile_options(witest.noexcept PRIVATE /wd4530) 23 | endif() 24 | 25 | target_sources(witest.noexcept PRIVATE 26 | ${COMMON_SOURCES} 27 | ${DOWNLEVEL_SOURCES} 28 | ${WINRT_SOURCES} 29 | ${WIN11_DESTKTOP_SOURCES} 30 | ) 31 | -------------------------------------------------------------------------------- /tests/normal/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest) 3 | 4 | target_precompile_headers(witest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | target_sources(witest PRIVATE 7 | ${COMMON_SOURCES} 8 | ${DOWNLEVEL_SOURCES} 9 | ${WINRT_SOURCES} 10 | ${WIN11_DESTKTOP_SOURCES} 11 | ) 12 | -------------------------------------------------------------------------------- /tests/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Suppress 'noreturn' attributes from causing issues 4 | #define RESULT_NORETURN 5 | #define RESULT_NORETURN_NULL 6 | #define RESULT_NORETURN_RESULT(expr) return (expr) 7 | 8 | // Definition using '__declspec(selectany)' not compatible with -fno-ms-compatibility 9 | #define XMGLOBALCONST inline constexpr const 10 | -------------------------------------------------------------------------------- /tests/sanitize-address/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest.asan) 3 | 4 | target_precompile_headers(witest.asan PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | target_compile_options(witest.asan PRIVATE -fsanitize=address) 7 | 8 | target_compile_features(witest.asan PRIVATE cxx_std_23) 9 | 10 | target_compile_definitions(witest.asan PRIVATE 11 | -DCATCH_CONFIG_NO_WINDOWS_SEH # ASAN relies on first chance AVs 12 | -DWITEST_ADDRESS_SANITIZER # To conditionally enable/disable code 13 | ) 14 | 15 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 16 | target_compile_definitions(witest.asan PRIVATE 17 | # See below; not compatible with exceptions 18 | -DCATCH_CONFIG_DISABLE_EXCEPTIONS 19 | ) 20 | 21 | # Clang ASan on Windows has issues with exceptions: https://github.com/google/sanitizers/issues/749 22 | if (MSVC) 23 | replace_cxx_flag("/EHsc" "/EHs-c-") 24 | else() 25 | target_compile_options(witest.asan PRIVATE -fno-exceptions) 26 | endif() 27 | 28 | # Since we're disabling exceptions, we also need to explicitly enable use of the STL since some tests rely on it 29 | target_compile_definitions(witest.asan PRIVATE 30 | -DWIL_USE_STL=1 31 | ) 32 | 33 | if ($ENV{Platform} STREQUAL "x86") 34 | target_link_libraries(witest.asan PRIVATE 35 | clang_rt.asan_dynamic-i386.lib 36 | clang_rt.asan_dynamic_runtime_thunk-i386.lib 37 | ) 38 | elseif ($ENV{Platform} STREQUAL "x64") 39 | target_link_libraries(witest.asan PRIVATE 40 | clang_rt.asan_dynamic-x86_64.lib 41 | clang_rt.asan_dynamic_runtime_thunk-x86_64.lib 42 | ) 43 | else() 44 | message(FATAL_ERROR "Platform '$ENV{Platform}' does not have support for ASan") 45 | endif() 46 | else() 47 | # Using exceptions; we can compile all of the C++/WinRT tests 48 | set(EXTRA_SOURCES 49 | ${CPP20_SOURCES} 50 | ) 51 | endif() 52 | 53 | target_compile_definitions(witest.asan PRIVATE 54 | # Since we link with Catch2, which is pre-built 55 | -D_DISABLE_VECTOR_ANNOTATION 56 | -D_DISABLE_STRING_ANNOTATION 57 | ) 58 | 59 | target_sources(witest.asan PUBLIC 60 | ${COMMON_SOURCES} 61 | ${DOWNLEVEL_SOURCES} 62 | ${WINRT_SOURCES} 63 | ${WIN11_DESTKTOP_SOURCES} 64 | ${EXTRA_SOURCES} 65 | ) 66 | -------------------------------------------------------------------------------- /tests/sanitize-undefined-behavior/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest.ubsan) 3 | 4 | target_precompile_headers(witest.ubsan PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | target_compile_options(witest.ubsan PRIVATE 7 | -fsanitize=undefined 8 | -fno-sanitize-recover=undefined # So we get test failures 9 | ) 10 | 11 | target_compile_definitions(witest.ubsan PRIVATE 12 | -DWITEST_UB_SANITIZER # To conditionally enable/disable code 13 | ) 14 | 15 | # UBSan libraries were built assuming static linking 16 | set_property(TARGET witest.ubsan 17 | PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded") 18 | 19 | target_sources(witest.ubsan PUBLIC 20 | ${COMMON_SOURCES} 21 | ${DOWNLEVEL_SOURCES} 22 | ${WINRT_SOURCES} 23 | ${WIN11_DESTKTOP_SOURCES} 24 | ) 25 | -------------------------------------------------------------------------------- /tests/test_objects.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "catch.hpp" 4 | 5 | // Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that 6 | // the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that 7 | // require CopyConstructible (e.g. for wistd::function) 8 | struct fail_on_copy 9 | { 10 | fail_on_copy() = default; 11 | 12 | fail_on_copy(const fail_on_copy&) 13 | { 14 | FAIL("Copy constructor invoked for fail_on_copy type"); 15 | } 16 | 17 | fail_on_copy(fail_on_copy&&) = default; 18 | 19 | fail_on_copy& operator=(const fail_on_copy&) 20 | { 21 | FAIL("Copy assignment operator invoked for fail_on_copy type"); 22 | return *this; 23 | } 24 | 25 | fail_on_copy& operator=(fail_on_copy&&) = default; 26 | }; 27 | 28 | // Useful for validating that objects get copied e.g. as opposed to capturing a reference 29 | struct value_holder 30 | { 31 | int value = 0xbadf00d; 32 | 33 | ~value_holder() 34 | { 35 | value = 0xbadf00d; 36 | } 37 | }; 38 | 39 | // Useful for validating that functions, etc. are callable with move-only types 40 | // Example real type that is move only is Microsoft::WRL::Wrappers::HString 41 | struct cannot_copy 42 | { 43 | cannot_copy() = default; 44 | cannot_copy(const cannot_copy&) = delete; 45 | cannot_copy& operator=(const cannot_copy&) = delete; 46 | 47 | cannot_copy(cannot_copy&&) = default; 48 | cannot_copy& operator=(cannot_copy&&) = default; 49 | }; 50 | 51 | // State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in 52 | // contexts that require a default constructible type, but has the nice property that it allows for tests to run 53 | // concurrently 54 | struct object_counter_state 55 | { 56 | volatile LONG constructed_count = 0; 57 | volatile LONG destructed_count = 0; 58 | volatile LONG copy_count = 0; 59 | volatile LONG move_count = 0; 60 | 61 | LONG instance_count() 62 | { 63 | return constructed_count - destructed_count; 64 | } 65 | }; 66 | 67 | struct object_counter 68 | { 69 | object_counter_state* state; 70 | 71 | object_counter(object_counter_state& s) : state(&s) 72 | { 73 | ::InterlockedIncrement(&state->constructed_count); 74 | } 75 | 76 | object_counter(const object_counter& other) : state(other.state) 77 | { 78 | ::InterlockedIncrement(&state->constructed_count); 79 | ::InterlockedIncrement(&state->copy_count); 80 | } 81 | 82 | object_counter(object_counter&& other) WI_NOEXCEPT : state(other.state) 83 | { 84 | ::InterlockedIncrement(&state->constructed_count); 85 | ::InterlockedIncrement(&state->move_count); 86 | } 87 | 88 | ~object_counter() 89 | { 90 | ::InterlockedIncrement(&state->destructed_count); 91 | state = nullptr; 92 | } 93 | 94 | object_counter& operator=(const object_counter&) 95 | { 96 | ::InterlockedIncrement(&state->copy_count); 97 | return *this; 98 | } 99 | 100 | object_counter& operator=(object_counter&&) WI_NOEXCEPT 101 | { 102 | ::InterlockedIncrement(&state->move_count); 103 | return *this; 104 | } 105 | }; 106 | -------------------------------------------------------------------------------- /tests/win7/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(witest.win7) 3 | 4 | target_precompile_headers(witest.win7 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) 5 | 6 | target_compile_definitions(witest.win7 PRIVATE 7 | -D_WIN32_WINNT=0x0601 8 | ) 9 | 10 | target_sources(witest.win7 PRIVATE 11 | ${COMMON_SOURCES} 12 | ${DOWNLEVEL_SOURCES} 13 | ) 14 | -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "registries": [ 3 | { 4 | "kind": "artifact", 5 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 6 | "name": "microsoft" 7 | } 8 | ], 9 | "overlay-ports": [ 10 | "./vcpkg-overlay" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /vcpkg-overlay/catch2/except.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/catch2/internal/catch_fatal_condition_handler.cpp b/src/catch2/internal/catch_fatal_condition_handler.cpp 2 | index 26f894d6..3dfa1383 100644 3 | --- a/src/catch2/internal/catch_fatal_condition_handler.cpp 4 | +++ b/src/catch2/internal/catch_fatal_condition_handler.cpp 5 | @@ -86,23 +86,27 @@ namespace Catch { 6 | { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, 7 | }; 8 | 9 | + // Since we do not support multiple instantiations, we put these 10 | + // into global variables and rely on cleaning them up in outlined 11 | + // constructors/destructors 12 | + static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr; 13 | + 14 | + 15 | static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { 16 | for (auto const& def : signalDefs) { 17 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { 18 | reportFatal(def.name); 19 | } 20 | } 21 | - // If its not an exception we care about, pass it along. 22 | + // If a filter was previously registered, invoke it 23 | + if (previousTopLevelExceptionFilter) { 24 | + return previousTopLevelExceptionFilter(ExceptionInfo); 25 | + } 26 | + // Otherwise, pass along all exceptions. 27 | // This stops us from eating debugger breaks etc. 28 | return EXCEPTION_CONTINUE_SEARCH; 29 | } 30 | 31 | - // Since we do not support multiple instantiations, we put these 32 | - // into global variables and rely on cleaning them up in outlined 33 | - // constructors/destructors 34 | - static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr; 35 | - 36 | - 37 | // For MSVC, we reserve part of the stack memory for handling 38 | // memory overflow structured exception. 39 | FatalConditionHandler::FatalConditionHandler() { 40 | -------------------------------------------------------------------------------- /vcpkg-overlay/catch2/fix-install-path.patch: -------------------------------------------------------------------------------- 1 | diff --git a/CMakeLists.txt b/CMakeLists.txt 2 | index 1676ee7..5231934 100644 3 | --- a/CMakeLists.txt 4 | +++ b/CMakeLists.txt 5 | @@ -167,13 +167,13 @@ if(NOT_SUBPROJECT) 6 | "extras/gdbinit" 7 | "extras/lldbinit" 8 | DESTINATION 9 | - ${CMAKE_INSTALL_DATAROOTDIR}/Catch2 10 | + ${CMAKE_INSTALL_DATAROOTDIR}/catch2 11 | ) 12 | endif() 13 | 14 | ## Provide some pkg-config integration 15 | set(PKGCONFIG_INSTALL_DIR 16 | - "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig" 17 | + "${CMAKE_INSTALL_LIBDIR}/pkgconfig" 18 | CACHE PATH "Path where catch2.pc is installed" 19 | ) 20 | 21 | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt 22 | index 1e3af14..265626c 100644 23 | --- a/src/CMakeLists.txt 24 | +++ b/src/CMakeLists.txt 25 | @@ -402,7 +402,6 @@ if(NOT_SUBPROJECT) 26 | install( 27 | TARGETS 28 | Catch2 29 | - Catch2WithMain 30 | EXPORT 31 | Catch2Targets 32 | LIBRARY DESTINATION 33 | @@ -413,6 +412,19 @@ if(NOT_SUBPROJECT) 34 | ${CMAKE_INSTALL_BINDIR} 35 | ) 36 | 37 | + install( 38 | + TARGETS 39 | + Catch2WithMain 40 | + EXPORT 41 | + Catch2Targets 42 | + LIBRARY DESTINATION 43 | + ${CMAKE_INSTALL_LIBDIR}/manual-link 44 | + ARCHIVE DESTINATION 45 | + ${CMAKE_INSTALL_LIBDIR}/manual-link 46 | + RUNTIME DESTINATION 47 | + ${CMAKE_INSTALL_BINDIR} 48 | + ) 49 | + 50 | install( 51 | EXPORT 52 | Catch2Targets 53 | -------------------------------------------------------------------------------- /vcpkg-overlay/catch2/portfile.cmake: -------------------------------------------------------------------------------- 1 | 2 | # NOTE: This custom port with 'synch.patch' is only temporary until the following PRs are available in the upstream vcpkg: 3 | # https://github.com/catchorg/Catch2/pull/3031 4 | # https://github.com/catchorg/Catch2/pull/3033 5 | 6 | if(VCPKG_TARGET_IS_WINDOWS) 7 | vcpkg_check_linkage(ONLY_STATIC_LIBRARY) 8 | endif() 9 | 10 | vcpkg_from_github( 11 | OUT_SOURCE_PATH SOURCE_PATH 12 | REPO catchorg/Catch2 13 | REF v${VERSION} 14 | SHA512 e36ad4eb68984d277bb8e4e609cdfb4305ef05a8896e17a646a1846cb848039314b98aca0034a1d6538b3cf8bb134736f0ddda12210d106de73b3ae828d36473 15 | HEAD_REF devel 16 | PATCHES 17 | fix-install-path.patch 18 | synch.patch 19 | except.patch 20 | ) 21 | 22 | vcpkg_check_features(OUT_FEATURE_OPTIONS OPTIONS 23 | FEATURES 24 | thread-safe-assertions CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS 25 | ) 26 | 27 | vcpkg_cmake_configure( 28 | SOURCE_PATH "${SOURCE_PATH}" 29 | OPTIONS 30 | ${OPTIONS} 31 | -DCATCH_INSTALL_DOCS=OFF 32 | -DCMAKE_CXX_STANDARD=17 33 | ) 34 | 35 | vcpkg_cmake_install() 36 | 37 | vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/Catch2) 38 | vcpkg_fixup_pkgconfig() 39 | vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/catch2-with-main.pc" [["-L${libdir}"]] [["-L${libdir}/manual-link"]]) 40 | if(NOT VCPKG_BUILD_TYPE) 41 | vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/catch2-with-main.pc" [["-L${libdir}"]] [["-L${libdir}/manual-link"]]) 42 | endif() 43 | 44 | file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") 45 | file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share") 46 | 47 | # We remove these folders because they are empty and cause warnings on the library installation 48 | file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/include/catch2/benchmark/internal") 49 | file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/include/catch2/generators/internal") 50 | 51 | file(WRITE "${CURRENT_PACKAGES_DIR}/include/catch.hpp" "#include ") 52 | vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt") 53 | -------------------------------------------------------------------------------- /vcpkg-overlay/catch2/synch.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/catch2/internal/catch_reusable_string_stream.cpp b/src/catch2/internal/catch_reusable_string_stream.cpp 2 | index a2dd5a63..875da4da 100644 3 | --- a/src/catch2/internal/catch_reusable_string_stream.cpp 4 | +++ b/src/catch2/internal/catch_reusable_string_stream.cpp 5 | @@ -12,6 +12,7 @@ 6 | 7 | #include 8 | #include 9 | +#include 10 | #include 11 | 12 | namespace Catch { 13 | @@ -23,16 +24,16 @@ namespace Catch { 14 | std::ostringstream m_referenceStream; // Used for copy state/ flags from 15 | Detail::Mutex m_mutex; 16 | 17 | - auto add() -> std::size_t { 18 | + auto add() -> std::pair { 19 | Detail::LockGuard _( m_mutex ); 20 | if( m_unused.empty() ) { 21 | m_streams.push_back( Detail::make_unique() ); 22 | - return m_streams.size()-1; 23 | + return { m_streams.size()-1, m_streams.back().get() }; 24 | } 25 | else { 26 | auto index = m_unused.back(); 27 | m_unused.pop_back(); 28 | - return index; 29 | + return { index, m_streams[index].get() }; 30 | } 31 | } 32 | 33 | @@ -46,10 +47,10 @@ namespace Catch { 34 | } 35 | }; 36 | 37 | - ReusableStringStream::ReusableStringStream() 38 | - : m_index( Singleton::getMutable().add() ), 39 | - m_oss( Singleton::getMutable().m_streams[m_index].get() ) 40 | - {} 41 | + ReusableStringStream::ReusableStringStream() { 42 | + std::tie( m_index, m_oss ) = 43 | + Singleton::getMutable().add(); 44 | + } 45 | 46 | ReusableStringStream::~ReusableStringStream() { 47 | static_cast( m_oss )->str(""); 48 | -------------------------------------------------------------------------------- /vcpkg-overlay/catch2/vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "catch2", 3 | "version-semver": "3.10.0", 4 | "description": "A modern, C++-native, test framework for unit-tests, TDD and BDD.", 5 | "homepage": "https://github.com/catchorg/Catch2", 6 | "license": "BSL-1.0", 7 | "dependencies": [ 8 | { 9 | "name": "vcpkg-cmake", 10 | "host": true 11 | }, 12 | { 13 | "name": "vcpkg-cmake-config", 14 | "host": true 15 | } 16 | ], 17 | "features": { 18 | "thread-safe-assertions": { 19 | "description": "Enables thread safe assertions" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vcpkg-overlay/detours/portfile.cmake: -------------------------------------------------------------------------------- 1 | vcpkg_check_linkage(ONLY_STATIC_LIBRARY) 2 | 3 | vcpkg_from_github( 4 | OUT_SOURCE_PATH SOURCE_PATH 5 | REPO microsoft/Detours 6 | REF 9764cebcb1a75940e68fa83d6730ffaf0f669401 7 | SHA512 30f689a7f7dd3d762f1194ad8d7e05517678b754d6c0db297220f946485a8c8ec8a07cf5f3f893aabcc5623f64c81ee358e2a1c3ba23ba1fbd5856f6b3dd9eb7 8 | HEAD_REF master 9 | ) 10 | 11 | vcpkg_build_nmake( 12 | SOURCE_PATH "${SOURCE_PATH}" 13 | PROJECT_SUBPATH "src" 14 | PROJECT_NAME "Makefile" 15 | OPTIONS "PROCESSOR_ARCHITECTURE=${VCPKG_TARGET_ARCHITECTURE}" 16 | OPTIONS_RELEASE "DETOURS_CONFIG=Release" 17 | OPTIONS_DEBUG "DETOURS_CONFIG=Release" 18 | ) 19 | 20 | file(INSTALL "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/lib.${VCPKG_TARGET_ARCHITECTURE}Release/" DESTINATION "${CURRENT_PACKAGES_DIR}/lib") 21 | file(INSTALL "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/include/" DESTINATION "${CURRENT_PACKAGES_DIR}/include" RENAME detours) 22 | file(INSTALL "${SOURCE_PATH}/LICENSE.md" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) 23 | file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") 24 | -------------------------------------------------------------------------------- /vcpkg-overlay/detours/usage: -------------------------------------------------------------------------------- 1 | detours can be used from CMake via: 2 | 3 | find_path(DETOURS_INCLUDE_DIRS "detours/detours.h") 4 | find_library(DETOURS_LIBRARY detours REQUIRED) 5 | 6 | target_include_directories(main PRIVATE ${DETOURS_INCLUDE_DIRS}) 7 | target_link_libraries(main PRIVATE ${DETOURS_LIBRARY}) 8 | -------------------------------------------------------------------------------- /vcpkg-overlay/detours/vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "detours", 3 | "version": "4.0.1", 4 | "port-version": 8, 5 | "description": "Detours is a software package for monitoring and instrumenting API calls on Windows.", 6 | "homepage": "https://github.com/microsoft/Detours", 7 | "license": "MIT", 8 | "supports": "windows & !uwp" 9 | } 10 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | "detours", 4 | { 5 | "name": "catch2", 6 | "features": [ 7 | "thread-safe-assertions" 8 | ] 9 | } 10 | ], 11 | "builtin-baseline": "7213cf8135c329c37c7e2778e40774489a0583a8" 12 | } 13 | --------------------------------------------------------------------------------