我正在使用由一些用 C++ 编写的带有 C# 前端的 COM 接口组成的现有代码库。有一些新功能需要添加,所以我不得不修改 COM 部分。在一种特殊情况下,我需要将一个数组(从 C# 分配)传递给要填充的组件。
我想做的是能够将一个 int 数组从 C# 传递给该方法,例如:
// 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# 端可以做些什么来让它更安全一点?