函数的IDL声明(在C++中),该函数将从C#中获取C样式数组

本文关键字:函数 获取 数组 样式 IDL 声明 C++ | 更新日期: 2023-09-27 17:47:25

我正在使用一个现有的代码库,该代码库由一些用C++编写的带有C#前端的COM接口组成。需要添加一些新功能,所以我不得不修改COM部分。在一个特定的情况下,我需要将一个数组(从C#分配)传递给要填充的组件。

我想做的是能够从C#向方法传递一个int数组,类似于:

// desired C# signature
void GetFoo(int bufferSize, int[] buffer);
// desired usage
int[] blah = ...;
GetFoo(blah.Length, blah);

工作中的几把扳手:

  • 不能使用C++/CLI或托管C++(在这种情况下可以取消COM)
  • C#端不能使用/unsafe进行编译(允许使用Marshal)

COM接口只由C#部分使用(只会使用),所以我不太关心与其他COM使用者的互操作性。32位和64位之间的可移植性也不是一个问题(所有东西都是在32位机器上编译和运行的,所以代码生成器将指针转换为整数)。最终,它将被C++/CLI所取代,但这还有一段路要走


我的初次尝试

类似于:

HRESULT GetFoo([in] int bufferSize, [in, size_is(bufferSize)] int buffer[]);

输出TLB的定义是(似乎合理):

HRESULT _stdcall GetFoo([in] int bufferSize, [in] int* buffer);

由C#导入的(不太合理):

void GetFoo(int bufferSize, ref int buffer);

可以与一起使用

int[] b = ...;
fixed(int *bp = &b[0])
{
    GetFoo(b.Length, ref *bp);
}

除了我不能用/unsafe编译之外。


此刻

我正在使用:

HRESULT GetFoo([in] int bufferSize, [in] INT_PTR buffer);

哪个进口为:

void GetFoo(int bufferSize, int buffer);

我需要像一样使用它

int[] b = ...;
GCHandle bPin = GCHandle.Alloc(b, GCHandleType.Pinned);
try
{
    GetFoo(b.Length, (int)Marshal.UnsafeAddrOfPinnedArrayElement(b, 0));
}
finally
{
    bPin.Free();
}

哪个有效。。。,但我想找一个更干净的方式。


所以,问题是

对于这种情况,是否有一个对从TLB生成器导入C#友好的IDL定义?如果没有,在C#端可以做些什么来使它更安全?

函数的IDL声明(在C++中),该函数将从C#中获取C样式数组

因此,您要求的IDL数据类型在32位机器上为32位,在64位机器上则为64位。但是你不希望封送处理代码把它当作指针,就像int一样。那么,当你从64位进程调用到32位进程时,你希望额外的32位会发生什么呢?

对我来说,这听起来像是违反了物理学。

如果仅在进行中,请参阅本讨论的底部:http://www.techtalkz.com/vc-net/125190-how-interop-net-client-com-dll.html.

建议使用void*而不是intptr,并用[local]标记,这样编组员就不会参与其中。

我对C#COM的可操作性不太了解,但你试过使用SAFEARRAY(INT_PTR)或类似的东西吗?

嗯。。。我发现了一些让我更接近的信息。。。

编组更改-一致C样式阵列

此IDL声明(C++)

HRESULT GetFoo([in] int bufferSize, [in, size_is(bufferSize)] int buffer[]);

作为(MSIL)导入

method public hidebysig newslot virtual instance void GetFoo([in] int32 bufferSize, [in] int32& buffer) runtime managed internalcall

如果更改为(MSIL)

method public hidebysig newslot virtual instance void GetFoo([in] int32 bufferSize, [in] int32[] marshal([]) buffer) runtime managed internalcall

可以像(C#)一样使用

int[] b = ...;
GetFoo(b.Length, b);

这正是我想要的!

但是,有没有其他解决方案不需要修复tlbimport生成的运行时可调用包装器的MSIL