I want to access the C++ function which is exposed by C++ DLL. I am able to call the fun from C# to C++ but somehow I am unable to access the double char pointer memory in c#.
How to marshal double char pointer in c#?
char* ABC::GetText(char* myFile, wchar_t** data)
{
char newdata[123] = {0};
char oldata[123] = {0}'
<does some operations>
*data = newdata;
<does some operations>
return oldata;
}
[DllImport(#"ABC.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText(String myFile, IntPtr tmpdata1);
IntPtr text = GetText(path, tmpdata1); --> fun called successfully
String Oldtext = Marshal.PtrToStringAnsi(text);
Console.WriteLine(Oldtext.ToString()); -> Printing content correctly
String Data1text = Marshal.PtrToStringAnsi(tmpdata1);
Console.WriteLine(Data1text.ToString()); -> Printing garbage value
Related
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();
got an issue on pass/recieve byte [] from/to C# code to/from C++ dll.
On execution it gives me "stack balancing" exeption, that means, values missmatch. Read a few articles before asking, and tried few... but still dont understand how i can solve it in that case:
__declspec(dllexport) void doProcessFile(const char* szSource, const char* szPassWord, char *strRet)
{
//simple RC4 encryption. szSource - is byte[]
//szPassWord- string.
//Function returns a byte[] as char*
strRet = RC4EncryptTool::Decrypt(szSource, szPassWord);
}
and i am trying to interop them via:
[DllImport("MyDLL.dll", EntryPoint = "doProcessFile")]
public static extern void doProcessFile(byte[] szSource, string szPassWord, ref byte[] strRet);
but that's wont work.
Still experiment with it, and cant return byte[] anyways...
[DllImport("MyDLL.dll", EntryPoint = "doProcessFile", CallingConvention = CallingConvention.Cdecl)]
public static extern void doProcessFile(byte[] szSource, string szPassWord, ref byte[] strRet);
with variation of it via changing char strRet to char strRet and char strRet... nothing... it's always show me an empty array.
Reading those & tried : Get byte[] in C# from char* in C++
Not works in my case... byte[] buffer = new byte[256*256]; from that solution - is always empty in my case, C++ part of code not return value into C# (or even i cant read them).
Also i played with
__declspec(dllexport) char* doProcessFile(const char* szSource, const char* szPassWord)
{
//simple RC4 encryption. szSource - is byte[]
//szPassWord- string.
//Function returns a byte[] as char*
return RC4EncryptTool::Decrypt(szSource, szPassWord);
}
And :
[DllImport("MyDLL.dll", EntryPoint = "doProcessFile")]
public static extern byte[] doProcessFile(byte[] szSource, string szPassWord);
return me a error - "Unable set a retun value...".
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;
}
The following code snippet is from Unities Bonjour client example, which demonstrates how to interact with native code from C#. It's a simple C function that returns a string back to C# (Unity):
char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
const char* _GetLookupStatus ()
{
// By default mono string marshaler creates .Net string for returned UTF-8 C string
// and calls free for returned value, thus returned strings should be allocated on heap
return MakeStringCopy([[delegateObject getStatus] UTF8String]);
}
The C# declaration of the function looks like:
[DllImport ("__Internal")]
private static extern string _GetLookupStatus ();
There are a few things that puzzle me here:
Is this the right way to return a string from iOS native code to C#?
How does the returned string ever get freed?
Is there a better way to do it?
Any insights in this matter are appreciated.
Thank you.
1.No.
2.You have to do that yourself.
3.Yes
If you allocate memory inside a function on the C or C++ side, you must free it. I don't see any code allocating memory on the side but I assume you left that part. Also, do not return a variable declared on the stack to C#. You will end up with undefined behavior including crashes.
Here is a C++ solution for this.
For the C solution:
char* getByteArray()
{
//Create your array(Allocate memory)
char * arrayTest = malloc( 2 * sizeof(char) );
//Do something to the Array
arrayTest[0]=3;
arrayTest[1]=5;
//Return it
return arrayTest;
}
int freeMem(char* arrayPtr){
free(arrayPtr);
return 0;
}
The only difference is that the C version uses malloc and free function to allocate and de-allocate memory.
C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);
//Test
void Start()
{
//Call and return the pointer
IntPtr returnedPtr = getIntArray();
//Create new Variable to Store the result
byte[] returnedResult = new byte[2];
//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, 2);
//Free native memory
freeMem(returnedPtr);
//The returned value is saved in the returnedResult variable
byte val1 = returnedResult[0];
byte val2 = returnedResult[1];
}
Note that this is only a test that uses char with 2 characters only. You can make the size of the string dynamic by adding a out int outValue parameter to the C# function then adding int* outValue parameter to the C function. You can then write to this parameter on the C side the size of the character is and access that size from the C# side.
This size can then be passed to the last argument of the Marshal.Copy function and remove the current hard-coded 2 value limit. I will leave this for you to do but if confused, see this post for example of that.
The better solution is to pass StringBuilder to the native side then write to it. The bad side is that you have to declare the size of the StringBuilder on time.
C++:
void __cdecl _GetLookupStatus (char* data, size_t size)
{
strcpy_s(data, size, "Test");
}
C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int _GetLookupStatus(StringBuilder data, int size);
//Test
void Start()
{
StringBuilder buffer = new StringBuilder(500);
_GetLookupStatus (buffer, buffer.Capacity);
string result = buffer.ToString();
}
If you are looking for the fastest way then you should use char array on the C# side, pin it on C# side then send it to C as IntPtr. On the C side, you can use strcpy_s to modify the char array. That way, no memory is allocated on the C side. You are just re-using the memory of the char array from C#. You can see the float[] example at the end of the answer here.
I defined a function in C DLL library.
__declspec(dllexport) void* GetText();
It will return a string which is dynamically allocated from heap memory (And GlobalAlloc is used here for allocating memory). Note that the returned string is not null-terminated.
Then at C# side I tried two methods to declare the function
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern String GetText();
When calling above method, the application will crash without any exception thrown.
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
ptr = GetText();
string text = Marshal.PtrToStringAuto(ptr, 1000);
And calling this method will return incorrect string. Checked the real bytes by using Marshal.Copy, I found the bytes value is not same as the value in DLL library. (I think it's caused by Virtual Memory, C# process cannot access memory space of the DLL directly)
(Don't mind the string length, I hard coded it to 1000 for ease)
This is the C++ code and the memory value of the string when debugging (It's a Console Application but not the original DLL, because Console Application is easy to debug. But the DLL code is same as this one except the logging part).
Following is the original DLL code
__declspec(dllexport) char* GetText(){
VTHDOC hDoc = NULL;
VTHTEXT hText = VTHDOC_INVALID;
DAERR da_err = NULL;
DAERR ta_err = NULL;
DAERR read_err = NULL;
char *buf = (char*)GlobalAlloc(GMEM_FIXED, 1000);
DWORD real_size;
DAInitEx(SCCOPT_INIT_NOTHREADS, OI_INIT_DEFAULT);
da_err = DAOpenDocument(&hDoc, 2, "D:\\1TB.doc", 0);
ta_err = TAOpenText(hDoc, &hText);
read_err = TAReadFirst(hText, (VTLPBYTE)buf, 1000, &real_size);
return buf;
}
But at C# side the bytes are not same as C++ side
You can see the first byte in C++ is 0, but it's 200 for C# (decimal)
Another thing to note: if I return a const string(e.g. "AASSDD") directly in DLL code, C# side will get the correct string
You can't do it that way. Marshaling of string works only for null-terminated strings (or for BSTR, if you specify some options). You can:
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
But from there, it isn't clear how the C# program should know the length of the string.
The various Marshal methods of C# handle BSTR (that have internally their length) or NUL terminated strings.
As already stated, it works for null-terminated strings only, in the following way:
C# part, declaration:
[DllImport("myDll.dll", EntryPoint = "myString", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
extern private static string myString(out int size);
C# part, usage:
int size;
string s = myString(out size);
C++ part:
char* myString(int* size)
{
*size = 20;
char* strg = (char*)::GlobalAlloc(GMEM_FIXED, *size);
memset(strg, 0x3f, *size); //preset with a questionmark
for (int i=0; i < 9; i++)
strg[i] = 0x40 + i;
strg[*size -1] = 0; //limit the maximum string length
return strg;
}
And the obtained C# string:
"#ABCDEFGH??????????", value of size: 20
A treatment of the issue may be found here