0

我试图复制我在 C# 中的 JScript 中观察到的行为。我正在使用 IDispatch 枚举成员并在后期绑定对象上调用它们。我是一个完整的 C++ 菜鸟,并且对 COM 的了解足够多,以至于非常危险。这是我的问题:

  • DISPID_VALUE 是否始终为零 (0)?(似乎是的)
  • 调用 COM 对象时,我应该何时调用 DISPID_VALUE 成员?(例如,当接口本身被索引或调用时......?)
  • 何时调用 .Item 是否有任何规则/提示?
  • 为什么在下面的示例中,BindingFlags.SetProperty 对 .Cells(x, x) 起作用(而不是 BindingFlags.InvokeMethod)?它是在调用 _Default(x, x) 吗?项目(x,x)?它怎么知道这样做?我怎样才能知道它在调用哪个?
  • 是否有一些关于调用后期绑定的 IDispatch COM 对象的好文档?

在下面的示例中,Excel 电子表格的单元格 1,1 将值设置为某些文本并“加粗”。

考虑以下 WSH JScript:


var objExcel = new ActiveXObject("Excel.Application");
objExcel.Workbooks.Add();
objExcel.Visible = true;
objExcel.Cells(1,1).Value = "some test value";
objExcel.Cells(1,1).Font.Bold = true;

此 C# 代码创建相同的结果(是的,抱歉,它非常冗长):


Type axType = Type.GetTypeFromProgID("Excel.Application");
object objExcel = Activator.CreateInstance(axType);
object workbooks = objExcel.GetType().InvokeMember("Workbooks", System.Reflection.BindingFlags.GetProperty, null, objExcel, null);
objExcel.GetType().InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty, null, objExcel, new object[] { true });
workbooks.GetType().InvokeMember("Add", System.Reflection.BindingFlags.InvokeMethod, null, workbooks, new object[] { true });
object cell = objExcel.GetType().InvokeMember("Cells", System.Reflection.BindingFlags.GetProperty, null, objExcel, new object[] { 1, 1 });
cell.GetType().InvokeMember("Value", System.Reflection.BindingFlags.SetProperty, null, cell, new object[] { "some test value" });
object font = cell.GetType().InvokeMember("Font", System.Reflection.BindingFlags.GetProperty, null, cell, null);
font.GetType().InvokeMember("Bold", System.Reflection.BindingFlags.SetProperty, null, font, new object[] { true });

当我有时间时,我计划尝试并了解更多有关此内容的一种方法是让 JScript 调用我将使用日志记录/调试创建的 C# COM 类。

4

1 回答 1

4

是的,在 oaidl.idl 中,DISPID_VALUE 是 #defined 为 0。为属性分配 0 使其成为默认属性。许多语言允许省略默认属性的名称。等效于 C# 索引器。

这只是一个约定,COM IDispatch 派生接口当然不需要公开默认属性。该属性也不必命名为“值”。这样做很常见。[ComVisible] C# 接口的索引器将得到 dispid 0,但名称为“Item”。周围有很多其他变化,你不能假设任何事情。

BindingFlags.SetProperty 有效,因为 Cells 成员是属性,而不是方法。它看起来有点像一个方法,只是因为它是一个索引属性。这在 C# 中几乎不受支持(仅用于索引器),但在 COM 或 VB.NET 中不受限制。您的示例代码中使用了 COM IDispatch 接口,它允许按名称查找成员。IDispatch::GetIDsOfNames() 完成了这项工作,将字符串映射到数字(dispid),然后可以使用 IDispatch::Invoke() 调用属性或方法。请注意,这仅适用于一种方式,即名称到编号。IDispatch 不支持等效的反射。

通过在 VB.NET 中编写或使用 C# 版本 4动态关键字来摆脱丑陋的 C# 代码。

于 2012-06-18T18:04:18.943 回答