在单元测试中使用 StringBuilder 进行 PInvoking

PInvoking with StringBuilder in a Unit Test

我有一个正在调用 PInvoking 的 C DLL。主要目标是取回一个 39 个字符的 GUID 字符串,例如 abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd.

我首先调用一个方法来获取这个字符串的大小,我希望它是 39 个字符,然后我调用另一个函数将它传递给一个容量为 39 的 StringBuilder:

[DllImport("test.dll")]
public static extern int get_size();

[DllImport("test.dll")]
public static extern void get_string(StringBuilder result);

我的代码看起来像这样:

int size = get_size(); // Returns 40, because it includes the null terminating character.
var result = new StringBuilder(size - 1); // Gives it a capacity of 39. Subtracting 1 here because this does not fancy that null terminator over the marshaling layer.
get_string(result);
Console.WriteLine(result.ToString());

当我在控制台应用程序中调用它时,我得到了这个结果:abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd

当我使用完全相同的代码从单元测试中调用它时,我得到了这个结果:abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcdq

注意最后的 q,添加的额外字符,并且通过调试单元测试我可以验证 StringBuilder 对象的容量已显着增加到 42 调用 get_string 之后,尽管初始化容量为 39。为什么会这样?这是正常的吗?难道我做错了什么?为什么只在单元测试中?

C 实现是这样的:

static char *_result = NULL; // At some point result is initialized and set.

int get_size() {
    if (_result != NULL)
        return strlen(_result) + 1;
    return 1;
}

void get_string(char *result) {
    if (result != NULL && _result != NULL)
        strncpy(result, _result, strlen(_result));
}

这需要一些修复。

需要更改的函数签名:

[DllImport("test.dll")]
public static extern int get_size();

[DllImport("test.dll")]
public static extern void get_string(int resultSize, StringBuilder result);

需要更改的 C 实现:

static char *_result = NULL; // At some point result is initialized and set.

int get_size() {
    if (_result != NULL)
        return strlen(_result) + 1;
    return 1;
}

void get_string(int resultSize, char *result) {
    memset(result, 0, resultSize);
    if (_result != NULL)
        strncpy(result, _result, resultSize);
}

需要更改的 C# 调用:

int resultSize = get_size();
var result = new StringBuilder(resultSize); // Needed to also include the null Terminator ("I'LL BE BACK" - ARNOLD).
get_string(resultSize, result);
Console.WriteLine(result.ToString());

给 C 新手的注意事项...如果您没有使用 char,而您使用的是 wchar_t 之类的东西或其他方式,以及您的字符串长度计算方法,您在执行 memset 之类的操作时,需要将缓冲区大小乘以 sizeof(wchar_t),因为字符串中的字符数和字符串中的字节数之间存在很大差异。我只是碰巧知道 sizeof(char) 是什么 1 所以我从实现中省略了它以保存代码。