【问题标题】:Setting a SAFARRAY output parameter in a COM method在 COM 方法中设置 SAFARRAY 输出参数
【发布时间】:2014-09-22 21:04:24
【问题描述】:

我正在尝试为现有(黑盒)客户端研究和重新实现(黑盒)COM 服务器。通过导入类型库并创建实现服务器接口的 .NET 类,我成功地 registered 一个虚拟实现。

我有一个有问题的签名,在客户端调用时似乎不起作用:

这是 IDL 定义:

[id(0x00000007), helpstring("method GetFoo")]
HRESULT GetFoo(VARIANT* vector);

导入的 .NET 定义:

[DispId(7)]
void GetFoo(ref object vector);

注意:vector 参数是一个输出参数。

客户端可以成功调用实现该接口的我的服务器。在此调用期间,我可以看到 vector 参数包含一个 16 字节的数组。但是,我在数组中设置的数据似乎没有正确发送到客户端。我已经对原始服务器进行了逆向工程,并找到了它期望参数的确切 VARIANT 标志。使用这些信息,我创建了一个 C++ 客户端,它在调用原始服务器时获取输出参数中的数据:

const int TABLE_LENGTH = 16;
SAFEARRAY* table = SafeArrayCreateVector(VT_I1, 0, TABLE_LENGTH);

VARIANT val; VariantInit(&val);
val.vt = VT_BYREF | VT_ARRAY | VT_I1 | VT_NULL;
val.pparray = &table;
realServer->GetFoo(&val); 
// now table contains valid data

如果我使用这个客户端调用我的服务器,我会得到一个SafeArrayTypeMismatchException方法中的断点将被命中。

我的问题是:如何实现 .NET 方法,以便上述客户端可以获取我在 ref 参数中设置的数据?

【问题讨论】:

  • 您是否尝试将其编组为非托管类型数组?
  • @MikeofSST 我该怎么做?我无法更改 .NET 定义,它来自生成的互操作程序集。
  • 您可以尝试:IntPtr dataPtr = Marshal.AllocCoTaskMem(16); 然后 dataPtr 将是数组的句柄,如果需要,您可以将其编组到托管数组中。请注意,“16”与您在问题中使用的幻数相同。
  • @MikeofSST 谢谢。不幸的是仍然抛出同样的异常。
  • 您是否将客户端数据从vector 复制到dataPtr 的未管理内存中?我认为 Marshal.Copy() 是执行此操作的 API。

标签: c# com interop


【解决方案1】:

您必须传递一个 VARIANT 才能让服务器满意。那是 .NET 程序中的 object。所以它必须看起来像这样:

object arg = null;
realServer.GetFoo(ref arg);
byte[] data = (byte[])arg;

IDL 编写不佳,该参数应归因于 [out, retval]。通常表明你会遇到更多问题。数组的另一个问题是它们的第一个元素可能位于索引 1 而不是 0。您可以在调试器中看到,arg 变量看起来像 System.Byte[*]。在这种情况下,您必须强制转换为 Array 而不是 byte[]。

【讨论】:

  • 感谢您的回答,不幸的是,它似乎也不是这样工作的(事实上,这是我尝试的第一个变体之一)。
  • Hmya,常见问题,所以用户发布了他们尝试过的代码的最后一个也是最严重的错误版本。更换服务器有什么用,为什么还要调用旧服务器?您是否有 任何 代码可以成功进行此调用?然后给我们看。
  • @HansPassant 似乎 API 需要一个内存块的地址,它可以将 Foo 复制到其中作为其输出。如果是这样,那么内存必须有足够的大小来保存Foo,因此了解指针后面应该是什么VT_ 变体类型会很有用。异常意味着某些“值”不正确,并且将vector 的内容作为输出意味着内存的内容没有问题,但地址的值可能有问题。如果地址不正确,错误消息会显示“值不在预期范围内”吗?
  • @HansPassant,重新实现服务器的原因属于我的保密协议,所以我不能透露。调用旧服务器的原因是能够研究其行为,以便重新实现的版本可以模仿其中的一些。如帖子中所述,真实客户端可以成功调用我的虚拟服务器,但我的测试客户端无法调用真实服务器。一个线索可能是这样一个事实,即在虚拟客户端中获得调用时,参数不是空的,而是一个 16 字节的数组。成功发出(或者更确切地说接收)调用的代码只是使用了 tlbimp-ed 接口。
  • @MikeofSST,HansPassant,我已经更改了问题(仍然在相同的上下文中)并添加了大量新信息。如果你有时间,请看一下。
猜你喜欢
  • 2011-02-08
  • 2014-08-22
  • 2014-03-05
  • 1970-01-01
  • 1970-01-01
  • 2011-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多