在unit testing中使用StringBuilder进行PInvoking
我有一个C DLL我是PInvoking。 主要目标是获取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
当我用完全相同的代码从unit testing中调用它时,我得到了这个结果: abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcdq
注意最后的q
,添加的额外字符,以及调试unit testing后我可以validation尽管初始化容量为39,但在调用get_string
之后 StringBuilder
对象的容量已大幅增加到42。为什么会这样? 这是正常的吗? 难道我做错了什么? 为什么只在unit testing?
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
或其他东西,以及你的字符串长度计算方法,你需要将你的缓冲区大小乘以sizeof(wchar_t)
而是在进行像memset
这样的操作时,因为字符串中的字符数和字符串中的字节数之间存在很大差异。 我碰巧知道sizeof(char)
是1,所以我从实现中省略了这个以保存代码。