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

时间:2017-07-14 12:47:37

标签: c# .net c unit-testing pinvoke

我有一个C DLL我是PInvoking。主要目标是返回39个字符的GUID字符串,例如abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd

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

[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));
}

1 个答案:

答案 0 :(得分:1)

这需要一些修复。

需要更改的功能签名:

[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或其他类似的东西,以及你的字符串长度计算方法,你将会在执行sizeof(wchar_t)之类的操作时,需要将缓冲区大小乘以memset,因为字符串中的字符数与字符串中的字节数之间存在很大差异。我碰巧知道sizeof(char)是什么,所以我从实现中省略了这个以保存代码。