├── .gitattributes ├── .gitignore ├── AUTHORS.md ├── LICENSE ├── README.md ├── include └── moderncom │ ├── com_ptr.h │ ├── guid.h │ ├── impl │ ├── errors.h │ ├── onexit.h │ ├── srwlock.h │ └── vector.h │ ├── interfaces.h │ └── library.h ├── moderncom.sln └── test ├── main.cpp ├── test.vcxproj └── test.vcxproj.filters /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.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 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | Alexander Bessonov 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 HHD Software Ltd. 2 | Author: Alexander Bessonov 3 | 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modern COM (for C++20) 2 | 3 | This library is a header-only lightweight wrapper for utilizing, declaring and implementing of Windows COM interfaces. 4 | 5 | ## Getting Started 6 | 7 | This is the code required to define `MyObject` class that implements two COM interfaces, `IFirstInterface` and `ISecondInterface`: 8 | 9 | ```C++ 10 | #include 11 | 12 | class MyObject : 13 | public belt::com::object< 14 | MyObject, // our class 15 | IFirstInterface, // any number of interfaces 16 | ISecondInterface> // we implement 17 | { 18 | // Implement methods of IFirstInterface 19 | ... 20 | // Implement methods of ISecondInterface 21 | ... 22 | 23 | public: 24 | // MyObject may optionally have non-empty constructor that is 25 | // allowed to throw exceptions 26 | MyObject(int a, int b); 27 | }; 28 | ``` 29 | 30 | That's all! `AddRef`, `Release` and even `QueryInterface` methods are automatically generated by the library. The objects of class `MyObject` may be created on heap, on stack, as singleton or deeply integrated with COM to be automatically constructed via factory objects returned by `DllGetClassObject` function. 31 | 32 | Read further to find out the details! 33 | 34 | ## Features 35 | 36 | * Supports declaration and implementation of COM interfaces with pure C++ code 37 | * Provides automatic generation of `QueryInterface`, `AddRef` and `Release` methods 38 | * Supports definition of native C++ classes that implement any number of COM interfaces either directly or through "implementation proxies" 39 | * Supports aggregation, objects on stack and singleton objects 40 | * Provides interoperability with ATL and serves as a natural upgrade path when upgrading legacy projects that use ATL 41 | * Allows classes that implement interfaces to have non-default constructors 42 | * Provides the COM "smart pointer" class, which can also be used independently from the rest of the library 43 | * Provides compile-time conversion of string GUIDs to `GUID`, which can be used independently from the rest of the library 44 | * Provides various customization points to simplify debugging or extend functionality of the library 45 | * Has built-in leak detection mechanism (that can be opted-in per class) to automatically search for leaked COM object references 46 | 47 | ## Requirements 48 | 49 | The library requires C++20 and has been tested on Microsoft Visual C++ compiler 19.26.28806 (Visual Studio 2019 16.6.5). 50 | 51 | See also [FAQ](#faq) section below for more information. 52 | 53 | ## Installation 54 | 55 | The library is header-only and does not require installation. Once brought into the project, the library's `include` folder should be made visible to the rest of the project. 56 | 57 | ## Documentation 58 | 59 | Use the links for fast navigation: 60 | 61 | * [Guid Helpers](#guid-helpers) 62 | * [Fetching Identifiers](#fetching-identifiers) 63 | * [COM Interface Smart Pointer](#com-interface-smart-pointer) 64 | * [COM Interface Support](#com-interface-support) 65 | * [Traits](#traits) 66 | * [Object Customization Points](#object-customization-points) 67 | * [Constructing Objects](#constructing-objects) 68 | * [Implementing COM DLL Server](#implementing-com-dll-server) 69 | * [Automatic Leak Detection](#automatic-leak-detection) 70 | * [FAQ](#faq) 71 | 72 | ### GUID Helpers 73 | 74 | In order to support backward compatibility and to simplify migration, the library can fetch interface and class identifiers (GUIDs) attached to classes via Microsoft Visual C++ extension `__declspec(uuid("..."))`. 75 | 76 | However, it also provides functions that parse string GUID into `GUID` structure at compile-time. Those functions are defined in `moderncpp/guid.h` header and may be used independently from the rest of the library. However, you don't need to include this header if you include any other library's header. 77 | 78 | The following functions are defined in the header: 79 | 80 | ```C++ 81 | template 82 | constexpr GUID belt::com::make_guid(const char(&str)[N]) { ... } 83 | ``` 84 | 85 | `make_guid` converts a passed string ID to `GUID`. Supports the following formats: `{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}` or `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`. 86 | 87 | The header also defines a user-defined literal (UDL) `_guid`, allowing the following code: 88 | 89 | ```C++ 90 | constexpr const GUID id = "{AB9A7AF1-6792-4D0A-83BE-8252A8432B45}"_guid; 91 | ``` 92 | 93 | ### Fetching Identifiers 94 | 95 | To be able to successfully work with interface types, the library must be able to fetch interface ID (as `GUID` structure) from a type at compile time. By default, it looks for specialization of the following function: 96 | 97 | ```C++ 98 | template 99 | constexpr GUID get_guid(Interface *) { ... } 100 | ``` 101 | 102 | A specialization is found using ADL. If specialization is not found, the default implementation tries to get an ID using Visual C++ extension `uuidof(type)`. 103 | 104 | `get_guid` may also be declared as a public static constexpr member of a class: 105 | 106 | ```C++ 107 | class MyClass ... 108 | { 109 | public: 110 | static constexpr GUID get_guid() { ... } 111 | }; 112 | ``` 113 | 114 | A specialization of `get_guid` function is automatically added with [`BELT_DEFINE_INTERFACE`](#BELT_DEFINE_INTERFACE), [`BELT_DEFINE_CLASS`](#BELT_DEFINE_CLASS) and other macros. 115 | 116 | The following function can be used to get GUID from the interface: 117 | 118 | ```C++ 119 | template 120 | constexpr GUID get_interface_guid() noexcept; 121 | ``` 122 | 123 | ### COM Interface Smart Pointer 124 | 125 | ```C++ 126 | #include 127 | ``` 128 | 129 | This header file provides the following template classes: `belt::com::com_ptr` and `belt::com::ref`. For convenience, they are also available in `bcom` namespace as `bcom::ptr` and `bcom::ref` correspondingly. 130 | 131 | #### `bcom::ptr` 132 | 133 | ```C++ 134 | template 135 | class com_ptr 136 | { 137 | ... 138 | }; 139 | ``` 140 | 141 | `com_ptr` takes a single template argument, the interface class itself. The library must be able to fetch an interface ID (IID) from this type. 142 | 143 | The following constructors are provided: 144 | 145 | 1. Default or null constructors 146 | 147 | ```C++ 148 | com_ptr(); 149 | com_ptr(std::nullptr_t) noexcept; 150 | ``` 151 | 152 | Default-construct or null-construct the smart pointer object. The object becomes empty. 153 | 154 | 1. Raw interface pointer constructor 155 | 156 | ```C++ 157 | com_ptr(Interface *) noexcept; 158 | ``` 159 | 160 | Constructs smart pointer object from a raw interface pointer. Successful construction increments object's usage counter with a call to `AddRef` method. 161 | 162 | 1. Attaching constructor 163 | 164 | ```C++ 165 | com_ptr(belt::com::attach_t, Interface *); 166 | ``` 167 | 168 | Constructs smart pointer object from a raw interface pointer. **DOES NOT** call `AddRef` method. Use a constant object `belt::com::attach` as a first parameter to constructor. 169 | 170 | 1. Other raw pointer constructor 171 | 172 | ```C++ 173 | template 174 | com_ptr(OtherInterface *punk) noexcept; 175 | ``` 176 | 177 | Constructs smart pointer object from a raw interface pointer to other interface. This constructor checks if `Interface` and `OtherInterface` are related: 178 | 179 | * If `OtherInterface` is derived from `Interface`, a simple `static_cast` is performed and `AddRef` is called on `Interface`. 180 | * Otherwise, an `Interface` pointer is obtained via `QueryInterface`. 181 | 182 | 1. Reference constructor 183 | 184 | ```C++ 185 | com_ptr(ref p) noexcept; 186 | ``` 187 | 188 | See the `bcom::ref` class below. 189 | 190 | 1. Copy constructor 191 | 192 | ```C++ 193 | com_ptr(const com_ptr &) noexcept; 194 | ``` 195 | 196 | 1. Move constructor 197 | 198 | ```C++ 199 | com_ptr(com_ptr &&) noexcept; 200 | ``` 201 | 202 | 1. Other smart pointer copy constructor 203 | 204 | ```C++ 205 | template 206 | com_ptr(const com_ptr &punk) noexcept; 207 | ``` 208 | 209 | Copy constructor from other smart pointer type. This constructor checks if `Interface` and `OtherInterface` are related: 210 | 211 | * If `OtherInterface` is derived from `Interface`, a simple `static_cast` is performed and `AddRef` is called on `Interface`. 212 | * Otherwise, an `Interface` pointer is obtained via `QueryInterface`. 213 | 214 | 1. Other smart pointer move constructor 215 | 216 | ```C++ 217 | template 218 | com_ptr(com_ptr &&punk) noexcept; 219 | ``` 220 | 221 | Move constructor from other smart pointer type. This constructor checks if `Interface` and `OtherInterface` are related: 222 | 223 | * If `OtherInterface` is derived from `Interface`, no calls to `AddRef` and `Release` are made. 224 | * Otherwise, an `Interface` pointer is obtained via `QueryInterface`. 225 | 226 | `com_ptr` also provides a symmetric set of assignment operators. 227 | 228 | The following methods are provided: 229 | 230 | Method | Description 231 | -- | -- 232 | `explicit operator bool() const noexcept` | Checks if the current smart pointer object not empty 233 | `release() noexcept` | Releases a current interface and empties smart pointer object 234 | `reset() noexcept` | Same as `release` 235 | `Interface *operator ->() const noexcept` | Dereferences the current smart pointer object 236 | `bool operator ==(const com_ptr &o) const noexcept` | Checks whether two smart pointer objects are equal 237 | `bool operator !=(const com_ptr &o) const noexcept` | Checks whether two smart pointer objects are not equal 238 | `bool operator <(const com_ptr &o) const noexcept` | Introduces ordering 239 | `void attach(Interface *p) noexcept` | Attaches a raw interface pointer. Asserts if smart pointer object is not empty 240 | `[[nodiscard]] Interface *detach() noexcept` | Detaches the currently stored raw interface pointer 241 | `Interface *get() const noexcept` | Retrieves the currently stored raw interface pointer 242 | `Interface **put() noexcept` | Provides a write access to the stored raw interface pointer. Asserts if the object is not empty 243 | `template auto as() const noexcept` | Constructs another smart pointer object with a given interface type 244 | `template HRESULT QueryInterface(OtherInterface **ppresult) const` | Calls QueryInterface to get raw result 245 | `HRESULT CoCreateInstance(const GUID &clsid, IUnknown *pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL) noexcept` | Calls `::CoCreateInstance` with provided parameters and stores the result in the current smart pointer object 246 | `HRESULT create_instance(const GUID &clsid, IUnknown *pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL) noexcept` | Same as `CoCreateInstance` 247 | `static com_ptr create(const GUID &clsid, IUnknown *pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL)` | Static method that calls `::CoCreateInstance` and returns a smart pointer object if successful. Otherwise throws an instance of `corsl::hresult_error`. 248 | 249 | Operators `==` and `!=` are also provided to any combination of `Interface *` and `const com_ptr &` pairs. 250 | 251 | #### `bcom::ref` 252 | 253 | This class is supposed to be used as a replacement for raw interface pointer in cases when interface pointer is used without adding a reference. Consider the following example: 254 | 255 | ```C++ 256 | void serialize(IStream *pStream) 257 | { 258 | // work with stream object and never store it 259 | // therefore, we don't have to call AddRef and Release 260 | ... 261 | } 262 | 263 | void foo() 264 | { 265 | bcom::ptr stream { construct_stream() }; 266 | 267 | serialize(stream.get()); // have to call get() here 268 | } 269 | ``` 270 | 271 | `serialize` function may be rewritten to take an instance of `bcom::ref` instead of a raw pointer. A benefit is additional lifetime checks in debug builds without any overhead in release builds: 272 | 273 | ```C++ 274 | void serialize(bcom::ref pStream) 275 | { 276 | // note pStream is passed by value 277 | // unmodified body of serialize function above 278 | } 279 | 280 | void foo() 281 | { 282 | bcom::ptr stream { construct_stream() }; 283 | 284 | serialize(stream); // implicitly construct bcom::ref here 285 | } 286 | ``` 287 | 288 | ```C++ 289 | template 290 | class ref { ... }; 291 | ``` 292 | 293 | `bcom::ref` takes a single template argument, the interface class itself. The library must be able to fetch an interface ID (IID) from this type. 294 | 295 | The following constructors are provided: 296 | 297 | 1. Default and null constructors 298 | 299 | ```C++ 300 | ref() = default; 301 | ref(std::nullptr_t) noexcept; 302 | ``` 303 | 304 | Construct an empty object. 305 | 306 | 1. Constructor from a raw pointer 307 | 308 | ```C++ 309 | ref(Interface *p) noexcept; 310 | ``` 311 | 312 | 1. Constructor from `com_ptr` 313 | 314 | ```C++ 315 | ref(const com_ptr &o) noexcept; 316 | ``` 317 | 318 | 1. Constructor from `com_ptr` temporary 319 | 320 | ```C++ 321 | ref(com_ptr &&o) noexcept; 322 | ``` 323 | 324 | This constructor is allowed, however it introduces additional lifetime checks in debug builds, unless the `BELT_COM_NO_CHECKED_REFS` macro is defined before including the `com_ptr.h` header. 325 | 326 | 1. Constructor from another smart pointer type: 327 | 328 | ```C++ 329 | template 330 | ref(const com_ptr &o) noexcept; 331 | ``` 332 | 333 | This constructor is allowed only if `OtherInterface` derives from `Interface`. Otherwise, the code is ill-formed. 334 | 335 | 1. Constructor from another smart pointer type temporary: 336 | 337 | ```C++ 338 | template 339 | ref(com_ptr &&o) noexcept; 340 | ``` 341 | 342 | This constructor is allowed only if `OtherInterface` derives from `Interface`. Otherwise, the code is ill-formed. Additional lifetime checks are performed in debug builds, unless the `BELT_COM_NO_CHECKED_REFS` macro is defined before including the `com_ptr.h` header. 343 | 344 | 1. Copy-constructor 345 | 346 | ```C++ 347 | template 348 | ref(const ref &o) noexcept; 349 | ``` 350 | 351 | This constructor is allowed only if `OtherInterface` derives from `Interface`. Otherwise, the code is ill-formed. 352 | 353 | Assignment operators are prohibited for `ref` objects. 354 | 355 | The same comparison operators are defined for `ref` class as for `com_ptr` class. 356 | 357 | The following methods are available: 358 | 359 | Method | Description 360 | -- | -- 361 | `Interface *operator ->() const noexcept` | Dereferences the current smart pointer object 362 | `Interface *get() const noexcept` | Retrieves the currently stored raw interface pointer 363 | `template auto as() const noexcept` | Constructs another smart pointer object with a given interface type 364 | 365 | ### COM Interface Support 366 | 367 | A `moderncom/interfaces.h` header provides infrastructure for working with COM interfaces in native C++ code. 368 | 369 | #### Declaring Interfaces 370 | 371 | The following macros may be used to declare interfaces: {#BELT_DEFINE_INTERFACE} 372 | 373 | ```C++ 374 | BELT_DEFINE_INTERFACE(name, guid) 375 | { 376 | // declare interface members here as normal C++ abstract methods, for example 377 | virtual int sum(int a, int b) = 0; 378 | }; 379 | ``` 380 | 381 | or 382 | 383 | ```C++ 384 | BELT_DEFINE_INTERFACE_BASE(name, baseInterfaceName, guid) 385 | { 386 | // declare interface members here as normal C++ abstract methods, for example 387 | virtual int sum(int a, int b) = 0; 388 | }; 389 | ``` 390 | 391 | First macro declares interface `name` derived from `IUnknown` and second macro declares interface `name` derived from `baseInterfaceName`. 392 | 393 | The library is also capable of working with "legacy" interfaces. This basically means that any abstract class that is derived from another legacy interface or `IUnknown` and for which library can fetch interface id can be directly used by the library. 394 | 395 | #### Implementing Interfaces 396 | 397 | Imagine you want to implement a class `MyObject` that implements two interfaces, `IFirstInterface` and `ISecondInterface`. Here's the full class declaration: 398 | 399 | ```C++ 400 | #include 401 | 402 | class __declspec(novtable) MyObject : 403 | public belt::com::object< 404 | MyObject, 405 | IFirstInterface, 406 | ISecondInterface> 407 | { 408 | // Implement methods of IFirstInterface 409 | ... 410 | // Implement methods of ISecondInterface 411 | ... 412 | }; 413 | ``` 414 | 415 | Constructing object on heap: 416 | 417 | ```C++ 418 | com_ptr construct_object() 419 | { 420 | return MyObject::create_instance().to_ptr(); 421 | } 422 | 423 | com_ptr construct_object_second() 424 | { 425 | return MyObject::create_instance().to_ptr(); 426 | } 427 | ``` 428 | 429 | #### `object` 430 | 431 | `object` is a variadic template class declared as 432 | 433 | ```C++ 434 | template 435 | class object; 436 | ``` 437 | 438 | `Derived` should be the name of the class that derives from `object`. 439 | 440 | `Interfaces` is a non-empty list of interfaces the class implements. Every entry in the list must be one of the following: 441 | 442 | * COM interface class, for which library is able to fetch IID (see above). Must not be `IUnknown`. 443 | * `also`. See [below](#also) for more information. 444 | * `eats_all` class. See [below](#eats_all) for more information. 445 | * `aggregates`. See [below](#aggregates) for more information. 446 | * A class that derives from [`intermediate`](#intermediate). 447 | 448 | **Important note**: `Derived` class declaration follows the so-called CRTP pattern and causes the resulting class to effectively derive from all COM interfaces directly or indirectly listed. 449 | 450 | `object` has the following members: 451 | 452 | * ```C++ 453 | using DefaultInterface = unspecified; 454 | ``` 455 | 456 | Returns the "default" (usually first) interface. Never equals `IUnknown`. 457 | 458 | * ```C++ 459 | IUnknown *GetUnknown() noexcept; 460 | ``` 461 | 462 | Can be used inside or outside of derived class to obtain a direct pointer to `IUnknown` interface. Does not call `AddRef`. 463 | 464 | * ```C++ 465 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) noexcept override; 466 | ``` 467 | 468 | Implements `IUnknown::QueryInterface`. 469 | 470 | * ```C++ 471 | template 472 | static object_holder create_instance(Args &&...args); 473 | ``` 474 | 475 | Static method that should be used to create an instance of `Derived` class on heap. Arguments, if passed are perfect-forwarded to `Derived`'s constructor. Returns a special wrapper object. See [`object_holder`](#object_holder) below. 476 | 477 | See also [object customization points](#object-customization-points) section below. 478 | 479 | * ```C++ 480 | template 481 | static com_ptr create_aggregate(IUnknown *pOuterUnknown, Args &&...args); 482 | ``` 483 | 484 | Create new instance of `Derived` aggregating `pOuterUnknown`. `Derived` must has [`supports_aggregation`](#supports_agregation) trait (see below). 485 | 486 | Arguments, if passed are perfect-forwarded to `Derived`'s constructor. 487 | 488 | See also [object customization points](#object-customization-points) section below. 489 | 490 | * ```C++ 491 | template 492 | com_ptr create_copy() const; 493 | ``` 494 | 495 | Creates a copy of the current object (invoking `Derived`'s copy constructor) and queries a copy for a given interface. `Derived` must derive from `OtherInterface` or `OtherInterface` must be `IUnknown`. 496 | 497 | * ```C++ 498 | auto addref() noexcept; 499 | ``` 500 | 501 | This protected method may be called by `Derived` class if it needs to explicitly add an object reference. 502 | 503 | * ```C++ 504 | auto release() noexcept; 505 | ``` 506 | 507 | This protected method may be called by `Derived` class if it needs to explicitly release an object reference. 508 | 509 | #### `also` 510 | 511 | `also` class template should be used whenever `Derived` implements "legacy" interface that itself derives from another legacy interface. Legacy interfaces are those interfaces that **are not** declared with `BELT_DEFINE_INTERFACE` macro. 512 | 513 | Consider the following example. `IDispatchEx` interface is defined in Platform SDK and derives from `IDispatch` interface. It is a legacy interface. We declare `MyClass` the following way: 514 | 515 | ```C++ 516 | class MyClass : 517 | public belt::com::object< 518 | MyObject, 519 | IDispatchEx> 520 | { 521 | // Implement IDispatch 522 | ... 523 | // Implement IDispatchEx 524 | ... 525 | }; 526 | ``` 527 | 528 | The generated `QueryInterface` automatically supports querying for `IDispatchEx` interface, but does not support querying for `IDispatch` interface. This example must be fixed in the following way: 529 | 530 | ```C++ 531 | class MyClass : 532 | public belt::com::object< 533 | MyObject, 534 | IDispatchEx, 535 | belt::com::also> // fix 536 | { 537 | // Implement IDispatch 538 | ... 539 | // Implement IDispatchEx 540 | ... 541 | }; 542 | ``` 543 | 544 | If interface is declared using `BELT_DEFINE_INTERFACE` macro, this fix is not required, even if declared interface derives from "legacy" interface: 545 | 546 | ```C++ 547 | BELT_DEFINE_INTERFACE_BASE(IMyDispatch, IDispatch, "{AB9A7AF1-6792-4D0A-83BE-8252A8432B45}") 548 | { 549 | ... 550 | }; 551 | 552 | class MyClass : 553 | public belt::com::object< 554 | MyObject, 555 | IMyDispatch> 556 | { 557 | // Implement IDispatch 558 | ... 559 | // Implement IMyDispatch 560 | ... 561 | }; 562 | ``` 563 | 564 | #### `eats_all` 565 | 566 | One of the `QueryInterface` customization points is an `eats_all` special entry in interface list. If the class contains this entry, it must implement the following public method: 567 | 568 | ```C++ 569 | void *on_eat_all(const IID &id) noexcept 570 | { 571 | ... 572 | } 573 | ``` 574 | 575 | If a class supports a given interface, it must obtain a pointer to this interface, call an `AddRef` method through the obtained pointer and return it, casted to `void *`. Otherwise, it must return `nullptr`. 576 | 577 | #### `aggregates` 578 | 579 | COM objects may support aggregation. That is, an object may advertise an interface that it does not directly implement. It then "forwards" the query for this particular interface to its class member variable, for example, or obtains the pointer using other ways. 580 | 581 | The library supports this scenario with a `aggregates` entry in interface list. For each aggregate interface listed, the `Derived` class must implement the following public method: 582 | 583 | ```C++ 584 | void *on_query(belt::com::interface_wrapper) noexcept 585 | { 586 | ... 587 | } 588 | ``` 589 | 590 | `on_query` must obtain a pointer to requested interface, call an `AddRef` method through the obtained pointer and return it, casted to `void *`: 591 | 592 | ```C++ 593 | class MyClass : 594 | public belt::com::object< 595 | MyClass, 596 | IDirectlySupportedInterface, 597 | belt::com::aggregates< 598 | MyClass, 599 | IAggregateInterface 600 | > 601 | > 602 | { 603 | bcom::ptr member { initialize_member() }; 604 | 605 | // Implement IDirectlySupportedInterface 606 | ... 607 | // Implement aggregation 608 | void *on_query(belt::com::interface_wrapper) noexcept 609 | { 610 | IAggregateInterface *result{}; 611 | member->QueryInterface(&result); 612 | return result; 613 | } 614 | }; 615 | ``` 616 | 617 | #### `intermediate` 618 | 619 | It is often convenient to create classes or template classes that provide (partial) implementation of a given interface or interfaces and then use them when implementing final classes. 620 | 621 | The library provides a machinery for such "implementation proxy" classes with a help of `intermediate` class template: 622 | 623 | ```C++ 624 | template 625 | struct intermediate; 626 | ``` 627 | 628 | This class template looks similar to `object` and should be used the same way. However, `intermediate` does not implement members declared in `object`. 629 | 630 | ```C++ 631 | BELT_DEFINE_INTERFACE(IMyInterface, "{AB9A7AF1-6792-4D0A-83BE-8252A8432B45}") 632 | { 633 | virtual void Method1() =0; 634 | virtual void Method2() =0; 635 | }; 636 | 637 | // Provide a partial implementation of IMyInterface 638 | class MyInterfaceImpl : 639 | public belt::com::intermediate< 640 | MyInterfaceImpl, 641 | IMyInterface> 642 | { 643 | // Partially implement Method1 644 | virtual void Method1() override { ... } 645 | }; 646 | 647 | // MyClass implements IMyInterface with a help of MyInterfaceImpl: 648 | class MyClass : 649 | public belt::com::object< 650 | MyClass, 651 | MyInterfaceImpl> 652 | { 653 | // We still have to implement IMyInterface 654 | virtual void Method2() override { ... } 655 | }; 656 | ``` 657 | 658 | #### `object_holder` 659 | 660 | `object_holder` is a temporary object holder class template that is returned by `object::create_instance` method: 661 | 662 | ```C++ 663 | template 664 | class object_holder; 665 | ``` 666 | 667 | Where `T` is an unspecified class derived from `Derived`. 668 | 669 | The class has the following members: 670 | 671 | * ```C++ 672 | com_ptr to_ptr() && noexcept; 673 | ``` 674 | 675 | May only be invoked on temporary `object_holder` object and "converts" it to `com_ptr`. 676 | 677 | The object is considered "moved-out" after this method returns. 678 | 679 | 680 | * ```C++ 681 | template 682 | com_ptr to_ptr() && noexcept; 683 | ``` 684 | 685 | May only be invoked on temporary `object_holder` object and "converts" it to `com_ptr`. `Derived` must derive from `Interface` or `Interface` must be `IUnknown`. 686 | 687 | The object is considered "moved-out" after this method returns. 688 | 689 | * ```C++ 690 | Derived *obj() const noexcept; 691 | ``` 692 | 693 | This special-purpose method is supposed to be used when additional initialization is required on constructed object: 694 | 695 | ```C++ 696 | class __declspec(novtable) MyObject : 697 | public belt::com::object 698 | { 699 | public: 700 | void additional_initialization_method(...); 701 | }; 702 | 703 | com_ptr construct_object() 704 | { 705 | auto my_object = MyObject::create_instance(); 706 | my_object.obj()->additional_initialization_method(); 707 | return std::move(my_object).to_ptr(); 708 | } 709 | ``` 710 | 711 | ### Traits 712 | 713 | A trait class is a special class that `Derived` must directly derive from to change various defaults. The following trait classes are available: 714 | 715 | * [`singleton_factory`](#singleton_factory) 716 | * [`single_cached_instance`](#single_cached_instance) 717 | * [`supports_aggregation`](#supports_aggregation) 718 | * [`increments_module_count`](#increments_module_count) 719 | * [`enable_leak_detection`](#enable_leak_detection) 720 | 721 | Some of these trait classes automatically "propagate" down on inheritance chain. That is, if an implementation proxy class or even an interface class specifies a trait, it will also be present in any derived final class. 722 | 723 | #### `singleton_factory` 724 | 725 | When object is constructed using library's [default construction mechanism](#default-construction-mechanism), use the single global instance. 726 | 727 | Singleton object is created at the time it is first requested and lives until the program is finished. There is no way to destroy the object before the program ends. 728 | 729 | Access to object creation is thread-safe. 730 | 731 | #### `single_cached_instance` 732 | 733 | A special case of a singleton. An object is created at the time it is first requested and cached for all subsequent create requests. If the created object's reference count reaches zero, it is destroyed. 734 | 735 | Access to object creation and destruction is thread-safe. 736 | 737 | #### `supports_aggregation` 738 | 739 | Marks the class as supporting aggregation. A class must be marked so if it is supposed to be used in aggregation (that is, constructed via `create_aggregate` or via [default construction mechanism](#default-construction-mechanism) with non-null `pOuterUnknown`). 740 | 741 | If the class is never to be used in aggregation, you can get more efficient implementation by **not including** this trait. 742 | 743 | #### `implements_module_count` 744 | 745 | Increment global module reference count whenever this class object's is referenced. Can be used in conjunction with `DllCanUnloadNow` implementation. 746 | 747 | #### `enable_leak_detection` 748 | 749 | Turn on leak detection for this class. See [Automatic Leak Detection](#automatic-leak-detection) section for more information. 750 | 751 | ### Object Customization Points 752 | 753 | Customization points allow the class to execute additional code at various object lifetime events. They are all completely optional. 754 | 755 | A customization point is a public method declared in the `Derived` class. The following customization points are supported: 756 | 757 | * [`final_construct`](#final_construct) 758 | * [`final_release`](#final_release) 759 | * [`on_add_ref`](#on_add_ref) 760 | * [`on_release`](#on_release) 761 | * [`pre_query_interface`](#pre_query_interface) 762 | * [`post_query_interface`](#post_query_interface) 763 | 764 | #### `final_construct` 765 | 766 | When constructor of the `Derived` class executes, reference-counting machinery is not yet initialized. Therefore, constructor cannot make any external calls that expect the current object to be a valid COM object. 767 | 768 | For such cases, a class may provide the following public method: 769 | 770 | ```C++ 771 | template 772 | HRESULT final_construct(Args &&...args) 773 | { 774 | ... 775 | } 776 | ``` 777 | 778 | The `final_construct` method is invoked when reference-counting machinery is fully initialized. If arguments are present, the user is supposed to pass `belt::com::delayed` object as a first argument to `create_instance` or `create_aggregate` methods and `Derived` constructor must take no parameters. 779 | 780 | `final_construct` is allowed to throw exceptions or return non-zero error codes. If `final_construct` returns an error code, an instance of `corsl::hresult_error` holding this error code is thrown. 781 | 782 | #### `final_release` 783 | 784 | Correspondingly, there is a symmetric `final_release` method. It must have one of the following signatures: 785 | 786 | ```C++ 787 | static void final_release(std::unique_ptr ptr) noexcept 788 | { 789 | ... 790 | } 791 | 792 | template 793 | static void final_release(std::unique_ptr ptr) noexcept 794 | { 795 | ... 796 | } 797 | ``` 798 | 799 | Note that `final_release` method is a static member and it takes a unique pointer to the current object in the heap. By default, the object will be destroyed at the end of the `final_release` method, but the customization is free to do anything it needs with a passed pointer. 800 | 801 | The second variant is used with aggregated objects. An implementation may use `if constexpr (std::is_same_v)` to check if it is called with an object or aggregate value of the object. In the latter case, it can obtain a pointer to an object itself with by calling the pointed object's `get()` method: 802 | 803 | ```C++ 804 | class MyObject : 805 | public belt::com::object< 806 | MyObject, 807 | ...>, 808 | public belt::com::supports_aggregation 809 | { 810 | void ifinal_release() 811 | { 812 | ... 813 | } 814 | public: 815 | // customization point 816 | template 817 | static void final_release(std::unique_ptr ptr) noexcept 818 | { 819 | if constexpr (std::is_same_v) 820 | { 821 | ptr->ifinal_release(); 822 | } else 823 | { 824 | ptr->get()->ifinal_release(); 825 | } 826 | // Object will auto-destruct here 827 | } 828 | }; 829 | ``` 830 | 831 | #### `on_add_ref` 832 | 833 | This customization point is invoked each time an object's reference counter is incremented. 834 | 835 | ```C++ 836 | void on_add_ref(int new_counter_value); 837 | ``` 838 | 839 | #### `on_release` 840 | 841 | This customization point is invoked each time an object's reference counter is decremented. 842 | 843 | ```C++ 844 | void on_release(int new_counter_value); 845 | ``` 846 | 847 | #### `pre_query_interface` 848 | 849 | This customization point is invoked before standard `QueryInterface` machinery: 850 | 851 | ```C++ 852 | HRESULT pre_query_interface(REFIID iid, void **ppresult) noexcept; 853 | ``` 854 | 855 | If method is capable of producing result, it must store it at `*ppresult` and return `S_OK`. Otherwise, it must return `E_NOINTERFACE`. If this method returns any other value, `QueryInterface` processing immediately stops and the given error code is returned to the caller. In this case, the method **must store nullptr** at `*ppresult`. 856 | 857 | If successful result is produced, implementation must call `AddRef` on obtained interface. 858 | 859 | #### `post_query_interface` 860 | 861 | This customization point is invoked after standard `QueryInterface` machinery was unable to find a requested interface: 862 | 863 | ```C++ 864 | HRESULT post_query_interface(REFIID iid, void **ppresult) noexcept; 865 | ``` 866 | 867 | If method is capable of producing result, it must store it at `*ppresult` and return `S_OK`. Otherwise, it **must store nullptr** at `*ppresult` and return `E_NOINTERFACE`. 868 | 869 | If successful result is produced, implementation must call `AddRef` on obtained interface. 870 | 871 | ### Constructing Objects 872 | 873 | The library provides several ways to construct COM objects: 874 | 875 | * [Simple Construction in the Heap](#simple-construction-in-the-heap) 876 | * [Simple Construction on the Stack](#simple-construction-on-the-stack) 877 | * [Default Construction Mechanism](#default-construction-mechanism) 878 | 879 | #### Simple Construction in the Heap 880 | 881 | To construct an object of a given class `Derived` in the heap, call the static method `Derived::create_instance`, passing any number of arguments for class's constructor. 882 | 883 | If the first argument is `belt::com::delayed`, then the default constructor is used instead and the rest of arguments are passed to the [`final_construct`](#final_construct) customization point. 884 | 885 | If object construction succeeds, the method returns a proxy object. This proxy object is usually kept temporary and you will immediately invoke its `to_ptr()` method, optionally passing an interface you want to query from the created object. 886 | 887 | Advanced usages of a proxy object are described above in [`object_holder`](#object_holder) section. 888 | 889 | Note that the class's constructor or `final_construct` customization point are allowed to throw exceptions. 890 | 891 | #### Simple Construction on the Stack 892 | 893 | For short-lived COM objects or for COM objects for which lifetime can be synchronized with a specific scope, you can use stack-based construction: 894 | 895 | ```C++ 896 | class MyClass : public belt::com::object {...}; 897 | 898 | void bar(bcom::ref p) 899 | { 900 | ... 901 | } 902 | 903 | void foo() 904 | { 905 | belt::com::value_on_stack obj{/* arguments to constructor or final_construct */}; 906 | bar(&obj); 907 | } 908 | ``` 909 | 910 | `AddRef` and `Release` methods for objects constructed on stack are no-op, however, in debug builds the object's destructor will assert if there were unmatched number of calls to `AddRef` and `Release`. 911 | 912 | #### Default Construction Mechanism 913 | 914 | This generic mechanism for constructing COM objects can be used in cases when the calling code does not know specific implementation class, or for runtime object construction. 915 | 916 | First of all, a class that wants to participate in default construction must register itself using one of the following macros: 917 | 918 | ```C++ 919 | BELT_OBJ_ENTRY_AUTO(classname) 920 | BELT_OBJ_ENTRY_AUTO2(classguid, classname) 921 | ``` 922 | 923 | The `BELT_OBJ_ENTRY_AUTO` macro registers `classname` class for which library can automatically fetch class ID. 924 | 925 | The `BELT_OBJ_ENTRY_AUTO2` macro registers `classname` with a given CLSID: 926 | 927 | ```C++ 928 | BELT_OBJ_ENTRY_AUTO2("{FFDBB4B7-8ECB-42FE-BF68-163B1E0829A2}"_guid, MyClass); 929 | ``` 930 | 931 | As described above, an ability to automatically fetch the class ID means that either `get_guid` was specialized for the class, or `__declspec(uuid("..."))` was added to class's declaration. 932 | 933 | The library also provides a macro to attach an ID to a class: {#BELT_DEFINE_CLASS} 934 | 935 | ```C++ 936 | BELT_DEFINE_CLASS(classGuidName, guid) 937 | ``` 938 | 939 | or inside a class 940 | 941 | ```C++ 942 | class MyClass ... { 943 | public: 944 | BELT_CLASS_GUID(guid) 945 | }; 946 | ``` 947 | 948 | After the class is registered, instances of this class may be created using one of the following functions: 949 | 950 | * ```C++ 951 | template 952 | HRESULT create_object(const GUID &clsid, const GUID &iid, void **ppv, IUnknown *pOuterUnknown = nullptr) noexcept; 953 | ``` 954 | 955 | Create an instance of a class with a given `CLSID` and query an interface with a given `IID`. Pass a non-null `pOuterUnknown` if you want a created object to be aggregated. 956 | 957 | This function never throws. It returns a non-zero error code. If object creation throws an instance of `corsl::hresult_error` exception, exception's error code is returned. If object creation throws any other exception, `E_FAIL` is returned. 958 | 959 | * ```C++ 960 | template 961 | HRESULT create_object(const GUID &clsid, bcom::ptr &result, IUnknown *pOuterUnknown = nullptr) noexcept; 962 | ``` 963 | 964 | The same as above, but automatically takes `IID` from `Interface` and fills `result` on success. 965 | 966 | * ```C++ 967 | template 968 | bcom::ptr create_object(const GUID &clsid, IUnknown *pOuterUnknown = nullptr); 969 | ``` 970 | 971 | Directly returns a smart pointer to a given `Interface` or throws an instance of `corsl::hresult_error` when object creation fails. 972 | 973 | `create_object` respects the [singleton](#singleton_factory) and [single cached instance](#single_cached_instance) traits when creating objects. 974 | 975 | ### Implementing COM DLL Server 976 | 977 | `create_object` function described above serves as a foundation for implementing `DllGetClassObject`. 978 | 979 | All you need to do to implement DLL COM server is to add the following code to one of your CPP files: 980 | 981 | ```C++ 982 | #include 983 | 984 | HRESULT_export CALLBACK DllCanUnloadNow() 985 | { 986 | return belt::com::DllCanUnloadNow(); 987 | } 988 | 989 | HRESULT_export CALLBACK DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppvObj) 990 | { 991 | return belt::com::DllGetClassObject(rclsid, riid, ppvObj); 992 | } 993 | ``` 994 | 995 | ### Automatic Leak Detection 996 | 997 | The library provides a built-in mechanism to search for leaked object references. It not only shows you which objects were leaked, but also provides detailed stack traces for the corresponding leaked `AddRef` calls! 998 | 999 | Leak detection is only enabled in debug builds. 1000 | 1001 | The following pre-requisites must be done in order for the leak detection to work: 1002 | 1003 | 1. `Boost.StackTrace` library is a dependency. If you cannot use `boost`, you must disable automatic leak detection by defining the following macro before including any library header: 1004 | 1005 | ```C++ 1006 | #define BELT_COM_NO_LEAK_DETECTION 1007 | ``` 1008 | 1009 | 1. `belt::com::init_leak_detection()` function must be called once before using `com_ptr` class or creating objects. 1010 | 1011 | Note that this function must be called no matter if you actually use automatic leak detection, unless you completely disable leak detection as described above. 1012 | 1013 | This function is empty in release builds. 1014 | 1015 | 1. Classes that you want to participate in leak detection must opt-in by [including](#enable_leak_detection) `belt::com::enable_leak_detection` trait. 1016 | 1017 | The recommendation is to only enable leak detection for those classes whose object references are found to be leaking. 1018 | 1019 | Once all pre-requisites are met, you should run your program under debugger. Currently, this mechanism does not detect leaked objects, you should use other facilities to detect leaked objects. For example, you can use tools built into Visual Studio or use tracing to find leaked objects. Alternatively, you can combine automatic leak detection with objects constructed on stack, because in debug builds library automatically asserts when destructor for such object is called with mismatched number of calls to `AddRef` and `Release`. 1020 | 1021 | Once leaked objects are found, add them to the Watch window in Visual Studio and expand until you find `umb_usages` member. It will contain a list of stack traces of calls to `AddRef` that were not matched with corresponding calls to `Release`. 1022 | 1023 | #### Limitations 1024 | 1025 | 1. Leak detection is built into `com_ptr` and `object` classes and therefore is unable to track calls to `AddRef` and `Release` made by other components. In other words, it always assumes that only `com_ptr` class makes calls to `AddRef` and `Release`. 1026 | 1027 | 1. Leak detection does not currently find leaked objects. Once a leaked object is found by other means, it can be viewed in the debugger to see a list of stack traces. 1028 | 1029 | ## FAQ 1030 | 1031 | 1. How robust is the library? 1032 | 1033 | This library was extracted from mature production code that had been in use for several years. `moderncom` should be in a good quality to be used in a project of any size. 1034 | 1035 | 1. Why C++20? 1036 | 1037 | The production code this library was extracted from is constantly updated to use latest language features. Initially `moderncom` required C++17 and some C++20 requirements were added at a later time. 1038 | 1039 | 1. Why Windows and MSVC only? 1040 | 1041 | COM is a Windows technology that is continued to be used even in modern OS components. In fact, the newest WinRT is also based on COM. However, COM can be considered a technology to provide binary inter-connectivity between native components with a stable C++ ABI and therefore, may theoretically be used on other platforms. It can also be used as a way to establish Inversion of Control principles in code. 1042 | 1043 | There are a few Windows bindings in the code that may be relatively easy decoupled from the rest of the code. After that, the library may be used on other platforms to establish a solid connectivity between application components. 1044 | 1045 | This has not been done, however. 1046 | 1047 | 1. What served as inspiration for this library? 1048 | 1049 | The library was initially created as a way to "renovate" old code base that used ATL for COM support. It was inspired by early works by Kenny Kerr on his `moderncpp` project (which later became C++/WinRT library). The library may share some ideas (but not implementation) with C++/WinRT. It also does not have dependency on WinRT and does not require Windows 10. 1050 | -------------------------------------------------------------------------------- /include/moderncom/com_ptr.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | 10 | #pragma once 11 | #include 12 | 13 | #if !defined(BELT_COM_NO_LEAK_DETECTION) && defined(_DEBUG) 14 | #define BELT_HAS_LEAK_DETECTION 1 15 | #else 16 | #define BELT_HAS_LEAK_DETECTION 0 17 | #endif 18 | 19 | #if !defined(BELT_COM_NO_CHECKED_REFS) && defined(_DEBUG) 20 | #define BELT_HAS_CHECKED_REFS 1 21 | #else 22 | #define BELT_HAS_CHECKED_REFS 0 23 | #endif 24 | 25 | 26 | #if BELT_HAS_LEAK_DETECTION 27 | #include 28 | #include 29 | // If you get a compilation error at the following line, do one of the following: 30 | // - Add boost 1.73.0 or later to your project 31 | // or 32 | // - #define BELT_COM_NO_LEAK_DETECTION before including any library's header to disable leak detection 33 | #include 34 | #endif 35 | 36 | #if BELT_HAS_CHECKED_REFS 37 | #include 38 | #endif 39 | 40 | #include "guid.h" 41 | #include "impl/srwlock.h" 42 | #include "impl/onexit.h" 43 | 44 | #include 45 | 46 | #include "impl/errors.h" 47 | 48 | namespace belt::com 49 | { 50 | namespace details 51 | { 52 | template 53 | class com_ptr; 54 | #if BELT_HAS_LEAK_DETECTION 55 | 56 | struct leak_detection 57 | { 58 | int ordinal; 59 | boost::stacktrace::stacktrace stack; 60 | 61 | static int get_next() noexcept 62 | { 63 | static std::atomic global_ordinal{}; 64 | return ++global_ordinal; 65 | } 66 | 67 | leak_detection() noexcept : 68 | ordinal{ get_next() }, 69 | stack{ boost::stacktrace::stacktrace() } 70 | { 71 | } 72 | }; 73 | 74 | inline void init_leak_detection() noexcept 75 | { 76 | using namespace std::literals; 77 | SYSTEM_INFO si{}; 78 | GetSystemInfo(&si); 79 | HANDLE section = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, si.dwPageSize, (L"BELT_LEAK_DETECTION_SECTION."s + std::to_wstring(GetCurrentProcessId())).c_str()); 80 | *reinterpret_cast(MapViewOfFile(section, FILE_MAP_WRITE, 0, 0, si.dwPageSize)) = TlsAlloc(); 81 | // We intentionally leave section mapped 82 | } 83 | 84 | inline DWORD get_slot() noexcept 85 | { 86 | using namespace std::literals; 87 | SYSTEM_INFO si{}; 88 | GetSystemInfo(&si); 89 | HANDLE section = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, si.dwPageSize, (L"BELT_LEAK_DETECTION_SECTION."s + std::to_wstring(GetCurrentProcessId())).c_str()); 90 | assert(section && GetLastError() == ERROR_ALREADY_EXISTS && "init_leak_detection() must be called from EXE module as early as possible! It has not yet been called."); 91 | auto ptr = reinterpret_cast(MapViewOfFile(section, FILE_MAP_WRITE, 0, 0, si.dwPageSize)); 92 | assert(ptr); 93 | CloseHandle(section); 94 | auto ret = *ptr; 95 | UnmapViewOfFile(ptr); 96 | return ret; 97 | } 98 | 99 | inline void set_current_cookie(int cookie) noexcept 100 | { 101 | static DWORD slot = get_slot(); 102 | TlsSetValue(slot, reinterpret_cast(static_cast(cookie))); 103 | } 104 | 105 | inline int get_current_cookie() noexcept 106 | { 107 | static DWORD slot = get_slot(); 108 | auto retval = TlsGetValue(slot); 109 | TlsSetValue(slot, nullptr); 110 | return static_cast(reinterpret_cast(retval)); 111 | } 112 | #else 113 | inline void init_leak_detection() noexcept 114 | { 115 | } 116 | #endif 117 | struct attach_t {}; 118 | 119 | template 120 | class ref; 121 | 122 | template 123 | class __declspec(empty_bases) com_ptr 124 | { 125 | friend class ref; 126 | 127 | friend Interface *&internal_get(com_ptr &obj) noexcept 128 | { 129 | return obj.p; 130 | } 131 | 132 | Interface *p{}; 133 | #if BELT_HAS_CHECKED_REFS 134 | std::vector *> weaks; 135 | #endif 136 | 137 | #if BELT_HAS_LEAK_DETECTION 138 | int cookie{}; 139 | 140 | friend int &internal_get_cookie(com_ptr &obj) noexcept 141 | { 142 | return obj.cookie; 143 | } 144 | #endif 145 | 146 | // smart construction from other interfaces 147 | template 148 | com_ptr(OtherInterface *punk, std::true_type) noexcept : 149 | p{ static_cast(punk) } 150 | { 151 | addref_pointer(p); 152 | } 153 | 154 | template 155 | com_ptr(OtherInterface *punk, std::false_type) noexcept 156 | { 157 | if (punk) 158 | { 159 | punk->QueryInterface(get_interface_guid(interface_wrapper{}), reinterpret_cast(&p)); 160 | store_cookie(); 161 | } 162 | } 163 | // 164 | 165 | // 166 | template 167 | com_ptr(com_ptr &&o, std::false_type) noexcept 168 | { 169 | if (o) 170 | { 171 | if (SUCCEEDED(o->QueryInterface(get_interface_guid(interface_wrapper{}), reinterpret_cast(&p)))) 172 | { 173 | store_cookie(); 174 | o.release(); 175 | } 176 | } 177 | } 178 | 179 | template 180 | com_ptr(com_ptr &&o, std::true_type) noexcept : 181 | p{ static_cast(o.get()) } 182 | #if BELT_HAS_LEAK_DETECTION 183 | , cookie{ internal_get_cookie(o) } 184 | #endif 185 | { 186 | internal_get(o) = nullptr; 187 | #if BELT_HAS_LEAK_DETECTION 188 | internal_get_cookie(o) = 0; 189 | #endif 190 | } 191 | // 192 | template 193 | com_ptr(const com_ptr &o, std::false_type) noexcept 194 | { 195 | if (o) 196 | { 197 | o->QueryInterface(get_interface_guid(interface_wrapper{}), reinterpret_cast(&p)); 198 | store_cookie(); 199 | } 200 | } 201 | 202 | template 203 | com_ptr(const com_ptr &o, std::true_type) noexcept : 204 | p{ static_cast(o.get()) } 205 | { 206 | addref_pointer(p); 207 | } 208 | 209 | #if BELT_HAS_LEAK_DETECTION 210 | void store_cookie() noexcept 211 | { 212 | cookie = get_current_cookie(); 213 | } 214 | #else 215 | static void store_cookie() noexcept 216 | { 217 | } 218 | #endif 219 | 220 | void addref_pointer(IUnknown *pint) noexcept 221 | { 222 | if (pint) 223 | { 224 | pint->AddRef(); 225 | store_cookie(); 226 | } 227 | } 228 | 229 | void release_pointer(IUnknown *pint) noexcept 230 | { 231 | if (pint) 232 | { 233 | #if BELT_HAS_LEAK_DETECTION 234 | set_current_cookie(std::exchange(cookie, 0)); 235 | #endif 236 | pint->Release(); 237 | } 238 | } 239 | 240 | public: 241 | explicit operator bool() const noexcept 242 | { 243 | return !!p; 244 | } 245 | 246 | com_ptr() = default; 247 | com_ptr(std::nullptr_t) noexcept {} 248 | 249 | com_ptr(Interface *p) noexcept : 250 | p{ p } 251 | { 252 | addref_pointer(p); 253 | } 254 | 255 | // the following constructor does not call addref (attaching constructor) 256 | com_ptr(attach_t, Interface *p) noexcept : 257 | p{ p } 258 | { 259 | store_cookie(); // might get incorrect cookie, check 260 | } 261 | 262 | template 263 | com_ptr(OtherInterface *punk) noexcept : 264 | com_ptr(punk, std::is_base_of{}) 265 | {} 266 | 267 | template 268 | com_ptr(const com_ptr &o) noexcept : 269 | com_ptr(o, std::is_base_of{}) 270 | {} 271 | 272 | template 273 | com_ptr(com_ptr &&o) noexcept : 274 | com_ptr(std::move(o), std::is_base_of{}) 275 | {} 276 | 277 | void release() noexcept 278 | { 279 | if (p) 280 | { 281 | release_pointer(p); 282 | p = nullptr; 283 | } 284 | } 285 | 286 | void reset() noexcept 287 | { 288 | release(); 289 | } 290 | 291 | ~com_ptr() noexcept 292 | { 293 | release_pointer(p); 294 | #if BELT_HAS_CHECKED_REFS 295 | assert(weaks.empty() && "There was ref constructed from this com_ptr that outlived this object!"); 296 | #endif 297 | } 298 | 299 | com_ptr(const com_ptr &o) noexcept : 300 | p{ o.p } 301 | { 302 | addref_pointer(p); 303 | } 304 | 305 | template 306 | com_ptr(ref o) noexcept; 307 | 308 | com_ptr &operator =(const com_ptr &o) noexcept 309 | { 310 | release_pointer(p); 311 | p = o.p; 312 | addref_pointer(p); 313 | return *this; 314 | } 315 | 316 | com_ptr(com_ptr &&o) noexcept : 317 | p{ o.p } 318 | #if BELT_HAS_LEAK_DETECTION 319 | , cookie {o.cookie } 320 | #endif 321 | { 322 | o.p = nullptr; 323 | #if BELT_HAS_LEAK_DETECTION 324 | o.cookie = 0; 325 | #endif 326 | } 327 | 328 | com_ptr &operator =(com_ptr &&o) noexcept 329 | { 330 | #if BELT_HAS_LEAK_DETECTION 331 | std::swap(cookie, o.cookie); 332 | #endif 333 | std::swap(p, o.p); 334 | return *this; 335 | } 336 | 337 | Interface *operator ->() const noexcept 338 | { 339 | return p; 340 | } 341 | 342 | // Comparison operators 343 | bool operator ==(const com_ptr &o) const noexcept 344 | { 345 | return p == o.p; 346 | } 347 | 348 | bool operator !=(const com_ptr &o) const noexcept 349 | { 350 | return p != o.p; 351 | } 352 | 353 | bool operator ==(Interface *pother) const noexcept 354 | { 355 | return p == pother; 356 | } 357 | 358 | bool operator !=(Interface *pother) const noexcept 359 | { 360 | return p != pother; 361 | } 362 | 363 | friend bool operator ==(Interface *p1, const com_ptr &p2) noexcept 364 | { 365 | return p1 == p2.p; 366 | } 367 | 368 | friend bool operator !=(Interface *p1, const com_ptr &p2) noexcept 369 | { 370 | return p1 != p2.p; 371 | } 372 | 373 | // Ordering operator 374 | bool operator<(const com_ptr &o) const noexcept 375 | { 376 | return p < o.p; 377 | } 378 | 379 | // Attach and detach operations 380 | void attach(Interface *p_) noexcept 381 | { 382 | assert(!p); 383 | p = p_; 384 | store_cookie(); // might get incorrect cookie 385 | } 386 | 387 | [[nodiscard]] 388 | Interface *detach() noexcept 389 | { 390 | Interface *cur{}; 391 | std::swap(cur, p); 392 | #if BELT_HAS_LEAK_DETECTION 393 | cookie = 0; 394 | #endif 395 | 396 | return cur; 397 | } 398 | 399 | // Pointer accessors 400 | Interface *get() const noexcept 401 | { 402 | return p; 403 | } 404 | 405 | #if BELT_HAS_LEAK_DETECTION 406 | int get_cookie() const noexcept 407 | { 408 | return cookie; 409 | } 410 | #endif 411 | 412 | Interface **put() noexcept 413 | { 414 | assert(!p && "Using put on non-empty object is prohibited"); 415 | return &p; 416 | } 417 | 418 | // Conversion operations 419 | template 420 | auto as() const noexcept 421 | { 422 | return com_ptr{*this}; 423 | } 424 | 425 | template 426 | HRESULT QueryInterface(T **ppresult) const 427 | { 428 | return p->QueryInterface(get_interface_guid(interface_wrapper{}), reinterpret_cast(ppresult)); 429 | } 430 | 431 | HRESULT CoCreateInstance(const GUID &clsid, IUnknown *pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL) noexcept 432 | { 433 | assert(!p && "Calling CoCreateInstance on initialized object is prohibited"); 434 | SCOPE_EXIT 435 | { 436 | store_cookie(); 437 | }; 438 | 439 | return ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, get_interface_guid(interface_wrapper{}), reinterpret_cast(&p)); 440 | } 441 | 442 | HRESULT create_instance(const GUID &clsid, IUnknown *pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL) noexcept 443 | { 444 | return CoCreateInstance(clsid, pUnkOuter, dwClsContext); 445 | } 446 | 447 | static com_ptr create(const GUID &clsid, IUnknown *pUnkOuter = nullptr, DWORD dwClsContext = CLSCTX_ALL) 448 | { 449 | com_ptr result; 450 | auto hr = result.CoCreateInstance(clsid, pUnkOuter, dwClsContext); 451 | if (SUCCEEDED(hr)) 452 | return result; 453 | else 454 | corsl::throw_error(hr); 455 | } 456 | 457 | #if BELT_HAS_CHECKED_REFS 458 | void add_weak(ref *pweak) 459 | { 460 | weaks.push_back(pweak); 461 | } 462 | 463 | void remove_weak(ref *pweak) 464 | { 465 | std::erase(weaks, pweak); 466 | } 467 | #endif 468 | }; 469 | 470 | constexpr const attach_t attach = {}; 471 | 472 | 473 | template 474 | class __declspec(empty_bases) ref 475 | { 476 | Interface *p{}; 477 | #if BELT_HAS_CHECKED_REFS 478 | com_ptr *parent{}; 479 | #endif 480 | struct move_tag {}; 481 | 482 | template 483 | ref(OtherInterface *p, std::true_type) noexcept : 484 | p{ static_cast(p) } 485 | {} 486 | 487 | template 488 | ref(com_ptr &&o, std::true_type) noexcept : 489 | p{ static_cast(o.p) } 490 | { 491 | #if BELT_HAS_CHECKED_REFS 492 | parent = &o; 493 | // We allow construction from temporary com_ptr, but in DEBUG build we make sure the com_ptr lives long enough 494 | o.add_weak(this); 495 | #endif 496 | } 497 | 498 | public: 499 | explicit operator bool() const noexcept 500 | { 501 | return !!p; 502 | } 503 | 504 | ref() = default; 505 | ref(std::nullptr_t) noexcept {} 506 | 507 | ref(const com_ptr &o) noexcept : 508 | p{ o.p } 509 | { 510 | } 511 | 512 | ref(com_ptr &&o) noexcept : 513 | p{ o.p } 514 | { 515 | #if BELT_HAS_CHECKED_REFS 516 | parent = &o; 517 | // We allow construction from temporary com_ptr, but in DEBUG build we make sure the com_ptr lives long enough 518 | o.add_weak(this); 519 | #endif 520 | } 521 | 522 | // allow construction from derived interfaces 523 | template 524 | ref(const com_ptr &o) noexcept : 525 | ref(o.get(), std::is_base_of{}) 526 | {} 527 | 528 | template 529 | ref(com_ptr &&o) noexcept : 530 | ref(move_tag{}, std::move(o), std::is_base_of{}) 531 | {} 532 | 533 | template 534 | ref(const ref &o) noexcept : 535 | ref(o.get(), std::is_base_of{}) 536 | {} 537 | 538 | #if BELT_HAS_CHECKED_REFS 539 | ~ref() 540 | { 541 | if (parent) 542 | parent->remove_weak(this); 543 | } 544 | 545 | ref(const ref &o) : 546 | p{ o.p }, 547 | parent{ o.parent } 548 | { 549 | if (parent) 550 | parent->add_weak(this); 551 | } 552 | 553 | ref(ref &&o) noexcept : 554 | p{ o.p }, 555 | parent{ o.parent } 556 | { 557 | if (parent) 558 | { 559 | parent->remove_weak(&o); 560 | parent->add_weak(this); 561 | } 562 | } 563 | #endif 564 | 565 | ref(Interface *p) noexcept : 566 | p{ p } 567 | { 568 | } 569 | 570 | template 571 | ref &operator =(const T &) = delete; 572 | 573 | Interface *operator ->() const noexcept 574 | { 575 | return p; 576 | } 577 | 578 | // Comparison operators 579 | bool operator ==(const ref &o) const noexcept 580 | { 581 | return p == o.p; 582 | } 583 | 584 | bool operator !=(const ref &o) const noexcept 585 | { 586 | return p != o.p; 587 | } 588 | 589 | bool operator ==(Interface *pother) const noexcept 590 | { 591 | return p == pother; 592 | } 593 | 594 | bool operator !=(Interface *pother) const noexcept 595 | { 596 | return p != pother; 597 | } 598 | 599 | friend bool operator ==(Interface *p1, const ref &p2) noexcept 600 | { 601 | return p1 == p2.p; 602 | } 603 | 604 | friend bool operator !=(Interface *p1, const ref &p2) noexcept 605 | { 606 | return p1 != p2.p; 607 | } 608 | 609 | bool operator ==(const com_ptr &o) noexcept 610 | { 611 | return p == o.p; 612 | } 613 | 614 | bool operator !=(const com_ptr &o) noexcept 615 | { 616 | return p != o.p; 617 | } 618 | 619 | // Ordering operator 620 | bool operator<(const ref &o) const noexcept 621 | { 622 | return p < o.p; 623 | } 624 | 625 | // Pointer accessors 626 | Interface *get() const noexcept 627 | { 628 | return p; 629 | } 630 | 631 | // Conversion operations 632 | template 633 | auto as() const noexcept 634 | { 635 | return com_ptr{ p }; 636 | } 637 | }; 638 | 639 | template 640 | template 641 | inline com_ptr::com_ptr(ref o) noexcept : com_ptr{ o.get() } 642 | { 643 | } 644 | } 645 | 646 | using details::com_ptr; 647 | using details::ref; 648 | 649 | using details::attach; 650 | using details::init_leak_detection; 651 | } 652 | 653 | namespace bcom 654 | { 655 | // Possibly guard by macro 656 | template 657 | using ptr = belt::com::com_ptr; 658 | template 659 | using ref = belt::com::ref; 660 | } 661 | -------------------------------------------------------------------------------- /include/moderncom/guid.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | 10 | #pragma once 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if !defined(GUID_DEFINED) 19 | #define GUID_DEFINED 20 | struct GUID { 21 | uint32_t Data1; 22 | uint16_t Data2; 23 | uint16_t Data3; 24 | uint8_t Data4[8]; 25 | }; 26 | #endif 27 | 28 | namespace belt::com 29 | { 30 | namespace details 31 | { 32 | constexpr const size_t short_guid_form_length = 36; // {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} 33 | constexpr const size_t long_guid_form_length = 38; // XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 34 | 35 | // 36 | constexpr int parse_hex_digit(const char c) 37 | { 38 | using namespace std::string_literals; 39 | if ('0' <= c && c <= '9') 40 | return c - '0'; 41 | else if ('a' <= c && c <= 'f') 42 | return 10 + c - 'a'; 43 | else if ('A' <= c && c <= 'F') 44 | return 10 + c - 'A'; 45 | else 46 | throw std::domain_error{ "invalid character in GUID"s }; 47 | } 48 | 49 | template 50 | constexpr T parse_hex(const char *ptr) 51 | { 52 | constexpr size_t digits = sizeof(T) * 2; 53 | T result{}; 54 | for (size_t i = 0; i < digits; ++i) 55 | result |= parse_hex_digit(ptr[i]) << (4 * (digits - i - 1)); 56 | return result; 57 | } 58 | 59 | constexpr GUID make_guid_helper(const char *begin) 60 | { 61 | GUID result{}; 62 | result.Data1 = parse_hex(begin); 63 | begin += 8 + 1; 64 | result.Data2 = parse_hex(begin); 65 | begin += 4 + 1; 66 | result.Data3 = parse_hex(begin); 67 | begin += 4 + 1; 68 | result.Data4[0] = parse_hex(begin); 69 | begin += 2; 70 | result.Data4[1] = parse_hex(begin); 71 | begin += 2 + 1; 72 | for (size_t i = 0; i < 6; ++i) 73 | result.Data4[i + 2] = parse_hex(begin + i * 2); 74 | return result; 75 | } 76 | 77 | template 78 | constexpr GUID make_guid(const char(&str)[N]) 79 | { 80 | using namespace std::string_literals; 81 | static_assert(N == (long_guid_form_length + 1) || N == (short_guid_form_length + 1), "String GUID of the form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} or XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is expected"); 82 | 83 | if constexpr(N == (long_guid_form_length + 1)) 84 | { 85 | if (str[0] != '{' || str[long_guid_form_length - 1] != '}') 86 | throw std::domain_error{ "Missing opening or closing brace"s }; 87 | } 88 | 89 | return make_guid_helper(str + (N == (long_guid_form_length + 1) ? 1 : 0)); 90 | } 91 | 92 | // 93 | template 94 | struct has_get_guid_impl : std::false_type {}; 95 | 96 | template 97 | struct has_get_guid_impl : std::true_type {}; 98 | 99 | template 100 | using has_get_guid = has_get_guid_impl; 101 | 102 | template 103 | struct has_free_get_guid_impl : std::false_type {}; 104 | 105 | template 106 | struct has_free_get_guid_impl())))> : std::true_type {}; 107 | 108 | template 109 | using has_free_get_guid = has_free_get_guid_impl; 110 | 111 | // 112 | template 113 | struct interface_wrapper 114 | { 115 | using type = T; 116 | }; 117 | 118 | template 119 | constexpr const auto msvc_get_guid_workaround = T::get_guid(); 120 | 121 | // 122 | template 123 | constexpr GUID get_interface_guid_impl(std::true_type, std::false_type) 124 | { 125 | return msvc_get_guid_workaround; 126 | } 127 | 128 | template 129 | constexpr GUID get_interface_guid_impl(Any, std::true_type) noexcept 130 | { 131 | return get_guid(static_cast(nullptr)); 132 | } 133 | 134 | template 135 | constexpr GUID get_interface_guid_impl(std::false_type, std::false_type) noexcept 136 | { 137 | return __uuidof(T); 138 | } 139 | 140 | // 141 | template 142 | constexpr GUID get_interface_guid(T); 143 | 144 | template 145 | constexpr GUID get_interface_guid(interface_wrapper) noexcept 146 | { 147 | return get_interface_guid_impl(has_get_guid{}, has_free_get_guid{}); 148 | } 149 | } 150 | using details::make_guid; 151 | 152 | namespace literals 153 | { 154 | constexpr GUID operator "" _guid(const char *str, size_t N) 155 | { 156 | using namespace details; 157 | using namespace std::string_literals; 158 | 159 | if (!(N == long_guid_form_length || N == short_guid_form_length)) 160 | throw std::domain_error{ "String GUID of the form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} or XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is expected"s }; 161 | if (N == long_guid_form_length && (str[0] != '{' || str[long_guid_form_length - 1] != '}')) 162 | throw std::domain_error{ "Missing opening or closing brace"s }; 163 | 164 | return details::make_guid_helper(str + (N == long_guid_form_length ? 1 : 0)); 165 | } 166 | } 167 | } 168 | 169 | using namespace belt::com::literals; 170 | -------------------------------------------------------------------------------- /include/moderncom/impl/errors.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | 10 | #pragma once 11 | 12 | namespace corsl 13 | { 14 | namespace details 15 | { 16 | // Simplified version of hresult_error 17 | // May be used on Vista+ 18 | class hresult_error 19 | { 20 | HRESULT m_code{ E_FAIL }; 21 | 22 | public: 23 | constexpr hresult_error() = default; 24 | 25 | explicit constexpr hresult_error(const HRESULT code) noexcept : 26 | m_code{ code } 27 | { 28 | } 29 | 30 | constexpr HRESULT code() const noexcept 31 | { 32 | return m_code; 33 | } 34 | 35 | constexpr bool is_aborted() const noexcept 36 | { 37 | return m_code == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED); 38 | } 39 | }; 40 | 41 | [[noreturn]] inline void throw_error(HRESULT hr) 42 | { 43 | throw hresult_error{ hr }; 44 | } 45 | 46 | [[noreturn]] inline void throw_win32_error(DWORD err) 47 | { 48 | throw_error(HRESULT_FROM_WIN32(err)); 49 | } 50 | 51 | [[noreturn]] inline void throw_last_error() 52 | { 53 | throw_win32_error(GetLastError()); 54 | } 55 | 56 | inline void check_hresult(HRESULT error) 57 | { 58 | if (FAILED(error)) 59 | throw_error(error); 60 | } 61 | 62 | //inline void check_win32(DWORD error) 63 | //{ 64 | // if (error) 65 | // throw hresult_error{ HRESULT_FROM_WIN32(error) }; 66 | //} 67 | 68 | //inline void check_io(BOOL result) 69 | //{ 70 | // if (!result) 71 | // { 72 | // auto err = GetLastError(); 73 | // if (err != ERROR_IO_PENDING) 74 | // throw hresult_error{ HRESULT_FROM_WIN32(err) }; 75 | // } 76 | //} 77 | 78 | //inline void check_win32_api(BOOL res) 79 | //{ 80 | // if (!res) 81 | // throw hresult_error{ HRESULT_FROM_WIN32(GetLastError()) }; 82 | //} 83 | } 84 | 85 | using details::hresult_error; 86 | 87 | using details::throw_error; 88 | using details::throw_win32_error; 89 | using details::throw_last_error; 90 | using details::check_hresult; 91 | //using details::check_win32; 92 | //using details::check_io; 93 | //using details::check_win32_api; 94 | } 95 | -------------------------------------------------------------------------------- /include/moderncom/impl/onexit.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | //////////////////////////// 14 | // The following code adapted from Andrei Alexandrescu talk on cppcon 2015 15 | 16 | namespace belt::details 17 | { 18 | class UncaughtExceptionsCounter 19 | { 20 | int exceptions_on_enter{ std::uncaught_exceptions() }; 21 | public: 22 | bool has_new_exceptions() const noexcept 23 | { 24 | return std::uncaught_exceptions() > exceptions_on_enter; 25 | } 26 | }; 27 | 28 | template 29 | class scope_guard 30 | { 31 | F f; 32 | UncaughtExceptionsCounter counter; 33 | public: 34 | explicit scope_guard(const F &f) noexcept : 35 | f{ f } 36 | {} 37 | 38 | explicit scope_guard(F &&f) noexcept : 39 | f{ std::move(f) } 40 | {} 41 | 42 | ~scope_guard() noexcept(execute_on_exception) 43 | { 44 | if (execute_on_exception == counter.has_new_exceptions()) 45 | f(); 46 | } 47 | }; 48 | 49 | template 50 | class scope_exit 51 | { 52 | F f; 53 | public: 54 | explicit scope_exit(const F &f) noexcept : 55 | f{ f } 56 | {} 57 | 58 | explicit scope_exit(F &&f) noexcept : 59 | f{ std::move(f) } 60 | {} 61 | 62 | ~scope_exit() noexcept 63 | { 64 | f(); 65 | } 66 | }; 67 | 68 | template 69 | class scope_exit_cancellable 70 | { 71 | F f; 72 | bool cancelled{ false }; 73 | public: 74 | explicit scope_exit_cancellable(const F &f) noexcept : 75 | f{ f } 76 | {} 77 | 78 | explicit scope_exit_cancellable(F &&f) noexcept : 79 | f{ std::move(f) } 80 | {} 81 | 82 | void cancel() 83 | { 84 | cancelled = true; 85 | } 86 | 87 | ~scope_exit_cancellable() noexcept 88 | { 89 | if (!cancelled) 90 | f(); 91 | } 92 | }; 93 | 94 | enum class ScopeGuardOnFail {}; 95 | 96 | template 97 | inline scope_guard, true> operator +(ScopeGuardOnFail, F &&f) noexcept 98 | { 99 | return scope_guard, true>(std::forward(f)); 100 | } 101 | 102 | enum class ScopeGuardOnSuccess {}; 103 | 104 | template 105 | inline scope_guard, false> operator +(ScopeGuardOnSuccess, F &&f) noexcept 106 | { 107 | return scope_guard, false>(std::forward(f)); 108 | } 109 | 110 | enum class ScopeGuardOnExit {}; 111 | 112 | template 113 | inline scope_exit> operator +(ScopeGuardOnExit, F &&f) noexcept 114 | { 115 | return scope_exit>(std::forward(f)); 116 | } 117 | 118 | enum class ScopeGuardOnExitCancellable {}; 119 | 120 | template 121 | inline scope_exit_cancellable> operator +(ScopeGuardOnExitCancellable, F &&f) noexcept 122 | { 123 | return scope_exit_cancellable>(std::forward(f)); 124 | } 125 | } 126 | 127 | #define PP_CAT(a,b) a##b 128 | 129 | // Alexandrescu 130 | #define ANONYMOUS_VARIABLE(x) PP_CAT(x,__COUNTER__) 131 | 132 | #define SCOPE_FAIL \ 133 | auto ANONYMOUS_VARIABLE(SCOPE_FAIL_STATE) \ 134 | = ::Belt::details::ScopeGuardOnFail() + [&]() noexcept \ 135 | // end of macro 136 | 137 | #define SCOPE_SUCCESS \ 138 | auto ANONYMOUS_VARIABLE(SCOPE_SUCCESS_STATE) \ 139 | = ::Belt::details::ScopeGuardOnSuccess() + [&]() \ 140 | // end of macro 141 | 142 | #define SCOPE_EXIT \ 143 | auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \ 144 | = ::Belt::details::ScopeGuardOnExit() + [&]() noexcept \ 145 | // end of macro 146 | 147 | #define SCOPE_EXIT_CANCELLABLE(name) \ 148 | auto name\ 149 | = ::Belt::details::ScopeGuardOnExitCancellable() + [&]() noexcept \ 150 | // end of macro 151 | -------------------------------------------------------------------------------- /include/moderncom/impl/srwlock.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | #pragma once 10 | 11 | namespace belt 12 | { 13 | // Windows SRW lock wrapped in shared_mutex-friendly class 14 | class srwlock 15 | { 16 | SRWLOCK m_lock{}; 17 | public: 18 | srwlock(const srwlock &) = delete; 19 | srwlock & operator=(const srwlock &) = delete; 20 | srwlock() noexcept = default; 21 | 22 | _Acquires_exclusive_lock_(&m_lock) 23 | void lock() noexcept 24 | { 25 | AcquireSRWLockExclusive(&m_lock); 26 | } 27 | 28 | _Acquires_shared_lock_(&m_lock) 29 | void lock_shared() noexcept 30 | { 31 | AcquireSRWLockShared(&m_lock); 32 | } 33 | 34 | _When_(return, _Acquires_exclusive_lock_(&m_lock)) 35 | bool try_lock() noexcept 36 | { 37 | return 0 != TryAcquireSRWLockExclusive(&m_lock); 38 | } 39 | 40 | _Releases_exclusive_lock_(&m_lock) 41 | void unlock() noexcept 42 | { 43 | ReleaseSRWLockExclusive(&m_lock); 44 | } 45 | 46 | _Releases_shared_lock_(&m_lock) 47 | void unlock_shared() noexcept 48 | { 49 | ReleaseSRWLockShared(&m_lock); 50 | } 51 | }; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /include/moderncom/impl/vector.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | namespace belt::mpl 14 | { 15 | // vector 16 | template 17 | struct vector 18 | { 19 | typedef vector type; 20 | }; 21 | 22 | // size 23 | template 24 | struct size; 25 | 26 | template 27 | struct size> 28 | { 29 | static const size_t value = sizeof...(Types); 30 | }; 31 | 32 | // push_back 33 | template 34 | struct push_back; 35 | 36 | template 37 | struct push_back, T> 38 | { 39 | typedef vector type; 40 | }; 41 | 42 | template 43 | using push_back_t = typename push_back::type; 44 | 45 | // push_front 46 | template 47 | struct push_front; 48 | 49 | template 50 | struct push_front, T> 51 | { 52 | typedef vector type; 53 | }; 54 | 55 | template 56 | using push_front_t = typename push_front::type; 57 | 58 | // append 59 | template 60 | struct append; 61 | 62 | template 63 | struct append, B> 64 | { 65 | typedef vector type; 66 | }; 67 | 68 | template 69 | struct append> 70 | { 71 | typedef vector type; 72 | }; 73 | 74 | template 75 | struct append, vector> 76 | { 77 | typedef vector type; 78 | }; 79 | 80 | template 81 | using append_t = typename append::type; 82 | 83 | // front 84 | template 85 | struct front; 86 | 87 | template 88 | struct front> 89 | { 90 | typedef First type; 91 | }; 92 | 93 | template 94 | using front_t = typename front::type; 95 | 96 | // back 97 | template 98 | struct back; 99 | 100 | template 101 | struct back> 102 | { 103 | typedef Last type; 104 | }; 105 | 106 | template 107 | using back_t = typename back::type; 108 | 109 | // remove_front 110 | template 111 | struct remove_front; 112 | 113 | template 114 | struct remove_front> 115 | { 116 | typedef vector type; 117 | }; 118 | 119 | template 120 | using remove_front_t = typename remove_front::type; 121 | 122 | // remove_back 123 | template 124 | struct remove_back; 125 | 126 | template 127 | struct remove_back> 128 | { 129 | typedef vector type; 130 | }; 131 | 132 | template 133 | using remove_back_t = typename remove_back::type; 134 | 135 | // as_tuple 136 | template 137 | struct as_tuple; 138 | 139 | template 140 | struct as_tuple> 141 | { 142 | typedef std::tuple type; 143 | }; 144 | 145 | template 146 | using as_tuple_t = typename as_tuple::type; 147 | } 148 | -------------------------------------------------------------------------------- /include/moderncom/interfaces.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | 10 | #pragma once 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(_DEBUG) 16 | #include 17 | #endif 18 | 19 | #include "impl/vector.h" 20 | #include "impl/errors.h" 21 | 22 | #include "com_ptr.h" 23 | 24 | namespace belt::com 25 | { 26 | namespace details 27 | { 28 | struct ModuleCount 29 | { 30 | static std::atomic lock_count; 31 | }; 32 | 33 | inline __declspec(selectany) std::atomic ModuleCount::lock_count{}; 34 | 35 | #pragma region object 36 | struct supports_aggregation_t {}; 37 | struct singleton_factory_t {}; 38 | struct smart_singleton_factory_t {}; 39 | struct increments_module_count_t {}; 40 | struct enable_leak_detection_t {}; 41 | 42 | struct delayed_t {}; 43 | constexpr const delayed_t delayed = {}; 44 | 45 | template 46 | struct __declspec(empty_bases)embeds_interface_id {}; 47 | 48 | // get_first 49 | template 50 | concept has_get_first = requires 51 | { 52 | T::get_first(); 53 | }; 54 | 55 | template 56 | concept has_implements = requires 57 | { 58 | typename T::can_query; 59 | }; 60 | 61 | // increments_module_count 62 | template 63 | concept has_increments_module_count = requires 64 | { 65 | typename T::increments_module_count_t; 66 | requires std::same_as; 67 | }; 68 | 69 | // supports_aggregation 70 | template 71 | struct has_supports_aggregation_impl : std::false_type {}; 72 | 73 | template 74 | struct has_supports_aggregation_impl>> : std::true_type {}; 75 | 76 | template 77 | using has_supports_aggregation = has_supports_aggregation_impl; 78 | 79 | // enable_leak_detector 80 | template 81 | struct has_enable_leak_detector_impl : std::false_type {}; 82 | 83 | template 84 | struct has_enable_leak_detector_impl>> : std::true_type {}; 85 | 86 | template 87 | using has_enable_leak_detector_helper = has_enable_leak_detector_impl; 88 | 89 | template 90 | using has_enable_leak_detector = typename has_enable_leak_detector_helper::type; 91 | 92 | // singleton 93 | template 94 | struct has_singleton_factory_impl : std::false_type {}; 95 | 96 | template 97 | struct has_singleton_factory_impl>> : std::true_type {}; 98 | 99 | template 100 | using has_singleton_factory = has_singleton_factory_impl; 101 | 102 | template 103 | struct has_smart_singleton_factory_impl : std::false_type {}; 104 | 105 | template 106 | struct has_smart_singleton_factory_impl>> : std::true_type {}; 107 | 108 | template 109 | using has_smart_singleton_factory = has_smart_singleton_factory_impl; 110 | 111 | // 112 | 113 | template 114 | inline void *query_single([[maybe_unused]] T *pobj, [[maybe_unused]] const GUID &iid) noexcept 115 | { 116 | if constexpr (has_implements) 117 | return Interface::query_self(pobj, iid); 118 | else if constexpr (std::is_same_v) 119 | return nullptr; 120 | else if (iid == get_interface_guid(interface_wrapper{})) 121 | { 122 | auto ret = static_cast(pobj); 123 | ret->AddRef(); 124 | return ret; 125 | } 126 | else 127 | return nullptr; 128 | } 129 | 130 | template 131 | inline void *query(T *pobj, const GUID &iid, mpl::vector) noexcept 132 | { 133 | void *result{ nullptr }; 134 | (... || (nullptr != (result = query_single(pobj, iid)))); 135 | return result; 136 | } 137 | 138 | template 139 | struct __declspec(empty_bases)extends_base : public Interfaces... 140 | { 141 | template 142 | static void *query_children(Derived *pobject, const GUID &riid) noexcept 143 | { 144 | return query(pobject, riid, mpl::vector {}); 145 | } 146 | }; 147 | 148 | // extends marks derived as pure interface 149 | template 150 | struct __declspec(empty_bases)extends : public extends_base 151 | { 152 | struct can_query 153 | { 154 | using type = mpl::vector; 155 | }; 156 | 157 | template 158 | static void *query_self(Derived *pobject, const GUID &iid) noexcept 159 | { 160 | if (get_interface_guid(interface_wrapper{}) == iid) 161 | { 162 | auto ret = static_cast(pobject); 163 | ret->AddRef(); 164 | return ret; 165 | } 166 | else 167 | return extends_base::query_children(pobject, iid); 168 | } 169 | }; 170 | 171 | template 172 | inline constexpr auto get_first_impl(interface_wrapper, std::true_type) noexcept 173 | { 174 | return T::get_first(); 175 | } 176 | 177 | template 178 | inline constexpr auto get_first_impl(interface_wrapper v, std::false_type) noexcept 179 | { 180 | return v; 181 | } 182 | 183 | template 184 | inline constexpr auto get_first([[maybe_unused]] interface_wrapper v) noexcept 185 | { 186 | if constexpr (has_get_first) 187 | return T::get_first(); 188 | else 189 | return v; 190 | } 191 | 192 | template 193 | struct __declspec(empty_bases)intermediate : public extends_base 194 | { 195 | struct can_query 196 | { 197 | using type = mpl::vector; 198 | }; 199 | 200 | template 201 | static void *query_self(Derived *pobject, const GUID &iid) noexcept 202 | { 203 | return extends_base::query_children(pobject, iid); 204 | } 205 | 206 | static constexpr auto get_first() noexcept 207 | { 208 | return details::get_first(interface_wrapper{}); 209 | } 210 | }; 211 | 212 | template 213 | struct __declspec(empty_bases)eats_all : public extends_base<> 214 | { 215 | struct can_query 216 | { 217 | using type = mpl::vector<>; 218 | }; 219 | 220 | template 221 | static void *query_self(Derived *pobject, const GUID &iid) noexcept 222 | { 223 | return static_cast(pobject)->on_eat_all(iid); 224 | } 225 | }; 226 | 227 | template 228 | struct __declspec(empty_bases)aggregates 229 | { 230 | struct can_query 231 | { 232 | using type = mpl::vector; 233 | }; 234 | 235 | template 236 | static void *query_self(Derived *pobject, const GUID &iid) noexcept 237 | { 238 | void *result{ nullptr }; 239 | (... || (iid == get_interface_guid(interface_wrapper{}) ? (result = static_cast(pobject)->on_query(interface_wrapper{})), true : false)); 240 | return result; 241 | } 242 | }; 243 | 244 | template 245 | struct __declspec(empty_bases)also // no inheriting from interfaces! 246 | { 247 | struct can_query 248 | { 249 | using type = mpl::vector; 250 | }; 251 | 252 | template 253 | static void *query_self(Derived *pobject, const GUID &iid) noexcept 254 | { 255 | return query(pobject, iid, mpl::vector {}); 256 | } 257 | }; 258 | 259 | template 260 | concept has_legacy_final_construct = requires(T obj, Args &&...args) 261 | { 262 | {obj.FinalConstruct(std::forward(args)...)}->std::same_as; 263 | }; 264 | 265 | template 266 | concept has_final_construct = requires(T obj, Args &&...args) 267 | { 268 | {obj.final_construct(std::forward(args)...)}->std::same_as; 269 | }; 270 | 271 | //template 272 | //struct has_final_construct_impl : std::false_type {}; 273 | 274 | //template 275 | //struct has_final_construct_impl, decltype(void(std::declval().FinalConstruct(std::declval()...)))> : std::true_type {}; 276 | 277 | //template 278 | //using has_final_construct = has_final_construct_impl, void>; 279 | 280 | template 281 | concept has_legacy_final_release = requires(T val) 282 | { 283 | val.FinalRelease(); 284 | }; 285 | 286 | template 287 | concept has_final_release = requires(std::unique_ptr instance) 288 | { 289 | T::final_release(std::move(instance)); 290 | }; 291 | 292 | template 293 | struct has_on_release_impl : std::false_type {}; 294 | 295 | template 296 | struct has_on_release_impl().on_release(std::declval())))> : std::true_type {}; 297 | 298 | template 299 | using has_on_release = has_on_release_impl; 300 | 301 | template 302 | struct has_on_add_ref_impl : std::false_type {}; 303 | 304 | template 305 | struct has_on_add_ref_impl().on_add_ref(std::declval())))> : std::true_type {}; 306 | 307 | template 308 | using has_on_add_ref = has_on_add_ref_impl; 309 | 310 | template 311 | class __declspec(empty_bases)contained_value final : public Derived 312 | { 313 | IUnknown *pOuterUnknown; 314 | 315 | public: 316 | template 317 | contained_value(IUnknown *pOuterUnknown, Args &&...args) : 318 | pOuterUnknown{ pOuterUnknown }, 319 | Derived{ std::forward(args)... } 320 | {} 321 | 322 | virtual ULONG STDMETHODCALLTYPE AddRef() noexcept override 323 | { 324 | return pOuterUnknown->AddRef(); 325 | } 326 | 327 | virtual ULONG STDMETHODCALLTYPE Release() noexcept override 328 | { 329 | return pOuterUnknown->Release(); 330 | } 331 | 332 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) noexcept override 333 | { 334 | return pOuterUnknown->QueryInterface(riid, ppvObject); 335 | } 336 | 337 | HRESULT RealQueryInterface(REFIID riid, void **ppvObject) noexcept 338 | { 339 | return Derived::QueryInterface(riid, ppvObject); 340 | } 341 | }; 342 | 343 | struct ref_count_base 344 | { 345 | std::atomic _rc_refcount{}; 346 | 347 | void safe_increment() noexcept 348 | { 349 | _rc_refcount.fetch_add(10, std::memory_order_relaxed); 350 | } 351 | 352 | void safe_decrement() noexcept 353 | { 354 | _rc_refcount.fetch_sub(10, std::memory_order_relaxed); 355 | } 356 | }; 357 | 358 | #if defined(_DEBUG) 359 | using no_count_base = ref_count_base; 360 | #else 361 | struct no_count_base 362 | { 363 | static void safe_increment() noexcept 364 | { 365 | } 366 | 367 | static void safe_decrement() noexcept 368 | { 369 | } 370 | }; 371 | #endif 372 | 373 | template 374 | struct usage_map_base 375 | { 376 | static void add_cookie() noexcept 377 | {} 378 | 379 | static void remove_cookie() noexcept 380 | {} 381 | }; 382 | 383 | #if BELT_HAS_LEAK_DETECTION 384 | template<> 385 | struct usage_map_base 386 | { 387 | std::vector umb_usages; 388 | belt::srwlock umb_lock; 389 | 390 | void add_cookie() noexcept 391 | { 392 | try 393 | { 394 | auto cookie = new leak_detection; 395 | 396 | { 397 | std::scoped_lock l{ umb_lock }; 398 | umb_usages.emplace_back(cookie); 399 | } 400 | set_current_cookie(cookie->ordinal); 401 | } 402 | catch (...) 403 | { 404 | } 405 | } 406 | 407 | void remove_cookie() noexcept 408 | { 409 | try 410 | { 411 | auto cookie = get_current_cookie(); 412 | if (cookie) 413 | { 414 | std::scoped_lock l{ umb_lock }; 415 | auto it = std::find_if(umb_usages.begin(), umb_usages.end(), [cookie](auto * leak) 416 | { 417 | return leak->ordinal == cookie; 418 | }); 419 | if (umb_usages.end() != it) 420 | { 421 | delete *it; 422 | umb_usages.erase(it); 423 | } 424 | else 425 | assert(false && "Cookie is not found in a map"); 426 | } 427 | } 428 | catch (...) 429 | { 430 | } 431 | } 432 | }; 433 | #endif 434 | 435 | template 436 | struct __declspec(empty_bases) final_construct_support : Base, usage_map_base> 437 | { 438 | template 439 | void do_final_construct([[maybe_unused]] Derived &obj, [[maybe_unused]] Args &&...args) 440 | { 441 | static_assert(!has_legacy_final_construct, "Legacy FinalConstruct is no longer supported. Replace with new final_construct (syntax does not change)."); 442 | if constexpr (has_final_construct) 443 | { 444 | Base::safe_increment(); 445 | auto hr = obj.final_construct(std::forward(args)...); 446 | if (FAILED(hr)) 447 | corsl::throw_error(hr); 448 | Base::safe_decrement(); 449 | } 450 | if constexpr (has_increments_module_count) 451 | ModuleCount::lock_count.fetch_add(1, std::memory_order_relaxed); 452 | } 453 | 454 | template 455 | static void do_final_release(std::unique_ptr obj, [[maybe_unused]] std::atomic &refcount) noexcept 456 | { 457 | static_assert(!has_legacy_final_release, "Legacy FinalRelease no longer supported. Use new style final_release instead"); 458 | if constexpr (has_final_release) 459 | { 460 | refcount.store(1, std::memory_order_relaxed); // allow for safe QueryInterface for an overloaded final_release function 461 | Derived::final_release(std::move(obj)); 462 | } 463 | else 464 | { 465 | obj.reset(); 466 | } 467 | 468 | if constexpr (has_increments_module_count) 469 | ModuleCount::lock_count.fetch_sub(1, std::memory_order_relaxed); 470 | } 471 | 472 | // 473 | static void debug_on_add_ref(const Derived &obj, int value, std::true_type) noexcept 474 | { 475 | obj.on_add_ref(value); 476 | } 477 | 478 | static void debug_on_add_ref(const Derived &, int, std::false_type) noexcept 479 | { 480 | } 481 | 482 | void debug_on_add_ref(const Derived &obj, int value) noexcept 483 | { 484 | usage_map_base>::add_cookie(); 485 | debug_on_add_ref(obj, value, has_on_add_ref{}); 486 | } 487 | 488 | // 489 | static void debug_on_release(const Derived &obj, int value, std::true_type) noexcept 490 | { 491 | obj.on_release(value); 492 | } 493 | 494 | static void debug_on_release(const Derived &, int, std::false_type) noexcept 495 | { 496 | } 497 | 498 | void debug_on_release(const Derived &obj, int value) noexcept 499 | { 500 | usage_map_base>::remove_cookie(); 501 | debug_on_release(obj, value, has_on_release{}); 502 | } 503 | }; 504 | 505 | template 506 | class __declspec(empty_bases)aggvalue final: public final_construct_support, public IUnknown 507 | { 508 | contained_value object; 509 | 510 | using final_construct_support::_rc_refcount; 511 | 512 | public: 513 | template 514 | aggvalue(IUnknown *pOuterUnknown, Args &&...args) : 515 | object{ pOuterUnknown, std::forward(args)... } 516 | { 517 | static_assert(!has_final_release || has_final_release>, "Class overrides final_release, but does not work with aggregate values. Consider taking templated holder if your object can be aggregated."); 518 | this->do_final_construct(object); 519 | } 520 | 521 | virtual ULONG STDMETHODCALLTYPE AddRef() noexcept override 522 | { 523 | return _rc_refcount.fetch_add(1, std::memory_order_relaxed) + 1; 524 | } 525 | 526 | virtual ULONG STDMETHODCALLTYPE Release() noexcept override 527 | { 528 | auto prev = _rc_refcount.fetch_sub(1, std::memory_order_relaxed); 529 | if (prev == 1) 530 | { 531 | this->do_final_release(std::unique_ptr{this}, _rc_refcount); 532 | } 533 | return prev - 1; 534 | } 535 | 536 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) noexcept override 537 | { 538 | if (riid == get_interface_guid(interface_wrapper{})) 539 | { 540 | *ppvObject = static_cast(this); 541 | AddRef(); 542 | return S_OK; 543 | } 544 | else 545 | { 546 | // "real" query interface 547 | return object.RealQueryInterface(riid, ppvObject); 548 | } 549 | } 550 | 551 | Derived *get() noexcept 552 | { 553 | return &object; 554 | } 555 | }; 556 | 557 | template 558 | class __declspec(empty_bases)value : public DerivedNonMatchingName, public final_construct_support 559 | { 560 | public: 561 | virtual ~value() = default; 562 | value(const value &o) : 563 | DerivedNonMatchingName{ static_cast(o) } 564 | { 565 | this->do_final_construct(*this); 566 | } 567 | 568 | template 569 | value(Args &&...args) : DerivedNonMatchingName{ std::forward(args)... } 570 | { 571 | this->do_final_construct(*this); 572 | } 573 | 574 | template 575 | value(delayed_t, Args &&...args) 576 | { 577 | this->do_final_construct(*this, std::forward(args)...); 578 | } 579 | 580 | virtual ULONG STDMETHODCALLTYPE AddRef() noexcept override 581 | { 582 | auto ret = this->_rc_refcount.fetch_add(1, std::memory_order_relaxed) + 1; 583 | this->debug_on_add_ref(*static_cast(this), ret); 584 | return ret; 585 | } 586 | 587 | virtual ULONG STDMETHODCALLTYPE Release() noexcept override 588 | { 589 | auto prev = this->_rc_refcount.fetch_sub(1, std::memory_order_relaxed); 590 | this->debug_on_release(*static_cast(this), prev); 591 | 592 | if (prev == 1) 593 | { 594 | this->do_final_release(std::unique_ptr{ this }, this->_rc_refcount); 595 | } 596 | 597 | return prev - 1; 598 | } 599 | }; 600 | 601 | template 602 | class __declspec(empty_bases)smart_singleton_value final : public value 603 | { 604 | std::shared_ptr self{ static_cast(this), [](auto *) {} }; 605 | public: 606 | std::weak_ptr get_weak() const noexcept 607 | { 608 | return self; 609 | } 610 | }; 611 | 612 | template 613 | class __declspec(empty_bases)value_on_stack : public Derived, public final_construct_support 614 | { 615 | public: 616 | value_on_stack(const value_on_stack &) = delete; 617 | value_on_stack &operator =(const value_on_stack &) = delete; 618 | 619 | template 620 | value_on_stack(Args &&...args) : Derived{ std::forward(args)... } 621 | { 622 | static_assert(!has_final_release, "Classes with final_release cannot be used on stack"); 623 | this->do_final_construct(*this); 624 | } 625 | 626 | template 627 | value_on_stack(delayed_t, Args &&...args) 628 | { 629 | static_assert(!has_final_release, "Classes with final_release cannot be used on stack"); 630 | this->do_final_construct(*this, std::forward(args)...); 631 | } 632 | 633 | #if defined(_DEBUG) 634 | ~value_on_stack() 635 | { 636 | assert(0 == this->_rc_refcount.load(std::memory_order_relaxed) && "value_on_stack is destroyed while still being referenced!"); 637 | } 638 | #endif 639 | 640 | virtual ULONG STDMETHODCALLTYPE AddRef() noexcept override 641 | { 642 | #if defined(_DEBUG) 643 | this->_rc_refcount.fetch_add(1, std::memory_order_relaxed); 644 | #endif 645 | return 2; 646 | } 647 | 648 | virtual ULONG STDMETHODCALLTYPE Release() noexcept override 649 | { 650 | #if defined(_DEBUG) 651 | this->_rc_refcount.fetch_sub(1, std::memory_order_relaxed); 652 | #endif 653 | return 2; 654 | } 655 | }; 656 | 657 | // trait checking 658 | 659 | template class Trait, class First, class...Rest> 660 | constexpr bool check_trait_deep() noexcept; 661 | 662 | template class Trait, class T> 663 | constexpr bool check_trait_vector() noexcept; 664 | 665 | template class Trait, class...Interfaces> 666 | constexpr bool check_trait_vector(mpl::vector) noexcept 667 | { 668 | return check_trait_deep(); 669 | } 670 | 671 | template class Trait, class Class> 672 | constexpr bool check_trait_single(std::true_type) noexcept 673 | { 674 | if constexpr (Trait{}) 675 | return true; 676 | else 677 | return check_trait_vector(typename Class::can_query::type{}); 678 | } 679 | 680 | template class Trait, class Class> 681 | constexpr bool check_trait_single(std::false_type) noexcept 682 | { 683 | return Trait{}; 684 | } 685 | 686 | template class Trait, class First, class...Rest> 687 | constexpr bool check_trait_deep() noexcept 688 | { 689 | if constexpr (check_trait_single(Trait{})) 690 | return true; 691 | else if constexpr (sizeof...(Rest) != 0) 692 | return check_trait_deep(); 693 | else 694 | return false; 695 | } 696 | 697 | template 698 | class object_holder 699 | { 700 | std::unique_ptr value; 701 | 702 | // 703 | template 704 | com_ptr get_impl(std::false_type) noexcept 705 | { 706 | return { static_cast(value.release()) }; 707 | } 708 | 709 | template 710 | com_ptr get_impl(std::true_type) noexcept 711 | { 712 | return value.release()->GetUnknown(); 713 | } 714 | 715 | public: 716 | object_holder(std::unique_ptr &&value) noexcept : 717 | value{ std::move(value) } 718 | {} 719 | 720 | template 721 | std::enable_if_t || std::is_same_v, com_ptr> to_ptr() && noexcept 722 | { 723 | return get_impl(std::is_same{}); 724 | } 725 | 726 | auto to_ptr() && noexcept 727 | { 728 | return std::move(*this).to_ptr(); 729 | } 730 | 731 | T *obj() const noexcept 732 | { 733 | return value.get(); 734 | } 735 | 736 | T *release() noexcept 737 | { 738 | return value.release(); 739 | } 740 | }; 741 | 742 | template 743 | class __declspec(empty_bases)object : public extends_base 744 | { 745 | using fint_t = decltype(details::get_first(interface_wrapper{})); 746 | using FirstRealInterface = typename fint_t::type; 747 | static_assert(!std::is_same{}, "Do not directly derive from IUnknown"); 748 | 749 | template class Trait> 750 | static constexpr bool check_trait() noexcept 751 | { 752 | return Trait::value || check_trait_deep(); 753 | } 754 | 755 | // Derived may override the following functions 756 | static HRESULT pre_query_interface(REFIID, void **) noexcept 757 | { 758 | return E_NOINTERFACE; 759 | } 760 | 761 | static HRESULT post_query_interface(REFIID, void **ppres) noexcept 762 | { 763 | *ppres = nullptr; 764 | return E_NOINTERFACE; 765 | } 766 | 767 | // instance creation helpers 768 | template 769 | com_ptr create_copy_impl(std::true_type) const 770 | { 771 | auto pobject = std::make_unique>(*static_cast *>(this)); 772 | return pobject.release()->GetUnknown(); 773 | } 774 | 775 | template 776 | com_ptr create_copy_impl(std::false_type) const 777 | { 778 | auto pobject = std::make_unique>(*static_cast *>(this)); 779 | return static_cast(pobject.release()); 780 | } 781 | public: 782 | virtual ~object() = default; 783 | using DefaultInterface = FirstRealInterface; 784 | 785 | IUnknown *GetUnknown() noexcept 786 | { 787 | return static_cast(static_cast(this)); 788 | } 789 | 790 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) noexcept override 791 | { 792 | auto *pobject = static_cast(this); 793 | auto hr = pobject->pre_query_interface(riid, ppvObject); 794 | if (SUCCEEDED(hr) || hr != E_NOINTERFACE) 795 | return hr; 796 | 797 | if (riid == get_interface_guid(interface_wrapper{})) 798 | { 799 | auto pUnk = pobject->GetUnknown(); 800 | *ppvObject = pUnk; 801 | pUnk->AddRef(); 802 | return S_OK; 803 | } 804 | 805 | auto result = this->query_children(pobject, riid); 806 | if (result) 807 | { 808 | // AddRef has already been called 809 | *ppvObject = result; 810 | return S_OK; 811 | } 812 | else 813 | return pobject->post_query_interface(riid, ppvObject); 814 | } 815 | 816 | // Instance creation 817 | template 818 | static object_holder> create_instance(Args &&...args) 819 | { 820 | static_assert(!check_trait(), "Objects marked as single_cached_instance (AKA smart_singleton_factory) cannot be currently created using create_instance method"); 821 | return { std::make_unique>(std::forward(args)...) }; 822 | } 823 | 824 | template 825 | static com_ptr create_aggregate(IUnknown *pOuterUnknown, Args &&...args) 826 | { 827 | static_assert(check_trait(), "Class is missing supports_aggregation type trait to support aggregation"); 828 | auto pobject = std::make_unique>(pOuterUnknown, std::forward(args)...); 829 | return static_cast(pobject.release()); 830 | } 831 | 832 | template 833 | std::enable_if_t || std::is_same_v, com_ptr> create_copy() const 834 | { 835 | return create_copy_impl(std::is_same{}); 836 | } 837 | 838 | static HRESULT factory_create_object(const GUID &iid, void **ppv, IUnknown *pOuterUnknown = nullptr) noexcept; 839 | protected: 840 | // for derived class to call 841 | auto addref() noexcept 842 | { 843 | return static_cast *>(this)->AddRef(); 844 | } 845 | 846 | auto release() noexcept 847 | { 848 | return static_cast *>(this)->Release(); 849 | } 850 | }; 851 | 852 | template 853 | inline HRESULT object::factory_create_object(const GUID &iid, void **ppv, IUnknown *pOuterUnknown) noexcept 854 | { 855 | try 856 | { 857 | HRESULT hr; 858 | if (pOuterUnknown == nullptr) 859 | { 860 | if constexpr (check_trait()) 861 | { 862 | static_assert(!has_final_release, "Singleton classes are not compatible with final_release"); 863 | static value_on_stack single_value; 864 | hr = single_value.QueryInterface(iid, ppv); 865 | } 866 | else if constexpr (check_trait()) 867 | { 868 | static srwlock init_lock; 869 | static std::weak_ptr current_instance; 870 | std::scoped_lock l{ init_lock }; 871 | if (auto instance = current_instance.lock()) 872 | return instance->QueryInterface(iid, ppv); 873 | else 874 | { 875 | auto object=std::make_unique>(); 876 | hr = object->QueryInterface(iid, ppv); 877 | if (SUCCEEDED(hr)) 878 | current_instance = object.release()->get_weak(); 879 | } 880 | } 881 | else 882 | { 883 | auto object = std::make_unique>(); 884 | hr = object->QueryInterface(iid, ppv); 885 | if (SUCCEEDED(hr)) 886 | object.release(); 887 | } 888 | } 889 | else 890 | { 891 | if constexpr (check_trait()) 892 | { 893 | assert(iid == get_interface_guid(interface_wrapper{}) && "Only IUnknown may be queried for an object being created aggregated"); 894 | auto object = std::make_unique>(pOuterUnknown); 895 | hr = object->QueryInterface(iid, ppv); 896 | if (SUCCEEDED(hr)) 897 | object.release(); 898 | } 899 | else 900 | { 901 | assert(false && "Class does not define 'supports_aggregation' type trait"); 902 | return CLASS_E_NOAGGREGATION; 903 | } 904 | } 905 | return hr; 906 | } 907 | catch (const std::bad_alloc &) 908 | { 909 | return E_OUTOFMEMORY; 910 | } 911 | catch (const corsl::hresult_error &o) 912 | { 913 | return o.code(); 914 | } 915 | catch (...) 916 | { 917 | return E_FAIL; 918 | } 919 | } 920 | 921 | #pragma endregion 922 | 923 | #pragma region Auto factory support 924 | using create_function_t = HRESULT(*)(const GUID &iid, void **ppv, IUnknown *) noexcept; 925 | struct _OBJMAP_ENTRY 926 | { 927 | GUID clsid; 928 | create_function_t create; 929 | }; 930 | 931 | #pragma section("BIS$__a", read) 932 | #pragma section("BIS$__z", read) 933 | #pragma section("BIS$__b", read) 934 | extern "C" 935 | { 936 | __declspec(selectany) __declspec(allocate("BIS$__a")) _OBJMAP_ENTRY* __pobjObjEntryFirst = nullptr; 937 | __declspec(selectany) __declspec(allocate("BIS$__z")) _OBJMAP_ENTRY* __pobjObjEntryLast = nullptr; 938 | } 939 | 940 | inline HRESULT create_object(const GUID &clsid, const GUID &iid, void **ppv, IUnknown *pOuterUnknown = nullptr) noexcept 941 | { 942 | for (auto p = &__pobjObjEntryFirst + 1; p < &__pobjObjEntryLast; ++p) 943 | { 944 | if (*p && (*p)->clsid == clsid) 945 | return (*p)->create(iid, ppv, pOuterUnknown); 946 | } 947 | return REGDB_E_CLASSNOTREG; 948 | } 949 | 950 | template 951 | inline HRESULT create_object(const GUID &clsid, bcom::ptr &result, IUnknown *pOuterUnknown = nullptr) noexcept 952 | { 953 | return create_object(clsid, get_interface_guid(interface_wrapper{}), reinterpret_cast(result.put()), pOuterUnknown); 954 | } 955 | 956 | template 957 | inline bcom::ptr create_object(const GUID &clsid, IUnknown *pOuterUnknown = nullptr) 958 | { 959 | bcom::ptr result; 960 | corsl::check_hresult(create_object(clsid, get_interface_guid(details::interface_wrapper{}), reinterpret_cast(result.put()), pOuterUnknown)); 961 | return result; 962 | } 963 | #pragma endregion 964 | } 965 | 966 | template 967 | inline constexpr auto get_interface_guid() noexcept 968 | { 969 | return details::get_interface_guid(details::interface_wrapper{}); 970 | } 971 | 972 | using details::extends; 973 | using details::object; 974 | using details::intermediate; 975 | using details::aggregates; 976 | using details::eats_all; 977 | using details::also; 978 | using details::create_object; 979 | using details::delayed; 980 | using details::value_on_stack; 981 | using details::interface_wrapper; 982 | 983 | struct __declspec(empty_bases)singleton_factory 984 | { 985 | using singleton_factory_t = details::singleton_factory_t; 986 | }; 987 | 988 | struct __declspec(empty_bases)single_cached_instance 989 | { 990 | using smart_singleton_factory_t = details::smart_singleton_factory_t; 991 | }; 992 | 993 | struct __declspec(empty_bases)supports_aggregation 994 | { 995 | using supports_aggregation_t = details::supports_aggregation_t; 996 | }; 997 | 998 | struct __declspec(empty_bases)increments_module_count 999 | { 1000 | using increments_module_count_t = details::increments_module_count_t; 1001 | }; 1002 | 1003 | struct __declspec(empty_bases)enable_leak_detection 1004 | { 1005 | using enable_leak_detection_t = details::enable_leak_detection_t; 1006 | }; 1007 | } 1008 | 1009 | #define BELT_CLASS_GUID(id) static constexpr auto get_guid() noexcept { constexpr auto guid = belt::com::make_guid(id); return guid; } 1010 | #define BELT_CLASS_GUID_EXISTING(id) static constexpr auto get_guid() noexcept { return id; } 1011 | 1012 | #define BELT_DEFINE_CLASS(name,id) constexpr const auto name = belt::com::make_guid(id) 1013 | 1014 | #define _BELT_GUID_HELPER(name, id) \ 1015 | struct name; \ 1016 | constexpr const auto msvc_get_guid_workaround_##name = belt::com::make_guid(id); \ 1017 | inline constexpr auto get_guid(name *) noexcept { return msvc_get_guid_workaround_##name; } \ 1018 | // end of macro 1019 | 1020 | // The following macro keeps __declspec(uuid()) for backward compatibility 1021 | #define BELT_DEFINE_INTERFACE(name, id) \ 1022 | _BELT_GUID_HELPER(name, id) \ 1023 | struct __declspec(novtable) __declspec(empty_bases) __declspec(uuid(id)) name : belt::com::extends \ 1024 | // end of macro 1025 | 1026 | #define BELT_DEFINE_INTERFACE_BASE(name,base,id) \ 1027 | _BELT_GUID_HELPER(name, id) \ 1028 | struct __declspec(novtable) __declspec(empty_bases) __declspec(uuid(id)) name : belt::com::extends \ 1029 | // end of macro 1030 | 1031 | #if !defined(_M_IA64) 1032 | #pragma comment(linker, "/merge:BIS=.rdata") 1033 | #endif 1034 | 1035 | #ifndef BELT_OBJ_ENTRY_PRAGMA 1036 | 1037 | #if defined(_M_IX86) 1038 | #define BELT_OBJ_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:___p2objMap_" #class)); 1039 | #elif defined(_M_IA64) || defined(_M_AMD64) || (_M_ARM) 1040 | #define BELT_OBJ_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:__p2objMap_" #class)); 1041 | #else 1042 | #error Unknown Platform. define BELT_OBJ_ENTRY_PRAGMA 1043 | #endif 1044 | 1045 | #endif 1046 | 1047 | #define BELT_OBJ_ENTRY_AUTO(class) \ 1048 | const belt::com::details::_OBJMAP_ENTRY __objxMap_##class = {belt::com::get_interface_guid(), &class::factory_create_object}; \ 1049 | extern "C" __declspec(allocate("BIS$__b")) __declspec(selectany) const belt::com::details::_OBJMAP_ENTRY* const __p2objMap_##class = &__objxMap_##class; \ 1050 | BELT_OBJ_ENTRY_PRAGMA(class) \ 1051 | // end of macro 1052 | 1053 | #define BELT_OBJ_ENTRY_AUTO2(clsid, class) \ 1054 | const belt::com::details::_OBJMAP_ENTRY __objxMap_##class = {clsid, &class::factory_create_object}; \ 1055 | extern "C" __declspec(allocate("BIS$__b")) __declspec(selectany) const belt::com::details::_OBJMAP_ENTRY* const __p2objMap_##class = &__objxMap_##class; \ 1056 | BELT_OBJ_ENTRY_PRAGMA(class) \ 1057 | // end of macro 1058 | 1059 | #define BELT_OBJ_ENTRY_AUTO2_NAMED(clsid, class, name) \ 1060 | const belt::com::details::_OBJMAP_ENTRY __objxMap_##class##name = {clsid, &class::factory_create_object}; \ 1061 | extern "C" __declspec(allocate("BIS$__b")) __declspec(selectany) const belt::com::details::_OBJMAP_ENTRY* const __p2objMap_##class##name = &__objxMap_##class##name; \ 1062 | BELT_OBJ_ENTRY_PRAGMA(class##name) \ 1063 | // end of macro 1064 | 1065 | // ATL interoperability to support RegisterServer/UnregisterServer 1066 | 1067 | #if defined(__ATLBASE_H__) 1068 | inline void WINAPI StubObjectMain(bool) 1069 | { 1070 | } 1071 | 1072 | inline const struct ATL::_ATL_CATMAP_ENTRY *StubGetCats() 1073 | { 1074 | return nullptr; 1075 | } 1076 | 1077 | inline __declspec(selectany) ATL::_ATL_OBJMAP_CACHE stub_cache{}; 1078 | 1079 | #define BELT_OBJ_ENTRY_AUTO_ATL_COMPAT(clsid, class) \ 1080 | __declspec(selectany) ATL::_ATL_OBJMAP_CACHE __objCache__##class = { nullptr, 0 }; \ 1081 | const ATL::_ATL_OBJMAP_ENTRY_EX __objMap_##class = {&clsid, class::UpdateRegistry, nullptr, nullptr, &stub_cache, nullptr, &StubGetCats, &StubObjectMain }; \ 1082 | extern "C" __declspec(allocate("ATL$__m")) __declspec(selectany) const ATL::_ATL_OBJMAP_ENTRY_EX* const __pobjMap_##class = &__objMap_##class; \ 1083 | OBJECT_ENTRY_PRAGMA(class) 1084 | 1085 | #endif 1086 | -------------------------------------------------------------------------------- /include/moderncom/library.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------------------------------- 2 | // moderncom - Part of HHD Software Belt library 3 | // Copyright (C) 2017 HHD Software Ltd. 4 | // Written by Alexander Bessonov 5 | // 6 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 7 | //------------------------------------------------------------------------------------------------------- 8 | 9 | #pragma once 10 | 11 | #include "interfaces.h" 12 | 13 | // Support for DllGetClassObject 14 | namespace belt::com 15 | { 16 | namespace details 17 | { 18 | class __declspec(novtable) Factory : 19 | public object< 20 | Factory, 21 | IClassFactory 22 | > 23 | { 24 | CLSID classId; 25 | public: 26 | Factory(const CLSID &classId) noexcept : 27 | classId{ classId } 28 | {} 29 | 30 | virtual HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) noexcept override 31 | { 32 | return belt::com::create_object(classId, riid, ppvObject, pUnkOuter); 33 | } 34 | 35 | virtual HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) noexcept override 36 | { 37 | if (fLock) 38 | ModuleCount::lock_count.fetch_add(1, std::memory_order_relaxed); 39 | else 40 | ModuleCount::lock_count.fetch_sub(1, std::memory_order_relaxed); 41 | 42 | return S_OK; 43 | } 44 | }; 45 | } 46 | 47 | inline HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) noexcept 48 | { 49 | try 50 | { 51 | auto factory = details::Factory::create_instance(rclsid).to_ptr(); 52 | return factory->QueryInterface(riid, ppv); 53 | } 54 | catch (const std::bad_alloc &) 55 | { 56 | return E_OUTOFMEMORY; 57 | } 58 | catch (const corsl::hresult_error &e) 59 | { 60 | return e.code(); 61 | } 62 | catch (...) 63 | { 64 | return E_FAIL; 65 | } 66 | } 67 | 68 | inline HRESULT DllCanUnloadNow() noexcept 69 | { 70 | return details::ModuleCount::lock_count.load(std::memory_order_relaxed) ? S_FALSE : S_OK; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /moderncom.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30317.65 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Debug|x64.ActiveCfg = Debug|x64 17 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Debug|x64.Build.0 = Debug|x64 18 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Debug|x86.ActiveCfg = Debug|Win32 19 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Debug|x86.Build.0 = Debug|Win32 20 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Release|x64.ActiveCfg = Release|x64 21 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Release|x64.Build.0 = Release|x64 22 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Release|x86.ActiveCfg = Release|Win32 23 | {E5D7B9F5-6E73-4988-8F32-C668E2FE1C78}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {0E8C2694-6A33-4306-AFC7-1CB5B483CFAD} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BELT_COM_NO_LEAK_DETECTION 4 | #include 5 | 6 | #include 7 | 8 | // Declare sample interface 9 | 10 | BELT_DEFINE_INTERFACE(ISampleInterface, "{AB9A7AF1-6792-4D0A-83BE-8252A8432B45}") 11 | { 12 | virtual int sum(int a, int b) const noexcept = 0; 13 | virtual int get_answer() const noexcept = 0; 14 | }; 15 | 16 | // Define implementation 17 | 18 | class __declspec(novtable) sample_object : 19 | public belt::com::object 20 | { 21 | int default_answer; 22 | 23 | // ISampleInterface implementation 24 | virtual int sum(int a, int b) const noexcept override 25 | { 26 | return a + b; 27 | } 28 | 29 | virtual int get_answer() const noexcept override 30 | { 31 | return default_answer; 32 | } 33 | 34 | public: 35 | sample_object(int default_answer) noexcept : 36 | default_answer{ default_answer } 37 | {} 38 | }; 39 | 40 | int main() 41 | { 42 | // Create new instance of sample_object and get its' ISampleInterface interface pointer 43 | 44 | { 45 | auto obj = sample_object::create_instance(42).to_ptr(); 46 | 47 | std::cout << obj->sum(obj->get_answer(), 5); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/test.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {e5d7b9f5-6e73-4988-8f32-c668e2fe1c78} 25 | test 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)/include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 76 | 77 | 78 | false 79 | $(SolutionDir)/include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 80 | 81 | 82 | true 83 | $(SolutionDir)/include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 84 | 85 | 86 | false 87 | $(SolutionDir)/include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 88 | 89 | 90 | 91 | Level3 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | stdcpplatest 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | Level3 105 | true 106 | true 107 | true 108 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | true 110 | stdcpplatest 111 | AnySuitable 112 | Speed 113 | true 114 | false 115 | Fast 116 | 117 | 118 | Console 119 | true 120 | true 121 | true 122 | 123 | 124 | 125 | 126 | Level3 127 | true 128 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 129 | true 130 | stdcpplatest 131 | 132 | 133 | Console 134 | true 135 | 136 | 137 | 138 | 139 | Level3 140 | true 141 | true 142 | true 143 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 144 | true 145 | stdcpplatest 146 | AnySuitable 147 | Speed 148 | true 149 | false 150 | Fast 151 | 152 | 153 | Console 154 | true 155 | true 156 | true 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /test/test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | --------------------------------------------------------------------------------