0

我正在使用由一些用 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# 端可以做些什么来让它更安全一点?

4

3 回答 3

1

因此,您要求的 IDL 数据类型在 32 位机器上为 32 位,在 64 位机器上为 64 位。但是您不希望编组代码将其视为指针,就像 int 一样。那么当您从 64 位进程调用到 32 位进程时,您期望额外的 32 位会发生什么?

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

如果它只是 inproc,请参阅此讨论的底部:http ://www.techtalkz.com/vc-net/125190-how-interop-net-client-com-dll.html 。

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

于 2008-10-14T15:52:12.210 回答
0

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

于 2008-10-14T01:24:28.857 回答
0

嗯...我找到了一些让我更接近的信息...

编组更改 - 符合 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?

于 2008-10-14T01:57:40.583 回答