用于从 C# 获取 C 风格数组的函数的 IDL 声明(在 C++ 中)
我正在使用现有的代码库,该代码库由一些用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
因此,您要求 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.
我对 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?
嗯...我找到了一些让我更接近的信息...
编组更改 - 符合 C 风格数组
此 IDL 声明 (C++)
导入为 (MSIL)
如果更改为 (MSIL)
可以像 (C#) 一样使用
正是我想要的!
但是,是否有任何其他解决方案不需要修复 tlbimport 生成的运行时可调用包装器的 MSIL?
Hmmm... I've found some information that gets me closer...
Marshaling Changes - Conformant C-Style Arrays
This IDL declaration (C++)
Is imported as (MSIL)
And if changed to (MSIL)
Can be used like (C#)
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?