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...".
Related
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
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
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.
I am trying to pass a byte array to a c++ dll:
c++:
extern "C" __declspec(dllexport) char* myfunction(byte bytes[])
{
char *byteschar = (char*)bytes;
//do somethings with it
return byteschar;
}
c#:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string myfunction(byte[] bytes);
but I get a System.AccessViolationException when I call myfunction.
When I run the executable without the debugger it seems to be working fine
If you want a buffer be allocated in C# and filled in C++, the approach is a little bit different.
You should allocate a kind of "unmanaged" buffer, pass to the DLL and then convert the result and free the buffer. It's exactly the same way in C, but calling from a managed environment.
Your C++ code should be something like:
extern "C" __declspec(dllexport) void myfunction(char* buffer, int length)
{
//Fill buffer with something observing the maximum length of the buffer.
}
The signature of your DLL in C# should be:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
public static extern string myfunction(IntPtr buffer, Int32 length);
To call it from C#, you should do:
IntPtr unmanagedBuffer = Marshal.AllocHGlobal(100);
// Your Unmanaged Call
myfunction(unmanagedBbuffer, 100);
string yourString = Marshal.PtrToStringUni(unmanagedBuffer);
Marshal.FreeHGlobal(unmanagedBuffer);
Don't forget to call FreeHGlobal if you don't want a memory leak in your app. It's interesting to protect this in "try/finally" clauses.
Other observation is the encoding of the string. Uni, means Unicode. If you use another string representation, check for an equivalent PtrToStringXXX function.
It suppose to be:
extern "C" __declspec(dllexport) char* myfunction(unsigned char * bytes)
{
//do somethings with it
return bytes;
}
I want to send a string from C# to a function in a native C++ DLL.
Here is my code:
The C# side:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Unicode)]
private static extern void func1(byte[] path);
public void func2(string path)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] arr = encoding.GetBytes(path);
func1(this.something, arr);
}
The C++ side:
void func1(char *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send.
Help?
Thanks.
It looks like you have 2 issues. The first is your native C++ uses an ANSI string but you are specifying unicode. Secondly, it's easiest to just marshal a string as a string.
Try changing the DllImport to the following
[DllImport(
#"Native3DHandler.dll",
EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(void* something, [In] string path);
Works fine for me with no extra marshalling instructions in VS2008:
C# side:
[DllImport("Test.dll")]
public static extern void getString(StringBuilder theString, int bufferSize);
func()
{
StringBuilder tstStr = new StringBuilder(BufSize);
getString(tstStr, BufSize);
}
C++ side:
extern "C" __declspec(dllexport) void getString(char* str, int bufferSize)
{
strcpy_s(str, bufferSize, "FOOBAR");
}
Your declaration is wrong. The parameter should be of type string, and you should set the character set encoding to Ansi, like so:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(string path);
This assumes that you are not modifying the contents of the path variable in your C++ code. Then, you pass the string parameter directly (no need for the wrapper).
If you just want to send a string, just declare func1's parameter as a string. If you want to receive a string, declare it as a StringBuilder and allocate enough buffer space for what you want to receive.
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx