用于从 C# 获取 C 风格数组的函数的 IDL 声明(在 C++ 中)

发布于 2024-07-07 08:59:23 字数 1663 浏览 8 评论 0原文

我正在使用现有的代码库,该代码库由一些用 C++ 编写的 COM 接口和 C# 前端组成。 由于需要添加一些新功能,因此我必须修改 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);

Which import as:

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();
}

Which Works...,但我想找到一种更干净的方法。


因此,问题是

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

I am working with an existing code base made up of some COM interfaces written in C++ with a C# front end. There is some new functionality that needs to be added, so I'm having to modify the COM portions. In one particular case, I need to pass an array (allocated from C#) to the component to be filled.

What I would like to do is to be able to pass an array of int to the method from C#, something like:

// desired C# signature
void GetFoo(int bufferSize, int[] buffer);

// desired usage
int[] blah = ...;
GetFoo(blah.Length, blah);

A couple of wrenches in the works:

  • C++/CLI or Managed C++ can't be used (COM could be done away with in this case).
  • The C# side can't be compiled with /unsafe (using Marshal is allowed).

The COM interface is only used (an will only ever be used) by the C# part, so I'm less concerned with interoperability with other COM consumers. Portability between 32 and 64 bit is also not a concern (everything is being compiled and run from a 32 bit machine, so code generators are converting pointers to integers). Eventually, it will be replaced by just C++/CLI, but that is a ways off.


My initial attempt

is something similar to:

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

And the output TLB definition is (seems reasonable):

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

Which is imported by C# as (not so reasonable):

void GetFoo(int bufferSize, ref int buffer);

Which I could use with

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

...except that I can't compile with /unsafe.


At the moment

I am using:

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

Which imports as:

void GetFoo(int bufferSize, int buffer);

And I need use use it like:

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

Which works..., but I'd like to find a cleaner way.


So, the question is

Is there an IDL definition that is friendly to the C# import from TLB generator for this case? If not, what can be done on the C# side to make it a little safer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

许你一世情深 2024-07-14 08:59:23

因此,您要求 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] 进行标记,这样编组器就不会参与其中。

So you're asking for an IDL datatype that is 32-bits on a 32-bit machine and 64-bits on a 64-bit machine. But you don't want the marshaling code to treat it like a pointer, just as an int. So what do you expect to happen to the extra 32-bits when you call from a 64-bit process to a 32-bit process?

Sound like a violation of physics to me.

If it's inproc only, see the bottom of this discussion: http://www.techtalkz.com/vc-net/125190-how-interop-net-client-com-dll.html.

The recommendation seems to be to use void * instead of intptr and flag with the [local] so the marshaller doesn't get involved.

闻呓 2024-07-14 08:59:23

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

I don't know much about C# COM operability, but have you tried using SAFEARRAY(INT_PTR) or something similar?

谈情不如逗狗 2024-07-14 08:59:23

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

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

Hmmm... I've found some information that gets me closer...

Marshaling Changes - Conformant C-Style Arrays

This IDL declaration (C++)

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

Is imported as (MSIL)

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

And if changed to (MSIL)

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

Can be used like (C#)

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

Exactly what I was gunning for!

But, are there any other solutions that don't require fixing up the MSIL of the runtime callable wrapper that is generated by tlbimport?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文