0

I connected a dll to my c# code to return a string from it:

extern "C" __declspec(dllexport) Dll_NIRS_API void realize(char*) 
void realize(char* buf) 
{
...
    wstring full_text;
    // operations with full_text 

    char* fill = (char*)(full_text.c_str());
    strcpy(buf, fill);

}

Connecting to C#:

[DllImport("dll_name.dll", CharSet = CharSet.Unicode)]
        public static extern void realize(StringBuilder text);

Then I use this function and put the resulting string in a textBox:

            StringBuilder text = new StringBuilder(100);
            realize(text);
            textBox4.Text = text.ToString();

But instead of text: РАЗВОРОТ(1, 1.1, 1.2, 1.3, 1.4) РАЗВОРОТ(1, 2.1, 2.2, 2.3, 2.4) the textBox outputs this: РАЗВОРОТ(듹翼

The "full_text" variable is filled in correctly, but I'm not sure about the "fill" variable, maybe the problem is in strcpy or in the dll export. I use StringBuilder on the advice from this question: Passing strings from C# to C++ DLL and back -- minimal example

EDIT I partially solved the problem, now the entire text is displayed in the textBox (and not just part of it, as it was before). I just converted full_text from wstring to string for use in strcpy. I also changed the CharSet.Unicode to CharSet.Ansi. However, the text is now like this: Р РђР—Р’РћР РћРў(1, 1.1, 1.2, 1.3, 1.4)Р РђР—Р’РћР РћРў(1, 2.1, 2.2, 2.3, 2.4)

Daria
  • 3
  • 2
  • Does not `realize(text);` cause trouble as `void realize(char* buf)` expects a `char *` and not a `StringBuilder`? – chux - Reinstate Monica Nov 21 '22 at 09:26
  • I found such a solution with ```stringBuilder``` to use strings from c dll in c# – Daria Nov 21 '22 at 09:35
  • @Daria If your comment means that you tried to follow an example you found somewhere, please [edit] your question and add a link to this example. Your C# prototype of function `realize` does not match the C++ prototype and implementation, so it is undefined behavior. Even if the memory of a `StringBuilder` object is compatible to a C string, there might be different encodings. – Bodo Nov 21 '22 at 10:25
  • I edited the question and added a link – Daria Nov 21 '22 at 11:14
  • You would need to specify `CharSet = CharSet.Ansi` or change the `buf` parameter to `wchar_t*`. Beware, your code is dangerous as `strcpy` will happily overwrite memory you did not allocate. Usually, you would pass another parameter to the C function specifying the maximum string size. – Klaus Gütter Nov 21 '22 at 12:00
  • @Klaus Gütter I changed CharSet to CharSet.Ansi and converted ```full_text``` from ```wstring``` to ```string```. It got better, but I also see unusually symbols (I edited my question) – Daria Nov 21 '22 at 12:52
  • Encoding issue. Is your `string` perhaps in UTF8? – Klaus Gütter Nov 21 '22 at 13:05
  • 1
    @Klaus Gütter yes, its UTF8. Should I change to UTF16? How I can do it? – Daria Nov 21 '22 at 13:10
  • If this is possible: yes. Then go back to CharSet.Unicode and wstring, but change the parameter to `wchar_t*` and `strcpy` to `wcscpy`. – Klaus Gütter Nov 21 '22 at 13:17

1 Answers1

0

There are two (easy) string marshalling options using P/Invoke:

  • Either declare [CharSet = CharSet.Ansi] in C# and have char* in C. This requires that the C library uses the "ANSI" character set, which seems not to be the case here.
  • Or declare [CharSet = CharSet.Unicode] in C# and have wchar_t* in C

For the second option, the last statements should be:

wchar_t* fill = full_text.c_str();
wcscpy(buf, fill);

Big caveat: strcpy/wcscpy is dangerous as it may overwrite memory you did not allocate. I strongly recommend replacing it with:

wcscpy_s(buf, size, fill);

size would be the buffer size (max number of characters, including terminating NUL) and has to be passed ad additional parameter to the method.

Klaus Gütter
  • 11,151
  • 6
  • 31
  • 36