Convert c++ std::string to c# byte array - c#

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.

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.

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.

Related

PInvoke: Issue with returned array of doubles?

I am using PInvoke to call a C++ function from my C# program. The code looks like this:
IntPtr data = Poll(this.vhtHand);
double[] arr = new double[NR_FINGERS /* = 5 */ * NR_JOINTS /* = 3*/];
Marshal.Copy(data, arr, 0, arr.Length);
With Poll()'s signature looking like this:
[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(IntPtr hand);
The C-function Poll's signature:
extern "C" __declspec(dllexport) double* Poll(CyberHand::Hand* hand)
Unless I'm having a huge brain failure (admittedly, fairly common for me), this looks to me like it should be working.
However, the double values I am getting are completely incorrect, and I think this is because of incorrect memory usage. I have looked it up, and I think doubles in C# and C++ are identical in size, but maybe there is some other issue playing here. One thing that rubs me the wrong way is that Marshal.Copy is never told what type of data it should expect, but I read that it is supposed to be used this way.
Any clues, anyone? If needed, I can post the correct results and the returned results.
You are missing the CallingConvention property, it is Cdecl.
You really want to favor a better function signature, the one you have is extremely brittle due to the memory management problem, the required manual marshaling, the uncertainty of getting the right size array and the requirement to copy the data. Always favor the caller passing a buffer that your native code fills in:
extern "C" __declspec(dllexport)
int __stdcall Poll(CyberHand::Hand* hand, double* buffer, size_t bufferSize)
[DllImport("foo.dll")]
private static extern int Poll(IntPtr hand, double[] buffer, int bufferSize)
Use the int return value to report a status code. Like a negative value to report an error code, a positive value to return the number of elements actually copied into the buffer.
You shouldn't even need to marshal the data like that, as long as you declare the P/Invoke correctly.
If your CyberHand::Hand* is in reality a pointer to a double, then you should declare your P/Invoke as
[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(double[] data);
And then just call it with your array of doubles.
If it isn't really an array of doubles, then you certainly can't do what you're doing.
Also, how does your 'C' function know how big the array will be? Is it a fixed size?
The IntPtr return value will be a problem. What is the double* pointing to? An array or a single item?
You could find that it's easier (if you can) to write a simpler more friendly 'C' wrapper for the function you're calling, and call the wrapper function itself. You can of course only do that if you can change the source code of the 'C' DLL. But without knowing exactly what your function does, I can't give you specific advice.
[EDIT]
Ok, your code should theoretically work if the memory being passed back isn't being messed around with (e.g. freed up). If it's not working, then I suspect something like that is happening. You'd definitely be better writing a wrapper 'C' function that fills in an array allocated by the C# and passed to the function, rather than passing back a pointer to some internal memory.
BTW: I don't like code which passes around pointers to blocks of memory without also passing the size of that block. Seems a bit prone to nasty things.

Returning a unsigned long array from C dll into C# as uint[] why is a MarshalDirectiveException error thrown?

I am using a pointer to a unsigned long array (manipulate data) then send it back to C#
in C#
[DllImport("some_dll.dll")]
private static extern uint[] func(uint[]x, uint[]y, uint[]z);
C header
_declspec(dllexport) unsigned long* _stdcall func(unsigned long[],
unsigned long[],unsigned long[]);
error
MarshalDirectiveException
Cannot marshal 'return value': Invalid managed/unmanaged type combination
Kindly let me know what is causing the problem.
The message means that the p/invoke marshaller isn't capable of marshalling that return value into a uint[].
As I see it you have the following options:
Declare the C# function as returning IntPtr. Then on the managed side you need to copy the memory into a uint[] allocated in your C# code. You can use Marshal.Copy to do that. Somehow you'll need to find out the length of the array. You also need to handle deallocation. Your C# code cannot do that. So it will have to call another function in the native code and ask the native code to deallocate.
Allocate the uint[] in the C# code before the call to your native code. Instead of using a function return value, you pass the uint[] as a parameter. This requires the calling code, the C# code, to know how big the array needs to be.
If you can choose option 2, it will result in simpler code on both sides of the interface. My guess is that the return array is the same length as the input arrays. In which case choose option 2.

Trying to get a string value from native dll

I have a native dll that I need to set a string value of a parameter. Below is the c++ method signature
__declspec(dllexport) void __stdcall getDetails(_Out_ LPTSTR lpDetails, _In_ UINT uSize)
c++ method
I've hardcoded this for the return lpDetails=L"test";
My c# signature
[return: MarshalAs(UnmanagedType.I1)]
[DllImport("test.dll",CharSet=CharSet.Auto)]
static extern bool getDetails(StringBuilder result, System.UInt32 uSize);
c# code
StringBuilder b = new StringBuilder(1000);
getDetails(b, 255);
m = b.ToString(); //always ""
Console.WriteLine(m);
When I step through the code I see the value (lpDetails) get set to the correct value. The problem is on the c# all I ever see is an empty string.
I have tried a things I found from other posts but so far no luck.
There are few things wrong here:
native method is shown with C signature, not C++ signature (I am
nitpicking here :)
lPDetails parameter is IN, not OUT, if you want
function to write details to a preallocated location (as it seems
from the second argument which should denote the length of the
allocated buffer)
your implementation lpDetails=L"test" is broken. If you provide the preallocated buffer to the function (as your signature implies) you need to _tcscpy the details into lpDetails. If you want to allocate the string inside the function (as your implementation implies, you need to declare lpDetails as LPTSTR*, but this is a bad design as caller might not now how to free it (and in this case indeed it cant as "test" points to a static memory in data segment and not to the heap.
This wouldn't work even without Interop:
LPTSTR pCallerData; // a pointer variable
void getDetails(LPTSTR lpData, /*what do you use second param for? */)
{
// lpData is a COPY of pCallerData, not a pointer to it,
// so you are not modifying pCallerData, but a local variable
lpData = "test";
// after the function exits pCallerData still has the same value as before
}_
better implementation would be:
LPTSTR txt[256];
getDetails(txt, 256);
// ----
void getDetails(LPTSTR lpData, int size)
{
_tcscpy(lpDetails, _T("Test"));
// or more realistically _tcsncpy and properly check
// if details string is longer than size argument
}_

Passing C# struct to C++/CLI for C++ wrapper

After posting a question yesterday I thought I had this cleared up but I'm still having problems, I have a C++/CLI wrapper for a C++ class, some functions of the C++ class take buffers for recv as parameters, the packet structures are defined as C++ structs and that is what is taken as a parameter.
In C# I have replicated these C++ structs using structlayout so that I have equivalent structs in C# which are laid out the same in memory as my C++ structs. In my C++/CLI code I attempted the following
UINT GetValues(value class^ JPVals) // value class, as C# structs are value types
{
IntPtr ptr;
Marshal::StructureToPtr(JPVals,ptr,false);
return m_pComms->GetValues(ptr,0); // m_pComms is a wrapped unmanaged class
//GetValues takes a pointer to a C++ struct
}
The error I get is cannot convert parameter 1 from 'System::IntPtr' to 'SJPVal *', why is it not possible to Marshall from value class to C++ struct pointer? And in this case what should I be passing in and how should I be marshalling it?
You didn't get the serialization process:
// !! Note the % !!
UINT GetValues(value class% JPVals) // value class, as C# structs are value types
{
// Allocate a buffer for serialization, pointer to NULL otherwise
IntPtr ptr = Marshal::AllocHGlobal(Marshal::SizeOf(JPVals));
try {
// Serialize the managed object to "static" memory (not managed by the GC)
Marshal::StructureToPtr(JPVals, ptr, false);
// Pass it to unmanaged code that will modify it.
auto ret = m_pComms->GetValues(reinterpret_cast<SJPVal*>(ptr.ToPointer()), 0);
// Copies the modifications back
Marshal::PtrToStructure(ptr, JPVals);
// Free resources
Marshal::FreeHGlobal(ptr);
return ret;
} catch (...) {
// Make sure we free the memory
Marshal.FreeHGlobal(ptr);
throw;
}
}
EDIT: shown how to copy back the value.
As you are using a C# struct you need to pass it by reference to make sure the changes are copied back. Alternatively, the code will work the same with a C# class. The first step (StructureToPtr) is probably useless, now, since you probably don't care about what was in there before your call to GetValues.
By the way your naming convention is a bit bad. You should NOT start variable names by a capital letter in C++.

char * in managed code?

I have a c++ plus method that generates a value. i am calling this method from a c# application.
The C++ method is like this:
extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[])
The generate method returns an array of chars (sOut[]=returnvalue; return sOut;)
Now I'm calling this method from my c# app:
[DllImport("mydll.dll")]
static extern string generate(string sIn, string sOut);
As you can see, the return type in c# is string. What is happening is that the returned value in c# is not correct and it is corrupted. (The value is correct inside the generate method, but whenever i call it from c# to extract it, i get some erroneous value.)
Is it OK that my method in C# has a string return value while in c++ it's a char*?
Please share your comments, it is urgent, thanks.
Unfortunately, char* can be somewhat ambiguous. (Literally, it's a pointer to a single character, where you may or may not be able to get at other characters by dereferencing the pointer like an array.) Usually, though, it's a pointer to a null-terminated string of single bytes in ANSI encoding (what Microsoft calls an LPStr). By default, P/Invoke marshals instances of System.String as the BStr type, which is like a Unicode Pascal string (with a length at the beginning). You'll need to use the MarshalAs attribute to explicitly specify how you want the strings marshaled between managed and unmanaged code.
Most likely, you'll want to declare it like so:
[DllImport("mydll.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
static extern string generate(
[MarshalAs(UnmanagedType.LPStr)] string sIn,
[MarshalAs(UnmanagedType.LPStr)] out string sOut);
However, your function signature is confusing: is it returning the string, or is it setting an output variable? You may have to adjust the P/Invoke declaration to fit your needs.
Is it OK that my method in C# has a
string return value while in c++ it's
a char*?
It depends really.
If that C++ method is allocating the memory for the string and expecting the caller to deallocate it then you have problems...big problems. You could try calling Marshal.FreeHGlobal on it, but there is no guarentee that the C++ method actually allocated the memory on the global heap. Most APIs have you feed the pointer back into one of their methods to free the memory.
It is also possible that the char * is pointing to memory which does not need to be freed. In that case using a string should be fine.
You could have the C# declaration return an IntPtr and then call one of the Marhsal.PtrToStringXXX methods.
This method cannot be called safely from a native C++ application either. It returns a pointer to an array on the stack frame. Any call to another function will overwrite that stack frame and corrupt the array.
This probably works by accident in a C++ program right now because there is no function call after obtaining the return value of this function. This kind of luck is no longer available when the P/Invoke marshaller sits in between.
You will have to redesign the C++ function. You should give it arguments that allows the caller to pass its own buffer to be filled with the result. For example:
extern "C" void __stdcall generate(const char* input, char* output, int outputSize)
Call this in C# code by passing a StringBuilder for the output argument, properly initialized with a sufficient Capacity to receive the string. The outputSize argument ensures that the C++ function can avoid writing past the end of the buffer and corrupt the garbage collected heap. Don't ignore it.

Categories

Resources