PInvoking with StringBuilder in a Unit Test

我的未来我决定 提交于 2019-12-25 08:59:12

问题


I have a C DLL I am PInvoking. The main goal is to get back a GUID string of 39 characters, such as abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd.

I first call one method to get the size of this string, which I expect to be 39 characters, and then I call another function passing it a StringBuilder with a capacity of 39:

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

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

My code looks something like this:

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());

When I call this in a console application, I get back this result: abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcd

When I call this from a unit test with the exact same code, I get back this result: abcd-abcd-abcd-abcd-abcd-abcd-abcd-abcdq

Note the q on the end, the extra character that is added, and that from debugging the unit test I can verify that the capacity of the StringBuilder object has increased significantly up to 42 after the call to get_string despite being initialized with a capacity of 39. Why is this happening? Is this normal? Am I doing something wrong? Why only in the unit tests?

The C implementation is something like this:

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:


This required a few fixes.

The function signature needed to change:

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

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

The C implementation needed to change:

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

The C# call needed to change:

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());

A note to rookies of C...if you're not using char, and you're using something like wchar_t or otherwise, along with your string length calculation methods, you'll need to multiply your buffer sizes by sizeof(wchar_t) instead when doing operations like memset, since there's a big difference between number of characters in a string and number of bytes in a string. I just happen to know what sizeof(char) is 1 so I've omitted this from the implementation to save code.



来源:https://stackoverflow.com/questions/45103396/pinvoking-with-stringbuilder-in-a-unit-test

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!