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.