Home > Back-end >  Convert c std::string to c# byte array
Convert c std::string to c# byte array

Time:06-10

I have a .NET program that uses a DLL export to get a name of a user.

public static extern string Name(byte[] buf);

This is the export, and I would really like to not change it, as a lot of code relies on it. So, I would like to know, in C , how would I convert a char* array to the byte buffer?

I have tried this:

void Name(std::byte buf[256])
{
    std::string s{ "test" };
    std::byte* ptr = reinterpret_cast<std::byte*>(s.data());
    buf = ptr;
    return;
}

When I print out the string that I convert, I get nothing, it is empty.

CodePudding user response:

Your C function implementation does not match the expectations of the C# function declaration.

The C# byte array is marshalled into the function as a pinned pointer to the array's raw memory. The syntax you are using in the C code for the parameter (std::byte buf[256]) is just syntax sugar, the compiler actually treats it as a pointer (std::byte* buf). Which is fine in this situation. However, your C function is not actually copying anything into the memory that the pointer is pointing at. You are simply changing the pointer itself to point at a different memory address. And the pointer itself is a local variable, so when the function exits, the pointer will no longer exist, and it won't matter what it is pointing at.

Also, the C# declaration is expecting the function to return something that can be marshalled to a .NET string, but the C function is not actually return'ing anything at all. The default marshalling behavior for a string return value is as UnmanagedType.LPStr, so the C function needs to return a pointer to a null-terminated char* string. The memory for that string must be allocated with CoTaskMemAlloc(), as the marshaller will take ownership of the memory and free it with CoTaskMemFree() after converting the char data to a string.

Also, the C function has no calling convention defined, so it is going to use the compiler's default (which is usually __cdecl, unless you change your compiler's settings). However, the default calling convention that .NET's DllImport expects the C function to use is __stdcall instead (for compatibility with Win32 APIs). This mismatch won't matter in 64bit, but it matters alot in 32bit.

Without changing your C# declaration, try this on the C side:

char* __stdcall Name(std::byte buf[256])
{
    std::string s{ "test" };
    size_t size = s.size()   1;
    memcpy(buf, s.c_str(), size);
    void *ptr = CoTaskMemAlloc(size);
    memcpy(ptr, s.c_str(), size);
    return ptr;
}

That being said, it is weird to have a function that returns a string in both a byte array output parameter and in a return value. Are you sure the byte array is not meant to be used as an input parameter instead, where the function parses its content to extract amd a string? That would make more sense. It would really help if you would update your question to show how your .NET code is actually calling the C function and using the byte array.

CodePudding user response:

When you write

void Name(std::byte buf[256])

that declares buf as pointer to 256 bytes, not an array. So when you later write

buf = ptr;

all you are doing is changing the local variable buf to now point at ptr. And at the end of the function the local variable dies.

Use std::array<std::byte, 256>& and copy the string contents into that. Or even better return a freshly made array instead of an in/out parameter.

  •  Tags:  
  • c# c
  • Related