Marshalling a char** in C# - c#

I am interfacing with code that takes a char** (that is, a pointer to a string):
int DoSomething(Whatever* handle, char** error);
Basically, it takes a handle to its state, and if something goes wrong, it returns an error code and optionally an error message (the memory is allocated externally and freed with a second function. That part I've figued out :) ).
I, however, am unsure how to handle in in C#. What I have currently:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern int DoSomething(IntPtr handle, byte** error);
public static unsafe int DoSomething(IntPtr handle, out string error) {
byte* buff;
int ret = DoSomething(handle, &buff);
if(buff != 0) {
// ???
} else {
error = "";
}
return ret;
}
I've poked around, but I can't figure out how to turn that into a byte[], suitable for feeding to UTF8Encoding.UTF8.GetString()
Am I on the right track?
EDIT: To make more explicit, the library function allocates memory, which must be freed by calling another library function. If a solution does not leave me with a pointer I can free, the solution is unacceptable.
Bonus question: As implied above, this library uses UTF-8 for its strings. Do I need to do anything special in my P/Invokes, or just use string for normal const char* parameters?

You should just be able to use a ref string and have the runtime default marshaller take care of this conversion for you. You can hint the char width on the parameter with [MarshalAs(UnmanagedType.LPStr)] to make sure that you are using 8-bit characters.
Since you have a special deallocation method to call, you'll need to keep the pointer, like you've already shown in your question's example.
Here's how I'd write it:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern int DoSomething(
MySafeHandle handle, void** error); // byte** should work, too, I'm just lazy
Then you can get a string:
var errorMsg = Marshal.PtrToStringAnsi(new IntPtr(*error));
And cleanup:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int FreeMyMemory(IntPtr h);
// ...
FreeMyMemory(new IntPtr(error));
And now we have the marshalled error, so just return it.
return errorMsg;
Also note the MySafeHandle type, which would inherit from System.Runtime.InteropServices.SafeHandle. While not strictly needed (you can use IntPtr), it gives you a better handle management when interoping with native code. Read about it here: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle.aspx.

For reference, here is code that compiles (but, not tested yet, working on that next tested, works 100%) that does what I need. If anyone can do better, that's what I'm after :D
public static unsafe int DoSomething(IntPtr handle, out string error) {
byte* buff;
int ret = DoSomething(handle, &buff);
if(buff != null) {
int i = 0;
//count the number of bytes in the error message
while (buff[++i] != 0) ;
//allocate a managed array to store the data
byte[] tmp = new byte[i];
//(Marshal only works with IntPtrs)
IntPtr errPtr = new IntPtr(buff);
//copy the unmanaged array over
Marshal.Copy(buff, tmp, 0, i);
//get the string from the managed array
error = UTF8Encoding.UTF8.GetString(buff);
//free the unmanaged array
//omitted, since it's not important
//take a shot of whiskey
} else {
error = "";
}
return ret;
}
Edit: fixed the logic in the while loop, it had an off by one error.

Related

PInvoke marshalling const char * fails with UnmanagedType.LPStr

I'm failing to understand why the .NET Core marshalling is failing to work with the one marshakking approach but not another. In the example below Marshal.PtrToStringAnsi can successfully marshal a string from the native dll but the same call with the [return: MarshalAs(UnmanagedType.LPStr)] attribute cannot? Or is the marshalling from the IntPtr just getting lucky and could also access invalid memory depending on the OS memory management?
The C library code is (compiled as C not C++):
static const char str[] = "Hello World";
__declspec(dllexport) const char* getstr() {
return str;
}
C# test program:
using System;
using System.Runtime.InteropServices;
namespace CLibraryPinvoke
{
class Program
{
public const string LIB_NAME = "CLibrary";
[DllImport(LIB_NAME, EntryPoint = "getstr")]
public static extern IntPtr getstrgood();
[DllImport(LIB_NAME, EntryPoint ="getstr")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string getstrbad();
static void Main(string[] args)
{
Console.WriteLine($"getstr={Marshal.PtrToStringAnsi(getstrgood())}.");
// Crash.
Console.WriteLine($"getstr={getstrbad()}.");
}
}
}
Execution result:
getstr=Hello World.
CLibraryPinvoke\bin\Debug\netcoreapp3.1\CLibraryPinvoke.exe (process 31848) exited with code -1073740940.
PtrToStringAnsi copies the string from the buffer pointed to by the return value.
UnmanagedType.LPStr copies the string from the buffer pointed to by the return value, and releases it with CoTaskMemFree. Releasing a static const char[] with CoTaskMemFree crashes the program.
This is because the semantic of the return value is that the memory that holds the result was allocated on the called side.
As documented, if the memory is allocated on the called side, but should not be released with CoTaskMemFree, you must marshal it as IntPtr and release it yourself using the appropriate method.
The appropriate method for memory backed up by a static const char[] is to leave it alone.
This requires the user of your function to have knowledge of implementation details of your function, which is not a good thing. Of course, you can document the function as returning memory that should never be released, but then it would probably not be a very useful function, and if you were to change its behaviour in the future, you would be stuck with returning one of the possible static const char[]s.
You can avoid all that by allocating the memory each time and letting the .NET runtime to manage the rest, or by making the function accept a char* argument to copy the response into.

Crash marshalling string to .NET [duplicate]

I am using DllImport to call method in c wrapper library from my own .net class. This method in c dll creates a string variable and returns the pointer of the string.
Something like this;
_declspec(dllexport) int ReturnString()
{
char* retval = (char *) malloc(125);
strcat(retval, "SOMETEXT");
strcat(retval, "SOMETEXT MORE");
return (int)retval;
}
Then i read the string using Marshall.PtrToStringAnsi(ptr). After i get a copy of the string, i simply call another c method HeapDestroy which is in c wrapper library that calls free(ptr).
Here is the question;
Recently while it is working like a charm, I started to get "Attempted to read or write protected memory area" exception. After a deeper analysis, i figured out, i beleive, although i call free method for this pointer, value of the pointer is not cleared, and this fills the heap unattended and makes my iis worker process to throw this exception. By the way, it is an web site project that calls this method in c library.
Would you kindly help me out on this issue?
Sure, here is C# code;
[DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private extern static int ReturnString();
[DllImport("MyCWrapper.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private extern static void HeapDestroy(int ptr);
public static string GetString()
{
try
{
int i = ReturnString();
string result = String.Empty;
if (i > 0)
{
IntPtr ptr = new IntPtr(i);
result = Marshal.PtrToStringAnsi(ptr);
HeapDestroy(i);
}
return result;
}
catch (Exception e)
{
return String.Empty;
}
}
What may be the problem is the underlying C code. You are not adding a NULL terminator to the string which strcat relies on (or checking for a NULL return from malloc). It's easy to get corrupted memory in that scenario. You can fix that by doing the following.
retval[0] = '\0';
strcat(retval, "SOMETEXT");
Also part of the problem is that you are playing tricks on the system. It's much better to write it correctly and let the system work on correctly functioning code. The first step is fixing up the native code to properly return the string. One thing you need to consider is that only certain types of memory can be natively freed by the CLR (HGlobal and CoTask allocations). So lets change the function signature to return a char* and use a different allocator.
_declspec(dllexport) char* ReturnString()
{
char* retval = (char *) CoTaskMemAlloc(125);
retval[0] = '\0';
strcat(retval, "SOMETEXT");
strcat(retval, "SOMETEXT MORE");
return retval;
}
Then you can use the following C# signature and free the IntPtr with Marshal.FreeCoTaskMem.
[DllImport("SomeDll.dll")]
public static extern IntPtr ReturnString();
Even better though. When marshalling, if the CLR ever thinks it needs to free memory it will use FreeCoTaskMem to do so. This is typically relevant for string returns. Since you allocated the memory with CoTaskMemAlloc you can save yourself the marshalling + freeing steps and do the following
[DllImport("SomeDll.dll", CharSet=Ansi)]
public static extern String ReturnString();
Freeing memory doesn't clear it, it just frees it up so it can be re-used. Some debug builds will write over the memory for you to make it easier to find problems with values such as 0xBAADFOOD
Callers should allocate memory, never pass back allocated memory:
_declspec(dllexport) int ReturnString(char*buffer, int bufferSize)
{
if (bufferSize < 125) {
return 125;
} else {
strcat(buffer, "SOMETEXT");
strcat(buffer, "SOMETEXT MORE");
return 0;
}
}
Although memory is allocated by the DLL in the same heap as your application, it MAY be using a different memory manager, depending on the library it was linked with. You need to either make sure you're using the same exact library, or add code to release the memory that the DLL allocates, in the DLL code itself.

void ** handles with P/Invoke

I am using a C API DLL from a 3rd party vendor. The problem I have is that I can't seem to find a good template for Marshalling the following C code:
API_Open( void ** handle );
API_Close( void * handle );
The calls are simplified, but the handle is a void *, which is (in C) passed into the API_Open call as &handle, and then passed into API_Close as handle.
I've tried to do the same in C#, but can't figure out how to Marshal handle properly. My C# version (latest try) is:
[DllImport("External.dll",EntryPoint="API_Open")]
public static extern int API_Open( out IntPtr handle );
[DllImport("External.dll",EntryPoint="API_Close")]
public static extern int API_Close( IntPtr handle );
public static int Wrapper_API_Open( ref Int32 handle )
{
int rc = SUCCESS;
// Get a 32bit region to act as our void**
IntPtr voidptrptr = Marshal.AllocHGlobal(sizeof(Int32));
// Call our function.
rc = API_Open(out voidptrptr);
// In theory, the value that voidptrptr points to should be the
// RAM address of our handle.
handle = Marshal.ReadInt32( Marshal.ReadIntPtr(voidptrptr) );
return rc;
}
public static int Wrapper_API_Close(ref Int32 handle)
{
int rc = SUCCESS;
// Get a 32bit region to act as our void *
IntPtr voidptr = Marshal.AllocHGlobal(sizeof(Int32));
// Write the handle into it.
Marshal.WriteInt32(voidptr,handle);
// Call our function.
rc = API_Close(voidptr);
return rc;
}
public void SomeRandomDrivingFunction()
{
.
.
.
Int32 handle;
Wrapper_API_Open( ref handle );
.
.
.
Wrapper_API_Close( ref handle );
.
.
.
}
The API return code is always INVALID_DEVICE_OBJECT when I call API_Close. Any thoughts? I thought this would be pretty straightforward, but I'm having trouble wrapping my head around the void** and void* parts of the function calls.
Thanks
You seem to be over-complicating this significantly. I don't know why you want to introduce Int32 for the handles since they do need to be pointer sized. You should use IntPtr.
The API_Open accepts the address of the variable where the handle is returned. The caller allocates that variable and passes it to the callee, which populates the variable. The C function might look like this:
int API_Open(void **handle)
{
*handle = InternalCreateHandle();
return CODE_SUCCESS;
}
You'd call that in C like this:
void *handle;
int retval = API_Open(&handle);
if (retval != CODE_SUCCESS)
// handle error
// go one and use handle
Translated to C#, the void* maps to IntPtr, and the use of the double pointer is just a means to get around the fact that C only supports pass-by-value. In C# you would use pass-by-reference.
For API_Close it is even simpler because we are passing the handle by value:
int API_Close(void *handle)
{
InternalCloseHandle(handle);
return CODE_SUCCESS;
}
And the calling code is simply:
int retval = API_Close(handle);
if (retval != CODE_SUCCESS)
// handle error
So, the C# wrapper functions should be:
public static int Wrapper_API_Open(out IntPtr handle)
{
return API_Open(out handle);
}
public static int Wrapper_API_Close(IntPtr handle)
{
return API_Close(handle);
}
At which point these wrapper methods do look somewhat pointless!

C++ Access violation when calling dll function

I have a function definition in my VC++ Win32 DLL
DEMO2_API void ProcessData(char* i_buff, unsigned short i_len, char* o_buf,
unsigned *o_len, unsigned short *errorCode)
{
__describe (i_buff,&i_len,o_buf,o_len,errorCode);
}
This dll function is called by a c# application.
When called, it generate access violation exception.
After reasearching i found, the cause for my problem.
http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/6e843243-baf4-4eb1-8a20-c691ad47762c
But could not understand what exactly they are doinng in example code.
Can someone explain it so me?
And what would be P/Invoke signature in c# after externally allocating memory?
C# uses IntPtr to represent externally allocated memory. C# pointers and references can only be used with memory provided by the garbage collector.
The System.InteropServices.Marshal class provides some methods for interacting with native memory areas represented by IntPtr, of course they aren't typesafe.
But I don't see anything in your function that could return a pointer to allocated memory. You'd need a double-pointer argument, or a pointer return value, and you have neither.
EDIT to add example as requested:
// this doesn't work right
void external_alloc_and_fill(int n, int* result)
{
result = new int[n];
while (n-- > 0) { result[n] = n; }
}
extern external_alloc_and_fill(int n, int* result)
int a = 5;
fixed (int* p = &a) {
external_alloc_and_fill(17, p);
// p still points to a, a is still 5
}
better:
// works fine
void external_alloc_and_fill2(int n, int** presult)
{
int* result = *presult = new int[n];
while (n-- > 0) { result[n] = n; }
}
extern external_alloc_and_fill2(int n, ref IntPtr result)
int a 5;
IntPtr p = &a;
external_alloc_and_fill2(17, ref p);
// a is still 5 but p is now pointing to the memory created by 'new'
// you'll have to use Marshal.Copy to read it though
I changed passing mode of O_len to out instead of ref and it works.
Thnaks everyone for giving nice answers and comments. I hope this would be useful for other community members ( plus those googling...)

Invalid Variant crash

I have a situation where I've wrapped a Native C++ DLL with C++/CLI for eventual use in C#.
There are a few callback functions that are causing some issues at run time. Particularly, I get the following exception:
An unhandled exception of type
'System.Runtime.InteropServices.InvalidOleVariantTypeException'
occurred in ToadWrapTest.dll
Additional information: Specified OLE
variant is invalid.
On this line of code (C++/CLI):
public delegate int ManagedCallbackFunction (Object^ inst, const Object^ data);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
System::IntPtr ip1 = IntPtr(pInstance);
System::IntPtr ip2 = IntPtr(temp);
Object^ oInst = Marshal::GetObjectForNativeVariant(ip1);
Object^ oData = Marshal::GetObjectForNativeVariant(ip2);
//invoke the callback to c#
//return m_callbackFn::Invoke(oInst, oData);
return 0;
};
The reason I've made this "intermediary callback" was an attempt to circumvent the Invalid variant exception being thrown when I tried to directly map the delegate from C# to the native C++ code. As an attempted work-around, I declare a delegate on the C# side and pass that funcptr to the C++/CLI wrapper. I then pass the intermediary funcptr to the native C++ and just daisy chain the calls together.
What I know is that it all works in native C++ world. The problem is mapping the void* to the managed world. The following code shows the native C++ version of the callback:
int (*CallbackFunction) (void *inst, const void *data);
If anyone can help here, I'd really appreciate it.
Are pInstance and pData really VARIANT? If they are, I would expect your callback function to be more strongly typed:
int (*CallbackFunction)(VARIANT *inst, VARIANT *data);
If that's the case, in your code you should be able to look at the actual VARIANT to hand check it. If you are not really getting VARIANTs (ie, you are really just getting void * pointers), you shouldn't try to turn them into C# objects since there is no inherent meaning to them. They should get passed through as IntPtr. If you know that they should have some other type of inherent meaning, you need to marshal them as appropriate types.
Big Thanks to plinth on this one! I am posting the final solution below to anyone else who has to deal with 3rd party fun like this one! Please feel free to critique, as I am not done optimizing the code. This may still be to roundabout a solution.
First, the callback functions became:
public delegate int ManagedCallbackFunction (IntPtr oInst, IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
Big props on this one. It just plain won't work if you try to cast from void* directly to Object^. Using the IntPtr and my intermediary callback:
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
return m_callbackFn->Invoke(IntPtr(pInstance), IntPtr(temp));
};
We finally get a working model on the C# side with some massaging of the objects:
public static int hReceiveTestMessage(IntPtr pInstance, IntPtr pData)
{
// provide object context for static member function
helloworld2 hw = (helloworld2)GCHandle.FromIntPtr(pInstance).Target;
if (hw == null || pData == null)
{
Console.WriteLine("hReceiveTestMessage received NULL data or instance pointer\n");
return 0;
}
// populate message with received data
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataPacketWrap(pData)));
DataPacketWrap dpw = (DataPacketWrap)GCHandle.FromIntPtr(ip2).Target;
uint retval = hw.m_testData.load_dataSets(ref dpw);
// display message contents
hw.displayTestData();
return 1;
}
I mention "massaging" the objects because the delegate is not specific to this callback function and I don't know what object pData will be until run time(from the delegates POV). Because of this issue, I have to do some extra work with the pData object. I basically had to overload the constructor in my wrapper to accept an IntPtr. Code is provided for full "clarity":
DataPacketWrap (IntPtr dp)
{
DataPacket* pdp = (DataPacket*)(dp.ToPointer());
m_NativeDataPacket = pdp;
};

Categories

Resources