c# - 将不可 Blittable 结构从 C# 编码到 C++

标签 c# c++ string pinvoke marshalling

我正在重写公司 C# 和 C++ 之间接口(interface)的库代码中过度设计且难以维护的部分。我已经开始研究 P/Invoke,但似乎没有太多可访问的帮助。

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

我的问题是:

  1. 传递字符串的最佳方式是什么?有些是短的(设备 ID 可以由我们设置),有些是文件路径(可能包含亚洲字符)
  2. 我应该将 IntPtr 传递给 C# 结构体,还是应该让 Marshaller 通过将结构体类型放入函数签名中来处理它?
  3. 我应该担心任何非指针数据类型,如 bool 或枚举(在其他相关结构中)吗?我们在 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;
}

最佳答案

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

不可直接传送的类型没有通用的表示形式,例如System.Array、System.String、System.Boolean 等

通过为不可直接传送的类型指定 MarshalAs 属性,您可以告诉编码器应将其转换为什么。 看这个article on Blittable and Non-Blittable Types了解更多信息

1 - What is the best way to pass strings? Some are short (device id's which can be set by us), and some are file paths (which may contain Asian characters)

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

2 - Should I pass an IntPtr to the C# struct or should I just let the Marshaller take care of it by putting the struct type in the function signature?

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

3 - Should I be worried about any non-pointer datatypes like bools or enums (in other, related structs)? We have the treat warnings as errors flag set in C++ so we can't use the Microsoft extension for enums to force a datatype.

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

4 - Is P/Invoke actually the way to go? There was some Microsoft documentation about Implicit P/Invoke that said it was more type-safe and performant.

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

关于c# - 将不可 Blittable 结构从 C# 编码到 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11416433/

相关文章:

ruby-on-rails - to_json 在 Rails 中返回字符串而不是 json

c++ - Visual Studio 2015 遇到断点

c++ - std::get<I>( std::tuple& ) 作为模板参数

c++ - 在编译时将字符串转换为大写

c# - 设计一个多对一的移动应用程序 -> 数据库 -> 桌面应用程序项目

c# - 这是 string.TrimEnd 中的错误吗?

java - 使用其他语言而不是英语作为java字符串(以波斯语为例)

c# - 泛型和数据库——一个设计问题

c# - 十进制。解析字符串,后缀为减号

c# - 使用 FlaUI 为电影和电视设置滚动百分比