Note
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãµã€ã³ã€ã³ãŸãã¯ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
ãã®ããŒãžã«ã¢ã¯ã»ã¹ããã«ã¯ãæ¿èªãå¿ èŠã§ãã ãã£ã¬ã¯ããªã®å€æŽã詊ãããšãã§ããŸãã
ãã¥ãŒããªã¢ã«:
ãã®ãã¥ãŒããªã¢ã«ã§ã¯ã ComWrappers åãé©åã«ãµãã¯ã©ã¹åããŠãæé©åããã AOT ã«åªãã COM çžäºéçšãœãªã¥ãŒã·ã§ã³ãæäŸããæ¹æ³ã«ã€ããŠèª¬æããŸãã ãã®ãã¥ãŒããªã¢ã«ãéå§ããåã«ãCOMããã®ã¢ãŒããã¯ãã£ãããã³æ¢åã® COM çžäºéçšãœãªã¥ãŒã·ã§ã³ã«ã€ããŠçè§£ããŠããå¿
èŠããããŸãã
ãã®ãã¥ãŒããªã¢ã«ã§ã¯ã次ã®ã€ã³ã¿ãŒãã§ã€ã¹å®çŸ©ãå®è£ ããŸãã ãããã®ã€ã³ã¿ãŒãã§ã€ã¹ãšãã®å®è£ ã¯ã次ã®äŸã瀺ããŸãã
- COM/.NET å¢çãè¶ããåã®ããŒã·ã£ã«åãšã¢ã³ããŒã·ã£ã«åã
- .NET ã§ãã€ãã£ã COM ãªããžã§ã¯ãã䜿çšããããã® 2 ã€ã®ç°ãªãæ¹æ³ã
- .NET 5 以éã§ã«ã¹ã¿ã COM çžäºéçšãæå¹ã«ããããã®æšå¥šãã¿ãŒã³ã
ãã®ãã¥ãŒããªã¢ã«ã§äœ¿çšããããã¹ãŠã®ãœãŒã¹ ã³ãŒãã¯ã dotnet/samples ãªããžããªã§å ¥æã§ããŸãã
泚
.NET 8 SDK 以éã®ããŒãžã§ã³ã§ã¯ã ComWrappers API å®è£
ãèªåçã«çæããããã®ãœãŒã¹ ãžã§ãã¬ãŒã¿ãŒãæäŸãããŠããŸãã 詳现ã«ã€ããŠã¯ããœãŒã¹çæComWrappersåç
§ããŠãã ããã
C# ã®å®çŸ©
interface IDemoGetType
{
string? GetString();
}
interface IDemoStoreType
{
void StoreString(int len, string? str);
}
Win32 C++ å®çŸ©
MIDL_INTERFACE("92BAA992-DB5A-4ADD-977B-B22838EE91FD")
IDemoGetType : public IUnknown
{
HRESULT STDMETHODCALLTYPE GetString(_Outptr_ wchar_t** str) = 0;
};
MIDL_INTERFACE("30619FEA-E995-41EA-8C8B-9A610D32ADCB")
IDemoStoreType : public IUnknown
{
HRESULT STDMETHODCALLTYPE StoreString(int len, _In_z_ const wchar_t* str) = 0;
};
ComWrappersèšèšã®æŠèŠ
ComWrappers API ã¯ã.NET 5 以éã®ã©ã³ã¿ã€ã ãšã® COM çžäºéçšãå®çŸããããã«å¿
èŠãªæå°éã®æäœãæäŸããããã«èšèšãããŠããŸãã ã€ãŸããçµã¿èŸŒã¿ã® COM çžäºéçšã·ã¹ãã ã«ååšããåªããæ©èœã®å€ãã¯ååšãããåºæ¬çãªæ§æèŠçŽ ããæ§ç¯ããå¿
èŠããããŸãã API ã®äž»ãªåœ¹å²ã¯æ¬¡ã® 2 ã€ãããŸãã
- å¹ççãªãªããžã§ã¯ãèå¥ (ããšãã°ã
IUnknown*ã€ã³ã¹ã¿ã³ã¹ãšãããŒãžã ãªããžã§ã¯ãã®éã®ãããã³ã°)ã - ã¬ããŒãž ã³ã¬ã¯ã¿ãŒ (GC) ã®å¯Ÿè©±ã
ãããã®å¹çã¯ã ComWrappers API ãçµç±ããããã«ã©ãããŒã®äœæãšååŸãèŠæ±ããããšã§å®çŸãããŸãã
ComWrappers API ã«ã¯è²¬ä»»ãéåžžã«å°ãªããããçžäºéçšäœæ¥ã®å€§éšåãã³ã³ã·ã¥ãŒããŒãåŠçããå¿
èŠããããšèããã®ãçŸç¶ã§ããããã¯äºå®ã§ãã ãã ãã远å äœæ¥ã¯äž»ã«æ©æ¢°çã§ããããœãŒã¹çæãœãªã¥ãŒã·ã§ã³ã«ãã£ãŠå®è¡ã§ããŸãã ããšãã°ã C#/WinRT ããŒã« ãã§ãŒã³ã¯ãWinRT çžäºéçšæ©èœã®ãµããŒããæäŸããããã« ComWrappers äžã«æ§ç¯ããããœãŒã¹çæãœãªã¥ãŒã·ã§ã³ã§ãã
ComWrappers ãµãã¯ã©ã¹ãå®è£
ãã
ComWrappers ãµãã¯ã©ã¹ãæå®ãããšã.NET ã©ã³ã¿ã€ã ã«ååãªæ
å ±ãæäŸããŠãCOM ãªããžã§ã¯ããš .NET ã«æåœ±ããã COM ãªããžã§ã¯ãã«æåœ±ããããããŒãžã ãªããžã§ã¯ãã®ã©ãããŒãäœæããã³èšé²ã§ããŸãã ãµãã¯ã©ã¹ã®æŠèŠãèŠãåã«ãããã€ãã®çšèªãå®çŸ©ããå¿
èŠããããŸãã
ãããŒãžã ãªããžã§ã¯ã ã©ãã㌠â ãããŒãžã .NET ãªããžã§ã¯ãã«ã¯ãnon-.NET ç°å¢ããã®äœ¿çšãæå¹ã«ããããã«ã©ãããŒãå¿ èŠã§ãã ãããã®ã©ãããŒã¯ãåŸæ¥ãCOM åŒã³åºãå¯èœã©ãã㌠(CCW) ãšåŒã°ããŸãã
ãã€ãã£ã ãªããžã§ã¯ã ã©ãã㌠â non-.NET èšèªã§å®è£ ããã COM ãªããžã§ã¯ãã§ã¯ã.NET ããã®äœ¿çšãæå¹ã«ããããã«ã©ãããŒãå¿ èŠã§ãã ãããã®ã©ãããŒã¯ãåŸæ¥ã¯ã©ã³ã¿ã€ã åŒã³åºãå¯èœã©ãã㌠(RCW) ãšåŒã°ããŸãã
æé 1 â ãã®æå³ãå®è£ ããŠçè§£ããã¡ãœãããå®çŸ©ãã
ComWrappersåãæ¡åŒµããã«ã¯ã次㮠3 ã€ã®ã¡ãœãããå®è£
ããå¿
èŠããããŸãã ãããã®åã¡ãœããã¯ãã©ãããŒã®çš®é¡ã®äœæãŸãã¯åé€ãžã®ãŠãŒã¶ãŒã®åå ã衚ããŸãã
ComputeVtables()ã¡ãœãããšCreateObject() ã¡ãœããã¯ããããããããŒãžã ãªããžã§ã¯ã ã©ãããŒãšãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒãäœæããŸãã
ReleaseObjects() ã¡ãœããã¯ãåºã«ãªããã€ãã£ã ãªããžã§ã¯ãããæå®ãããã©ãããŒã®ã³ã¬ã¯ã·ã§ã³ã "è§£æŸ" ããããã«èŠæ±ããããã«ã©ã³ã¿ã€ã ã«ãã£ãŠäœ¿çšãããŸãã ã»ãšãã©ã®å ŽåãReleaseObjects() ã¡ãœããã®æ¬äœã¯ãNotImplementedExceptionã䜿çšããé«åºŠãªã·ããªãªã§ã®ã¿åŒã³åºããããããåã«ãã¹ããŒããã ãã§ååã§ãã
// See referenced sample for implementation.
class DemoComWrappers : ComWrappers
{
protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) =>
throw new NotImplementedException();
protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags) =>
throw new NotImplementedException();
protected override void ReleaseObjects(IEnumerable objects) =>
throw new NotImplementedException();
}
ComputeVtables() ã¡ãœãããå®è£
ããã«ã¯ããµããŒããããããŒãžãåãæ±ºå®ããŸãã ãã®ãã¥ãŒããªã¢ã«ã§ã¯ã以åã«å®çŸ©ãã 2 ã€ã®ã€ã³ã¿ãŒãã§ã€ã¹ (IDemoGetType ãš IDemoStoreType) ãšã2 ã€ã®ã€ã³ã¿ãŒãã§ã€ã¹ (DemoImpl) ãå®è£
ãããããŒãžãåããµããŒãããŸãã
class DemoImpl : IDemoGetType, IDemoStoreType
{
string? _string;
public string? GetString() => _string;
public void StoreString(int _, string? str) => _string = str;
}
CreateObject()ã¡ãœããã®å Žåã¯ããµããŒãããå
å®¹ãæ±ºå®ããå¿
èŠããããŸãã ãã ãããã®å Žåã¯ãCOM ã¯ã©ã¹ã§ã¯ãªããé¢å¿ã®ãã COM ã€ã³ã¿ãŒãã§ã€ã¹ã®ã¿ãèªèãããŸãã COM åŽãã䜿çšãããã€ã³ã¿ãŒãã§ã€ã¹ã¯ã.NET åŽããæåœ±ããã€ã³ã¿ãŒãã§ã€ã¹ (ã€ãŸãã IDemoGetType ãš IDemoStoreType) ãšåãã§ãã
ãã®ãã¥ãŒããªã¢ã«ã§ã¯ã ReleaseObjects() ãå®è£
ããŸããã
æé 2 â å®è£
ComputeVtables()
ãããŒãžã ãªããžã§ã¯ã ã©ãããŒããå§ããŸãããããããã®ã©ãããŒã®æ¹ãç°¡åã§ãã COM ç°å¢ã«æåœ±ããããã«ãã€ã³ã¿ãŒãã§ã€ã¹ããšã« ä»®æ³ã¡ãœãã ããŒãã« ( vtable) ãäœæããŸãã ãã®ãã¥ãŒããªã¢ã«ã§ã¯ãvtable ããã€ã³ã¿ãŒã®ã·ãŒã±ã³ã¹ãšããŠå®çŸ©ããŸããããã§ã¯ãåãã€ã³ã¿ãŒãã€ã³ã¿ãŒãã§ã€ã¹äžã®é¢æ°ã®å®è£
ã衚ããŸããé åºã¯ éåžžã« éèŠã§ãã COM ã§ã¯ããã¹ãŠã®ã€ã³ã¿ãŒãã§ã€ã¹ã IUnknownããç¶æ¿ãããŸãã
IUnknownåã«ã¯ãQueryInterface()ãAddRef()ãRelease()ã® 3 ã€ã®ã¡ãœãããå®çŸ©ãããŠããŸãã
IUnknownã¡ãœããã®åŸã«ãç¹å®ã®ã€ã³ã¿ãŒãã§ã€ã¹ ã¡ãœãããæäŸãããŸãã ããšãã°ã IDemoGetType ãš IDemoStoreTypeã«ã€ããŠèããŠã¿ãŸãããã æŠå¿µçã«ã¯ãåã® vtable ã¯æ¬¡ã®ããã«ãªããŸãã
IDemoGetType | IDemoStoreType
==================================
QueryInterface | QueryInterface
AddRef | AddRef
Release | Release
GetString | StoreString
DemoImplãèŠããšãæ¢ã«GetString()ãšStoreString()ã®å®è£
ããããŸãããIUnknown颿°ã¯ã©ãã§ããããã
IUnknown ã€ã³ã¹ã¿ã³ã¹ãå®è£
ããæ¹æ³ã¯ããã®ãã¥ãŒããªã¢ã«ã®ç¯å²å€ã§ãããComWrappersã§æåã§è¡ãããšãã§ããŸãã ãã ãããã®ãã¥ãŒããªã¢ã«ã§ã¯ãã©ã³ã¿ã€ã ããã®éšåãåŠçã§ããããã«ããŸãã
IUnknown ã¡ãœããã䜿çšããŠãComWrappers.GetIUnknownImpl()å®è£
ãååŸã§ããŸãã
ãã¹ãŠã®ã¡ãœãããå®è£
ããããã«æãããããããŸããããæ®å¿µãªãããCOM vtable ã§ã¯ IUnknown 颿°ã®ã¿ã䜿çšã§ããŸãã COM ã¯ã©ã³ã¿ã€ã ã®å€éšã«ãªãããã DemoImpl å®è£
ãžã®ãã€ãã£ã颿°ãã€ã³ã¿ãŒãäœæããå¿
èŠããããŸãã ããã¯ãC# 颿°ãã€ã³ã¿ãŒãš UnmanagedCallersOnlyAttributeã䜿çšããŠè¡ãããšãã§ããŸãã COM 颿°ã·ã°ããã£ãæš¡å£ãã static 颿°ãäœæããããšã§ãvtable ã«æ¿å
¥ãã颿°ãäœæã§ããŸãã æ¬¡ã«ã IDemoGetType.GetString() ã® COM ã·ã°ããã£ã®äŸã瀺ããŸããæåã®åŒæ°ãã€ã³ã¹ã¿ã³ã¹èªäœã§ããããšã COM ABI ããæãåºããŠãã ããã
[UnmanagedCallersOnly]
public static int GetString(IntPtr _this, IntPtr* str);
IDemoGetType.GetString()ã®ã©ãããŒå®è£
ã¯ãããŒã·ã£ãªã³ã° ããžãã¯ãšãã©ããããããããŒãžã ãªããžã§ã¯ããžã®ãã£ã¹ãããã§æ§æãããŠããå¿
èŠããããŸãã ãã£ã¹ãããã®ãã¹ãŠã®ç¶æ
ã¯ãæå®ããã _this åŒæ°å
ã«å«ãŸããŠããŸãã
_thisåŒæ°ã¯å®éã«ã¯ComInterfaceDispatch*åã«ãªããŸãã ãã®åã¯ãåŸã§èª¬æãã 1 ã€ã®ãã£ãŒã«ã ( Vtable) ãæã€äžäœã¬ãã«ã®æ§é äœã衚ããŸãã ãã®åãšãã®ã¬ã€ã¢ãŠãã®è©³çްã¯ãã©ã³ã¿ã€ã ã®å®è£
ã®è©³çްã§ãããäŸåããªãã§ãã ããã
ComInterfaceDispatch* ã€ã³ã¹ã¿ã³ã¹ãããããŒãžã ã€ã³ã¹ã¿ã³ã¹ãååŸããã«ã¯ã次ã®ã³ãŒãã䜿çšããŸãã
IDemoGetType inst = ComInterfaceDispatch.GetInstance<IDemoGetType>((ComInterfaceDispatch*)_this);
vtable ã«æ¿å
¥ã§ãã C# ã¡ãœãããäœæãããã®ã§ãvtable ãæ§ç¯ã§ããŸãã
RuntimeHelpers.AllocateTypeAssociatedMemory()ã¢ã»ã³ããªã§åäœããæ¹æ³ã§ã¡ã¢ãªãå²ãåœãŠãã®äœ¿çšã«æ³šæããŠãã ããã
GetIUnknownImpl(
out IntPtr fpQueryInterface,
out IntPtr fpAddRef,
out IntPtr fpRelease);
// Local variables with increment act as a guard against incorrect construction of
// the native vtable. It also enables a quick validation of final size.
int tableCount = 4;
int idx = 0;
var vtable = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(
typeof(DemoComWrappers),
IntPtr.Size * tableCount);
vtable[idx++] = fpQueryInterface;
vtable[idx++] = fpAddRef;
vtable[idx++] = fpRelease;
vtable[idx++] = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr*, int>)&ABI.IDemoGetTypeManagedWrapper.GetString;
Debug.Assert(tableCount == idx);
s_IDemoGetTypeVTable = (IntPtr)vtable;
vtable ã®å²ãåœãŠã¯ã ComputeVtables()ã®å®è£
ã®æåã®éšåã§ãã ãŸãããµããŒãããäºå®ã®åã«å¯ŸããŠå
æ¬ç㪠COM å®çŸ©ãäœæããå¿
èŠããããŸãã DemoImpl èãããã®äžéšã COM ãã䜿çšã§ããããã«ããå¿
èŠããããŸãã æ§ç¯ããã vtable ã䜿çšããŠãCOM ã®ãããŒãžã ãªããžã§ã¯ãã®å®å
šãªãã¥ãŒã衚ãäžé£ã® ComInterfaceEntry ã€ã³ã¹ã¿ã³ã¹ãäœæã§ããããã«ãªããŸããã
s_DemoImplDefinitionLen = 2;
int idx = 0;
var entries = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(
typeof(DemoComWrappers),
sizeof(ComInterfaceEntry) * s_DemoImplDefinitionLen);
entries[idx].IID = IDemoGetType.IID_IDemoGetType;
entries[idx++].Vtable = s_IDemoGetTypeVTable;
entries[idx].IID = IDemoStoreType.IID_IDemoStoreType;
entries[idx++].Vtable = s_IDemoStoreVTable;
Debug.Assert(s_DemoImplDefinitionLen == idx);
s_DemoImplDefinition = entries;
ãããŒãžã ãªããžã§ã¯ã ã©ãããŒã® vtable ãšãšã³ããªã®å²ãåœãŠã¯ãåã®ãã¹ãŠã®ã€ã³ã¹ã¿ã³ã¹ã§ããŒã¿ã䜿çšã§ãããããäºåã«è¡ãããšãã§ããŸãã ããã§ã®äœæ¥ã¯ã static ã³ã³ã¹ãã©ã¯ã¿ãŒãŸãã¯ã¢ãžã¥ãŒã«åæååã§å®è¡ã§ããŸããã ComputeVtables() ã¡ãœãããã§ããã ãç°¡åãã€è¿
éã«ãªãããã«ãäºåã«è¡ãå¿
èŠããããŸãã
protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags,
out int count)
{
if (obj is DemoImpl)
{
count = s_DemoImplDefinitionLen;
return s_DemoImplDefinition;
}
// Unknown type
count = 0;
return null;
}
ComputeVtables() ã¡ãœãããå®è£
ãããšãComWrappers ãµãã¯ã©ã¹ã¯ãDemoImplã®ã€ã³ã¹ã¿ã³ã¹ã®ãããŒãžã ãªããžã§ã¯ã ã©ãããŒãçæã§ããããã«ãªããŸãã
GetOrCreateComInterfaceForObject()ã®åŒã³åºãããè¿ããããããŒãžã ãªããžã§ã¯ã ã©ãããŒã¯IUnknown*åã§ããããšã«æ³šæããŠãã ããã ã©ãããŒã«æž¡ããããã€ãã£ã API ã«å¥ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå¿
èŠãªå Žåã¯ããã®ã€ã³ã¿ãŒãã§ã€ã¹ã® Marshal.QueryInterface() ãå®è¡ããå¿
èŠããããŸãã
var cw = new DemoComWrappers();
var demo = new DemoImpl();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
æé 3 â å®è£
CreateObject()
ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã®æ§ç¯ã«ã¯ããããŒãžã ãªããžã§ã¯ã ã©ãããŒãæ§ç¯ãããããå®è£
ãªãã·ã§ã³ãå€ããéåžžã«åŸ®åŠã§ãã 察åŠãã¹ãæåã®è³ªåã¯ã ComWrappers ãµãã¯ã©ã¹ã COM åããµããŒãããæ¹æ³ãå¶éããããšã§ãã å¯èœãªãã¹ãŠã® COM åããµããŒãããã«ã¯ã倧éã®ã³ãŒããèšè¿°ãããã Reflection.Emitã®å·§åŠãªäœ¿ç𿹿³ãæ¡çšããå¿
èŠããããŸãã ãã®ãã¥ãŒããªã¢ã«ã§ã¯ã IDemoGetType ãš IDemoStoreTypeã®äž¡æ¹ãå®è£
ãã COM ã€ã³ã¹ã¿ã³ã¹ã®ã¿ããµããŒãããŸãã æéã®ã»ããããããæå®ããã COM ã€ã³ã¹ã¿ã³ã¹ãäž¡æ¹ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããå¿
èŠãããããšãå¶éããŠãããããéçã«å®çŸ©ãããåäžã®ã©ãããŒãæäŸã§ããŸãããã ããåçãªã±ãŒã¹ã¯ COM ã§ååã«äžè¬çã§ãããäž¡æ¹ã®ãªãã·ã§ã³ã«ã€ããŠèª¬æããŸãã
éçãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒ
ãŸãéçå®è£ ãèŠãŠã¿ãŸãããã éçãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã«ã¯ã.NET ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£ ãããããŒãžãåã®åŒã³åºãã COM ã€ã³ã¹ã¿ã³ã¹ã«è»¢éã§ãããããŒãžãåã®å®çŸ©ãå«ãŸããŸãã éçã©ãããŒã®å€§ãŸããªæŠèŠã次ã«ç€ºããŸãã
// See referenced sample for implementation.
class DemoNativeStaticWrapper
: IDemoGetType
, IDemoStoreType
{
public string? GetString() =>
throw new NotImplementedException();
public void StoreString(int len, string? str) =>
throw new NotImplementedException();
}
ãã®ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãæ§ç¯ããã©ãããŒãšããŠæäŸããã«ã¯ãããã€ãã®ããªã·ãŒãå®çŸ©ããå¿
èŠããããŸãã ãã®åãã©ãããŒãšããŠäœ¿çšãããŠããå Žåãäž¡æ¹ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ãããŠãããããåºã«ãªã COM ã€ã³ã¹ã¿ã³ã¹ãäž¡æ¹ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããå¿
èŠããããšæãããŸãã ãã®ããªã·ãŒãæ¡çšããŠããããšãèãããšãCOM ã€ã³ã¹ã¿ã³ã¹ã§ Marshal.QueryInterface() ãåŒã³åºããŠããã確èªããå¿
èŠããããŸãã
int hr = Marshal.QueryInterface(ptr, ref IDemoGetType.IID_IDemoGetType, out IntPtr IDemoGetTypeInst);
if (hr != 0)
{
return null;
}
hr = Marshal.QueryInterface(ptr, ref IDemoStoreType.IID_IDemoStoreType, out IntPtr IDemoStoreTypeInst);
if (hr != 0)
{
Marshal.Release(IDemoGetTypeInst);
return null;
}
return new DemoNativeStaticWrapper()
{
IDemoGetTypeInst = IDemoGetTypeInst,
IDemoStoreTypeInst = IDemoStoreTypeInst
};
åçãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒ
åçã©ãããŒã¯ãéçã§ã¯ãªãå®è¡æã«åãç
§äŒããæ¹æ³ãæäŸãããããæè»æ§ãé«ããªããŸãã ãã®ãµããŒããæäŸããã«ã¯ã IDynamicInterfaceCastableã䜿çšããŸãã
DemoNativeDynamicWrapperããã®ã€ã³ã¿ãŒãã§ã€ã¹ã®ã¿ãå®è£
ããŠããããšã確èªããŸãã ã€ã³ã¿ãŒãã§ã€ã¹ãæäŸããæ©èœã¯ãå®è¡æã«ãµããŒããããåãæ±ºå®ããæ©äŒã§ãã ãã®ãã¥ãŒããªã¢ã«ã®ãœãŒã¹ã§ã¯ãäœææã«éçãã§ãã¯ãå®è¡ããŸãããããã¯åã«ã³ãŒãå
±æçšã§ãããã®ãã§ãã¯ã¯ã DemoNativeDynamicWrapper.IsInterfaceImplemented()ãžã®åŒã³åºããè¡ããããŸã§é
å»¶ãããå¯èœæ§ãããããã§ãã
// See referenced sample for implementation.
internal class DemoNativeDynamicWrapper
: IDynamicInterfaceCastable
{
public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType) =>
throw new NotImplementedException();
public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) =>
throw new NotImplementedException();
}
DemoNativeDynamicWrapperãåçã«ãµããŒãããã€ã³ã¿ãŒãã§ã€ã¹ã® 1 ã€ãèŠãŠã¿ãŸãããã æ¬¡ã®ã³ãŒãã¯ãIDemoStoreTypeæ©èœã䜿çšããã®å®è£
ãæäŸããŸãã
[DynamicInterfaceCastableImplementation]
unsafe interface IDemoStoreTypeNativeWrapper : IDemoStoreType
{
public static void StoreString(IntPtr inst, int len, string? str);
void IDemoStoreType.StoreString(int len, string? str)
{
var inst = ((DemoNativeDynamicWrapper)this).IDemoStoreTypeInst;
StoreString(inst, len, str);
}
}
ãã®äŸã§ã¯ã次㮠2 ã€ã®éèŠãªç¹ã«æ³šæããŠãã ããã
-
DynamicInterfaceCastableImplementationAttribute屿§ã ãã®å±æ§ã¯ãIDynamicInterfaceCastableã¡ãœããããè¿ããããã¹ãŠã®åã§å¿ èŠã§ãã IL ããªãã³ã°ã容æã«ããå©ç¹ã远å ãããŠããŸããã€ãŸããAOT ã·ããªãªã®ä¿¡é Œæ§ãé«ããªããŸãã -
DemoNativeDynamicWrapperãžã®ãã£ã¹ãã ããã¯ãIDynamicInterfaceCastableã®åçãªæ§è³ªã®äžéšã§ããIDynamicInterfaceCastable.GetInterfaceImplementation()ããè¿ãããåã¯ãIDynamicInterfaceCastableãå®è£ ããåã "ãã©ã³ã±ãã" ããããã«äœ¿çšãããŸãã ããã§ã®èŠç¹ã¯ãthisããDemoNativeDynamicWrapperãžã®ã±ãŒã¹ãèš±å¯ããŠãããããIDemoStoreTypeNativeWrapperãã€ã³ã¿ãŒããµããããŠããããã§ã¯ãããŸããã
COM ã€ã³ã¹ã¿ã³ã¹ãžã®åŒã³åºãã®è»¢é
䜿çšãããŠãããã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã«é¢ä¿ãªããCOM ã€ã³ã¹ã¿ã³ã¹ã§é¢æ°ãåŒã³åºãæ©èœãå¿
èŠã§ãã
IDemoStoreTypeNativeWrapper.StoreString()ã®å®è£
ã¯ãunmanaged C# 颿°ãã€ã³ã¿ãŒã䜿çšããäŸãšããŠæ©èœããŸãã
public static void StoreString(IntPtr inst, int len, string? str)
{
IntPtr strLocal = Marshal.StringToCoTaskMemUni(str);
int hr = ((delegate* unmanaged<IntPtr, int, IntPtr, int>)(*(*(void***)inst + 3 /* IDemoStoreType.StoreString slot */)))(inst, len, strLocal);
if (hr != 0)
{
Marshal.FreeCoTaskMem(strLocal);
Marshal.ThrowExceptionForHR(hr);
}
}
COM ã€ã³ã¹ã¿ã³ã¹ã®éåç
§ã調ã¹ãŠããã® vtable å®è£
ã«ã¢ã¯ã»ã¹ããŠã¿ãŸãããã COM ABI ã¯ããªããžã§ã¯ãã®æåã®ãã€ã³ã¿ãŒãåã® vtable ãžã®ãã€ã³ã¿ãŒã§ãããããããç®çã®ã¹ãããã«ã¢ã¯ã»ã¹ã§ããããšãå®çŸ©ããŸãã COM ãªããžã§ã¯ãã®ã¢ãã¬ã¹ã 0x10000ãããŠãããšããŸãã æåã®ãã€ã³ã¿ãŒ ãµã€ãºã®å€ã¯ vtable ã®ã¢ãã¬ã¹ã§ããå¿
èŠããããŸãããã®äŸã§ã¯ 0x20000ã vtable ã«ã¢ã¯ã»ã¹ãããã4 çªç®ã®ã¹ããã (0 ããå§ãŸãã€ã³ããã¯ã¹äœæã®ã€ã³ããã¯ã¹ 3) ãæ¢ããŠã StoreString() å®è£
ã«ã¢ã¯ã»ã¹ããŸãã
COM instance
0x10000 0x20000
VTable for IDemoStoreType
0x20000 <Address of QueryInterface>
0x20008 <Address of AddRef>
0x20010 <Address of Release>
0x20018 <Address of StoreString>
颿°ãã€ã³ã¿ãŒã䜿çšãããšããªããžã§ã¯ã ã€ã³ã¹ã¿ã³ã¹ãæåã®ãã©ã¡ãŒã¿ãŒãšããŠæž¡ãããšã«ãã£ãŠããã®ãªããžã§ã¯ãã®ã¡ã³ããŒé¢æ°ã«ãã£ã¹ãããã§ããŸãã ãã®ãã¿ãŒã³ã¯ããããŒãžã ãªããžã§ã¯ã ã©ãããŒå®è£ ã®é¢æ°å®çŸ©ã«åºã¥ããŠãäœ¿ãæ £ããŠããã¯ãã§ãã
CreateObject() ã¡ãœãããå®è£
ããããšãComWrappers ãµãã¯ã©ã¹ã¯ãIDemoGetTypeãšIDemoStoreTypeã®äž¡æ¹ãå®è£
ãã COM ã€ã³ã¹ã¿ã³ã¹ã®ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒãçæã§ããããã«ãªããŸãã
IntPtr iunk = ...; // Get a COM instance from native code.
object rcw = cw.GetOrCreateObjectForComInstance(iunk, CreateObjectFlags.UniqueInstance);
æé 4 â ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã®æå¹æéã®è©³çްãåŠçãã
ComputeVtables()ãšCreateObject()ã®å®è£
ã§ã¯ãã©ãããŒã®æå¹æéã®è©³çްã«ã€ããŠèª¬æããŸãããããã®ä»ã®èæ
®äºé
ããããŸãã ããã¯çãæé ã§ãããŸããŸãããã ComWrappers èšèšã®è€éãã倧å¹
ã«å¢ããããšãã§ããŸãã
AddRef()ã¡ãœãããšRelease()ã¡ãœããã®åŒã³åºãã«ãã£ãŠå¶åŸ¡ããããããŒãžã ãªããžã§ã¯ã ã©ãããŒãšã¯ç°ãªãããã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã®æå¹æé㯠GC ã«ãã£ãŠé決å®çã«åŠçãããŸãã ããã§ã®åé¡ã¯ãNative Object Wrapper ããCOM ã€ã³ã¹ã¿ã³ã¹ã衚ãRelease()ã§IntPtrãåŒã³åºãã¿ã€ãã³ã°ã¯ãã€ãã§ããã äžè¬çãªãã±ãã㯠2 ã€ãããŸãã
ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã®ãã¡ã€ãã©ã€ã¶ãŒã¯ãCOM ã€ã³ã¹ã¿ã³ã¹ã®
Release()ã¡ãœãããåŒã³åºã圹å²ãæ ããŸãã ããã¯ããã®ã¡ãœãããå®å šã«åŒã³åºãå¯äžã®æéã§ãã ãã®æç¹ã§ãGC ã«ãã£ãŠã.NET ã©ã³ã¿ã€ã ã«ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒãžã®ä»ã®åç §ããªãããšãæ£ãã倿ããŸããã COM ã¢ããŒãã¡ã³ããé©åã«ãµããŒãããŠããå Žåã¯ãè€éã«ãªãå¯èœæ§ããããŸãã詳现ã«ã€ããŠã¯ãã ãã®ä»ã®èæ ®äºé ãã»ã¯ã·ã§ã³ãåç §ããŠãã ãããNative Object Wrapper ã¯ã
IDisposableã§Release()ãå®è£ ããDispose()ãåŒã³åºããŸãã
泚
IDisposable ãã¿ãŒã³ã¯ãCreateObject()åŒã³åºãäžã«CreateObjectFlags.UniqueInstance ãã©ã°ãæž¡ãããå Žåã«ã®ã¿ãµããŒãããå¿
èŠããããŸãã ãã®èŠä»¶ã«åŸããªãå Žåã¯ãç Žæ£ããããã€ãã£ã ãªããžã§ã¯ã ã©ãããŒãç Žæ£åŸã«åå©çšã§ããŸãã
ComWrappers ãµãã¯ã©ã¹ã®äœ¿çš
ããã§ããã¹ãã§ãã ComWrappers ãµãã¯ã©ã¹ãäœæãããŸããã
IDemoGetTypeãšIDemoStoreTypeãå®è£
ãã COM ã€ã³ã¹ã¿ã³ã¹ãè¿ããã€ãã£ã ã©ã€ãã©ãªãäœæããªãããã«ããã«ã¯ããããŒãžã ãªããžã§ã¯ã ã©ãããŒã䜿çšã㊠COM ã€ã³ã¹ã¿ã³ã¹ãšããŠæ±ããŸããããã¯ãCOM ãæž¡ãããã«å¯èœã§ããå¿
èŠããããŸãã
ãŸãããããŒãžã ãªããžã§ã¯ã ã©ãããŒãäœæããŸãããã
DemoImpl ã€ã³ã¹ã¿ã³ã¹ãã€ã³ã¹ã¿ã³ã¹åããçŸåšã®æååç¶æ
ã衚瀺ããŸãã
var demo = new DemoImpl();
string? value = demo.GetString();
Console.WriteLine($"Initial string: {value ?? "<null>"}");
ããã§ã DemoComWrappers ã®ã€ã³ã¹ã¿ã³ã¹ãšãããŒãžã ãªããžã§ã¯ã ã©ãããŒãäœæããCOM ç°å¢ã«æž¡ãããšãã§ããŸãã
var cw = new DemoComWrappers();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
ãããŒãžã ãªããžã§ã¯ã ã©ãããŒã COM ç°å¢ã«æž¡ã代ããã«ããã® COM ã€ã³ã¹ã¿ã³ã¹ãåãåã£ããµããããŠã代ããã«ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒãäœæããŸãã
var rcw = cw.GetOrCreateObjectForComInstance(ccw, CreateObjectFlags.UniqueInstance);
Native Object Wrapper ã䜿çšãããšãç®çã®ã€ã³ã¿ãŒãã§ã€ã¹ã®ããããã«ãã£ã¹ãããéåžžã®ãããŒãžã ãªããžã§ã¯ããšããŠäœ¿çšã§ããŸãã
DemoImpl ã€ã³ã¹ã¿ã³ã¹ã調ã¹ã管çã€ã³ã¹ã¿ã³ã¹ãã©ããããŠãããããŒãžã ãªããžã§ã¯ã ã©ãããŒãããã«ã©ããããŠãããã€ãã£ã ãªããžã§ã¯ã ã©ãããŒãžã®æäœã®åœ±é¿ã確èªã§ããŸãã
var getter = (IDemoGetType)rcw;
var store = (IDemoStoreType)rcw;
string msg = "hello world!";
store.StoreString(msg.Length, msg);
Console.WriteLine($"Setting string through wrapper: {msg}");
value = demo.GetString();
Console.WriteLine($"Get string through managed object: {value}");
msg = msg.ToUpper();
demo.StoreString(msg.Length, msg.ToUpper());
Console.WriteLine($"Setting string through managed object: {msg}");
value = getter.GetString();
Console.WriteLine($"Get string through wrapper: {value}");
ComWrapper ãµãã¯ã©ã¹ã¯CreateObjectFlags.UniqueInstanceããµããŒãããããã«èšèšãããŠãããããGC ãçºçããã®ãåŸ
ããã«ããã€ãã£ã ãªããžã§ã¯ã ã©ãããŒãããã«ã¯ãªãŒã³ã¢ããã§ããŸãã
(rcw as IDisposable)?.Dispose();
COM ã¢ã¯ãã£ããŒã·ã§ã³ ComWrappers
COM ãªããžã§ã¯ãã®äœæã¯ãéåžžããã®ããã¥ã¡ã³ãã®ç¯å²å€ã®è€éãªã·ããªãªã§ãã COM ã©ã€ã»ã³ã¹èªèšŒã䜿çšããŠå®è¡ãããŸãã åŸãæŠå¿µãã¿ãŒã³ãæäŸããããã«ãCOM ã®ã¢ã¯ãã£ãåã«äœ¿çšããã CoCreateInstance() API ã玹ä»ãã ComWrappersã§äœ¿çšããæ¹æ³ã瀺ããŸãã
ã¢ããªã±ãŒã·ã§ã³ã«æ¬¡ã® C# ã³ãŒãããããšããŸãã æ¬¡ã®äŸã§ã¯ã CoCreateInstance() ã䜿çšã㊠COM ã¯ã©ã¹ãšçµã¿èŸŒã¿ã® COM çžäºéçšã·ã¹ãã ãã¢ã¯ãã£ãåããCOM ã€ã³ã¹ã¿ã³ã¹ãé©åãªã€ã³ã¿ãŒãã§ã€ã¹ã«ããŒã·ã£ãªã³ã°ããŸãã
typeof(I).GUIDã®äœ¿çšã¯ã¢ãµãŒãã«éå®ãããŠãããã³ãŒãã AOT ã«åªããå Žåã«åœ±é¿ãäžããå¯èœæ§ããããªãã¬ã¯ã·ã§ã³ã䜿çšããå Žåã«æ³šæããŠãã ããã
public static I ActivateClass<I>(Guid clsid, Guid iid)
{
Debug.Assert(iid == typeof(I).GUID);
int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out object obj);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
return (I)obj;
}
[DllImport("Ole32")]
private static extern int CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
int dwClsContext,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppObj);
ComWrappersã䜿çšããããã«äžèšã倿ããã«ã¯ãMarshalAs(UnmanagedType.Interface) P/Invoke ããCoCreateInstance()ãåé€ããããŒã·ã£ãªã³ã°ãæåã§å®è¡ããå¿
èŠããããŸãã
static ComWrappers s_ComWrappers = ...;
public static I ActivateClass<I>(Guid clsid, Guid iid)
{
Debug.Assert(iid == typeof(I).GUID);
int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out IntPtr obj);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
return (I)s_ComWrappers.GetOrCreateObjectForComInstance(obj, CreateObjectFlags.None);
}
[DllImport("Ole32")]
private static extern int CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
int dwClsContext,
ref Guid riid,
out IntPtr ppObj);
ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã®ã¯ã©ã¹ ã³ã³ã¹ãã©ã¯ã¿ãŒã«ã¢ã¯ãã£ãåããžãã¯ãå«ããããšã§ã ActivateClass<I> ãªã©ã®ãã¡ã¯ã㪠ã¹ã¿ã€ã«ã®é¢æ°ãæœè±¡åããããšãã§ããŸãã ã³ã³ã¹ãã©ã¯ã¿ãŒã¯ã ComWrappers.GetOrRegisterObjectForComInstance() API ã䜿çšããŠãæ°ããæ§ç¯ããããããŒãžã ãªããžã§ã¯ããã¢ã¯ãã£ãåããã COM ã€ã³ã¹ã¿ã³ã¹ã«é¢é£ä»ããããšãã§ããŸãã
ãã®ä»ã®èæ ®äºé
ãã€ãã£ã AOT â äºåã³ã³ãã€ã« (AOT) ã䜿çšãããšãJIT ã³ã³ãã€ã«ãåé¿ããããããã¹ã¿ãŒãã¢ãã ã³ã¹ããåäžããŸãã äžéšã®ãã©ãããã©ãŒã ã§ã¯ãJIT ã³ã³ãã€ã«ã®å¿
èŠæ§ãåãé€ãããšãå¿
èŠã«ãªãããšããããããŸãã AOT ã®ãµããŒã㯠ComWrappers API ã®ç®æšã§ããããã©ãããŒã®å®è£
ã§ã¯ããªãã¬ã¯ã·ã§ã³ã®äœ¿çšãªã©ãAOT ãäžæ³šæã§äžæãããã±ãŒã¹ã誀ã£ãŠå°å
¥ããªãããã«æ³šæããå¿
èŠããããŸãã
Type.GUID ããããã£ã¯ããªãã¬ã¯ã·ã§ã³ã䜿çšããäžäŸã§ãããæããã§ã¯ãããŸããã
Type.GUIDããããã£ã¯ããªãã¬ã¯ã·ã§ã³ã䜿çšããŠåã®å±æ§ãæ€æ»ããå¿
èŠã«å¿ããŠåã®ååãå«ãŸããã¢ã»ã³ããªã調ã¹ããã®å€ãçæããŸãã
ãœãŒã¹çæ â COM çžäºéçšãš ComWrappers å®è£
ã«å¿
èŠãªã»ãšãã©ã®ã³ãŒãã¯ãäžéšã®ããŒã«ã«ãã£ãŠèªåçæãããå¯èœæ§ããããŸãã é©å㪠COM å®çŸ© (ã¿ã€ã ã©ã€ãã©ãª (TLB)ãIDLããã©ã€ããªçžäºéçšæ©èœã¢ã»ã³ã㪠(PIA) ãªã©) ã䜿çšããŠãäž¡æ¹ã®ã¿ã€ãã®ã©ãããŒã®ãœãŒã¹ãçæã§ããŸãã
ã°ããŒãã«ç»é² â ComWrappers API 㯠COM çžäºéçšã®æ°ãããã§ãŒãºãšããŠèšèšãããŠãããããæ¢åã®ã·ã¹ãã ãšéšåçã«çµ±åããæ¹æ³ãå¿
èŠã§ããã
ComWrappers API ã«ã¯ãããŸããŸãªãµããŒãã®ããã®ã°ããŒãã« ã€ã³ã¹ã¿ã³ã¹ã®ç»é²ãèš±å¯ãããã°ããŒãã«ã«åœ±é¿ãäžããéçã¡ãœããããããŸãã ãããã®ã¡ãœããã¯ãçµã¿èŸŒã¿ã® COM çžäºéçšã·ã¹ãã ãšåãããã¹ãŠã®ã±ãŒã¹ã§å
æ¬ç㪠COM çžäºéçšæ©èœã®ãµããŒããæäŸããããšãæåŸ
ããŠãã ComWrappers ã€ã³ã¹ã¿ã³ã¹åãã«èšèšãããŠããŸãã
ãªãã¡ã¬ã³ã¹ ãã©ãã«ãŒã®ãµããŒã â ãã®ãµããŒãã¯ãWinRT ã·ããªãªã§äž»ã«äœ¿çšãããé«åºŠãªã·ããªãªã衚ããŸãã ã»ãšãã©ã® ComWrapper å®è£
ã§ã¯ã CreateComInterfaceFlags.TrackerSupport ãŸã㯠CreateObjectFlags.TrackerObject ãã©ã°ã NotSupportedExceptionãã¹ããŒããå¿
èŠããããŸãã Windows ãŸã㯠Windows 以å€ã®ãã©ãããã©ãŒã ã§ããã®ãµããŒããæå¹ã«ããå Žåã¯ã C#/WinRT ããŒã« ãã§ãŒã³ãåç
§ããããšã匷ããå§ãããŸãã
åã«èª¬æããæå¹æéãåã·ã¹ãã ãããã³æ©èœæ©èœãšã¯å¥ã«ãcom ã«æºæ ãã ComWrappers ã®å®è£
ã«ã¯ã远å ã®èæ
®äºé
ãå¿
èŠã§ãã Windows ãã©ãããã©ãŒã ã§äœ¿çšãããå®è£
ã«ã¯ã次ã®èæ
®äºé
ããããŸãã
ã¢ããŒãã¡ã³ã â ã¹ã¬ããåŠçã®ããã® COM ã®çµç¹æ§é 㯠"ã¢ããŒãã¡ã³ã" ãšåŒã°ããå®å®ããéçšã®ããã«åŸãå¿ èŠããã峿 ŒãªèŠåããããŸãã ãã®ãã¥ãŒããªã¢ã«ã§ã¯ãã¢ããŒãã¡ã³ã察å¿ã®ãã€ãã£ã ãªããžã§ã¯ã ã©ãããŒã¯å®è£ ããŸããããéçšç°å¢ã«å¯Ÿå¿ããå®è£ ã¯ã¢ããŒãã¡ã³ã察å¿ã«ããå¿ èŠããããŸãã ãããå®çŸããã«ã¯ãWindows 8 ã§å°å ¥ããã
RoGetAgileReferenceAPI ã䜿çšããããšããå§ãããŸãã Windows 8 ããåã®ããŒãžã§ã³ã§ã¯ã ã°ããŒãã« ã€ã³ã¿ãŒãã§ã€ã¹ ããŒãã«ãæ€èšããŠãã ãããã»ãã¥ãªã㣠â COM ã¯ãã¯ã©ã¹ã®ã¢ã¯ãã£ãåãšãããã·ã¢ã¯ã»ã¹èš±å¯ã®ããã®è±å¯ãªã»ãã¥ãªã㣠ã¢ãã«ãæäŸããŸãã