diff options
| author | Konstantin <const@const.me> | 2023-01-16 14:52:43 +0100 |
|---|---|---|
| committer | Konstantin <const@const.me> | 2023-01-16 14:52:43 +0100 |
| commit | 8c4603c73675958efc960fbd4bb599a2909d106a (patch) | |
| tree | 714dc6fc9a1672d5fd7f89676b97e10959662abc /ComLightLib | |
| parent | 990a8d0dbaefc996244097397259e92758b15cce (diff) | |
Source codes
Diffstat (limited to 'ComLightLib')
| -rw-r--r-- | ComLightLib/ComLightLib.vcxproj | 116 | ||||
| -rw-r--r-- | ComLightLib/ComLightLib.vcxproj.filters | 28 | ||||
| -rw-r--r-- | ComLightLib/Exception.hpp | 20 | ||||
| -rw-r--r-- | ComLightLib/Readme.txt | 3 | ||||
| -rw-r--r-- | ComLightLib/client/CComPtr.hpp | 110 | ||||
| -rw-r--r-- | ComLightLib/comLightClient.h | 23 | ||||
| -rw-r--r-- | ComLightLib/comLightCommon.h | 11 | ||||
| -rw-r--r-- | ComLightLib/comLightServer.h | 15 | ||||
| -rw-r--r-- | ComLightLib/hresult.h | 26 | ||||
| -rw-r--r-- | ComLightLib/pal/guiddef.h | 21 | ||||
| -rw-r--r-- | ComLightLib/pal/hresult.h | 101 | ||||
| -rw-r--r-- | ComLightLib/server/Object.hpp | 139 | ||||
| -rw-r--r-- | ComLightLib/server/ObjectRoot.hpp | 51 | ||||
| -rw-r--r-- | ComLightLib/server/RefCounter.hpp | 38 | ||||
| -rw-r--r-- | ComLightLib/server/freeThreadedMarshaller.cpp | 17 | ||||
| -rw-r--r-- | ComLightLib/server/freeThreadedMarshaller.h | 29 | ||||
| -rw-r--r-- | ComLightLib/server/interfaceMap.h | 31 | ||||
| -rw-r--r-- | ComLightLib/streams.h | 61 | ||||
| -rw-r--r-- | ComLightLib/unknwn.h | 36 | ||||
| -rw-r--r-- | ComLightLib/utils/guid_parse.hpp | 103 | ||||
| -rw-r--r-- | ComLightLib/utils/typeTraits.hpp | 43 |
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> \ +{ \ +}; |
