编组从C#到C ++的非Blittable结构

时间:2012-07-10 15:13:02

标签: c# c++ string pinvoke marshalling

我正在重写我公司的C#和C ++之间接口的过度工程和不可维护的库代码。我已经开始研究P / Invoke,但似乎没有太多的可访问帮助。

我们将包含各种参数和设置的结构传递给非托管代码,因此我们要定义相同的结构。我们不需要在C ++方面更改任何这些参数,但我们确实需要在P / Invoked函数返回后访问它们。

我的问题是:

     
  1. 传递字符串的最佳方法是什么?有些是短的(设备ID可以由我们设置),有些是文件路径(可能包含亚洲字符)  
  2. 我应该将IntPtr传递给C#结构,还是应该让Marshaller通过将结构类型放在函数签名中来处理它?  
  3. 我是否应该担心任何非指针数据类型,如bool或enums(在其他相关结构中)?我们将处理警告作为在C ++中设置的错误标志,因此我们无法使用Microsoft扩展来强制使用数据类型。  
  4. P / Invoke实际上是走的路吗?有一些关于Implicit P / Invoke的Microsoft文档说它更安全,性能更高。

作为参考,这是我到目前为止写的一对结构:

C ++

/**
    Struct used for marshalling Scan parameters from managed to unmanaged code.
*/
struct ScanParameters
{
    LPSTR deviceID;
    LPSTR spdClock;
    LPSTR spdStartTrigger;
    double spinRpm;
    double startRadius;
    double endRadius;
    double trackSpacing;
    UINT64 numTracks;
    UINT32 nominalSampleCount;
    double gainLimit;
    double sampleRate;
    double scanHeight;
    LPWSTR qmoPath; //includes filename
    LPWSTR qzpPath; //includes filename
};

C#

/// <summary>
/// Struct used for marshalling scan parameters between managed and unmanaged code.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ScanParameters
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string deviceID;
    [MarshalAs(UnmanagedType.LPStr)]
    public string spdClock;
    [MarshalAs(UnmanagedType.LPStr)]
    public string spdStartTrigger;
    public Double spinRpm;
    public Double startRadius;
    public Double endRadius;
    public Double trackSpacing;
    public UInt64 numTracks;
    public UInt32 nominalSampleCount;
    public Double gainLimit;
    public Double sampleRate;
    public Double scanHeight;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string qmoPath;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string qzpPath;
}

1 个答案:

答案 0 :(得分:2)

blittable类型是一种在托管代码和非托管代码之间具有共同表示的类型,因此可以在它们之间传递而几乎没有问题,例如: byte,int32等。

非blittable类型没有共同的表示,例如System.Array,System.String,System.Boolean等。

通过为非blittable类型指定MarshalAs属性,您可以告诉编组器应该将其转换为什么。 有关详细信息,请参阅此article on Blittable and Non-Blittable Types

  

1 - 传递字符串的最佳方法是什么?有些是短的(设备ID可以由我们设置),有些是文件路径(可能包含亚洲字符)

通常建议使用StringBuilder作为最容易使用的,但我经常使用普通字节数组。

  

2 - 我应该将IntPtr传递给C#结构,还是应该让Marshaller通过将结构类型放在函数签名中来处理它?<​​/ p>

如果方法期望一个指针然后传递一个IntPtr,尽管在很多情况下你可能会得到一个ref,这取决于它将用于什么。如果它需要长时间停留在同一个地方,那么我会手动为Marshal分配内存并传递生成的IntPtr。

  

3 - 我是否应该担心任何非指针数据类型,如bool或枚举(在其他相关结构中)?我们将处理警告作为在C ++中设置的错误标志,因此我们不能使用Microsoft扩展来强制使用枚举数据类型。

一旦你使用正确的编组属性设置了所有内容,我就不明白为什么你需要担心。如果对该属性存在疑问,如果该结构仅被托管代码使用,则该属性将不会被使用。

  

4 - P / Invoke实际上是走的路吗?有一些关于Implicit P / Invoke的Microsoft文档说它更安全,性能更高。

无法对此发表评论,您将进入Visual C ++领域。