C# DllImport and Marshaling char** - c#

I'm working in c# and I need to use this function from a c++ dll:
extern "C" char IMPEXP __stdcall service_GetParameter ( const char* parameter, const int value_lenght, char** value );
I have used it in c++ code as follow:
char *val = new char[256];
service_GetParameter("firmware_version", 255, &val);
AnsiString FirmwareVersion = val;
delete[] val;
How can I import this function and use it in c#?
Thanks in advance

If this function allocates memory and makes the caller responsible for freeing it, I'm afraid you'll have to manage this manually: Declare the parameter as a ref IntPtr and use the methods of the Marshal class to get a String with a copy of the pointed data.
Then call the appropriate function for freeing the memory (as Dirk said, we can't say more about this without more information on the function).
If it really must be allocated before calling, it should be something looking like this:
[DllImport("yourfile.dll", CharSet = CharSet.Ansi)]
public static extern sbyte service_GetParameter ( String parameter, Int32 length, ref IntPtr val);
public static string ServiceGetParameter(string parameter, int maxLength)
{
string ret = null;
IntPtr buf = Marshal.AllocCoTaskMem(maxLength+1);
try
{
Marshal.WriteByte(buf, maxLength, 0); //Ensure there will be a null byte after call
IntPtr buf2 = buf;
service_GetParameter(parameter, maxLength, ref buf2);
System.Diagnostics.Debug.Assert(buf == buf2, "The C++ function modified the pointer, it wasn't supposed to do that!");
ret = Marshal.PtrToStringAnsi(buf);
}
finally { Marshal.FreeCoTaskMem(buf); }
return ret;
}

I'd start with something like this:
[DllImport("yourfile.dll", CharSet = CharSet.Ansi]
public static extern Int32 service_GetParameter([MarshalAs(UnmanagedType.LPStr)] String szParameter, Int32 value_length, [Out] StringBuilder sbValue);

Related

Issue with API with char* return being caught by IntPtr in P/invoke

I am trying to P/Invoke a C++ API returning a char* which is a string in C#. I know this should be caught by IntPtr then convert the pointer to string using the Marshaler like so,
C++ API
char* WINAPI MY_API(const char *basebuf, char *strbuf)
{
return targetfunction(basebuf, strbuf);
}
C# P/Invoke
[DllImport("myDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
internal static extern IntPtr MY_API([MarshalAs(UnmanagedType.LPStr)] string basebuf,
[MarshalAs(UnmanagedType.LPArray), Out] byte[] strbuf);
C# Wrapper
public string MY_API_WRAPPER(string basebuf, out string strbuf)
{
byte[] strbufTemp = new byte[256];
IntPtr ret = MY_API(basebuf, strbufTemp);
string retstr = Marshal.PtrToStringAnsi(ret);
strbuf = MyDataConverter.ByteToString(strbufTemp);
return retstr;
}
The out parameter string is OK. but the returned string (converted from IntPtr) is garbage.
I can modify the C++ API to allocate the char* to Task Memory then free it in the C# Wrapper, but changing the native code was not an option. also it has a risk of memory leak if the API was used without the wrapper.
i was wondering what happened to the pointer when the API call ended?
Thanks to David, i was able to fix my problem. Since the return value of the function is a pointer to the basebuf parameter, it acquires garbage value when the function returns.
The basebuf should be an IntPtr not a string for the IntPtr return to get the correct value after the call. So it should be P/Invoked like this.
C# P/Invoke
[DllImport("myDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
internal static extern IntPtr MY_API(IntPtr basebuf,
[MarshalAs(UnmanagedType.LPArray), Out] byte[] strbuf);
C# Wrapper
public string MY_API_WRAPPER(string basebuf, out string strbuf)
{
byte[] strbufTemp = new byte[256];
IntPtr basebufPtr = Marshal.StringtoHGlobalAnsi(basebuf)
IntPtr ret = MY_API(basebufPtr , strbufTemp);
string retstr = Marshal.PtrToStringAnsi(ret);
strbuf = MyDataConverter.ByteToString(strbufTemp);
Marshal.FreeHGlobal(basebufPtr);
return retstr;
}

Pinvoke cdecl convention with char**

In summary:
I`m trying to use a C++ dll with cdecl calling convention all ran fine unless i get to this method signature:
int SaveToBuffer( char **buf, int *buf_size );
from what i have read i should use it like this:
[DllImport("entry.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "SaveToBuffer")]
private static int SaveToBuffer( ref sbyte[] buf, ref int buf_size );
This does not work if this function is called from C# program crashes.
I suppose this is related to Cdecl calling model and should use Marshal.AllocHGlobal(value),
I can`t imagine how should it be done correct.
I also tryed this:
[DllImport("entry.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "SaveToBuffer")]
private static int SaveToBuffer( IntPtr buf, ref int buf_size );
And then alocate enough memory
IntPtr data=Marshal.AllocHGlobal(128000);
int bufSize=128000;
var sCode=SaveToBuffer(data,bufSize ); /* value of scode idicate succses*/
Calling this way i get return value from SaveToBuffer indicating function succseeded but: bufSize returns to 0 and how should i read my data from IntPtr.
I`m completly stuck on this.
This is not an issue with the calling convention. The problem is in the buffer handling.
There's really only one sensible way to interpret the C++ argument types and the apparent intent to return an array of bytes. That is that the buffer is allocated and populated by the callee, and its address returned in buf. The buffer length is returned in buf_size.
With these semantics the function arguments cannot be marshalled automatically and you'll have to do it manually:
[DllImport("entry.dll", CallingConvention = CallingConvention.Cdecl)]
private static int SaveToBuffer(out IntPtr buf, out int buf_size);
Call like this
IntPtr buf;
int buf_size;
int retval SaveToBuffer(out buf, out buf_size);
// check retval
Then copy to byte array like this:
byte[] buffer = new byte[buf_size];
Marshal.Copy(buf, buffer, 0, buf_size);
The DLL will also need to export a function to deallocate the unmanaged buffer.

Passing array of strings from c# to c++ - memory management

In my code I have c DLL that accepts array of strings:
void Helper::ProcessEvent(PEVENT_RECORD pEvent,wchar_t** OutPutFormattedData)
I'm invoking it with this code:
[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
internal static extern uint ProcessEvent(
[In, Out]
ref EVENT_RECORD pEvent,
[In, Out]
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] ref string[] pResult);
In c++ code here is the main code I'm using to fill the array:
for(int i=0;i<list.Size;i++)
{
EventWrapperClass *EWC = new EventWrapperClass();
EWC = list.Events[i];
OutPutFormattedData[i] = new wchar_t [wcslen(HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()))+1];
wcscpy(OutPutFormattedData[i] ,HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()));
}
And the invoke code:
string[] strArr= new string[1];
NativeHelper.ProcessEvent(ref eventRecord, ref strArr);
I have two questions:
Why when I check the value of the passed array in c# after calling this function I see it's empty (data is exist in c++ code, I debugged it) ?
If I'm allocating memory in c++ dll, where I need to free it? in c++ or c#?
Many thanks!
Edit:
signture of c++:
static __declspec(dllexport) void ProcessEvent(PEVENT_RECORD pEvent, wchar_t** OutPutFormattedData);
If you allocate a (managed or unmanaged) buffer (or array or chars) in C# and then fill in C++:
1. Deallocate in C#.
2. Buffer should be [in] and not have ref or out in C# signature.
If you allocate a buffer (or array of chars) in C++:
1. Pass buffer as [out] out IntPtr.
2. Add deallocate method to C++ signature.
3. Copy buffer into C# buffer or new string
4. Call C++ deallocater from C# with IntPtr.
You can use System.Runtime.InteropServices.Marshal to allocate/deallocate unmanged memory within C#. Do not use to deallocate memory allocated outside .NET!
System.Runtime.InteropServices.Marshal can also be used to copy memory from external buffer (IntPtr) to .NET buffer or string.
Mark imported method as private and wrap with a public (or protected or internal) method that uses the .NET parameters (e.g. that uses System.Runtime.InteropServices.Marshal.Copy and calls external deallocate).
C++:
int ProcessEvent(PEVENT_RECORD eventData, wchar_t*& message)
{
// example code
message = new wchar_t[100];
if (message == 0)
return 1;
}
void DeallocateString(wchar_t* array)
{
delete[] arrayPtr;
}
wchar_t* ErrorCodeToMessage(int errorCode)
{
switch (errorCode)
{
case 0: return 0; // return NULL pointer
case 1: return L"No!!!";
default: return L"WTF!?";
}
}
C#:
[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
private static extern uint ProcessEventExternal(
[In, Out] ref EventData eventData,
[In, Out, MarshalAs(UnmanagedType.SysInt))] ref IntPtr resultMessages);
[DllImport("Helper.dll", EntryPoint = "DeallocateString")]
private static extern voidDeallocateStringExternal(
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr arrayPtr);
[DllImport("Helper.dll", EntryPoint = "ErrorCodeToMessage")]
private static extern
[return: MarshalAs(UnmanagedType.SysInt)] IntPtr
ErrorCodeToMessageExternal(int errorCode);
public string ProcessEvent(ref EventData eventData)
{
IntPtr resultPtr = IntPtr.Zero;
uint errorCode = ProcessEventExternal(eventData, ref resultPtr);
if (errorCode != null)
{
var errorPtr = ErrorCodeToMessageExternal(errorCode);
// returns constant string - no need to deallocate
var errorMessage = Marshal.PtrToStringUni(errorPtr);
throw new ApplicationException(errorMessage);
}
var result = Marshal.PtrToStringUni(resultPtr);
ExternalDeallocate(resultPtr);
return result;
}
I don't know about question 1, but about question 2:
for(int i=0;i<list.Size;i++)
{
EventWrapperClass *EWC = new EventWrapperClass();
EWC = list.Events[i];
...
}
In the first line, within the loop, you create a new object. In the second line, you assign a different object to your pointer, which is the one stored in list.Events[i].
At this point, nothing points to your first object any longer, and you have a memory leak for every iteration of the loop!
Change the two lines to
EventWrapperClass *EWC = list.Events[i];
as there is no need to create a new object.
Then, if you don't need the event any longer, delete it within the loop and be sure to clear the list afterwards. You may only do this if you are 100% sure the event is not needed any longer. This can easily produce dangling pointers!

A Problem in Pinvoke

I have the following function in C++ native dll, and I want to use it in a C# app.
DWORD __cdecl Foo(
LPCTSTR Input,
TCHAR** Output,
DWORD Options,
ErroneousWord** List = NULL,
LPDWORD Count = 0
);
Using Pinvoke
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count);
Calling code:
string output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y,out dummyError,out dummyCount);
I got the following exception
Attempted to read or write protected
memory. This is often an indication
that other memory is corrupt
P.S:
ErroneousWord is struct and I do not need its output, so I marshal it as object
That error more than likely means that you have a marshaling problem.
You don't show us what the ErroneousWord type is, but I assume it's some kind of class defined in your C++ code. My guess is that it's not being marshaled correctly to a .NET object.
Considering that it's a pointer (or a pointer to a pointer), try changing that parameter to an IntPtr type to represent a pointer, instead. It shouldn't matter, since you're simply passing NULL for the argument anyway, easily represented using the static IntPtr.Zero field.
You probably also want to marshal Output the exact same way. If you change the parameter to an IntPtr type, you'll receive a pointer to a TCHAR*, which you can then pass to the other unmanaged functions however you see fit (e.g., to free it).
Try the following code:
[
DllImport("dllName",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)
]
public static extern UInt32 Foo(
string InputWord,
out IntPtr Output, // change to IntPtr
UInt32 Options,
out IntPtr List, // change to IntPtr
out UInt32 Count);
IntPtr output;
IntPtr dummyError = IntPtr.Zero;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y, out dummyError, out dummyCount);
You might also need to use the Marshal.AllocHGlobal method to allocate unmanaged memory from your process that is accessible to the C++ code. Make sure that if you do so, you also call the corresponding Marshal.FreeHGlobal method to release the memory.
Given Cody's answer and the comments, you will have to do it this way:
[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count);
Now to get the string value in Output marshalled over to managed memory you will do:
string outputValue = Marshal.PtrToStringAnsi(Output);
You must know if TCHAR is Ansi or Unicode and use the appropriate marshal.
Remember to hang onto the Output IntPtr so you can pass that to the native Free method.
Thanks Cody for your answer but I want to make a seperate one, first Output is created by Foo from the native side, and I call FreeFoo to free the allocated memory by Foo.
The following is the code
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count);
[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeFoo(IntPtr Output);
}
To use it:
public string FooWrapper(string Text)
{
IntPtr output;
object dummyError = null;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x,out dummyError,out dummyCount);
string str = Marshal.PtrToStringUni(output);
FreeFoo(output);
return str;
}
Whatever the ErroneousWord type is, you can't marshal an array as a single out object. If it is at all possible to marshal as an object...

passing structure reference from a c# code to call a c++ DLL function that accept structure reference in its prototype

I hav a function in c++ DLL that has following prototype
int function(RefPar &params);
how can i call this function from a c# program using "DLLImport".
when i tried like below, AccessViolationException happened while running in visual studio 2008..
[DllImport("VistaGMMDLL.dll", EntryPoint = "function"]
unsafe static extern int function(ref RefPar params);
and called as..
int ret=function(ref params);
Note:RefPar structure has many
unsigned integer values and 1 enum
value as its members.
pls anyone help me to call the function correctly..
A couple of things jump out at me. First of all I don't see why you need to use unsafe. Secondly, you probably have a calling convention mismatch, cdecl in the C++ and stdcall in the C#.
I'd do it like this:
C++
struct RESOURCE_PARAMETERS{
unsigned int uSurfaceHeight;
unsigned int uSurfaceDepth;
unsigned int uSurfaceWidth;
unsigned int uMSAAHeight;
unsigned int uMSAAWidth;
unsigned int uArraySize;
unsigned int uNumSamples;
unsigned int uMaxLod;
unsigned int uBpp;
unsigned int uprefFlag;
unsigned int uusageFlag;
RESOURCE_TYPE_REC ResourceType;
int ResourceFormat;
int iBuildNumber;
};
int function(RefPar &parameters)
{
}
C#
[StructLayout(LayoutKind.Sequential)]
public struct RESOURCE_PARAMETERS
{
uint uSurfaceHeight;
uint uSurfaceDepth;
uint uSurfaceWidth;
uint uMSAAHeight;
uint uMSAAWidth;
uint uArraySize;
uint uNumSamples;
uint uMaxLod;
uint uBpp;
uint uprefFlag;
uint uusageFlag;
[MarshalAs(UnmanagedType.U4)]
ResourceType ResourceType;
int ResourceFormat;
int iBuildNumber;
}
[DllImport("VistaGMMDLL.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function(ref RESOURCE_PARAMETERS parameters);
RESOURCE_PARAMETERS parameters = new RESOURCE_PARAMETERS();
int result = function(ref parameters);
I'm not sure how big the enum is on the C++ size. That's why I've put an explicit MarshalAs in the C# code. If it's just a single byte, then use UnmanagedType.U1 instead. I trust you get the idea.
If your C++ function treats its parameter as an in/out parameter then using ref on the C# side is correct. If its actually an out parameter then change the code to be like this:
[DllImport("VistaGMMDLL.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function(out RESOURCE_PARAMETERS parameters);
RESOURCE_PARAMETERS parameters;
int result = function(out parameters);
Try in this way:
[StructLayout(LayoutKind.Sequential)]
public struct RefPar
{
UInt32 uint1;
UInt32 unti2;
....
}
[DllImport("VistaGMMDLL.dll", EntryPoint = "function"]
unsafe static extern int function(IntPtr params);
//calling
//fill the refParStructure
//create the IntPtr
refParStruct rs = new RefPar();
IntPtr refparPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(refPar)));
Marshal.StructureToPtr(refParStruct, refparPtr,false);
ret=function(refParPtr);
Let Me know if you need more details
Another very simple way to call this function is:
Create a c++ dll warapper that link your original dll and contains this function
//c++ code
function2(uint param1, uint param2.....)
{
RefPar refpar;
refpar.param1=param1
refpar.param2=param2
function(&refpar)
}
in this way you have just to import (in C#) the dll wrapper function in this way
[DllImport("wrapperdll.dll", EntryPoint = "function2"]
static extern int function2(Uint32 param1,Uint32 param2....);
that is very simple to call.
Regards

Categories

Resources