Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在COM方法中设置SAFARRAY输出参数_C#_Com_Interop - Fatal编程技术网

C# 在COM方法中设置SAFARRAY输出参数

C# 在COM方法中设置SAFARRAY输出参数,c#,com,interop,C#,Com,Interop,我正在尝试为现有的(黑盒)客户端研究并重新实现(黑盒)COM服务器。通过导入类型库并创建实现服务器接口的.NET类,我成功地实现了一个虚拟实现 我有一个有问题的签名,当客户呼叫时,该签名似乎不起作用: 这是IDL的定义: [id(0x00000007), helpstring("method GetFoo")] HRESULT GetFoo(VARIANT* vector); [DispId(7)] void GetFoo(ref object vector); 导入的.NET定义: [id

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

我有一个有问题的签名,当客户呼叫时,该签名似乎不起作用:

这是IDL的定义:

[id(0x00000007), helpstring("method GetFoo")]
HRESULT GetFoo(VARIANT* vector);
[DispId(7)]
void GetFoo(ref object vector);
导入的.NET定义:

[id(0x00000007), helpstring("method GetFoo")]
HRESULT GetFoo(VARIANT* vector);
[DispId(7)]
void GetFoo(ref object vector);
注意:
vector
参数是一个输出参数

客户端可以成功调用实现此接口的服务器。在这个调用过程中,我可以看到
vector
参数包含一个16字节的数组。但是,我在数组中设置的数据似乎没有正确发送到客户端。我已经对原始服务器进行了反向工程,并找到了参数所需的确切变体标志。使用此信息,我创建了一个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
参数中设置的数据

您必须传递一个变体才能让服务器满意。这是.NET程序中的对象。所以它必须是这样的:

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

IDL编写得很糟糕,该参数应使用[out,retval]进行赋值。通常是你会有更多问题的迹象。数组的另一个问题是,它们的第一个元素可能位于索引1而不是0。您可以在调试器中看到,arg变量看起来像
System.Byte[*]
。在这种情况下,您必须强制转换到数组,而不是字节[]。

您是否尝试将其作为非托管类型数组进行marsall?@mikeofst我该怎么做?我无法更改.NET定义,它来自生成的互操作程序集。您可以尝试:
IntPtr dataPtr=Marshal.alloctaskmem(16)
那么dataPtr将是数组的句柄,如果需要,可以将其封送到托管数组中。请注意,“16”与您在问题中使用的数字相同。@Mikeofst谢谢。很遗憾,仍然引发相同的异常。您是否将客户机数据从
向量
复制到dataPtr的未管理内存中?我认为Marshal.Copy()是实现这一点的API。感谢您的回答,不幸的是,它似乎也不能以这种方式工作(事实上,这是我尝试的第一个变体之一)。嗯,这是一个常见的问题,所以用户发布了他们尝试的最后一个也是最严重错误的代码版本。更换服务器有什么用,为什么你还在打电话给旧服务器?您是否有成功进行此调用的代码?然后告诉我们。@HansPassant似乎API需要一个内存块的地址,它可以将
Foo
复制到该内存块中作为其输出。如果是这样,那么内存必须有足够的大小来容纳
Foo
,因此了解指针后面应该是什么
VT
变量类型是很有用的。异常意味着某些“值”不正确,并且将
vector
的内容作为输出意味着内存的内容没有故障,但地址的值可能有问题。如果地址不正确,错误消息会说“值不在预期范围内”吗?@HansPassant,重新实现服务器的原因属于我的保密协议,所以我不能透露。调用旧服务器的原因是为了能够研究它的行为,以便重新实现的版本可以模拟其中的一些行为。正如在文章中所解释的,真实的客户端可以成功地调用我的虚拟服务器,但是我的测试客户端不能调用真实的服务器。一个线索可能是,在虚拟客户机中获得调用时,参数不是null,而是一个16字节的数组。成功进行(或者更确切地说是接收)调用的代码只是使用tlbimp ed接口。@MikeofSST,HansPassant,我已经更改了问题(仍然在相同的上下文中)并添加了大量新信息。如果你有时间,请看一下。