【发布时间】:2022-01-14 19:44:41
【问题描述】:
.NET 6 无法调用自己的 IDispatch 对象(如果已编组)。
复制:
if (Array.IndexOf(Environment.GetCommandLineArgs(), "/s")>=0) //Server
{
Thread t = new Thread(new ThreadStart(() =>
{
Hello h = new Hello();
new RunningObjectTable().Register("HelloTest", h);
Thread.Sleep(1000 * 3600);
}));
t.SetApartmentState(ApartmentState.MTA);
t.Start();
Thread.Sleep(1000 * 3600);
}
else //Client
{
object o = new RunningObjectTable().Get("HelloTest");
IHello h = o as IHello;
int f = h.Foo();
Console.WriteLine(h);
}
[ComImport]
[Guid("00020400-0000-0000-C000-000000000046")] //IID_IDispatch
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[TypeLibType(TypeLibTypeFlags.FDispatchable)]
public interface IHello
{
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
[DispId(1)]
public int Foo();
}
public class Hello: IHello
{
public int Foo()
{
Debug.WriteLine("Hello from server");
return 19;
}
}
使用 /s 运行程序,即对象服务器。然后运行另一个副本成为客户端。
客户端中的方法调用行因“未找到成员”而崩溃 - HRESULT 0x80020003,DISP_E_MEMBERNOTFOUND。通常这意味着一个虚假的 DISPID,但差异可能来自哪里?
该代码中的一个小违规行为是该接口使用 IDispatch 的 IID 进行修饰。按照 COM 的规定,使用自定义 IID,它甚至不会解组为分发接口(@987654323@ 行返回 null);在内部,使用所述自定义 IID 跨进程调用 QueryInterface,dispinterface 未在 HKCR\Interfaces 下注册,因此进程间 COM 机器不知道如何编组它。至少这是我的理论。
如果服务器是本机 (C++) 服务器,则类似的逻辑可以正常工作。如果针对 .NET 框架 4.72 重新编译相同的 C# 部分,它甚至没有达到那么远,o as IHello; 行返回 null。
RunningObjectTable 是一个围绕 ROT 的辅助类。为了完整起见,这里:
internal class RunningObjectTable
{
#region API
[DllImport("ole32.dll")]
private static extern int CreateItemMoniker([MarshalAs(UnmanagedType.LPWStr)] string
lpszDelim, [MarshalAs(UnmanagedType.LPWStr)] string lpszItem,
out IMoniker ppmk);
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
#endregion
private IRunningObjectTable m_rot;
public RunningObjectTable()
{
GetRunningObjectTable(0, out m_rot);
}
private IMoniker CreateItemMoniker(string s)
{
IMoniker mon;
CreateItemMoniker("", s, out mon);
return mon;
}
public int Register(string ItemName, object o)
{
return m_rot.Register(0, o, CreateItemMoniker(ItemName));
}
public void Unregister(int ROTCookie)
{
m_rot.Revoke(ROTCookie);
}
public object Get(string ItemName)
{
object o;
m_rot.GetObject(CreateItemMoniker(ItemName), out o);
return o;
}
}
能否请有 .NET Core 内部访问权限的人告诉我发生了什么?
【问题讨论】:
-
我在 .NET 6 和 .NET Framework 之间有完全相同的行为,即 DISP_E_MEMBERNOTFOUND。如果您不注册类型库,我看不出这将如何工作。如果你用这个替换你的调用代码:
dynamic o = new RunningObjectTable().Get("HelloTest");Console.WriteLine(o.Foo);那么它在 .NET 6 和 .NET Framework 中都可以工作。 -
原来可以:)
-
你的回答与你的问题无关
-
我的问题是跨进程方法调用期间 DISP_E_MEMBERNOTFOUND 的异常。解决了。另外,我已经弄清楚了错误的 DISPID 是从哪里来的。
标签: c# .net-core com-interop .net-6.0 idispatch