2

我在 C 中有一个带有这个签名的函数:

int addPos(int init_array_size, 
           int *cnt, 
           int *array_size, 
           PosT ***posArray, 
           char *infoMsg);

这是 PostT 的样子:

typedef union pu
{
    struct  dpos   d;
    struct  epo    e;
    struct  bpos   b;
    struct  spos   c;
} PosT ;

通过 P/Invoke 在 C# 中调用此方法的最佳方法是什么?我需要在 C# 中定义一个代表 PostT 的类吗?如何将 PosT ***posArray 参数从 C# 传递到 C?

4

2 回答 2

2

您已经描述了 PostT 的外观,但这还不够。首先,必须知道函数期望作为 ***PostT 参数传递的内容,然后才能考虑从 C++ 或 C# 端调用它。

我知道这可能不符合您的意愿,但请看:

PosT p;
PosT* ptr = &p;
PosT** ptr2 = &ptr;
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!?


PosT* ptr = new PosT[123];
PosT** ptr2 = &ptr;
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!?


PosT** ptr2 = new PosT[5];
for(int i=0;i<5;++i) ptr2[i] = new PosT[123];
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!??

等等。我快速构建的哪个内存结构对于该功能是正确的?这决定了您必须从 C# 端传递的数据类型。

如果该函数采用您称为“对锯齿状阵列的可变引用”的东西(所以我提供的最后一个示例),那么 P/Invoke 声明将是:

[..extern..]
void func(....., ref PosT[][] posArray, ...);

调用类似于:

func(...., new PosT[][] { 
         new PosT[] { new PosT{ d = ... }, new PosT{ d = ... }, ... },
         new PosT[] { new PosT{ d = ... }, new PosT{ d = ... }, ... },
         ... },
    .... );

但是,请先检查这个函数的期望。有了*真的有太多的可能性只是猜测。你说它来自一些 API - 首先检查它的文档!告诉我们这个函数到底需要什么,我/有人会告诉你如何在 C# 中构建这样的 POD。反之则行不通!:)

PS。对不起,糟糕的 C++/C# 代码,我很匆忙,只有几分钟的时间来写这个:/

于 2012-12-19T18:28:30.040 回答
1

问题 #1 我需要在 CSharp 中定义一个代表 PostT 的类吗?
您应该将 PostT 定义为 c# 中的结构。由于这是一个联合结构,您将需要应用 StructLayout 属性,并完全定义您的其他结构。它可能看起来像这样:

struct  dpos { };
struct  epo  { };
struct  bpos { };
struct  spos { };
[System.Runtime.InteropServices.StructLayout(
    System.Runtime.InteropServices.LayoutKind.Explicit, Size=99)]
struct PosT {
    [System.Runtime.InteropServices.FieldOffset(0)] dpos   d;
    [System.Runtime.InteropServices.FieldOffset(0)] epo    e;
    [System.Runtime.InteropServices.FieldOffset(0)] bpos   b;
    [System.Runtime.InteropServices.FieldOffset(0)] spos   c;
    };

** 请注意,“Size=99”正确。理想情况下,您应该将此大小设置为最大封闭结构使用的字节数。

问题 #2- 我如何将 Post *** posArray 参数通过 frm CSharp 传递给 C?
你必须非常小心地做这种事情,特别是如果你希望在 C# 下为 posArray 分配内存。您不想将 .NET GC 即将移动/更改的缓冲区传递给 C。如果 C 返回缓冲区,你应该没问题(但如果没有 C 函数让你释放内存,你会泄漏内存)。你应该看看Default Marshaling for Arrays和 www.pinvoke.net 可能有一些例子。如果您将 posArray 缓冲区从 C# 发送到 C,您将不得不求助于不安全的上下文(见下文)。我不确定如何处理该级别或重定向。您的 cnt 和 array_size 可以使用 'ref' 关键字来处理。

问题 #3- 我如何为这一切指定封送处理?
看看上面的链接,尤其是数组的默认封送处理。您可能还需要使用一些测试代码,在调用后中断并使用即时窗口来调查结构。


记住

如果您使用“UNSAFE”开关编译 c#,您几乎可以直接(类似 c)调用。例如:

unsafe public MainWindow() {
    InitializeComponent();
    int abc = 4;
    int* abcPtr = &abc;
    *abcPtr = 8;

    fixed (PosT* gog = new PosT[30]) {
        PosT* gogPtr = (gog + 1);
        }
    }

你必须非常小心,因为 C# 管理它自己的内存并且可以在你身上移动东西(参见上面的“固定”)。我不推荐这个,但它对于快速和肮脏的事情很有用

此外,如果 API 在 tlb(表库)中定义,Visual Studio 通常会提供通过存根 dll 为您创建绑定(请参阅 tlbimp 命令)


希望有人可以提供更具体的回应,但我认为这些信息至少可以让你开始前进。

于 2012-12-19T20:43:10.837 回答