【问题标题】:Marshalling an array of strucs to call an unmanaged function from C#编组结构数组以从 C# 调用非托管函数
【发布时间】:2014-07-23 17:31:04
【问题描述】:

我必须从 C# 调用一个非托管函数,并且必须为其提供一个坐标数组(双精度)。对于这种情况,编组如何正确工作?

在非托管方面:

typedef struct dPoint3dTag
{
  double x, y, z;
} dPoint3d;

void UnmanagedModifyGeometry(char *strFeaId, dPoint3d *pnts, int iNumPnts);

我在托管端为 DPoint3d 定义了一个托管结构:

[StructLayout(LayoutKind.Sequential)]
public struct DPoint3d
{
// Constructor
public DPoint3d(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}

public double x, y, z;
}

我正在尝试以这种方式从 C# 调用非托管函数:

// Import of the unmanaged function
[DllImport("Unmanaged.dll")]
public static extern void UnmanagedModifyGeometry([MarshalAs(UnmanagedType.LPStr)] string strFeaId, DPoint3d[] pnts, int iNumPnts);

// Using the unmanaged function from C#
// Allocating points
DPoint3d[] pnts = new DPoint3d[iPntCnt];
String strFeaId = "4711";

// After filling in the points call the unmanaged function
UnmanagedModifyGeometry(strFeaId, pnts, iPntCnt);

这个工作流程正确吗?

问候 汤姆托雷尔

【问题讨论】:

    标签: c# pinvoke marshalling


    【解决方案1】:

    首先在非托管方面,char* 是一个可修改的字符串。您应该在这里使用const 表示数据从调用者流向被调用者。对其他参数做同样的事情是有意义的:

    void UnmanagedModifyGeometry(
        const char *strFeaId, 
        const dPoint3d *pnts, 
        const int iNumPnts
    );
    

    现在大家都清楚数据是如何流动的了。

    在托管方面,声明存在一个明显的问题,即您没有指定调用约定。默认是stdcall,但你的非托管代码将是cdecl,假设问题中的声明是准确的。

    您展示的结构声明完美匹配。关于这个话题没有什么可说的了。

    您还可以使用默认编组来简化 p/invoke。我会这样写:

    [DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void UnmanagedModifyGeometry(
        string strFeaId, 
        [In] DPoint3d[] pnts, 
        int iNumPnts
    );
    

    然后这样称呼它:

    DPoint3d[] pnts = new DPoint3d[...]; // supply appropriate value for array length
    // populate pnts
    UnmanagedModifyGeometry("4711", pnts, pnts.Length);
    

    【讨论】:

    • +1 真正的问题只是错误的调用约定(但我仍然会添加属性以明确iNumPntspnts 中的元素数,坦率地说我不知道​​是否marshaler 将永远使用它,但它是一个额外的检查 - 如果使用 - 或文档 - 如果未使用)。
    • @AdrianoRepetti UnmanagedType.LPArray 是这里的默认值。 SizeParamIndex 可用于允许编组器仅编组数组的一部分。但是,DPoint3d[] 是 blittable,因此无论如何它都会被编组器固定。包含SizeParamIndex 并没有什么坏处,但我也不认为省略它真的很痛苦。
    • 我同意省略它并没有什么坏处,我只是想知道包含它是否有(或会有)好处。 SizeParamIndex 不应该用来告诉 marshaler 一个参数携带数组的大小?如果我这样做 unmanagedFunction(myArray, myArray.Length + 1)?
    • @AdrianoRepetti 它所做的就是控制编组器如何编组数组。如果未指定,则使用提供的数组的长度。如果已指定,则使用另一个参数的值。在这种情况下,数组是 blittable 并且它根本没有影响,因为 marshaller 引脚而不是 marshals。
    • 谢谢!我以为是运行时检查!
    猜你喜欢
    • 2015-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-11
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多