PInvoke - 如何编组'SomeType * []'?

Posted

技术标签:

【中文标题】PInvoke - 如何编组\'SomeType * []\'?【英文标题】:PInvoke - How to marshal for 'SomeType* []'?PInvoke - 如何编组'SomeType * []'? 【发布时间】:2012-01-05 11:40:23 【问题描述】:

我有一个本机库,其中包含一些本机 ntype,并希望在其中 p/调用一些函数。

我能够编组:

foo1(ntype** p) ==> foo1(IntPtr[] p)

但不知道该怎么做:

foo1(ntype*[] p) ==> foo1(<???> p)

至少IntPtr[] 不起作用。

编辑

我试图编组的非托管函数是:

extern mxArray* mclCreateSimpleFunctionHandle(mxFunctionPtr fcn);

mxFunctionPtr 在哪里:

typedef void(*mxFunctionPtr)(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[]);

这表示对以下 matlab 函数签名的调用:

function [varargout] = callback(varargins)
%[
    %% Do callback code %%
%]

显然,按照我的预期,这个函数指针应该为我提供 2 个mxArray* 列表:

输入参数列表(即 prhs,在 matlab 端初始化) 输出参数列表(即 plhs,全部初始化为零,但我应该写入其中)

目前从我所做的测试来看,它只返回plhsprhs 列表中的第一个mxArray*

【问题讨论】:

接受指针和数组的 C 函数是完全模棱两可的。 ntype** 和 ntype*[] 之间没有区别,除非您在代码中以某种方式赋予它不同的语义。 【参考方案1】:

首先要做的是将您的原生ntype 转换为托管struct

例如:

public struct Ntype

    public int Field1;
    public long Field2;

然后,您在 C# 代码中使用简单的 IntPtr 参数定义您的方法。

[DllImport]
static void foo1(IntPtr myParam);

最后是你如何使用它:

IntPtr buffer = IntPtr.Zero;

try

    // Allocates a buffer. The size must be known
    buffer = Marshal.AllocHGlobal(0x1000);

    // Call to your unmanaged method that fills the buffer
    foo1(buffer);

    // Casting the unmanaged memory to managed structure that represents
    // your data
    Ntype obj = (Ntype)Marshal.PtrToStructure(buffer, typeof(Ntype));

finally

    // Free unmanaged memory
    if (buffer != IntPtr.Zero)
    
        Marshal.FreeHGlobal(buffer);
    

【讨论】:

感谢@ken2k 和@Ramhound 的帮助。我想我真的不需要这个结构,只有一个指向它的指针对我来说就可以了。事实上,我正在尝试解决调用mclCreateSimpleFunctionHandle 的问题,如link 中所述。显然,这个函数只返回一个指向第一个 mxArray* 的指针,我正在尝试获取 mxArray* 的完整列表(我对 mxArray 本身不感兴趣)......至少对于读者来说这另一个帖子,我想我正在修复它的路上。【参考方案2】:

知道了

SomeTime* []”的正确编组:

extern mxArray* mclCreateSimpleFunctionHandle(mxFunctionPtr fcn);
typedef void(*mxFunctionPtr)(int nlhs, mxArray* plhs[], int nrhs, mxArray* prhs[]);

是:

// For function pointer
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public delegate void MCRInteropDelegate(int nlhs,
                                        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.SysInt, SizeParamIndex = 0)][Out] IntPtr[] plhs, 
                                        int nrhs,
                                        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.SysInt, SizeParamIndex = 2)][In] IntPtr[] prhs);

// For API function
[DllImport(DLLNAME, EntryPoint = "mclCreateSimpleFunctionHandle", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
private static extern IntPtr _mclCreateSimpleFunctionHandle(MCRInteropDelegate fctn);

说明

MarshalAs 属性指示将SomeTime*[] 编组为LPArrayLPArray,其中数组的大小包含在从零开始的索引SizeParamIndex 处的函数参数中

【讨论】:

以上是关于PInvoke - 如何编组'SomeType * []'?的主要内容,如果未能解决你的问题,请参考以下文章

在 c# 和 c++ 之间将 double 类型的二维多维数组作为输入和输出的 pinvoke 编组

编组 LPSTR 和浮动时的不平衡堆栈

C# Marshal / Pinvoke CBitmap?

如何使用 pinvoke 将二进制数据缓冲区从 C 传递到 C#

编组 SIZE_T* 的正确方法?

将 C++ 结构编组到 C# 类时出现 AccessViolationException