summaryrefslogtreecommitdiffstats
path: root/ComLightLib
diff options
context:
space:
mode:
authorKonstantin <const@const.me>2023-01-16 14:52:43 +0100
committerKonstantin <const@const.me>2023-01-16 14:52:43 +0100
commit8c4603c73675958efc960fbd4bb599a2909d106a (patch)
tree714dc6fc9a1672d5fd7f89676b97e10959662abc /ComLightLib
parent990a8d0dbaefc996244097397259e92758b15cce (diff)
Source codes
Diffstat (limited to 'ComLightLib')
-rw-r--r--ComLightLib/ComLightLib.vcxproj116
-rw-r--r--ComLightLib/ComLightLib.vcxproj.filters28
-rw-r--r--ComLightLib/Exception.hpp20
-rw-r--r--ComLightLib/Readme.txt3
-rw-r--r--ComLightLib/client/CComPtr.hpp110
-rw-r--r--ComLightLib/comLightClient.h23
-rw-r--r--ComLightLib/comLightCommon.h11
-rw-r--r--ComLightLib/comLightServer.h15
-rw-r--r--ComLightLib/hresult.h26
-rw-r--r--ComLightLib/pal/guiddef.h21
-rw-r--r--ComLightLib/pal/hresult.h101
-rw-r--r--ComLightLib/server/Object.hpp139
-rw-r--r--ComLightLib/server/ObjectRoot.hpp51
-rw-r--r--ComLightLib/server/RefCounter.hpp38
-rw-r--r--ComLightLib/server/freeThreadedMarshaller.cpp17
-rw-r--r--ComLightLib/server/freeThreadedMarshaller.h29
-rw-r--r--ComLightLib/server/interfaceMap.h31
-rw-r--r--ComLightLib/streams.h61
-rw-r--r--ComLightLib/unknwn.h36
-rw-r--r--ComLightLib/utils/guid_parse.hpp103
-rw-r--r--ComLightLib/utils/typeTraits.hpp43
21 files changed, 1022 insertions, 0 deletions
diff --git a/ComLightLib/ComLightLib.vcxproj b/ComLightLib/ComLightLib.vcxproj
new file mode 100644
index 0000000..ee7f07b
--- /dev/null
+++ b/ComLightLib/ComLightLib.vcxproj
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="comLightClient.h" />
+ <ClInclude Include="client\CComPtr.hpp" />
+ <ClInclude Include="comLightServer.h" />
+ <ClInclude Include="comLightCommon.h" />
+ <ClInclude Include="server\freeThreadedMarshaller.h" />
+ <ClInclude Include="hresult.h" />
+ <ClInclude Include="server\ObjectRoot.hpp" />
+ <ClInclude Include="pal\guiddef.h" />
+ <ClInclude Include="server\Object.hpp" />
+ <ClInclude Include="server\interfaceMap.h" />
+ <ClInclude Include="server\RefCounter.hpp" />
+ <ClInclude Include="Exception.hpp" />
+ <ClInclude Include="streams.h" />
+ <ClInclude Include="utils\guid_parse.hpp" />
+ <ClInclude Include="pal\hresult.h" />
+ <ClInclude Include="unknwn.h" />
+ <ClInclude Include="utils\typeTraits.hpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="server\freeThreadedMarshaller.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="Readme.txt" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{52F486E7-830C-45D8-BE47-E76B5AAB2772}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>ComLightLib</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(Platform)\$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp20</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <LanguageStandard>stdcpp20</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/ComLightLib/ComLightLib.vcxproj.filters b/ComLightLib/ComLightLib.vcxproj.filters
new file mode 100644
index 0000000..aa2c81d
--- /dev/null
+++ b/ComLightLib/ComLightLib.vcxproj.filters
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="pal\hresult.h" />
+ <ClInclude Include="pal\guiddef.h" />
+ <ClInclude Include="utils\guid_parse.hpp" />
+ <ClInclude Include="unknwn.h" />
+ <ClInclude Include="comLightClient.h" />
+ <ClInclude Include="comLightServer.h" />
+ <ClInclude Include="client\CComPtr.hpp" />
+ <ClInclude Include="comLightCommon.h" />
+ <ClInclude Include="server\RefCounter.hpp" />
+ <ClInclude Include="server\interfaceMap.h" />
+ <ClInclude Include="server\Object.hpp" />
+ <ClInclude Include="utils\typeTraits.hpp" />
+ <ClInclude Include="server\freeThreadedMarshaller.h" />
+ <ClInclude Include="hresult.h" />
+ <ClInclude Include="server\ObjectRoot.hpp" />
+ <ClInclude Include="Exception.hpp" />
+ <ClInclude Include="streams.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="server\freeThreadedMarshaller.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="Readme.txt" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/ComLightLib/Exception.hpp b/ComLightLib/Exception.hpp
new file mode 100644
index 0000000..57c1b78
--- /dev/null
+++ b/ComLightLib/Exception.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+namespace ComLight
+{
+ class Exception : public std::runtime_error
+ {
+ // I don't like C++ exceptions too much, but for some cases they are useful.
+ // You can throw ComLight::Exception from constructor, or from FinalConstruct() method, the library will catch & return the code from the class factory function.
+ // Unfortunately, for interface methods this doesn't work, the C++ parts of the library can't catch them without very complex trickery like code generation.
+ // You can still use this class in methods, but you'll need to catch them manually near the API boundary or the app will crash.
+ // C++ doesn't have an ABI, the framework can't catch C++ exception across the modules.
+ const HRESULT m_code;
+
+ public:
+
+ Exception( HRESULT hr ) : runtime_error( "ComLight HRESULT exception" ), m_code( hr ) { }
+
+ HRESULT code() const { return m_code; }
+ };
+} \ No newline at end of file
diff --git a/ComLightLib/Readme.txt b/ComLightLib/Readme.txt
new file mode 100644
index 0000000..1eabeec
--- /dev/null
+++ b/ComLightLib/Readme.txt
@@ -0,0 +1,3 @@
+Copy-pasted from there:
+https://github.com/Const-me/ComLightInterop/tree/master/ComLightLib
+With only a few minor changes. \ No newline at end of file
diff --git a/ComLightLib/client/CComPtr.hpp b/ComLightLib/client/CComPtr.hpp
new file mode 100644
index 0000000..7786591
--- /dev/null
+++ b/ComLightLib/client/CComPtr.hpp
@@ -0,0 +1,110 @@
+#pragma once
+
+namespace ComLight
+{
+ // COM smart pointer, very comparable to CComPtr from ATL
+ template <class I>
+ class CComPtr
+ {
+ I* p;
+
+ void callAddRef() const
+ {
+ if( nullptr == p )
+ return;
+ p->AddRef();
+ }
+
+ public:
+
+ // Construct with nullptr
+ CComPtr() : p( nullptr ) { }
+
+ // Release the pointer
+ void release()
+ {
+ if( nullptr == p )
+ return;
+ p->Release();
+ p = nullptr;
+ }
+
+ ~CComPtr()
+ {
+ release();
+ }
+
+ // Attach without AddRef()
+ void attach( I* raw )
+ {
+ release();
+ p = raw;
+ }
+
+ // Detach without Release(), set this pointer to nullptr
+ I* detach()
+ {
+ I* const result = p;
+ p = nullptr;
+ return result;
+ }
+
+ // Detach without Release() and place to the specified address, set this pointer to nullptr
+ template<class Other>
+ void detach( Other** pp )
+ {
+ // If the argument points to a non-empty object, release the old instance: would leak memory otherwise.
+ if( nullptr != *pp )
+ ( *pp )->Release();
+ ( *pp ) = detach();
+ }
+
+ // Set and AddRef()
+ void assign( I* raw )
+ {
+ release();
+ attach( raw );
+ callAddRef();
+ }
+
+ void swap( CComPtr<I>& that )
+ {
+ std::swap( p, that.p );
+ }
+
+ // Set and AddRef()
+ CComPtr( I* raw ) : p( raw )
+ {
+ callAddRef();
+ }
+
+ // Set and AddRef()
+ CComPtr( const CComPtr<I>& that ) : CComPtr( that.p ) { }
+ // Move constructor
+ CComPtr( CComPtr<I>&& that ) : p( that.p ) { that.p = nullptr; }
+
+ // Set and AddRef()
+ void operator=( I* raw )
+ {
+ assign( raw );
+ }
+
+ // Set and AddRef()
+ void operator=( const CComPtr<I>& that )
+ {
+ assign( that.p );
+ }
+
+ // Move assignment operator, destroys the other one
+ void operator=( CComPtr<I>&& that )
+ {
+ attach( that.detach() );
+ }
+
+ operator I*( ) const { return p; }
+ I* operator -> () const { return p; }
+ I** operator &() { return &p; }
+
+ operator bool() const { return nullptr != p; }
+ };
+} \ No newline at end of file
diff --git a/ComLightLib/comLightClient.h b/ComLightLib/comLightClient.h
new file mode 100644
index 0000000..3174c92
--- /dev/null
+++ b/ComLightLib/comLightClient.h
@@ -0,0 +1,23 @@
+#pragma once
+#include "comLightCommon.h"
+#include "client/CComPtr.hpp"
+#include "utils/typeTraits.hpp"
+
+namespace ComLight
+{
+ namespace details
+ {
+ template<typename T>
+ inline constexpr void** castDoublePointerToVoid( T** pp )
+ {
+ static_assert( pointersAssignable<IUnknown, T>(), "IID_PPV_ARGS macro should be used with IUnknown interfaces" );
+ return reinterpret_cast<void**>( pp );
+ }
+ }
+}
+
+#ifdef IID_PPV_ARGS
+#undef IID_PPV_ARGS
+#endif
+
+#define IID_PPV_ARGS( pp ) decltype( **pp )::iid, ::ComLight::details::castDoublePointerToVoid( pp ) \ No newline at end of file
diff --git a/ComLightLib/comLightCommon.h b/ComLightLib/comLightCommon.h
new file mode 100644
index 0000000..c571910
--- /dev/null
+++ b/ComLightLib/comLightCommon.h
@@ -0,0 +1,11 @@
+#pragma once
+#include "hresult.h"
+
+#ifdef _MSC_VER
+#include <guiddef.h>
+#else
+#include "pal/guiddef.h"
+using LPCTSTR = const char*;
+#endif
+
+#include "unknwn.h" \ No newline at end of file
diff --git a/ComLightLib/comLightServer.h b/ComLightLib/comLightServer.h
new file mode 100644
index 0000000..8b2e844
--- /dev/null
+++ b/ComLightLib/comLightServer.h
@@ -0,0 +1,15 @@
+#pragma once
+#include "comLightCommon.h"
+#include "client/CComPtr.hpp"
+
+#include "server/ObjectRoot.hpp"
+#include "server/interfaceMap.h"
+#include "server/Object.hpp"
+#include "server/freeThreadedMarshaller.h"
+
+#ifdef _MSC_VER
+// On Windows, it's controlled by library.def module definition file. There's __declspec(dllexport), but it adds underscore, I don't like that.
+#define DLLEXPORT extern "C"
+#else
+#define DLLEXPORT extern "C" __attribute__((visibility("default")))
+#endif \ No newline at end of file
diff --git a/ComLightLib/hresult.h b/ComLightLib/hresult.h
new file mode 100644
index 0000000..dbaee67
--- /dev/null
+++ b/ComLightLib/hresult.h
@@ -0,0 +1,26 @@
+#pragma once
+#include <stdint.h>
+#ifdef _MSC_VER
+#include <winerror.h>
+#include <OleCtl.h>
+#else
+#include "pal/hresult.h"
+#endif
+
+#define CHECK( hr ) { const HRESULT __hr = ( hr ); if( FAILED( __hr ) ) return __hr; }
+
+#ifndef _MSC_VER
+inline constexpr HRESULT HRESULT_FROM_WIN32( int c )
+{
+ return c < 0 ? c : ( ( 0xFFFF & c ) | 0x80070000 );
+}
+
+constexpr HRESULT OLE_E_BLANK = _HRESULT_TYPEDEF_( 0x80040007 );
+constexpr HRESULT E_BOUNDS = _HRESULT_TYPEDEF_( 0x8000000BL );
+
+constexpr int ERROR_HANDLE_EOF = 38;
+constexpr int ERROR_ALREADY_INITIALIZED = 1247;
+#endif
+
+constexpr HRESULT E_EOF = HRESULT_FROM_WIN32( ERROR_HANDLE_EOF );
+constexpr HRESULT E_ALREADY_INITIALIZED = HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED ); \ No newline at end of file
diff --git a/ComLightLib/pal/guiddef.h b/ComLightLib/pal/guiddef.h
new file mode 100644
index 0000000..ed8259f
--- /dev/null
+++ b/ComLightLib/pal/guiddef.h
@@ -0,0 +1,21 @@
+#pragma once
+#include <stdint.h>
+#include <array>
+#ifndef GUID_DEFINED
+#define GUID_DEFINED
+#endif
+
+struct GUID
+{
+ uint32_t Data1;
+ uint16_t Data2;
+ uint16_t Data3;
+ std::array<uint8_t, 8> Data4;
+
+ constexpr inline bool operator==( const GUID& that ) const
+ {
+ return Data1 == that.Data1 && Data2 == that.Data2 && Data3 == that.Data3 && Data4 == that.Data4;
+ }
+};
+
+using REFIID = const GUID&; \ No newline at end of file
diff --git a/ComLightLib/pal/hresult.h b/ComLightLib/pal/hresult.h
new file mode 100644
index 0000000..c261458
--- /dev/null
+++ b/ComLightLib/pal/hresult.h
@@ -0,0 +1,101 @@
+#pragma once
+#include <stdint.h>
+using HRESULT = int32_t;
+#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc)
+#define SEVERITY_ERROR 1
+#define FACILITY_CONTROL 10
+
+inline constexpr HRESULT MAKE_SCODE( uint32_t sev, uint32_t fac, uint32_t code )
+{
+ return (HRESULT)( ( (uint32_t)( sev ) << 31 ) | ( (unsigned long)( fac ) << 16 ) | ( (unsigned long)( code ) ) );
+};
+
+// ==== Copy-pasted from coreclr-master\src\pal\inc\rt\palrt.h ====
+#define S_OK _HRESULT_TYPEDEF_(0x00000000L)
+#define S_FALSE _HRESULT_TYPEDEF_(0x00000001L)
+
+#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L)
+#define E_NOINTERFACE _HRESULT_TYPEDEF_(0x80004002L)
+#define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFFL)
+#define E_OUTOFMEMORY _HRESULT_TYPEDEF_(0x8007000EL)
+#define E_INVALIDARG _HRESULT_TYPEDEF_(0x80070057L)
+#define E_POINTER _HRESULT_TYPEDEF_(0x80004003L)
+#define E_HANDLE _HRESULT_TYPEDEF_(0x80070006L)
+#define E_ABORT _HRESULT_TYPEDEF_(0x80004004L)
+#define E_FAIL _HRESULT_TYPEDEF_(0x80004005L)
+#define E_ACCESSDENIED _HRESULT_TYPEDEF_(0x80070005L)
+#define E_PENDING _HRESULT_TYPEDEF_(0x8000000AL)
+
+#define DISP_E_PARAMNOTFOUND _HRESULT_TYPEDEF_(0x80020004L)
+#define DISP_E_TYPEMISMATCH _HRESULT_TYPEDEF_(0x80020005L)
+#define DISP_E_BADVARTYPE _HRESULT_TYPEDEF_(0x80020008L)
+#define DISP_E_OVERFLOW _HRESULT_TYPEDEF_(0x8002000AL)
+#define DISP_E_DIVBYZERO _HRESULT_TYPEDEF_(0x80020012L)
+
+#define CLASS_E_CLASSNOTAVAILABLE _HRESULT_TYPEDEF_(0x80040111L)
+#define CLASS_E_NOAGGREGATION _HRESULT_TYPEDEF_(0x80040110L)
+
+#define CO_E_CLASSSTRING _HRESULT_TYPEDEF_(0x800401F3L)
+
+#define MK_E_SYNTAX _HRESULT_TYPEDEF_(0x800401E4L)
+
+#define STG_E_INVALIDFUNCTION _HRESULT_TYPEDEF_(0x80030001L)
+#define STG_E_FILENOTFOUND _HRESULT_TYPEDEF_(0x80030002L)
+#define STG_E_PATHNOTFOUND _HRESULT_TYPEDEF_(0x80030003L)
+#define STG_E_WRITEFAULT _HRESULT_TYPEDEF_(0x8003001DL)
+#define STG_E_FILEALREADYEXISTS _HRESULT_TYPEDEF_(0x80030050L)
+#define STG_E_ABNORMALAPIEXIT _HRESULT_TYPEDEF_(0x800300FAL)
+
+#define NTE_BAD_UID _HRESULT_TYPEDEF_(0x80090001L)
+#define NTE_BAD_HASH _HRESULT_TYPEDEF_(0x80090002L)
+#define NTE_BAD_KEY _HRESULT_TYPEDEF_(0x80090003L)
+#define NTE_BAD_LEN _HRESULT_TYPEDEF_(0x80090004L)
+#define NTE_BAD_DATA _HRESULT_TYPEDEF_(0x80090005L)
+#define NTE_BAD_SIGNATURE _HRESULT_TYPEDEF_(0x80090006L)
+#define NTE_BAD_VER _HRESULT_TYPEDEF_(0x80090007L)
+#define NTE_BAD_ALGID _HRESULT_TYPEDEF_(0x80090008L)
+#define NTE_BAD_FLAGS _HRESULT_TYPEDEF_(0x80090009L)
+#define NTE_BAD_TYPE _HRESULT_TYPEDEF_(0x8009000AL)
+#define NTE_BAD_KEY_STATE _HRESULT_TYPEDEF_(0x8009000BL)
+#define NTE_BAD_HASH_STATE _HRESULT_TYPEDEF_(0x8009000CL)
+#define NTE_NO_KEY _HRESULT_TYPEDEF_(0x8009000DL)
+#define NTE_NO_MEMORY _HRESULT_TYPEDEF_(0x8009000EL)
+#define NTE_SIGNATURE_FILE_BAD _HRESULT_TYPEDEF_(0x8009001CL)
+#define NTE_FAIL _HRESULT_TYPEDEF_(0x80090020L)
+
+#define CRYPT_E_HASH_VALUE _HRESULT_TYPEDEF_(0x80091007L)
+
+#define TYPE_E_SIZETOOBIG _HRESULT_TYPEDEF_(0x800288C5L)
+#define TYPE_E_DUPLICATEID _HRESULT_TYPEDEF_(0x800288C6L)
+
+#define STD_CTL_SCODE(n) MAKE_SCODE(SEVERITY_ERROR, FACILITY_CONTROL, n)
+#define CTL_E_OVERFLOW STD_CTL_SCODE(6)
+#define CTL_E_OUTOFMEMORY STD_CTL_SCODE(7)
+#define CTL_E_DIVISIONBYZERO STD_CTL_SCODE(11)
+#define CTL_E_OUTOFSTACKSPACE STD_CTL_SCODE(28)
+#define CTL_E_FILENOTFOUND STD_CTL_SCODE(53)
+#define CTL_E_DEVICEIOERROR STD_CTL_SCODE(57)
+#define CTL_E_PERMISSIONDENIED STD_CTL_SCODE(70)
+#define CTL_E_PATHFILEACCESSERROR STD_CTL_SCODE(75)
+#define CTL_E_PATHNOTFOUND STD_CTL_SCODE(76)
+
+#define INET_E_CANNOT_CONNECT _HRESULT_TYPEDEF_(0x800C0004L)
+#define INET_E_RESOURCE_NOT_FOUND _HRESULT_TYPEDEF_(0x800C0005L)
+#define INET_E_OBJECT_NOT_FOUND _HRESULT_TYPEDEF_(0x800C0006L)
+#define INET_E_DATA_NOT_AVAILABLE _HRESULT_TYPEDEF_(0x800C0007L)
+#define INET_E_DOWNLOAD_FAILURE _HRESULT_TYPEDEF_(0x800C0008L)
+#define INET_E_CONNECTION_TIMEOUT _HRESULT_TYPEDEF_(0x800C000BL)
+#define INET_E_UNKNOWN_PROTOCOL _HRESULT_TYPEDEF_(0x800C000DL)
+
+#define DBG_PRINTEXCEPTION_C _HRESULT_TYPEDEF_(0x40010006L)
+// ==== Done pasting ====
+
+inline constexpr bool SUCCEEDED( HRESULT hr )
+{
+ return hr >= 0;
+}
+
+inline constexpr bool FAILED( HRESULT hr )
+{
+ return hr < 0;
+} \ No newline at end of file
diff --git a/ComLightLib/server/Object.hpp b/ComLightLib/server/Object.hpp
new file mode 100644
index 0000000..d2e3257
--- /dev/null
+++ b/ComLightLib/server/Object.hpp
@@ -0,0 +1,139 @@
+#pragma once
+#include <type_traits>
+#include "../comLightClient.h"
+#include "../utils/typeTraits.hpp"
+#include "../Exception.hpp"
+
+namespace ComLight
+{
+ namespace details
+ {
+ GENERATE_HAS_MEMBER( implQueryInterface );
+ GENERATE_HAS_MEMBER( implAddRef );
+ GENERATE_HAS_MEMBER( implRelease );
+ }
+
+ // Outer class of objects, implements IUnknown methods, also the class factory. The type argument must be your class implementing your interfaces, inherited from ObjectRoot<I>
+ template<class T>
+ class Object : public T
+ {
+ public:
+ Object() = default;
+
+ template<typename ... Args>
+ Object( Args&& ... args ) : T{ std::forward<Args>( args )... } {};
+
+ inline virtual ~Object() override { }
+
+ // Implement IUnknown methods
+ HRESULT COMLIGHTCALL QueryInterface( REFIID riid, void** ppvObject ) override
+ {
+ static_assert( details::has_member_implQueryInterface<T>::value, "Your object class must inherit from ComLight::ObjectRoot" );
+
+ if( nullptr == ppvObject )
+ return E_POINTER;
+
+ if( T::implQueryInterface( riid, ppvObject ) )
+ return S_OK;
+ if( T::queryExtraInterfaces( riid, ppvObject ) )
+ return S_OK;
+
+ if( riid == IUnknown::iid() )
+ {
+ ComLight::IUnknown* unk = T::getUnknown();
+ unk->AddRef();
+ *ppvObject = unk;
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ uint32_t COMLIGHTCALL AddRef() override
+ {
+ static_assert( details::has_member_implAddRef<T>::value, "Your object class must inherit from ComLight::ObjectRoot" );
+ return T::implAddRef();
+ }
+
+ uint32_t COMLIGHTCALL Release() override
+ {
+ static_assert( details::has_member_implRelease<T>::value, "Your object class must inherit from ComLight::ObjectRoot" );
+ const uint32_t ret = T::implRelease();
+ if( 0 == ret )
+ {
+ T::FinalRelease();
+ delete this;
+ }
+ return ret;
+ }
+
+ // Create a new object on the heap, store in smart pointer
+ static inline HRESULT create( CComPtr<Object<T>>& result )
+ {
+ CComPtr<Object<T>> ptr;
+ try
+ {
+ ptr = new Object<T>(); // The RefCounter constructor creates it with ref.counter 0. But then CComPtr constructor calls AddRef so we have RC=1 after this line.
+
+ HRESULT hr = ptr->internalFinalConstruct();
+ if( FAILED( hr ) )
+ return hr;
+
+ hr = ptr->FinalConstruct();
+ if( FAILED( hr ) )
+ return hr;
+
+ ptr.swap( result );
+ return S_OK;
+ }
+ catch( const Exception& ex )
+ {
+ return ex.code();
+ }
+ }
+
+ // Create a new object on the heap, store in smart pointer
+ template<typename ... Args>
+ static inline HRESULT create( CComPtr<Object<T>>& result, Args&& ... args )
+ {
+ CComPtr<Object<T>> ptr;
+ try
+ {
+ ptr = new Object<T>( std::forward<Args>( args )... );
+
+ HRESULT hr = ptr->internalFinalConstruct();
+ if( FAILED( hr ) )
+ return hr;
+
+ hr = ptr->FinalConstruct();
+ if( FAILED( hr ) )
+ return hr;
+
+ ptr.swap( result );
+ return S_OK;
+ }
+ catch( const Exception& ex )
+ {
+ return ex.code();
+ }
+ catch( HRESULT hr )
+ {
+ return hr;
+ }
+ }
+
+ // Create a new object on the heap, return one of it's interfaces. The caller is assumed to take ownership of the new object.
+ template<class I>
+ static inline HRESULT create( I** pp )
+ {
+ if( pp == nullptr )
+ return E_POINTER;
+
+ static_assert( details::pointersAssignable<I, T>(), "Object::create can't cast object to the requested interface" );
+ CComPtr<Object<T>> ptr;
+ CHECK( create( ptr ) );
+ ptr.detach( pp );
+ return S_OK;
+ }
+ };
+} \ No newline at end of file
diff --git a/ComLightLib/server/ObjectRoot.hpp b/ComLightLib/server/ObjectRoot.hpp
new file mode 100644
index 0000000..1cc8f4d
--- /dev/null
+++ b/ComLightLib/server/ObjectRoot.hpp
@@ -0,0 +1,51 @@
+#pragma once
+#include "RefCounter.hpp"
+#include "../comLightCommon.h"
+#include "../utils/typeTraits.hpp"
+
+namespace ComLight
+{
+ // Base class of objects, implements reference counting, also a few lifetime methods.
+ // The template argument is the interface you want clients to get when they ask for IID_IUnknown. By convention, that pointer defines object's identity.
+ template<class I>
+ class ObjectRoot : public RefCounter, public I
+ {
+ protected:
+
+ inline HRESULT internalFinalConstruct()
+ {
+ return S_FALSE;
+ }
+
+ inline HRESULT FinalConstruct()
+ {
+ return S_FALSE;
+ }
+
+ inline void FinalRelease() { }
+
+ IUnknown* getUnknown()
+ {
+ static_assert( details::pointersAssignable<IUnknown, I>(), "The interface doesn't derive from IUnknown" );
+ return static_cast<I*>( this );
+ }
+
+ bool queryExtraInterfaces( REFIID riid, void **ppvObject ) const
+ {
+ return false;
+ }
+
+ // Implement query interface with 2 entries, IUnknown and I.
+ bool implQueryInterface( REFIID riid, void** ppvObject )
+ {
+ if( riid == I::iid() || riid == IUnknown::iid() )
+ {
+ I* const result = this;
+ result->AddRef();
+ *ppvObject = result;
+ return true;
+ }
+ return false;
+ }
+ };
+} \ No newline at end of file
diff --git a/ComLightLib/server/RefCounter.hpp b/ComLightLib/server/RefCounter.hpp
new file mode 100644
index 0000000..9698cc8
--- /dev/null
+++ b/ComLightLib/server/RefCounter.hpp
@@ -0,0 +1,38 @@
+#pragma once
+#include <atomic>
+#include <assert.h>
+#include <limits.h>
+
+namespace ComLight
+{
+ // Very base class of objects, implements reference counting.
+ class RefCounter
+ {
+ std::atomic_uint referenceCounter;
+
+ public:
+
+ RefCounter() : referenceCounter( 0 ) { }
+
+ inline virtual ~RefCounter() { }
+
+ RefCounter( const RefCounter &that ) = delete;
+ RefCounter( RefCounter &&that ) = delete;
+
+ protected:
+
+ uint32_t implAddRef()
+ {
+ return ++referenceCounter;
+ }
+
+ uint32_t implRelease()
+ {
+ // Might be a good idea to use locks, at least in debug builds. They're much slower than atomics, but with locks it's possible to detect when 2 threads call release at the same time, for object with counter = 1.
+ // It's a memory management bug, but it would be nice if debug builds would handle that case gracefully.
+ const uint32_t rc = --referenceCounter;
+ assert( rc != UINT_MAX );
+ return rc;
+ }
+ };
+} \ No newline at end of file
diff --git a/ComLightLib/server/freeThreadedMarshaller.cpp b/ComLightLib/server/freeThreadedMarshaller.cpp
new file mode 100644
index 0000000..fc1ea80
--- /dev/null
+++ b/ComLightLib/server/freeThreadedMarshaller.cpp
@@ -0,0 +1,17 @@
+#include "freeThreadedMarshaller.h"
+#ifdef _MSC_VER
+#include <combaseapi.h>
+
+HRESULT ComLight::details::createFreeThreadedMarshaller( IUnknown* pUnkOuter, IUnknown** ppUnkMarshal )
+{
+ return ::CoCreateFreeThreadedMarshaler( (LPUNKNOWN)pUnkOuter, (LPUNKNOWN *)ppUnkMarshal );
+}
+
+bool ComLight::details::queryMarshallerInterface( REFIID riid, void **ppvObject, IUnknown* marshaller )
+{
+ if( riid != IID_IMarshal || nullptr == marshaller )
+ return false;
+ const HRESULT hr = marshaller->QueryInterface( IID_IMarshal, ppvObject );
+ return SUCCEEDED( hr ) ? true : false;
+}
+#endif \ No newline at end of file
diff --git a/ComLightLib/server/freeThreadedMarshaller.h b/ComLightLib/server/freeThreadedMarshaller.h
new file mode 100644
index 0000000..8ef774e
--- /dev/null
+++ b/ComLightLib/server/freeThreadedMarshaller.h
@@ -0,0 +1,29 @@
+#pragma once
+#ifdef _MSC_VER
+#include "../comLightCommon.h"
+
+namespace ComLight
+{
+ namespace details
+ {
+ HRESULT createFreeThreadedMarshaller( IUnknown* pUnkOuter, IUnknown** ppUnkMarshal );
+ bool queryMarshallerInterface( REFIID riid, void **ppvObject, IUnknown* marshaller );
+ }
+}
+
+#define DECLARE_FREE_THREADED_MARSHALLER() \
+private: \
+ComLight::CComPtr<ComLight::IUnknown> m_freeThreadedMarshaller; \
+protected: \
+HRESULT internalFinalConstruct() \
+{ \
+ return ComLight::details::createFreeThreadedMarshaller( getUnknown(), &m_freeThreadedMarshaller ); \
+} \
+bool queryExtraInterfaces( REFIID riid, void **ppvObject ) const \
+{ \
+ return ComLight::details::queryMarshallerInterface( riid, ppvObject, m_freeThreadedMarshaller ); \
+}
+
+#else
+#define DECLARE_FREE_THREADED_MARSHALLER()
+#endif \ No newline at end of file
diff --git a/ComLightLib/server/interfaceMap.h b/ComLightLib/server/interfaceMap.h
new file mode 100644
index 0000000..605ed33
--- /dev/null
+++ b/ComLightLib/server/interfaceMap.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "../utils/typeTraits.hpp"
+
+// Unlike ATL, the interface map is optional for ComLight.
+// If you won't declare a map, the object will support 2 interfaces: IUnknown, and whatever template argument was passed to ObjectRoot class.
+#define BEGIN_COM_MAP() \
+protected: \
+bool implQueryInterface( REFIID iid, void** ppvObject ) {
+
+#define END_COM_MAP() return false; }
+
+namespace ComLight
+{
+ namespace details
+ {
+ template<typename I, typename C>
+ inline bool tryReturnInterface( REFIID iid, C* pThis, void** ppvResult )
+ {
+ static_assert( pointersAssignable<IUnknown, I>(), "Trying to implement an interface that doesn't derive from IUnknown" );
+ static_assert( pointersAssignable<I, C>(), "Declared support for an interface, but the class doesn't implement it" );
+ if( I::iid() != iid )
+ return false;
+ I* const result = pThis;
+ result->AddRef();
+ *ppvResult = result;
+ return true;
+ }
+ }
+}
+
+#define COM_INTERFACE_ENTRY( I ) if( ComLight::details::tryReturnInterface<I>( iid, this, ppvObject ) ) return true; \ No newline at end of file
diff --git a/ComLightLib/streams.h b/ComLightLib/streams.h
new file mode 100644
index 0000000..87680e1
--- /dev/null
+++ b/ComLightLib/streams.h
@@ -0,0 +1,61 @@
+#pragma once
+#include <vector>
+#include "comLightCommon.h"
+
+// COM interfaces to marshal streams across the interop.
+namespace ComLight
+{
+ enum struct eSeekOrigin : uint8_t
+ {
+ Begin = 0,
+ Current = 1,
+ End = 2
+ };
+
+ namespace details
+ {
+ template<class E>
+ inline size_t sizeofVector( const std::vector<E>& vec )
+ {
+ return sizeof( E ) * vec.size();
+ }
+ }
+
+ // COM interface for readonly stream. You'll get these interfaces what you use [ReadStream] attribute in C#.
+ struct DECLSPEC_NOVTABLE iReadStream : public IUnknown
+ {
+ DEFINE_INTERFACE_ID( "006af6db-734e-4595-8c94-19304b2389ac" );
+
+ virtual HRESULT COMLIGHTCALL read( void* lpBuffer, int nNumberOfBytesToRead, int &lpNumberOfBytesRead ) = 0;
+ virtual HRESULT COMLIGHTCALL seek( int64_t offset, eSeekOrigin origin ) = 0;
+ virtual HRESULT COMLIGHTCALL getPosition( int64_t& position ) = 0;
+ virtual HRESULT COMLIGHTCALL getLength( int64_t& length ) = 0;
+
+ template<class E>
+ inline HRESULT read( std::vector<E>& vec )
+ {
+ const int cb = (int)details::sizeofVector( vec );
+ int cbRead = 0;
+ CHECK( read( vec.data(), cb, cbRead ) );
+ if( cbRead >= cb )
+ return S_OK;
+ return E_EOF;
+ }
+ };
+
+ // COM interface for readonly stream. You'll get these interfaces what you use [WriteStream] attribute in C#.
+ struct DECLSPEC_NOVTABLE iWriteStream : public IUnknown
+ {
+ DEFINE_INTERFACE_ID( "d7c3eb39-9170-43b9-ba98-2ea1f2fed8a8" );
+
+ virtual HRESULT COMLIGHTCALL write( const void* lpBuffer, int nNumberOfBytesToWrite ) = 0;
+ virtual HRESULT COMLIGHTCALL flush() = 0;
+
+ template<class E>
+ inline HRESULT write( const std::vector<E>& vec )
+ {
+ const int cb = (int)details::sizeofVector( vec );
+ return write( vec.data(), cb );
+ }
+ };
+} \ No newline at end of file
diff --git a/ComLightLib/unknwn.h b/ComLightLib/unknwn.h
new file mode 100644
index 0000000..5f38359
--- /dev/null
+++ b/ComLightLib/unknwn.h
@@ -0,0 +1,36 @@
+#pragma once
+#include <type_traits>
+
+// Calling conventions
+#ifdef _MSC_VER
+#define COMLIGHTCALL __stdcall
+#define DECLSPEC_NOVTABLE __declspec(novtable)
+#elif defined(__GNUC__) || defined(__clang__)
+#if defined(__i386__)
+#define COMLIGHTCALL __attribute__((stdcall))
+#else
+#define COMLIGHTCALL
+#endif
+#define DECLSPEC_NOVTABLE
+#else
+#error Unsupported C++ compiler
+#endif
+
+#include "utils/guid_parse.hpp"
+
+#define DEFINE_INTERFACE_ID( guidString ) static constexpr GUID iid() { return ::ComLight::make_guid( guidString ); }
+
+namespace ComLight
+{
+ // This thing is binary compatible with IUnknown from Windows SDK. See DesktopClient demo project, it uses normal COM interop in .NET framework 4.7 to call my implementation.
+ struct DECLSPEC_NOVTABLE IUnknown
+ {
+ DEFINE_INTERFACE_ID( "00000000-0000-0000-c000-000000000046" );
+
+ virtual HRESULT COMLIGHTCALL QueryInterface( REFIID riid, void **ppvObject ) = 0;
+
+ virtual uint32_t COMLIGHTCALL AddRef() = 0;
+
+ virtual uint32_t COMLIGHTCALL Release() = 0;
+ };
+} \ No newline at end of file
diff --git a/ComLightLib/utils/guid_parse.hpp b/ComLightLib/utils/guid_parse.hpp
new file mode 100644
index 0000000..435fcb3
--- /dev/null
+++ b/ComLightLib/utils/guid_parse.hpp
@@ -0,0 +1,103 @@
+// https://github.com/tobias-loew/constexpr-GUID-cpp-11
+
+//-------------------------------------------------------------------------------------------------------
+// constexpr GUID parsing
+// Written by Alexander Bessonov
+// Written by Tobias Loew
+//
+// Licensed under the MIT license.
+//-------------------------------------------------------------------------------------------------------
+
+#pragma once
+#include <stdexcept>
+#include <string>
+#include <cassert>
+#include <cstdint>
+
+#if !defined(GUID_DEFINED)
+#define GUID_DEFINED
+struct GUID {
+ uint32_t Data1;
+ uint16_t Data2;
+ uint16_t Data3;
+ uint8_t Data4[ 8 ];
+};
+#endif
+
+namespace ComLight
+{
+ namespace details
+ {
+ constexpr const size_t short_guid_form_length = 36; // XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+ constexpr const size_t long_guid_form_length = 38; // {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
+
+ constexpr uint8_t parse_hex_digit( const char c )
+ {
+ using namespace std::string_literals;
+ return
+ ( '0' <= c && c <= '9' )
+ ? c - '0'
+ : ( 'a' <= c && c <= 'f' )
+ ? 10 + c - 'a'
+ : ( 'A' <= c && c <= 'F' )
+ ? 10 + c - 'A'
+ :
+ throw std::domain_error{ "invalid character in GUID"s };
+ }
+
+ constexpr uint8_t parse_hex_uint8_t( const char *ptr )
+ {
+ return ( parse_hex_digit( ptr[ 0 ] ) << 4 ) + parse_hex_digit( ptr[ 1 ] );
+ }
+
+ constexpr uint16_t parse_hex_uint16_t( const char *ptr )
+ {
+ return ( parse_hex_uint8_t( ptr ) << 8 ) + parse_hex_uint8_t( ptr + 2 );
+ }
+
+ constexpr uint32_t parse_hex_uint32_t( const char *ptr )
+ {
+ return ( parse_hex_uint16_t( ptr ) << 16 ) + parse_hex_uint16_t( ptr + 4 );
+ }
+
+ constexpr GUID parse_guid( const char *begin )
+ {
+ return GUID{
+ parse_hex_uint32_t( begin ),
+ parse_hex_uint16_t( begin + 8 + 1 ),
+ parse_hex_uint16_t( begin + 8 + 1 + 4 + 1 ),
+ {
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 ),
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 ),
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 ),
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 ),
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 ),
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 + 2 ),
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 + 2 + 2 ),
+ parse_hex_uint8_t( begin + 8 + 1 + 4 + 1 + 4 + 1 + 2 + 2 + 1 + 2 + 2 + 2 + 2 + 2 )
+ }
+ };
+ }
+
+ constexpr GUID make_guid_helper( const char *str, size_t N )
+ {
+ using namespace std::string_literals;
+ using namespace details;
+
+ return ( !( N == long_guid_form_length || N == short_guid_form_length ) )
+ ? throw std::domain_error{ "String GUID of the form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} or XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is expected"s }
+ : ( N == long_guid_form_length && ( str[ 0 ] != '{' || str[ long_guid_form_length - 1 ] != '}' ) )
+ ? throw std::domain_error{ "Missing opening or closing brace"s }
+
+ : parse_guid( str + ( N == long_guid_form_length ? 1 : 0 ) );
+ }
+
+
+ template<size_t N>
+ constexpr GUID make_guid( const char( &str )[ N ] )
+ {
+ return make_guid_helper( str, N - 1 );
+ }
+ }
+ using details::make_guid;
+} \ No newline at end of file
diff --git a/ComLightLib/utils/typeTraits.hpp b/ComLightLib/utils/typeTraits.hpp
new file mode 100644
index 0000000..c5ddb84
--- /dev/null
+++ b/ComLightLib/utils/typeTraits.hpp
@@ -0,0 +1,43 @@
+#pragma once
+#include <type_traits>
+
+namespace ComLight
+{
+ namespace details
+ {
+ template<class TResult, class TValue>
+ constexpr bool pointersAssignable()
+ {
+ // See this for why `&` is required: https://stackoverflow.com/a/52429468/126995
+ return std::is_assignable<TResult*&, TValue*>::value;
+ }
+ }
+}
+
+// https://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector
+#define GENERATE_HAS_MEMBER(member) \
+ \
+template < class T > \
+class HasMember_##member \
+{ \
+private: \
+ using Yes = char[2]; \
+ using No = char[1]; \
+ \
+ struct Fallback { int member; }; \
+ struct Derived : T, Fallback { }; \
+ \
+ template < class U > \
+ static No& test ( decltype(U::member)* ); \
+ template < typename U > \
+ static Yes& test ( U* ); \
+ \
+public: \
+ static constexpr bool RESULT = sizeof(test<Derived>(nullptr)) == sizeof(Yes); \
+}; \
+ \
+template < class T > \
+struct has_member_##member \
+: public std::integral_constant<bool, HasMember_##member<T>::RESULT> \
+{ \
+};