无法封送包含 StringBuilder 字段的结构

发布于 2024-12-02 00:35:54 字数 1697 浏览 0 评论 0原文

我需要对用 C++ 编写的 DLL 进行互操作调用。在 C++ 代码中,有各种接收和返回字符串的函数。这些都使用 C++ 中通用定义的类型(结构),其中包含指向字符串的指针以及其大小的整数,如下所示:

struct StringParam
{
    int size;    // 4 byte integer for the size of the string buffer
    LPWSTR buff; // Pointer to a wide char buffer
}

当从 DLL 返回字符串时以及当字符串缓冲区太小而无法存储字符串时结果,返回错误代码以及整数 size 字段中所需的正确大小的缓冲区,以便调用者可以提供正确大小的缓冲区。

暂时忽略结构,这可以通过使用 StringBuilder 参数和大小的 ref int 参数组合的互操作轻松完成。但是,我们使用的是结构体,并且编组的结构体中不允许使用 StringBuilder 字段。

给定一个接收字符串的 C++ 函数,如下所示:

int __stdcall DoSomethingWithString(StringParam *stringParam)

可以在 C# 中声明以下结构:

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
 internal struct StringParam
 {
     public int Size;
     [MarshalAs(UnmanagedType.LPWStr)]
     public string Text;

     public StringParam(string text)
     {
         this.Text = text;
         this.Size = text.Length;
     }
 }

并可以成功初始化结构并调用具有以下签名的 C++ 函数:

[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int DoSomethignWithString(ref StringParam stringParam);

但是,给定一个需要返回字符串的函数,如下所示,提出了一个问题:

[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetSomeString(ref StringParam stringParam);

当从互操作调用接收字符串时,我们不知道字符串的大小,并且必须分配足够大小的缓冲区来存储字符串结果。 StringBuilder 是理想的选择,但不能在编组的结构中使用。我们可以为字符串预先分配 2048 个虚拟字符,然后将其用于存储结果。事实上,在尝试使用 StringBuilder 类型时收到的错误消息中建议这样做:

无法封送“StringParam”类型的字段“Text”:结构或类字段不能为 StringBuilder 类型。通常可以通过使用字符串字段并将其预初始化为长度与适当缓冲区的长度匹配的字符串来实现相同的效果。

用虚拟值预初始化字符串似乎有点混乱。还有另一种/更好的方法来解决这个问题吗?

I need to make interop calls to a DLL written in C++. In the C++ code, there are various functions that receive and return strings. These all use a commonly defined type (struct) in C++, which comprises a pointer to a string along with an integer for its size as follows:

struct StringParam
{
    int size;    // 4 byte integer for the size of the string buffer
    LPWSTR buff; // Pointer to a wide char buffer
}

When a string is returned from the DLL and when the string buffer is too small to store the string result, an error code is returned along with the correct size buffer that is required in the integer size field, so that the caller can provide the right size buffer.

Ignoring the struct for the moment, this could be easily accomplished through interop using the combination of a StringBuilder parameter, along with a ref int parameter for the size. However, we are using a struct and StringBuilder fields are not permitted in structs that are marshalled.

Given a C++ function that receives a string as follows:

int __stdcall DoSomethingWithString(StringParam *stringParam)

One can declare the following struct in C#:

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
 internal struct StringParam
 {
     public int Size;
     [MarshalAs(UnmanagedType.LPWStr)]
     public string Text;

     public StringParam(string text)
     {
         this.Text = text;
         this.Size = text.Length;
     }
 }

and can successfully initialize a struct and call a C++ function with the following signature:

[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int DoSomethignWithString(ref StringParam stringParam);

However, given a function that needs to return a string as follows, presents a problem:

[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetSomeString(ref StringParam stringParam);

When receiving a string from the interop call, we do not know the size of the string and must allocate a buffer sufficient in size for storing the string result. StringBuilder would be ideal, but cannot be used within a struct that is marshalled. We could pre-allocate the string with 2048 dummy characters, which could then be used to store the results. This is in fact recommended in the error message one gets when attempting to use the StringBuilder type:

Cannot marshal field 'Text' of type 'StringParam': Struct or class fields cannot be of type StringBuilder. The same effect can usually be achieved by using a String field and preinitializing it to a string with length matching the length of the appropriate buffer.

This seems kind of messy to pre-initialize a string with dummy values. Is there another/better way to go about this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文