【发布时间】:2021-07-10 12:29:51
【问题描述】:
我正在尝试开发一个客户端只能通过 IDispatch 访问的 C# COM 服务器。我的 COM 对象的一些方法返回一个与其他 COM 对象的接口数组。
当我尝试使用早期绑定(例如通过this answer 中提供的代码)执行此操作时,它可以正常工作。但是,一旦我尝试通过 IDispatch 装饰我的类型以使用后期绑定,请求就会失败,并显示 0x80020005:类型不匹配。其他非接口类型可以通过 IDispatch 毫无问题地返回,它只是未能正确传输的 COM 接口。
给定以下 C++ 客户端
CoInitialize(NULL);
IMyRootClassPtr ptr(__uuidof(MyRootClass));
try
{
auto entities = ptr->GetEntities();
}
catch (_com_error &err)
{
wprintf(L"The server throws the error: %s\n", err.ErrorMessage());
wprintf(L"Description: %s\n", (PCWSTR)err.Description());
}
以下代码适用于早期绑定
C#:
[ComVisible(true)]
public interface IMyRootClass
{
IEmailEntity[] GetEntities();
}
[ComVisible(true)]
public class MyRootClass : IMyRootClass // some class to start with
{
public IEmailEntity[] GetEntities()
{
List<IEmailEntity> list = new List<IEmailEntity>();
for (int i = 0; i < 10; i++)
{
EmailEntity entity = new EmailEntity();
entity.Body = "hello world " + i;
list.Add(entity);
}
return list.ToArray();
}
}
[ComVisible(true)]
public interface IEmailEntity
{
string Body { get; set; }
}
public class EmailEntity : IEmailEntity
{
public string Body { get; set; }
}
通过进行以下更改
- 将
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]添加到IMyRootClass - 将
[ClassInterface(ClassInterfaceType.None)]添加到MyRootClass
我认为这现在应该通过 IDispatch 工作,但是我得到了上面提到的类型不匹配错误。我花了一个下午调试错误的确切来源。对 .NET Framework 的 Dispatch 调用似乎成功,返回类型为 VT_ARRAY | VT_DISPATCH (2009) 的 VARIANT,这是预期的,但最终结果的验证似乎落在了
main
_com_dispatch_method
_com_invoke_helper
VariantChangeType
VariantChangeTypeEx
我已经查看了它在 oleaut32.dll 中掉落位置的反汇编,现阶段我只是得出结论,这一定是 Windows 中的错误。有没有人可能有其他建议?
【问题讨论】:
-
你想要后期绑定,现在你有了:-) #import 将生成现在期望 GetEntities 返回 IDispatch(对象)数组的代码。将
IEmailEntity[] GetEntities()更改为object[] GetEntities()并在最后一行将 'list.ToArray()' 更改为 'list.Cast -
你解决了!我之前尝试返回一个 object[] 而不是 IWhatever[] 并且没有用,但是我这样做是协变的(即没有显式转换)。当我将返回类型转换为
object“正确”时,这似乎可以解决问题。我可以在_com_invoke_helper的反汇编中看到,它仅根据对返回值的某种检查调用VariantChangeType,所以我想那里一定有一些元数据说“这不是真正的object大批”。如果您发布需要以非协变量方式返回object[],我可以接受它作为答案