简体   繁体   English

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

[英]IDL declaration (in C++) for a function that will take a C-style array from C#

I am working with an existing code base made up of some COM interfaces written in C++ with a C# front end. 我正在使用由一些用C ++前端用C ++编写的COM接口组成的现有代码库。 There is some new functionality that needs to be added, so I'm having to modify the COM portions. 有一些新功能需要添加,因此我必须修改COM部分。 In one particular case, I need to pass an array (allocated from C#) to the component to be filled. 在一种特殊情况下,我需要将一个数组(从C#分配)传递给要填充的组件。

What I would like to do is to be able to pass an array of int to the method from C#, something like: 我想做的是能够将int数组从C#传递给方法,例如:

// 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). 不能使用C ++ / CLI或Managed C ++(在这种情况下,可以取消COM)。
  • The C# side can't be compiled with /unsafe (using Marshal is allowed). C#端无法使用/ unsafe进行编译(允许使用Marshal)。

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. COM接口仅由C#部分使用(将永远只使用),因此我不太关心与其他COM使用者的互操作性。 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). 也不关心32位和64位之间的可移植性(所有内容都在32位计算机上编译和运行,因此代码生成器将指针转换为整数)。 Eventually, it will be replaced by just C++/CLI, but that is a ways off. 最终,它将仅由C ++ / CLI代替,但这是一种解决方法。


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): 输出的TLB定义为(似乎合理):

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

Which is imported by C# as (not so reasonable): C#将其导入为(不太合理):

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. ...除了我不能使用/ 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? 在这种情况下,是否存在对从TLB生成器导入C#友好的IDL定义? If not, what can be done on the C# side to make it a little safer? 如果没有,那么在C#方面可以做些什么来使其更加安全?

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. 因此,您需要的IDL数据类型在32位计算机上为32位,在64位计算机上为64位。 But you don't want the marshaling code to treat it like a pointer, just as an int. 但是,您不希望封送处理的代码将其视为一个指针,就像一个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? 那么,当您从64位进程调用到32位进程时,您期望额外的32位发生什么?

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 . 如果仅是inproc,请参阅此讨论的底部: 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. 推荐的方法似乎是使用void *代替intptr并使用[local]进行标记,这样编组器就不会参与进来。

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

Hmmm... I've found some information that gets me closer... 嗯...我发现了一些可以使我更加靠近的信息...

Marshaling Changes - Conformant C-Style Arrays 封送处理更改-符合C样式的数组

This IDL declaration (C++) 该IDL声明(C ++)

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

Is imported as (MSIL) 导入为(MSIL)

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

And if changed to (MSIL) 如果更改为(MSIL)

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

Can be used like (C#) 可以像(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? 但是,还有其他解决方案不需要修复tlbimport生成的运行时可调用包装的MSIL吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM