How To P/Invoke char* [] in C# - c#

How To P/Invoke char* [] in C#
Any 1 tell how to continue hereafter.
I want to send parameters to C++ DLL from C#. I had googled many sites but no solution.
C Function
public void* find(char*[] argv)
{
}
C# Function
i want invoke this function with following paramter arguments
char *Argv[] = { "Tool", "Sachin", "192.168.1.1", "3", "400"};
Thanks in Advance.

There are multiple ways to do it...here are just a couple...but I outline other information you need to be aware of.
Properly Exporting/Importing your "find" Function
First you need to make sure your "exported" C function and the DllImport are defined properly (if you have this working already, then ignore this section).
If your C function is compiled to use the cdecl calling convention (usually the default in C/C++ projects), then you need to use CallingConvention = CallingConvention.Cdecl on the DllImport e.g.
[DllImport("yourdll.dll", CharSet = Ansi, CallingConvention = CallingConvention.Cdecl)]
public IntPtr find([In] String[] args);
If your C function is compiled to use the 'stdcall' calling convention (by project options or by putting the WINAPI macro, or __stdcall decoration on the function definition), then you need to use CallingConvention = CallingConvention.Stdcall (which is the default on DllImport anyway) e.g.
[DllImport("yourdll.dll", CharSet = Ansi)]
public IntPtr find([In] String[] args);
In addition when defining a "C" function you can use extern "C" to stop the the C++ compiler mangling the name of the function.
You then either use __declspec(export), or use a .DEF file to specify the function as an exported entry.
What is your "find" Contract?
Important: you need to know the contract of your "find" function...i.e. what will it look for to signify the "end" of that list of arguments...it might use NULL, or it might use an empty string, etc.
Usually, functions like that, have another parameter that signifies the "count" of the number of items in the array so that a "marker" isn't needed.
For the example below, I will assume it uses NULL at the end...you will have to modify the code accordingly if it's not that way.
If it's possible for some of your parameters to be NULL, then you will have to change the "sentinel" used to signify the end.
You can do the "marshalling" between C# and native types (using the Marshalling attributes):
// Note: this uses a "NULL" to mark the end of the array...
// because your "find" function doesn't have a "count" parameter, and I'm
// assuming it uses a NULL parameter to detect the end.
IntPtr opaqueresult = find(new string[] { "Tool", "Sachin", "192.168.1.1", "3", "400", null}); // call the function in that DLL.
Or you can do the marshaling logic yourself (rearrange and expand the code as necessary to clean up memory etc):
// When need to use IntPtr as we are allocating the native memory.
[DllImport("yourdll.dll", CharSet = Ansi)]
public IntPtr find([In] IntPtr args);
IntPtr []allocatednativestrings;
IntPtr args = AllocateAnsiIntPtrArrayWithSentinel( new string[] { "Tool", "Sachin", "192.168.1.1", "3", "400"}, out allocatednativestrings);
IntPtr opaqueresult = find(args); // call the function in that DLL.
// If "find" DOESN'T hold onto the pointers passed into it, then you can "free"
// the memory just after you make the call....otherwise you can't...you then
// have to decide how/who has responsibility for freeing that memory and when.
// Free the "strings", and the memory containing the pointers
FreeAnsiIntPtrArrayWithSentinel(args, allocatednativestrings);
(Note: I am only using this bit of hacky code, as it was mentioned in a comment above...
http://www.codeproject.com/Articles/17450/Marshal-an-Array-of-Zero-Terminated-Strings-or-Str ....
the crucial thing is to make sure you "free" the memory properly, when you are finished with it....
this can be done neater...just giving you enough to get the idea)
public static IntPtr AllocateAnsiIntPtrArrayWithSentinel(string[] InputStrArray, out IntPtr[] InPointers)
{
int size = InputStrArray.Length + 1; // +1 for NULL sentinel
//build array of pointers to string
InPointers = new IntPtr[size];
int dim = IntPtr.Size * size;
IntPtr rRoot = Marshal.AllocCoTaskMem(dim);
int i = 0;
foreach(string arg in args)
{
if (arg == null)
{
System.Diagnostics.Debug.Assert(false, "this code needs changing to support NULL arguments");
}
InPointers[i++] = Marshal.StringToCoTaskMemAnsi(arg);
}
// The NULL sentinel...don't need to do this as already initialized to null...
// but just making it clearer for you.
InPointers[size-1] = IntPtr.Zero;
//copy the array of pointers
Marshal.Copy(InPointers, 0, rRoot, size);
return rRoot;
}
public static void FreeAnsiIntPtrArrayWithSentinel(IntPtr args, IntPtr[] intptrs)
{
foreach(IntPtr ptr in intptrs)
{
if (ptr != IntPtr.Zero) // we need to avoid the sentinel
Marshal.FreeCoTaskMem(ptr); // free the mem allocated for the string
}
// free the memory that contained the list of string pointers and sentinel
Marshal.FreeCoTaskMem(args);
}

Related

Passing a return char* from C++ to C# with DllImport

I am a newbie on both C# WPF and C++.
Recently, I got an external .dll, which returns a char*, and I want to receive the return value in C# by using DllImport. Then, using str.Split(';') to separate the characters. To the purpose, I created a button to show the first character in the string I split on a label when I click the button.
Therefore, I use an IntPtr to receive a char*, which is from C++ .dll, and call Marshal.PtrToStringAnsi() to turn it into a string. However, when I execute the code, it sometimes works but sometimes crushes. Then error code always shows
Unhandled exception at 0x00007FFC06839269 (ntdll.dll) in UITest.exe: 0xC0000374: heap corruption (parameters: 0x00007FFC068A27F0).
I thought my code is reasonable and I couldn't find root causes. Can anyone help me? Thanks!
The below shows the content in .dll and also the code I used in C#.
C++ code in Dlltest.h:
#define DLL_EXPORT extern "C" __declspec(dllexport)
char* getRbtData = nullptr;
DLL_EXPORT char* func_getRbtData();
C++ code in Dlltest.cpp:
char* func_getRbtData()
{
getRbtData = new char(128);
memset(getRbtData, 0, strlen(getRbtData));
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
return getRbtData;
};
C# code in UITest.xaml.cs:
[DllImport("DllTest.dll",EntryPoint = "func_getRbtData", CharSet = CharSet.Ansi)]
public static extern IntPtr func_getRbtData();
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
There are several problems with your code.
On the C++ side, your DLL function is implemented all wrong:
getRbtData = new char(128);
You are allocating a single char whose value is 128, not an array of 128 chars. You need to use new char[128] instead for that.
memset(getRbtData, 0, strlen(getRbtData));
getRbtData is not a pointer to a null-terminated string, so strlen(getRbtData) is undefined behavior. It reads into surrounding memory while calculating the length until it finds a random 0x00 byte in memory.
And then the subsequent memset() into getRbtData will overwrite that surrounding memory. If it doesn't just crash outright.
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
Prior to C++11, this assignment is OK but discouraged. In C++11 and later, this assignment is actually illegal and won't compile.
String literals are read-only data, so you need to use const char instead of char in your pointer type. You should do that even in older compilers.
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
strlen(_getRbtData) is OK since _getRbtData is a pointer to a null-terminated string.
However, since getRbtData is not allocated with enough memory to receive all of the copied chars, memcpy() into getRbtData is also undefined behavior and will trash memory, if not crash outright.
return getRbtData;
This is OK to pass the pointer to C#.
However, since the memory is being allocated with new (better, new[]), it needs to be freed with delete (delete[]), which you are not doing. So you are leaking the memory.
Marshal.PtrToStringAnsi() on the C# side will not (and cannot) free new'ed memory for you. So your C# code will need to pass the pointer back to the DLL so it can delete the memory properly.
Otherwise, you will need to allocate the memory using the Win32 API LocalAlloc() or CoTaskMemAlloc() function so that the Marshal class can be used on the C# side to free the memory directly without passing it back to the DLL at all.
On the C# side, you are using the wrong calling convention on your DllImport statement. The default is StdCall for compatibility with most Win32 API functions. But your DLL function is not specifying any calling convention at all. Most C/C++ compilers will default to __cdecl unless configured differently.
With that said, try this instead:
Dlltest.h
#define DLL_EXPORT extern "C" __declspec(dllexport)
DLL_EXPORT char* func_getRbtData();
DLL_EXPORT void func_freeRbtData(char*);
Dlltest.cpp
char* func_getRbtData()
{
const char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
int len = strlen(_getRbtData);
char *getRbtData = new char[len+1];
// alternatively:
/*
char *getRbtData = (char*) LocalAlloc(LMEM_FIXED, len+1);
if (!getRbtData) return NULL;
*/
memcpy(getRbtData, _getRbtData, len+1);
return getRbtData;
}
void func_freeRbtData(char *p)
{
delete[] p;
// alternatively:
// LocalFree((HLOCAL)p);
}
UITest.xaml.cs
[DllImport("DllTest.dll", EntryPoint = "func_getRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr func_getRbtData();
[DllImport("DllTest.dll", EntryPoint = "func_freeRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern void func_freeRbtData(IntPtr p);
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
func_freeRbtData(intptr);
// alternatively:
// Marshal.FreeHGlobal(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
new char(128) returns a pointer to one character, with initial value 128.
I could tell you how to allocate 128 characters, but the chief problem with that is that you can't clean it up so that's not a useful answer. Check the existing questions about returning a string to C#.

Initializing a C# IntPtr to accept data from unmanaged C++ DLL?

I have an exported function in unmanaged C++ code that expects a pointer to a BStr, where it will write some text data (258 bytes, max)
extern "C" __declspec(dllexport)
int CppFunc(BSTR *data)
{ ... }
I want that data as a string.
This works
[DllImport( ... CallingConvention = CallingConvention.Cdecl)]
public static extern int CppFunc([MarshalAs(UnmanagedType.BStr)] ref string data);
but it creates a memory leak.
I assume what I should be doing is creating and passing an IntPtr, then Marshal out the Bstr as a string, then free the IntPtr:
IntPtr p = Marshal.AllocHGlobal(512);
CppFunction(p);
string data = Marshal.PtrToStringBSTR(p);
Marshal.FreeHGlobal(p) ;
The problem is, with that code, I get a System.AccessViolationException on the call into Marshal.PtrToStringBSTR(p).
What am I doing wrong?!
The first line of the Remarks for Marshal.PtrToStringBSTR is
Call this method only on strings that were allocated with the unmanaged SysAllocString and SysAllocStringLen functions.
Which is probably where your crash came from.
Add to this your C++ function expects BSTR* (effectively a pointer to a pointer to the first character of data in the string), but you pass it a pointer to data.
Remember that a BSTR has a special structure: it starts with 4 bytes of length, then data, then a null. The pointer points to the first character of data. So Marshal.PtrToStringBSTR is looking backwards from the pointer to find the length of the string - but that isn't memory which was allocated by Marshal.AllocHGlobal.
It could be that your C++ function does something like *data = ....AllocSysString(); - that is, it never reads the string it's given, but instead assigns the pointer to a string which it allocates.
In that case, you probably want something like:
[DllImport( ... CallingConvention = CallingConvention.Cdecl)]
public static extern int CppFunc(out IntPtr data);
...
CppFunc(out IntPtr p);
string data = Marshal.PtrToStringBSTR(p);
Marshal.FreeBSTR(p) ;
Here, we pass it a pointer to a pointer. The C++ function re-assigns the pointer to point to the first character of data in a BSTR, and we use that to deserialize the BSTR, and then free it (using a method which knows how to free BSTRs).
If this isn't the case, it's unclear why your C++ function takes a BSTR* (as opposed to a BSTR), and what it does with it. I think we need to see that before much else can be said.
If your C++ function took a BSTR instead (remember that BSTR is itself a pointer), then what you should be doing is using a StringBuilder (with a particular initial capacity) - the marshalling layer turns that into a pointer the C++ code can write to, and then you can turn the StringBuilder into a string.
[DllImport( ... CallingConvention = CallingConvention.Cdecl)]
public static extern int CppFunc([MarshalAs(UnmanagedType.BStr)] StringBuilder data);
...
var data = new StringBuilder(512);
CppFunction(data);
string result = data.ToString();

C DLL does NOT Work in C# but Does work in C++

I don`t understand this code below, that it is working with my c++ but it just does not want to work with c#.
Can you please help me to understand what is wrong here and i think i have to say that i am absolutely new to C#.
My_Lib.h
extern "C" __declspec(dllexport) void __cdecl get(char** buffer);
My_lib.c
void get(char** buffer)
{
*buffer = (char*)calloc(6, sizeof(char));
assert(*buffer);
buffer[5] = '\0';
*buffer = "Hello";
}
in my C#----->
public static class NativeMethods
{
[DllImport("My_C_Lib.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern void get(char** buffer);
}
//////////////////// Main()///////
unsafe
{
char* my_buf;
NativeMethods.get(&my_buf);
string s = new string(my_buf);
Console.WriteLine(s);
}
NOTE: Actually my DLL does Work when i call this c function from c++ but as i said above NOT in C#, there is no Errors but string s variable in C# prints some undefined sibols, but DLL "works"
thanks in advance!!!
The code is nearly correct, but...
C DLL does NOT Work in C# but Does work in C++
In C and C++ char is a 8-bit data type. In C# char is a 16-bit data type.
This means that C# expects that the pointer returned by the get() function is expected to be a "wide string", while in C++ expects an "ANSI string".
I simply changed one single line in your program:
*buffer = "H\0e\0l\0l\0o\0\0\0";
... and it works!
You may of course also use the "wide string" functions of the modern C compilers:
void get(wchar_t** buffer)
{
*buffer = L"Hello";
}
By the way
There is another error in your program:
*buffer = (char*)calloc(6, sizeof(char));
...
*buffer = "Hello";
This makes no sense:
The second line (*buffer = "Hello";) will not copy the string to the memory allocated by calloc, but it will write the address of the string "Hello" to the variable buffer and the value (address) returned by calloc is overwritten (and lost).
Your best bet is to change the PInvoke signature of your C function to taking a ref IntPtr:
[DllImport("My_C_Lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void get(ref IntPtr buffer);
To call this, you'll need to instantiate an IntPtr and pass it by reference to the C function (this is analagous to declaring a char * in C and passing it by address):
IntPtr ptr;
get(ref ptr);
// ptr is now an unmanaged pointer to the allocated string
Now, you need to convert the pointed-to string to a managed string. To do this you must make a copy of the string.
string str = Marshal.PtrToStringAnsi(ptr);
Be sure you understand the distinctions between ANSI and Unicode strings and make sure you call the appropriate PtrToString...() variant.
Note that the .NET runtime will not manage the allocated ptr for you since it was allocated by unmanaged code. You must free it yourself using whatever the appropriate mechanism is for your DLL (ideally, the DLL should provide a corresponding free function since it is the one who allocated the memory in the first place).
For robustness, add appropriate error / null pointer checking. Make sure get() succeeded and that ptr is not IntPtr.Zero.
To avoid leaks, if there is any possibility of any code throwing an exception between the time get() returns and the time you free the pointer, the try/finally construct is your friend.
(Aside: Note that get is a Contextual Keyword in C# and thus while you can use it as an identifier, you may prefer not to in order to avoid confusion.)

Marshaling a pointer to a pointer

Problem:
I have a test application that is looking for a scanner attached to a PC. The European manufacturer supplies an SDK written in C++ and provides some header files and other information. Their C++ API populates a structure as shown below. My concern is de-referencing (Marshaling) the returned char** inside of that structure into a set of strings.
//
//The struct, as per the manufacturer's C++ header file:
//
typedef struct {
int count;
char** serialNumbers;
} ScannerList;
//
// My interpretation of the struct
//
struct ScannerList
{
public int count;
public IntPtr serialNumbers;
}
//
// C++ API Method Signature:
//
bool SCN_GetScannerList( ScannerList** scannerList );
//
// My DLLImport:
//
[DllImport(#"C:\ScannerSDK\Scanner.dll", CharSet = CharSet.Unicode)]
public static extern bool SCN_GetScannerList(out IntPtr scannerList);
Inside my C# wrapper class, I have several classes that wrap each specific set of Scanner API calls. For this method, I'm using the ScannerNative class that the DllImport resides in, along with the struct. This is the method charged with summoning the list:
private object GetScanlistFromDLL()
{
// our managed code return object - class instead of struct
ScannerNative.ScannerListSafe safeList = new ScannerNative.ScannerListSafe();
// IntPtr to give to the API
IntPtr ptr = new IntPtr();
// unmanaged code struct
ScannerNative.ScannerList scanList = new ScannerNative.ScannerList();
// Call the API with our IntPtr
bool success = ScannerNative.GetScannerList(out ptr);
if(success)
{
// Map the pointer to the type of structure from the API call
scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));
// copy the unsafe struct members to our safe class
safeList.count = scanList.count;
//Problem - the marshaling returns a string full of garbage
//likely because char** is an array of char* ...
//Not sure how to Marshal a pointer to a pointer to a string :-\
safeList.serialNumbers = Marshal.PtrToStringUni(scanList.serialNumbers);
}
else
{
return null;
}
//
// API call to release unmanaged scanner list ..
//
return safeList;
}
Results:
I do get what appears to be a valid structure back from the API call
bool success = ScannerNative.GetScannerList(out ptr);
The subsequent casting in the if(success) block:
if(success)
{
// Map the pointer to the type of structure from the API call
scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));
This works all the way until we get to our IntPtr stored in the scanList.serialNumbers member, which is holding a reference to the char** from the API struct.
scanList.count shows a correct value of '1' (when the scanner is attached) but I'm not able to de-ref the char** held in the scanList.serialNumbers member.
I've tried attacking this in a couple ways, but nothing is coming out and either I'm using the wrong search engines, or trying to find "c# DllImport Char** Marshal" and variants is asking the wrong question.

How do I call this c function in c# (unmarshalling return struct)?

I want to use c# interop to call a function from a dll written in c. I have the header files.
Take a look at this:
enum CTMBeginTransactionError {
CTM_BEGIN_TRX_SUCCESS = 0,
CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS,
CTM_BEGIN_TRX_ERROR_NOT_CONNECTED
};
#pragma pack(push)
#pragma pack(1)
struct CTMBeginTransactionResult {
char * szTransactionID;
enum CTMBeginTransactionError error;
};
struct CTMBeginTransactionResult ctm_begin_customer_transaction(const char * szTransactionID);
How do I call ctm_begin_customer_transaction from c#. The const char * mapps well to string, but despite various attempts (looking at stackoverflow and other sites), I fail to marshal the return structure. If I define the function to return IntPtr it works ok...
Edit
I changed the return type to IntPtr and use:
CTMBeginTransactionResult structure = (CTMBeginTransactionResult)Marshal.PtrToStructure(ptr, typeof(CTMBeginTransactionResult));
but it throws AccessViolationException
I also tried:
IntPtr ptr = Transactions.ctm_begin_customer_transaction("");
int size = 50;
byte[] byteArray = new byte[size];
Marshal.Copy(ptr, byteArray, 0, size);
string stringData = Encoding.ASCII.GetString(byteArray);
stringData == "70e3589b-2de0-4d1e-978d-55e22225be95\0\"\0\0\a\0\0\b\b?" at this point. The "70e3589b-2de0-4d1e-978d-55e22225be95" is the szTransactionID from the struct. Where is the Enum? Is it the next byte?
There's a memory management problem hidden in this struct. Who owns the C string pointer? The pinvoke marshaller will always assume that the caller owns it so it will try to release the string. And passes the pointer to CoTaskMemFree(), same function as the one called by Marshal.FreeCoTaskMem(). These functions use the COM memory allocator, the universal interop memory manager in Windows.
This rarely comes to a good end, C code does not typically use that allocator unless the programmer designed his code with interop in mind. In which case he'd never have used a struct as a return value, interop always works much less trouble-free when the caller supplies buffers.
So you cannot afford to let the marshaller do its normal duty. You must declare the return value type as IntPtr so it doesn't try to release the string. And you must marshal it yourself with Marshal.PtrToStructure().
That however still leaves the question unanswered, who owns the string? There is nothing you can do to release the string buffer, you don't have access to the allocator used in the C code. The only hope you have is that the string wasn't actually allocated on the heap. That's possible, the C program might be using string literals. You need to verify that guess. Call the function a billion times in a test program. If that doesn't explode the program then you're good. If not then only C++/CLI can solve your problem. Given the nature of the string, a "transaction ID" ought to change a lot, I'd say you do have a problem.
I hate to answer my own question, but I found the solution to marshal the resulting struct. The struct is 8 bytes long (4 bytes for the char * and 4 bytes for enum). Marshalling the string does not work automatically, but the following works:
// Native (unmanaged)
public enum CTMBeginTransactionError
{
CTM_BEGIN_TRX_SUCCESS = 0,
CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS,
CTM_BEGIN_TRX_ERROR_NOT_CONNECTED
};
// Native (unmanaged)
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
internal struct CTMBeginTransactionResult
{
public IntPtr szTransactionID;
public CTMBeginTransactionError error;
};
// Managed wrapper around native struct
public class BeginTransactionResult
{
public string TransactionID;
public CTMBeginTransactionError Error;
internal BeginTransactionResult(CTMBeginTransactionResult nativeStruct)
{
// Manually marshal the string
if (nativeStruct.szTransactionID == IntPtr.Zero) this.TransactionID = "";
else this.TransactionID = Marshal.PtrToStringAnsi(nativeStruct.szTransactionID);
this.Error = nativeStruct.error;
}
}
[DllImport("libctmclient-0.dll")]
internal static extern CTMBeginTransactionResult ctm_begin_customer_transaction(string ptr);
public static BeginTransactionResult BeginCustomerTransaction(string transactionId)
{
CTMBeginTransactionResult nativeResult = Transactions.ctm_begin_customer_transaction(transactionId);
return new BeginTransactionResult(nativeResult);
}
The code works, but I still need to investigate, if calling the unmanaged code results in memory leaks.

Categories

Resources