gpt4 book ai didi

C# 将 byte[] 编码到带有 "ref object"签名的 COM SAFEARRAY 参数

转载 作者:太空宇宙 更新时间:2023-11-03 16:30:37 25 4
gpt4 key购买 nike

我一直在谷歌上兜兜转转,各种讨论,各种建议,但似乎都没什么用。我有一个 ActiveX 组件,它将图像作为字节数组。当我进行 TLB 导入时,它带有以下签名:

int HandleImage([MarshalAs(UnmanagedType.Struct)] ref object Bitmap);

我如何将 byte[] 传递给它?

还有另一个函数可以返回具有类似签名的数据,它可以工作,因为我可以传入“null”。返回的类型是一个字节[1..size](非零有界字节[] ).但即使我尝试传入返回的内容,它仍然会出现类型不匹配异常。


更多详情:

我一直在编辑 IDispatch 接口(interface)签名中的方法(使用 ILSpy 从自动生成的互操作程序集中提取接口(interface))。我已经尝试了以下所有组合,它总是会出现类型不匹配异常:

  1. 添加和删除“ref”
  2. 将参数数据类型更改为“byte[]”或“Array”
  3. 编码为 [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]。在对 MarshalAs 进行了大量研究后,我开始确信 IDispatch 不使用这些属性。

还尝试按原样使用“ref object”接口(interface),并向其传递不同的类型:byte[]、Array.CreateInstance(typeof(byte)(我认为它们是相同的,但我发现有人建议这样做,所以它尝试一下也没什么坏处)。

下面是创建适当数组以传入的 Delphi 代码示例:

var
image: OLEVariant;
buf: Pointer;

image := VarArrayCreate([0, Stream.Size], VarByte);
Buf := VarArrayLock(image);
Stream.ReadBuffer(Buf^, Stream.Size);
VarArrayUnlock(image);

这是执行相同操作的 C 代码。我想如果我不能让它在 C# 中运行,我可以通过托管 C++ 调用它,尽管我宁愿将所有内容都放在一个项目中:

long HandleImage(unsigned char* Bitmap, int Length)
{
VARIANT vBitmap;
VariantInit (&vBitmap);
VariantClear(&vBitmap);

SAFEARRAYBOUND bounds[1];
bounds[0].cElements = Length;
bounds[0].lLbound = 1;

SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
SafeArrayLock(arr);
memcpy(arr->pvData, Bitmap, Length);
SafeArrayUnlock(arr);
vBitmap.parray = arr;
vBitmap.vt = VT_ARRAY | VT_UI1;

long result;
static BYTE parms[] = VTS_PVARIANT;
InvokeHelper(0x5e, DISPATCH_METHOD, VT_I4, (void*)&result, parms,
&vBitmap);

SafeArrayDestroy(arr);
VariantClear(&vBitmap);

return result;
}

最佳答案

我终于想出了如何在 100% C# 代码中做到这一点。显然,Microsoft 从未考虑过有人可能会使用具有此签名的方法来传递数据的想法,因为它正确地编码到另一个方向(它正确地作为字节 [] 返回)。

此外,ICustomMarshaler 不会在 IDispatch 调用时被调用,它永远不会在自定义编码(marshal)拆收器中遇到断点(获取它的实例的静态方法除外)。

Hans Passant 在这个问题中的回答让我走上了正确的道路:Calling a member of IDispatch COM interface from C#

那里的 IDispatch 副本不包含 IUnknown 上的“Invoke”方法,但可以使用 System.Runtime.InteropServices.ComTypes 中的适当类型将其添加到接口(interface):http://msdn.microsoft.com/en-us/library/windows/desktop/ms221479%28v=vs.85%29.aspx

这意味着您可以 100% 控制编码参数。 Microsoft 不公开 VARIANT 结构的实现,因此您必须定义自己的:http://limbioliong.wordpress.com/2011/09/19/defining-a-variant-structure-in-managed-code-part-2/

Invoke 的输入参数是一个变体数组,因此您必须将它们编码到一个非托管数组,并且有一个变体输出参数。

那么现在我们有了一个变体,它应该包含什么?这是自动编码失败的地方。它不是直接嵌入指向 SAFEARRAY 的指针,而是需要指向另一个变体的指针,并且该变体应指向 SAFEARRAY。

您可以通过 P/调用这些方法构建 SAFEARRAY:http://msdn.microsoft.com/en-us/library/windows/desktop/ms221145%28v=vs.85%29.aspx

所以主要变体应该是 VT_VARIANT | VT_BYREF,它应该指向另一个变体 VT_UI8 | VT_ARRAY,它应该指向通过 SafeArrayCreate() 生成的 SAFEARRAY。然后应该将该最外层的变体复制到一 block 内存中,并将其 IntPtr 发送到 Invoke 方法。

关于C# 将 byte[] 编码到带有 "ref object"签名的 COM SAFEARRAY 参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10905043/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com