diff --git a/core/interop/IDynamicInterfaceCastable/Directory.Build.props b/core/interop/IDynamicInterfaceCastable/Directory.Build.props new file mode 100644 index 00000000000..b9dea15456b --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/Directory.Build.props @@ -0,0 +1,7 @@ + + + + $(MSBuildThisFileDirectory)src + $(MSBuildThisFileDirectory)bin + + \ No newline at end of file diff --git a/core/interop/IDynamicInterfaceCastable/README.md b/core/interop/IDynamicInterfaceCastable/README.md new file mode 100644 index 00000000000..fdbe4fa42b0 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/README.md @@ -0,0 +1,76 @@ +--- +languages: +- csharp +- cpp +products: +- dotnet-core +page_type: sample +name: "IDynamicInterfaceCastable: Supporting Interfaces Dynamically" +urlFragment: "idynamicinterfacecastable" +description: "A .NET application that shows how to implement IDynamicInterfaceCastable to project a native object as implementing different managed interfaces." +--- + +# `IDynamicInterfaceCastable` Sample + +The [`IDynamicInterfaceCastable` API](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.idynamicinterfacecastable) was introduced in .NET 5 as a way for creating a .NET class that supports interfaces which are not in its metadata. + +This sample provides an implementation of `IDynamicInterfaceCastable` that projects a native object as implementing different known managed interfaces. It uses COM conventions (such as `QueryInterface`) for interacting with the native object, but does not require the actual COM system. + +> [!NOTE] +> The sample uses `Marshal` APIs as part of interacting with the native library. The introduction of [C# function pointers](https://github.com/dotnet/csharplang/blob/994c41586e07e38fb6b30902b1715b4025d80c52/proposals/function-pointers.md) will allow that interaction to occur in a more performant manner. + +## Prerequisites + +* [.NET 5.0 SDK](https://dotnet.microsoft.com/download) Preview 7 or later + +* C++ compiler + * Windows: `cl.exe` + * See [installation instructions](https://docs.microsoft.com/cpp/build/building-on-the-command-line#download-and-install-the-tools). + * Linux/macOS: `g++` + +## Build and Run + +1) In order to build and run, all prerequisites must be installed. The following are also required: + + * On Linux/macOS, the C++ compiler (`g++`) must be on the path. + * The C++ compiler (`cl.exe` or `g++`) and `dotnet` must be the same bitness (32-bit versus 64-bit). + * On Windows, the sample is set up to use the bitness of `dotnet` to find the corresponding `cl.exe` + +1) Navigate to the root directory. + +1) Run the sample. Do one of the following: + + * Use `dotnet run` (which will build and run at the same time). + * Use `dotnet build` to build the executable. The executable will be in `bin` under a subdirectory for the configuration (`Debug` is the default). + * Windows: `bin\Debug\IDynamicInterfaceCastableSample.exe` + * Non-Windows: `bin/Debug/IDynamicInterfaceCastableSample` + +The expected output will show information about the native objects as they are represented in manage as well as results from calls to the native objects: + +``` +Native Object #0 + - does not implement IGreet + - does not implement ICompute + +Native Object #1 + - implements IGreet + -- Hello World from NativeObject #1 + - does not implement ICompute + +Native Object #2 + - does not implement IGreet + - implements ICompute + -- Returned sum: 5 + +Native Object #3 + - implements IGreet + -- Hello World from NativeObject #3 + - implements ICompute + -- Returned sum: 6 +``` + +Note: The way the sample is built is relatively complicated. The goal is that it's possible to build and run the sample with simple `dotnet run` with minimal requirements on pre-installed tools. Typically real-world projects which have both managed and native components will use different build systems for each; for example msbuild/dotnet for managed and CMake for native. + +## Visual Studio support + +The `src\IDynamicInterfaceCastableSample.sln` can be used to open the sample in Visual Studio 2019. In order to be able to build from Visual Studio, though, it has to be started from the correct developer environment. From the developer environment console, start it with `devenv src\IDynamicInterfaceCastableSample.sln`. With that, the solution can be built. To run it, set the start project to `IDynamicInterfaceCastableSample`. diff --git a/core/interop/IDynamicInterfaceCastable/build.proj b/core/interop/IDynamicInterfaceCastable/build.proj new file mode 100644 index 00000000000..ed859572b32 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/build.proj @@ -0,0 +1,15 @@ + + + + Debug + + $(BinRoot)\$(Configuration)\IDynamicInterfaceCastableSample + $(BinRoot)\$(Configuration)\IDynamicInterfaceCastableSample.exe + + + + + + + + \ No newline at end of file diff --git a/core/interop/IDynamicInterfaceCastable/global.json b/core/interop/IDynamicInterfaceCastable/global.json new file mode 100644 index 00000000000..e5716884662 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/global.json @@ -0,0 +1,5 @@ +{ + "msbuild-sdks": { + "Microsoft.Build.Traversal": "2.0.2" + } +} \ No newline at end of file diff --git a/core/interop/IDynamicInterfaceCastable/src/IDynamicInterfaceCastableSample.sln b/core/interop/IDynamicInterfaceCastable/src/IDynamicInterfaceCastableSample.sln new file mode 100644 index 00000000000..e20a74e12cd --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/IDynamicInterfaceCastableSample.sln @@ -0,0 +1,68 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30224.187 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IDynamicInterfaceCastableSample", "ManagedApp\IDynamicInterfaceCastableSample.csproj", "{5990550F-54A5-4518-841D-AE06C045883A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeLib", "NativeLib\NativeLib.csproj", "{5CA1D5C3-2478-4E91-A841-45128CB3F87D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeLib.vs", "NativeLib\NativeLib.vs.vcxproj", "{6FCE3D39-44AA-4234-85A8-950E57E8F038}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{FB75DD88-ABF9-46E5-879D-5D7BBE0323BD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5990550F-54A5-4518-841D-AE06C045883A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Debug|x64.ActiveCfg = Debug|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Debug|x64.Build.0 = Debug|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Debug|x86.ActiveCfg = Debug|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Debug|x86.Build.0 = Debug|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Release|Any CPU.Build.0 = Release|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Release|x64.ActiveCfg = Release|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Release|x64.Build.0 = Release|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Release|x86.ActiveCfg = Release|Any CPU + {5990550F-54A5-4518-841D-AE06C045883A}.Release|x86.Build.0 = Release|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Debug|x64.ActiveCfg = Debug|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Debug|x64.Build.0 = Debug|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Debug|x86.ActiveCfg = Debug|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Debug|x86.Build.0 = Debug|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Release|Any CPU.Build.0 = Release|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Release|x64.ActiveCfg = Release|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Release|x64.Build.0 = Release|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Release|x86.ActiveCfg = Release|Any CPU + {5CA1D5C3-2478-4E91-A841-45128CB3F87D}.Release|x86.Build.0 = Release|Any CPU + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Debug|x64.ActiveCfg = Debug|x64 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Debug|x64.Build.0 = Debug|x64 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Debug|x86.ActiveCfg = Debug|Win32 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Debug|x86.Build.0 = Debug|Win32 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Release|Any CPU.ActiveCfg = Release|Win32 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Release|x64.ActiveCfg = Release|x64 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Release|x64.Build.0 = Release|x64 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Release|x86.ActiveCfg = Release|Win32 + {6FCE3D39-44AA-4234-85A8-950E57E8F038}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5CA1D5C3-2478-4E91-A841-45128CB3F87D} = {FB75DD88-ABF9-46E5-879D-5D7BBE0323BD} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5569A5E7-D4A8-4E4C-9834-90E589AF337C} + EndGlobalSection +EndGlobal diff --git a/core/interop/IDynamicInterfaceCastable/src/ManagedApp/IDynamicInterfaceCastableSample.csproj b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/IDynamicInterfaceCastableSample.csproj new file mode 100644 index 00000000000..f4c05535be0 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/IDynamicInterfaceCastableSample.csproj @@ -0,0 +1,29 @@ + + + + Exe + net5.0 + true + $(DefineConstants);WINDOWS + + + + + false + + + + + + + + + + + + + + + + + diff --git a/core/interop/IDynamicInterfaceCastable/src/ManagedApp/NativeObject.cs b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/NativeObject.cs new file mode 100644 index 00000000000..297c846391a --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/NativeObject.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace IDynamicInterfaceCastableSample +{ + class NativeObject : IDynamicInterfaceCastable, IDisposable + { + public string Name { get; } + + private delegate int QueryInterfaceDelegate(IntPtr _this, ref Guid iid, out IntPtr ppv); + private delegate uint ReleaseDelegate(IntPtr _this); + + [StructLayout(LayoutKind.Sequential)] + private struct IUnknownVtbl + { + public QueryInterfaceDelegate QueryInterface; + public IntPtr AddRef; + public ReleaseDelegate Release; + } + + [StructLayout(LayoutKind.Sequential)] + private struct VtblPtr + { + public IntPtr Vtbl; + } + + private class NativeImpl + { + public IntPtr Ptr; + public object Vtbl; + } + + private readonly Dictionary _interfaceTypeToImplType; + private readonly IntPtr _objPtr; + private readonly IUnknownVtbl _unknownVtbl; + private readonly Dictionary _typeToNativeImpl = new Dictionary(); + + public NativeObject(string name, IntPtr obj, Dictionary interfaceMap) + { + Name = name; + _objPtr = obj; + _interfaceTypeToImplType = interfaceMap; + VtblPtr vtblPtr = Marshal.PtrToStructure(obj); + _unknownVtbl = Marshal.PtrToStructure(vtblPtr.Vtbl); + } + + bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) + { + if (!_interfaceTypeToImplType.ContainsKey(interfaceType)) + return false; + + if (_typeToNativeImpl.ContainsKey(interfaceType)) + return true; + + Type type = Type.GetTypeFromHandle(interfaceType); + Guid guid = type.GUID; + bool success = _unknownVtbl.QueryInterface(_objPtr, ref guid, out IntPtr ppv) == 0; + if (!success) + return false; + + _typeToNativeImpl.Add(interfaceType, new NativeImpl { Ptr = ppv }); + return true; + } + + RuntimeTypeHandle IDynamicInterfaceCastable.GetInterfaceImplementation(RuntimeTypeHandle interfaceType) + { + Type type = Type.GetTypeFromHandle(interfaceType); + if (!_typeToNativeImpl.ContainsKey(interfaceType) || !_interfaceTypeToImplType.ContainsKey(interfaceType)) + return default; + + return _interfaceTypeToImplType[interfaceType]; + } + + public T GetVtbl(RuntimeTypeHandle interfaceType, out IntPtr ptr) + { + if (!_typeToNativeImpl.TryGetValue(interfaceType, out NativeImpl impl)) + throw new InvalidOperationException(); + + ptr = impl.Ptr; + if (impl.Vtbl != null) + return (T)impl.Vtbl; + + VtblPtr vtblPtr = Marshal.PtrToStructure(ptr); + T vtbl = Marshal.PtrToStructure(vtblPtr.Vtbl); + impl.Vtbl = vtbl; + + return vtbl; + } + + public void Dispose() + { + _unknownVtbl.Release(_objPtr); + + // Release for every successful QI + for (var i = 0; i < _typeToNativeImpl.Count; i++) + _unknownVtbl.Release(_objPtr); + } + } +} diff --git a/core/interop/IDynamicInterfaceCastable/src/ManagedApp/Program.cs b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/Program.cs new file mode 100644 index 00000000000..4755c6944b3 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/Program.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace IDynamicInterfaceCastableSample +{ + [Guid("0412BF07-0261-4191-B5DA-9B86340931CB")] + public interface IGreet + { + void Hello(); + } + + [Guid("B88C195F-3052-467A-97D2-817A1698AAFB")] + public interface ICompute + { + int Sum(int a, int b); + } + + class Program + { + [DynamicInterfaceCastableImplementation] + private interface IGreetImpl : IGreet + { + [StructLayout(LayoutKind.Sequential)] + private struct IGreetVtbl + { + public IntPtr QueryInterface; + public IntPtr AddRef; + public IntPtr Release; + public HelloDelegate Hello; + } + + private delegate void HelloDelegate(IntPtr _this); + + void IGreet.Hello() + { + var obj = (NativeObject)this; + IGreetVtbl vtbl = obj.GetVtbl(typeof(IGreet).TypeHandle, out IntPtr ptr); + vtbl.Hello(ptr); + } + } + + [DynamicInterfaceCastableImplementation] + private interface IComputeImpl : ICompute + { + [StructLayout(LayoutKind.Sequential)] + private struct IComputeVtbl + { + public IntPtr QueryInterface; + public IntPtr AddRef; + public IntPtr Release; + public SumDelegate Sum; + } + + private delegate int SumDelegate(IntPtr _this, int a, int b); + + int ICompute.Sum(int a, int b) + { + var obj = (NativeObject)this; + IComputeVtbl vtbl = obj.GetVtbl(typeof(ICompute).TypeHandle, out IntPtr ptr); + return vtbl.Sum(ptr, a, b); + } + } + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void CreateObjectsDelegate([In, Out] IntPtr[] objects, int size); + + static void Main() + { + IntPtr nativeLib = NativeLibrary.Load("NativeLib", System.Reflection.Assembly.GetExecutingAssembly(), DllImportSearchPath.AssemblyDirectory); + IntPtr func = NativeLibrary.GetExport(nativeLib, "CreateObjects"); + + int count = 4; + var nativePtrArray = new IntPtr[count]; + + var createObjects = Marshal.GetDelegateForFunctionPointer(func); + createObjects(nativePtrArray, count); + + var interfaceMap = new Dictionary + { + [typeof(IGreet).TypeHandle] = typeof(IGreetImpl).TypeHandle, + [typeof(ICompute).TypeHandle] = typeof(IComputeImpl).TypeHandle + }; + for (int i = 0; i < count; i++) + { + using var obj = new NativeObject($"Native Object #{i}", nativePtrArray[i], interfaceMap); + InspectObject(obj); + } + + NativeLibrary.Free(nativeLib); + } + + private static void InspectObject(NativeObject obj) + { + Console.WriteLine(obj.Name); + + if (obj is IGreet greet) + { + Console.WriteLine($" - implements {nameof(IGreet)}"); + greet.Hello(); + } + else + { + Console.WriteLine($" - does not implement {nameof(IGreet)}"); + } + + if (obj is ICompute compute) + { + Console.WriteLine($" - implements {nameof(ICompute)}"); + Console.WriteLine($" -- Returned sum (1 + 2 + ): {compute.Sum(1, 2)}"); + } + else + { + Console.WriteLine($" - does not implement {nameof(ICompute)}"); + } + + Console.WriteLine(); + } + } +} diff --git a/core/interop/IDynamicInterfaceCastable/src/ManagedApp/Properties/launchSettings.json b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/Properties/launchSettings.json new file mode 100644 index 00000000000..07d8944a8f5 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/ManagedApp/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "MarshalingSample": { + "commandName": "Executable", + "executablePath": "$(SolutionDir)\\..\\bin\\$(Configuration)\\IDynamicInterfaceCastableSample.exe", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.cpp b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.cpp new file mode 100644 index 00000000000..4cd6f40831b --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.cpp @@ -0,0 +1,163 @@ +#include +#include +#include + +#ifdef WINDOWS + +#define DLL_EXPORT __declspec(dllexport) + +#ifndef STDMETHODCALLTYPE +#define STDMETHODCALLTYPE __stdcall +#endif + +#else // !WINDOWS + +#ifndef STDMETHODCALLTYPE +#define STDMETHODCALLTYPE +#endif + +#if __GNUC__ >= 4 +#define DLL_EXPORT __attribute__ ((visibility ("default"))) +#else +#define DLL_EXPORT +#endif + +#endif + +typedef struct _GUID { + unsigned int Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +} GUID; + +#define HRESULT int +#define ULONG unsigned int +#define REFIID const GUID& +#define E_NOINTERFACE 0x80004002L + +bool operator==(REFIID a, REFIID b) +{ + return 0 == ::memcmp(&a, &b, sizeof(GUID)); +} + +// {00000000-0000-0000-C000-000000000046} +static const GUID IID_IUnknown = { 0x00000000, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; +struct IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void **ppvObject) = 0; + + virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; + + virtual ULONG STDMETHODCALLTYPE Release(void) = 0; +}; + +// {0412BF07-0261-4191-B5DA-9B86340931CB} +static const GUID IID_IGreet = { 0x412bf07, 0x261, 0x4191, { 0xb5, 0xda, 0x9b, 0x86, 0x34, 0x9, 0x31, 0xcb } }; +struct IGreet : public IUnknown +{ + virtual void STDMETHODCALLTYPE Hello() = 0; +}; + +// {B88C195F-3052-467A-97D2-817A1698AAFB} +static const GUID IID_ICompute = { 0xb88c195f, 0x3052, 0x467a, { 0x97, 0xd2, 0x81, 0x7a, 0x16, 0x98, 0xaa, 0xfb } }; +struct ICompute : public IUnknown +{ + virtual int STDMETHODCALLTYPE Sum(int a, int b) = 0; +}; + +enum class SupportedInterfaces +{ + None = 0, + IGreet = 1, + ICompute = 2, + + All = IGreet | ICompute +}; + +inline SupportedInterfaces operator&(SupportedInterfaces a, SupportedInterfaces b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +class NativeObject : public IGreet, public ICompute +{ +public: + NativeObject(int id, SupportedInterfaces supportedInterfaces) + : _id{ id } + , _supportedInterfaces { supportedInterfaces } + { } + +public: + void STDMETHODCALLTYPE Hello() + { + std::cout << " -- Hello World from NativeObject #" << _id << std::endl; + } + +public: + int STDMETHODCALLTYPE Sum(int a, int b) + { + return a + b + _id; + } + +public: + ULONG STDMETHODCALLTYPE AddRef() + { + return ++_refCount; + } + + ULONG STDMETHODCALLTYPE Release() + { + ULONG c = --_refCount; + if (c == 0) + delete this; + + return c; + } + + HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void **ppvObject) + { + if (riid == IID_IGreet && (_supportedInterfaces & SupportedInterfaces::IGreet) == SupportedInterfaces::IGreet) + { + *ppvObject = static_cast(this); + } + else if (riid == IID_ICompute && (_supportedInterfaces & SupportedInterfaces::ICompute) == SupportedInterfaces::ICompute) + { + *ppvObject = static_cast(this); + } + else if (riid == IID_IUnknown) + { + *ppvObject = static_cast(this); + } + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + AddRef(); + return 0; + } + +private: + std::atomic _refCount = { 1 }; + + int _id; + SupportedInterfaces _supportedInterfaces; +}; + +#if defined(WINDOWS) && defined(_M_IX86) +#pragma comment(linker, "/export:CreateObjects=_CreateObjects@8") +#endif +extern "C" DLL_EXPORT void STDMETHODCALLTYPE CreateObjects(void **outArray, int size) +{ + for (int i = 0; i < size; ++i) + { + int flags = i % (static_cast(SupportedInterfaces::All) + 1); + outArray[i] = new NativeObject(i, static_cast(flags)); + } +} diff --git a/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.csproj b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.csproj new file mode 100644 index 00000000000..0f63d5902d3 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.csproj @@ -0,0 +1,94 @@ + + + + net5.0 + + + + $(BinRoot)/$(Configuration) + NativeLib + + $([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture) + $(MSBuildThisFileDirectory)obj/$(Configuration)/$(NativePlatform)/ + + + + + $(NativePlatform) + + + + + + + + + + + + PreserveNewest + false + + + + + + + + + + + .so + .dylib + .dll + $(NativeBinDir)/$(NativeOutputName)$(NativeOutputExtension) + + @(NativeSource-> '"%(RootDir)%(Directory)%(Filename)%(Extension)"', ' ') + + + + + + + + + -Iinc + -g -shared -fPIC + + + -D LINUX + -ldl -Wl,-rpath,'$ORIGIN',--disable-new-dtags + + + -D OSX + -ldl -Wl,-rpath,'@loader_path' + + + + + + + + @(MSVCIncludePaths-> '/I "%(RootDir)%(Directory)%(Filename)"', ' ') + $(IncPaths) /I inc + /EHsc /Od /GS /sdl /Zi + /D WINDOWS + @(MSVCLibPaths-> '/LIBPATH:"%(RootDir)%(Directory)%(Filename)"', ' ') + + + + + diff --git a/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.vs.vcxproj b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.vs.vcxproj new file mode 100644 index 00000000000..840a2ed643c --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.vs.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + 16.0 + {6FCE3D39-44AA-4234-85A8-950E57E8F038} + Win32Proj + MarshalingSampleNative + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;MARSHALLINGSAMPLENATIVE_EXPORTS;WINDOWS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + false + + + + + Use + Level3 + Disabled + true + _DEBUG;MARSHALLINGSAMPLENATIVE_EXPORTS;WINDOWS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + false + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;MARSHALLINGSAMPLENATIVE_EXPORTS;WINDOWS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;MARSHALLINGSAMPLENATIVE_EXPORTS;WINDOWS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + true + false + + + + + + \ No newline at end of file diff --git a/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.vs.vcxproj.filters b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.vs.vcxproj.filters new file mode 100644 index 00000000000..3036de29ca4 --- /dev/null +++ b/core/interop/IDynamicInterfaceCastable/src/NativeLib/NativeLib.vs.vcxproj.filters @@ -0,0 +1,18 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + + + Source Files + + + \ No newline at end of file