1

我正在重写我公司的库代码中的一个过度设计且无法维护的块,该库代码是 C# 和 C++ 之间的接口。我已经开始研究 P/Invoke,但似乎没有太多可访问的帮助。

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

我的问题是:

  1. 传递字符串的最佳方法是什么?有些很短(可以由我们设置的设备 ID),有些是文件路径(可能包含亚洲字符)
  2. 我应该将 IntPtr 传递给 C# 结构,还是应该让 Marshaller 通过将结构类型放入函数签名中来处理它?
  3. 我是否应该担心任何非指针数据类型,如 bool 或 enums(在其他相关结构中)?我们在 C++ 中设置了将警告视为错误标志,因此我们不能使用枚举的 Microsoft 扩展来强制数据类型。
  4. P / Invoke实际上是要走的路吗?有一些关于隐式 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;
}
4

1 回答 1

2

blittable 类型是一种在托管代码和非托管代码之间具有共同表示的类型,因此可以在它们之间传递很少或没有问题,例如字节、int32 等。

非 blittable 类型没有通用表示,例如 System.Array、System.String、System.Boolean 等。

通过为非 blittable 类型指定 MarshalAs 属性,您可以告诉编组器应该将其转换为什么。有关更多信息,请参阅有关 Blittable 和 Non-Blittable 类型的文章

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

StringBuilder 通常被推荐为最容易使用的,但我经常使用纯字节数组。

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

如果该方法需要一个指针,则传递一个 IntPtr ,尽管在许多情况下您可能会摆脱一个 ref ,具体取决于它将用于什么。如果它需要在同一个地方停留很长时间,那么我将使用 Marshal 手动分配内存并传递生成的 IntPtr。

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

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

4 - P/Invoke 实际上是要走的路吗?有一些关于隐式 P/Invoke 的 Microsoft 文档说它更加类型安全和高性能。

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

于 2012-07-10T16:19:57.203 回答