Char * marshalling in C# - c#

I have this function in Visual C++ DLL
char * __stdcall GetMessage(int id) {
char buffer[250];
.
.
.
strcpy(buffer, entry); // entry is a char * that has allocated 250 chars
return (char *)buffer;
}
i am trying to import this function from C# with the following code
[DllImport("mydll.dll", CharSet=CharSet.Ansi)]
public static extern string GetMessage(int id);
I am trying to present this in a MessageBox it always has strange symbols in the end of the string. Any suggestions how to overcome this problem?

I'll re-emphasize the fact that your C++ code is invalid. You are returning a pointer to a local variable on a stack frame that is no longer valid when the function returns. That it works now is merely an accident. As soon as you call another function, the stack space will be reused, corrupting the buffer content. This is guaranteed to happen when you call this code from C#, the P/Invoke marshaller implementation will overwrite the stack frame and corrupt the buffer.
To make matters worse, the P/Invoke marshaller will try to free the buffer. It will assume that the buffer was allocated by CoTaskMemAlloc() and call CoTaskMemFree() to release the buffer. That will silently fail on Windows XP and earlier but crash your program on Vista and up.
Which is one solution to your issue, use CoTaskMemAlloc() to allocate the buffer instead of using a local variable. The all-around better solution is to let the caller of the function pass the buffer, to be filled with the string. That way, the caller can decide how to allocate the buffer and clean it up as necessary. The function signature should look like this:
extern "C" __declspec(dllexport)
void __stdcall GetMessage(int id, char* buffer, size_t bufferSize);
Use strcpy_s() to safely fill the buffer and avoid any buffer overflow. The corresponding C# declaration would then be:
[DllImport("mydll.dll")]
private static extern void GetMessage(int id, StringBuilder buffer, int bufferSize);
and called like this:
var sb = new StringBuilder(250);
GetMessage(id, sb, sb.Capacity);
string retval = sb.ToString();

From the DLL you are returning an array of chars i.e. buffer which is on the stack within the GetMessage function, and when the return is executed the data is messed up since the buffer is popped off the stack and is local to the stack also, hence the strange characters you are seeing. There is two ways you can fix this, change the buffer variable declaration to be of a pointer and malloc it before returning it or declare it as a static.
char *buffer;
if (!(buffer = (char *)malloc((250 * sizeof(char)) + 1)){
// Handle the out of memory condition
}
// Now the buffer has 250 + 1 for null terminating character.
Or
static char buffer[250];
Also can you clarify entry pointer variable - is that global?
Hope this helps,
Best regards,
Tom.

Try using a StringBuilder for the return type and leave out the CharSet attribute:
[DllImport("mydll.dll")]
public static extern StringBuilder GetMessage(int id);

Related

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

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.

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.

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
}_

Using C++ native dll in .Net External component has thrown an exception

I'm trying to use a native C++ dll in C# and am getting the "External component has thrown an exception" with an error code of -2147467259.
C#:
[DllImport(#"MyDLL.dll", CharSet = CharSet.Auto)]
public static extern string MyFunction([MarshalAs(UnmanagedType.LPStr)] StringBuilder input);
C++:
__declspec(dllexport) char * __stdcall MyFunction(const char* inputPtr);
The function works just fine in C++. How can I track this error down?
I have tried using string and string builder for the parameter.
Update
I found this article http://tom-shelton.net/index.php/2008/12/11/creating-a-managed-wrapper-for-a-lib-file/
It details a way to use managed C++ to wrap an unmanaged static library in C++ which can then be used in a managed language. Would this be a good way of going about tackling this problem? Can the lib be an unmanaged dll?
try the technique from http://blog.rednael.com/2008/08/29/MarshallingUsingNativeDLLsInNET.aspx - this has saved the day several times :-)
If the parameter is in-only (and not out), stringbuilder is not required. If it's an out parameter (or ref) you should use stringbuilder and pre-allocate the buffer using stringbuilder constructor.
I can guess the problem is that you are returning an Ansi string instead of the expected unicode string. This cause the default pinvoke marshaler to read too much memory.
Try this:
[DllImport(#"MyDLL.dll", CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.LPStr)]
public static extern string MyFunction([MarshalAs(UnmanagedType.LPStr)] string input);
In any case, in the vast majority of times, it does not make any sense to write Ansi C++ code. I would suggest to convert the C++ code to unicode only (not tchar but wchar_t).
CharSet = CharSet.Auto?? use CharSet.Ansi
You cannot use a char array as return, a block of memory allocated with c++ cannot be used by C#! In your case you probably need to copy it into C# managed memory.
Use StringBuilder for that or MarshalAs attribute that will copy for you the buffer into C# managed memory.
You have to change your C++ function, your C++ function must write into the destination buffer that must be allocate so it contains at least the number of characters you need plus one (for null termination character).
[DllImport(#"MyDLL.dll", CharSet = CharSet.Ansi)]
private static extern void MyFunction([MarshalAs(UnmanagedType.LPStr)] string input, StringBuilder result);
public static string MyFunctionPublic(string input)
{
StringBuilder sb = new StringBuilder(input.Length + 1);
MyFunction(input, sb);
return sb.ToString();
}
And i expect your C function to do something like this:
void __stdcall MyFunction(const char* input, char* result)
{
strcpy(result, input); // this is a dummy stupid code to show how it works.
}
Probably you will need a function that gives you idea of how much bytes you have to allocate in C#. This function can be something like this:
int __stdcall ComputeMyFunctionBytes(const char* input)
{
return strlen(input); // this is a dummy stupid code to show how it works.
}
Never return a piece of allocated memory to C#, it cannot do anything with that, nor deallocating, nor reading and nor writing, until you use unsafe code, but this is not what you are trying to do.
A good article seems to be here: http://www.devx.com/dotnet/Article/6990/1954
Also note that C# uses unicode, 16 bit per character, so it will be converted from ansi to unicode.

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