<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      使用 CsWin32 和 ComWrappers 實現 COM 接口

      基礎概念

      CsWin32 是微軟開發的一個 C# 的源生成器,可以按需生成 C# PInvoke 代碼,也支持生成系統的 COM 接口定義。
      ComWrappers 是 dotnet 5 引入的新的和 COM api 互操作的組件。

      生成支持 AOT 的 COM 接口

      使用 CsWin32 生成 COM 接口定義時,默認會生成使用 Builtin COM Interop 技術的代碼,這種接口使用 ComImportAttribute 修飾,不支持 Native AOT。

      CsWin32 提供了一個開關,在 NativeMethods.json 中設置 { "allowMarshaling": false },可以使 CsWin32 生成更為原始的 COM 接口。

      CsWin32 生成的 IClassFactory 接口
      using winmdroot = global::Windows.Win32;
      namespace Windows.Win32
      {
      	namespace System.Com
      	{
      		[Guid("00000001-0000-0000-C000-000000000046")]
      		[SupportedOSPlatform("windows5.0")]
      		[global::System.CodeDom.Compiler.GeneratedCode("Microsoft.Windows.CsWin32", "0.3.183+73e6125f79.RR")]
      		internal unsafe partial struct IClassFactory
      			:IVTable<IClassFactory,IClassFactory.Vtbl>,IComIID		{
      
                  // 對 IUnknown 和 IClassFactory 接口指針的函數調用的包裝
      
      			[OverloadResolutionPriority(1)]
      			internal unsafe winmdroot.Foundation.HRESULT QueryInterface(in global::System.Guid riid, out void* ppvObject)
      			{
      				fixed (void** ppvObjectLocal = &ppvObject)
      				{
      					fixed (global::System.Guid* riidLocal = &riid)
      					{
      						winmdroot.Foundation.HRESULT __result = this.QueryInterface(riidLocal, ppvObjectLocal);
      						return __result;
      					}
      				}
      			}
      
      			public unsafe winmdroot.Foundation.HRESULT QueryInterface(global::System.Guid* riid, void** ppvObject)
      			{
      				return ((delegate *unmanaged [Stdcall]<IClassFactory*,global::System.Guid* ,void** ,winmdroot.Foundation.HRESULT>)lpVtbl[0])((IClassFactory*)Unsafe.AsPointer(ref this), riid, ppvObject);
      			}
      
      			public uint AddRef()
      			{
      				return ((delegate *unmanaged [Stdcall]<IClassFactory*,uint>)lpVtbl[1])((IClassFactory*)Unsafe.AsPointer(ref this));
      			}
      
      			public uint Release()
      			{
      				return ((delegate *unmanaged [Stdcall]<IClassFactory*,uint>)lpVtbl[2])((IClassFactory*)Unsafe.AsPointer(ref this));
      			}
      
      			/// <inheritdoc cref="CreateInstance(winmdroot.System.Com.IUnknown*, global::System.Guid*, void**)"/>
      			[OverloadResolutionPriority(1)]
      			internal unsafe winmdroot.Foundation.HRESULT CreateInstance(winmdroot.System.Com.IUnknown* pUnkOuter, in global::System.Guid riid, out void* ppvObject)
      			{
      				fixed (void** ppvObjectLocal = &ppvObject)
      				{
      					fixed (global::System.Guid* riidLocal = &riid)
      					{
      						winmdroot.Foundation.HRESULT __result = this.CreateInstance(pUnkOuter, riidLocal, ppvObjectLocal);
      						return __result;
      					}
      				}
      			}
      
                  // 將 COM 接口方法調用轉發到托管對象方法調用的包裝
      
      			[UnmanagedCallersOnly(CallConvs = new []{
      typeof(CallConvStdcall)}
      )]
      			private static winmdroot.Foundation.HRESULT CreateInstance(IClassFactory* pThis, [Optional] winmdroot.System.Com.IUnknown* pUnkOuter, global::System.Guid* riid, void** ppvObject)
      			{
      				try
      				{
      					winmdroot.Foundation.HRESULT __hr = ComHelpers.UnwrapCCW(pThis, out Interface __object);
      					if (__hr.Failed)
      					{
      						return __hr;
      					}
      					return __object.CreateInstance(pUnkOuter, riid, ppvObject);
      				}
      				catch (Exception ex)
      				{
      					return (winmdroot.Foundation.HRESULT)ex.HResult;
      				}
      			}
      
      			public unsafe winmdroot.Foundation.HRESULT CreateInstance([Optional] winmdroot.System.Com.IUnknown* pUnkOuter, global::System.Guid* riid, void** ppvObject)
      			{
      				return ((delegate *unmanaged [Stdcall]<IClassFactory*,winmdroot.System.Com.IUnknown* ,global::System.Guid* ,void** ,winmdroot.Foundation.HRESULT>)lpVtbl[3])((IClassFactory*)Unsafe.AsPointer(ref this), pUnkOuter, riid, ppvObject);
      			}
      
      			[UnmanagedCallersOnly(CallConvs = new []{
      typeof(CallConvStdcall)}
      )]
      			private static winmdroot.Foundation.HRESULT LockServer(IClassFactory* pThis, winmdroot.Foundation.BOOL fLock)
      			{
      				try
      				{
      					winmdroot.Foundation.HRESULT __hr = ComHelpers.UnwrapCCW(pThis, out Interface __object);
      					if (__hr.Failed)
      					{
      						return __hr;
      					}
      					return __object.LockServer(fLock);
      				}
      				catch (Exception ex)
      				{
      					return (winmdroot.Foundation.HRESULT)ex.HResult;
      				}
      			}
      
      			public winmdroot.Foundation.HRESULT LockServer(winmdroot.Foundation.BOOL fLock)
      			{
      				return ((delegate *unmanaged [Stdcall]<IClassFactory*,winmdroot.Foundation.BOOL ,winmdroot.Foundation.HRESULT>)lpVtbl[4])((IClassFactory*)Unsafe.AsPointer(ref this), fLock);
      			}
      
      			internal unsafe global::Windows.Win32.Foundation.HRESULT QueryInterface<T>(out T* ppv)
      where T : unmanaged
      
      			{
      				Guid guid = typeof(T).GUID;
      				void* pv;
      				var hr = this.QueryInterface(&guid, &pv);
      				if (hr.Succeeded)
      
      				{
      					ppv = (T*)pv;
      				}
      				else
      
      				{
      					ppv = null;
      				}
      
      				return hr;
      			}
      
                  // IClassFactory 接口的函數表定義
      
      			internal struct Vtbl
      			{
      				internal delegate *unmanaged [Stdcall]<IClassFactory*,global::System.Guid* ,void** ,winmdroot.Foundation.HRESULT> QueryInterface_1;
      
      				internal delegate *unmanaged [Stdcall]<IClassFactory*,uint> AddRef_2;
      
      				internal delegate *unmanaged [Stdcall]<IClassFactory*,uint> Release_3;
      
      				internal delegate *unmanaged [Stdcall]<IClassFactory*,winmdroot.System.Com.IUnknown* ,global::System.Guid* ,void** ,winmdroot.Foundation.HRESULT> CreateInstance_4;
      
      				internal delegate *unmanaged [Stdcall]<IClassFactory*,winmdroot.Foundation.BOOL ,winmdroot.Foundation.HRESULT> LockServer_5;
      			}
      
                  // 使用生成的函數邏輯填充函數表
      
      			public static void PopulateVTable(Vtbl* vtable)
      			{
      				vtable->CreateInstance_4 = &CreateInstance;
      				vtable->LockServer_5 = &LockServer;
      			}
      
                  // COM 接口指針中的函數表指針
      
      			private void** lpVtbl;
      
      
                  // 接口的 GUID
      
      			internal static readonly Guid IID_Guid = new Guid(0x00000001, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
      
      			static ref readonly Guid IComIID.Guid			{
      								[MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      				{
      					ReadOnlySpan<byte> data = new byte[]					{
      0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46					};
      					return ref Unsafe.As<byte,Guid>(ref MemoryMarshal.GetReference(data));
      				}
      			}
      
                  // COM 接口對等的 C# 接口定義
      
      			[Guid("00000001-0000-0000-C000-000000000046"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown),ComImport()]
      			[SupportedOSPlatform("windows5.0")]
      			internal interface Interface
      			{
      				[PreserveSig()]
      				unsafe winmdroot.Foundation.HRESULT CreateInstance([Optional] winmdroot.System.Com.IUnknown* pUnkOuter, global::System.Guid* riid, void** ppvObject);
      
      								[PreserveSig()]
      winmdroot.Foundation.HRESULT LockServer(winmdroot.Foundation.BOOL fLock);
      			}
      		}
      	}
      }
      
      
      

      生成的 COM 接口是一個結構體,它表達了 COM 接口規范中定義的內存結構,即 COM 接口指針是指向虛函數指針表的指針。

      結構體有以下幾部分:

      • IUnknown 和 IClassFactory 接口方法的函數指針調用的包裝。
      • 使用 C# 實現 COM 接口時,將 COM 接口方法調用轉發到托管對象方法調用的包裝。
      • IClassFactory 接口的函數表對應的結構體定義。
      • 使用填充函數表結構的封裝。
      • COM 接口指針中的函數表指針。
      • 更容易訪問的接口 GUID 屬性。
      • COM 接口對等的 C# 接口定義。

      使用生成的 COM 接口定義操作傳入的 COM 接口指針

      使用這個結構體的方式很簡單,將 COM 接口指針直接轉換成此結構體指針,就能調用其中的實例方法了。

      unsafe void Test(nint punk)
      {
          if (Marshal.QueryInterface(punk, in IClassFactory.IID_Guid, out var ppv) == 0)
          {
              try
              {
                  var pClassFactory = (IClassFactory*)ppv;
                  pClassFactory->LockServer(true);
                  // ...
                  pClassFactory->Release();
              }
              finally
              {
                  Marshal.Release(ppv);
              }
          }
      }
      

      使用 C# 編寫支持 AOT 的 COM 對象

      編寫 COM 對象時情況稍微有些復雜。dotnet 8 時引入了 StrategyBasedComWrappersComWrappers 源生成器,可以使用 GeneratedComClassAttribute 編寫 COM 對象,但相關的接口定義都需要在源碼中提供,沒辦法利用 CsWin32 已經整理好的接口定義。

      好在 CsWin32 提供了和 ComWrappers 互操作的接口,經過點點簡單的配置就能復用上述生成的 COM 接口定義了。

      參考 ComWrappers 文檔,創建托管對象包裝器最重要的一部分就是通過 ComWrappers.ComputeVtables 方法向運行時提供目標 COM 接口的 GUID 和函數表定義。

      上文分析了 CsWin32 生成的 COM 接口定義的內容,其中有接口 GUID,接口函數表結構,將調用轉發到托管對象調用的接口函數實現,也就是說我們只需要將這些東西組合在一起,就能將 CsWin32 和 ComWrappers 聯合使用了。

      我們以 IStream 為例,托管實現參考 WPF 中的 ManagedIStream

      首先我們編寫一些輔助代碼用以生成 ComWrappers 所需的 ComInterfaceEntry。
      參考 ComInterfaceTable,其中 IComIID 接口定義了接口 GUID 靜態屬性,IVTable 提供了靜態函數表指針屬性,它從 IVTable 接口中獲取靜態的指向 COM 接口函數表的指針。

      internal readonly unsafe struct ComInterfaceTable
      {
          public ComWrappers.ComInterfaceEntry* Entries { get; init; }
          public int Count { get; init; }
      
          /// <summary>
          ///  Create an interface table for the given interface.
          /// </summary>
          public static ComInterfaceTable Create<TComInterface>()
              where TComInterface : unmanaged, IComIID, IVTable
          {
              Span<ComWrappers.ComInterfaceEntry> entries = AllocateEntries<TComInterface>(1);
              entries[0] = GetEntry<TComInterface>();
      
              return new()
              {
                  Entries = (ComWrappers.ComInterfaceEntry*)Unsafe.AsPointer(ref entries[0]),
                  Count = entries.Length
              };
          }
      
          [MethodImpl(MethodImplOptions.AggressiveInlining)]
          private static Span<ComWrappers.ComInterfaceEntry> AllocateEntries<T>(int count)
          {
              Span<ComWrappers.ComInterfaceEntry> entries = new(
                  (ComWrappers.ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(T), sizeof(ComWrappers.ComInterfaceEntry) * (count + 1)),
                  count);
              return entries;
          }
      
          [MethodImpl(MethodImplOptions.AggressiveInlining)]
          private static ComWrappers.ComInterfaceEntry GetEntry<TComInterface>() where TComInterface : unmanaged, IComIID, IVTable
              => new()
              {
                  Vtable = (nint)TComInterface.VTable,
                  IID = *GetIID<TComInterface>()
              };
      
          // https://github.com/dotnet/winforms/blob/f020fe71f615cb51aa61970f5aaa757bb981499e/src/System.Private.Windows.Core/src/Windows/Win32/System/Com/IID.cs#L32
          private static Guid* GetIID<T>() where T : unmanaged, IComIID
              => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in T.Guid));
      }
      

      接下來需要將 COM 接口的函數表與托管對象關聯的接口。參考 IManagedWrapper

      internal unsafe interface IManagedWrapper
      {
          ComInterfaceTable GetComInterfaceTable();
      }
      
      internal unsafe interface IManagedWrapper<TComInterface> : IManagedWrapper
          where TComInterface : unmanaged, IVTable, IComIID
      {
          // Allocates a ComInterfaceTable include VTable for the given interface type.
          private static ComInterfaceTable InterfaceTable { get; set; } = ComInterfaceTable.Create<TComInterface>();
      
          ComInterfaceTable IManagedWrapper.GetComInterfaceTable() => InterfaceTable;
      }
      

      第一次讀取 IVTable 接口中的函數表指針時,它會分配一段內存,并且將函數表中的函數指針指向 CsWin32 生成的靜態方法,參考 IVTable`2.csComHelpers.cs

      COM 體系中通過 IUnknown 接口定義的 QueryInterface、AddRef、Release 三個方法提供類型轉換和引用計數管理功能,這部分我們需要對接到 ComWrappers 上,以將引用計數和 dotnet gc 關聯起來。

      訪問 IVTable 接口生成函數表時會自動調用 ComHelpers.PopulateIUnknownComHelpers.PopulateIUnknown 內部會調用 ComHelpers.PopulateIUnknownImpl,我們可以通過此方法和 ComWrappers 的運行時部分關聯。參考 WinFormsComWrappers

      namespace Windows.Win32
      {
          unsafe partial class ComHelpers
          {
              // Populate vtable using IUnknown method implemented by ComWrappers
              static partial void PopulateIUnknownImpl<TComInterface>(IUnknown.Vtbl* vtable) where TComInterface : unmanaged
              {
                  ComWrappers.GetIUnknownImpl(out nint fpQueryInterface, out nint fpAddRef, out nint fpRelease);
      
                  vtable->QueryInterface_1 = (delegate* unmanaged[Stdcall]<IUnknown*, Guid*, void*, HRESULT>)fpQueryInterface;
                  vtable->AddRef_2 = (delegate* unmanaged[Stdcall]<IUnknown*, uint>)fpAddRef;
                  vtable->Release_3 = (delegate* unmanaged[Stdcall]<IUnknown*, uint>)fpRelease;
              }
          }
      }
      

      隨后實現我們自定義的 ComWrappers,讓運行時可以從我們的 C# 類型中讀取 COM 接口函數表,并將生成的 RCW 和托管對象關聯起來。參考 WinFormsComWrappers

      public class CustomComWrappers : ComWrappers
      {
          protected override unsafe ComWrappers.ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
          {
              if (obj is not IManagedWrapper vtables)
              {
                  Debug.Fail("object does not implement IManagedWrapper");
                  count = 0;
                  return null;
              }
      
              // Bind the vtables for the interfaces implemented by the object.
              ComInterfaceTable table = vtables.GetComInterfaceTable();
              count = table.Count;
      
              return table.Entries;
          }
      
          protected override object? CreateObject(nint externalComObject, CreateObjectFlags flags)
          {
              throw new NotImplementedException();
          }
      
          protected override void ReleaseObjects(IEnumerable objects)
          {
              throw new NotImplementedException();
          }
      }
      

      現在我們有了全部的基礎設施,可以開始編寫 ManagedIStream 了。這里只展示類型定義,具體方法實現不再贅述。

      public class ManagedIStream : IManagedWrapper<IStream>, IStream.Interface
      {
          //...
      }
      
      // 創建 ComWrappers
      var comWrappers = new CustomComWrappers();
      
      // 創建內存流
      var stream = new MemoryStream(100);
      stream.Write([1, 2, 3, 4, 5, 6, 7, 8, 9]);
      
      // 創建托管包裝器
      var wrapper = new ManagedIStream(stream);
      
      // 使用 ComWrappers 創建托管包裝器的 COM 指針,注意此時返回的是 IUnknown 而非 IStream
      var punk = (IUnknown*)comWrappers.GetOrCreateComInterfaceForObject(
          wrapper,
          CreateComInterfaceFlags.None);
      
      punk->QueryInterface<IStream>(out var pStream).ThrowOnFailure();
      
      // 此時 pStream 可以傳遞給其他 COM 接口使用。
      

      查看全部代碼 https://gist.github.com/cnbluefire/7438e4c062f34b89e6855ad57605219a

      posted on 2025-09-26 15:58  叫我藍火火  閱讀(321)  評論(1)    收藏  舉報

      導航

      主站蜘蛛池模板: 国产在线观看91精品亚瑟| 国产高潮视频在线观看| 好吊视频一区二区三区| 国产精品爆乳奶水无码视频免费 | 少妇人妻偷人偷人精品| 日韩人妻无码一区二区三区| 伊人成伊人成综合网222| 少妇人妻综合久久中文字幕| 久久精品国产亚洲成人av| 国产精品麻豆中文字幕| 人人做人人爽人人爱| 在线精品自拍亚洲第一区| 日韩有码中文字幕av| 波多野结衣乳喷高潮视频| 亚洲综合网国产精品一区| 扒开双腿猛进入喷水高潮叫声| 日本妇人成熟免费| 国产无遮挡真人免费视频| 亚洲小说乱欧美另类| 日本精品极品视频在线| 国产做无码视频在线观看| 自慰无码一区二区三区| 亚洲中文久久久精品无码| 日韩在线视频一区二区三| 亂倫近親相姦中文字幕| 国产明星精品无码AV换脸| 午夜片神马影院福利| 亚洲国产精品区一区二区| 免费现黄频在线观看国产| 国产91午夜福利精品| 国产精品久久国产精品99 gif| 日本大片在线看黄a∨免费| 昂仁县| 中文字幕一区二区久久综合| 无码人妻丰满熟妇啪啪| 国产乱码日韩精品一区二区| 午夜精品一区二区三区免费视频 | 欧美 变态 另类 人妖| 亚洲精品第一页中文字幕| 国产精品中文字幕二区| 日韩精品一区二区蜜臀av|