Interop sending string from C# to C++ [duplicate] - c#

This question already has answers here:
Closed 13 years ago.
Duplicate of Interop sending string from C# to C++
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(string str);
public void func2(string str)
{
func1(str);
}
The C++ side:
void func1(wchar_t *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send. Help?
I already asked it here before, but I didn't get an answer that worked.
Thanks.

You need
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22", CharSet = CharSet.Unicode)]
private static extern void func1 ([MarshalAs (UnmanagedType.LPWSTR)] string str) ;
in this case (wchar_t*). And pay attention to the calling convention, as #danbystrom suggests.

have you read this ?
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h(VS.71).aspx

Try to put
MarshalAs(UnmanagedType.BStr)
for string type that you are passing to method.

You should declare your C++ func like this:
extern "C" void __stdcall func1(wchar_t *path)
If that doesn't help, try passing a StringBuilder instead of a string.
(Disclaimer: I've never actually passed Unicode code strings, so if neither of the above suggestions work, then just a a test, you could try with "Ansi" instead just to see what happens.)

Related

Marshalling char * to StringBuilder in C#

I know this has already been discussed but after 3days I haven't figured out why I keep getting a blank string after calling a C function wrapped in a DLL in C# :
[DllImport(DllName, CharSet = CharSet.Ansi)]
public static extern int QA_FormatExample(int viHandle, int viExample [MarshalAs(UnmanagedType.LPStr)]StringBuilder rsComment)
In Main :
StringBuilder rsComment = new StringBuilder(256);
apiWrap.QA_FormatExample(currentInstance, viExample, rsComment);
System.Diagnostics.Debug.WriteLine(rsComment);
Function's signature :
int __stdcall QA_FormatExample(int, int, char*);
The function QA_FormatExample initializes rsComment once called but I keep getting rsComment blank. Any suggestion would be really appreciated, thank you.
In order to match the C++ the function should be declared like this:
[DllImport(DllName)]
public static extern int QA_FormatExample(
int viHandle,
int viExample,
StringBuilder rsComment
);
And you call it like this:
StringBuilder comment = new StringBuilder(256);
int res = QA_FormatExample(handle, example, comment);
Which is very close to what you present in the question. So, if your calls to the function do not work, then your problems lie in the code that we cannot see. What I show above is the correct way to marshal text from native to managed via char*.
I would point out that it is a little unusual that you do not pass the length of buffer to the function. Without that information, the maximum length must be hardcoded.

String to Char* Without memory leaks

I had try many ways to convert a string into a Char* but always got 2 errors when I import my DLL to a C# project.
the main function of my C++ DLL is like this:
//Example
extern "C" __declspec (dllexport) void Conv(std::string str)
{
FFileList file_list;
char temp_path[1024];
sprintf(temp_path,"%s*",arg_path);
GetFindFileListWin(temp_path,".mrs",file_list);
}
So, I Need to convert "str" to a char* because GetFindFileListWin is like this:
GetFindFileListWin(char* path,char* ext,FFileList& pList);
and pass it to arg_path.
I tried to do this:
char* arg_path = new char[str.length()+1];
strcpy(arg_path, str.c_str());
sprintf(temp_path,"%s*",arg_path);
delete[] arg_path;
but when I run Conv() in my C# program It says Windows has triggered a breakpoint in Program.exe.
This may be due to a corruption of the heap, which indicates a bug in Program.exe or any of the DLLs it has loaded. (Same happens if I use _strdup).
So, I tried in other way:
std::vector<char> Chr(str.size() + 1);
std::copy(str.begin(), str.end(), Chr.begin());
char *arg_path = &Chr[0];
sprintf(temp_path,"%s*",arg_path);
And I got Attempted to read or write protected memory message
My C# program do this:
[DllImport("Mrs.dll")]
public static extern void Conv(string str);
public void Convert(TextBox Tx)
{
Conv(Tx.Text);
}
Hope someone could help me to solve this error,
Thanks in advance.
I suspect the asterisk in the sprintf statements could be causing the problem. sprintf would expect a length parameter for the asterisk in the format string.
sprintf(temp_path,"%s*",arg_path);
^
string is c++ object.So in dll it will not work
What you should do as I mentioned above just to do such
extern "C" __declspec (dllexport) void Conv(const char* str)
{
//do whatever
}
If your p/invoke signature is
[DllImport("Mrs.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void Conv(string str);
then your C++ declaration will be simply
extern "C" __declspec(dllexport) void Conv(const char* str);
and you won't need any std::string at all.
.NET and p/invoke do not know anything about std::string.

AccessViolationException when calling C function from dll on C#

I'm trying to use PInvoke in order to call an unmanaged function from a C dll. Due to the fact that the source of the dll can't be released to developers, some of them have called the function in Delphi using the following declaration. We use SAT.dll with a CDECL calling convention.
function AssociarAssinatura( numeroSessao : Longint; codigoDeAtivacao: PChar;
CNPJvalue : PChar; assinaturaCNPJs : PChar ) : PChar ;
cdecl; External 'SAT.DLL';
Based on that structure, I made the following Console Application in C# in order to test the same function from the same DLL. I made some research and found out that the equivalent to Longint in delphi is int in C# and the equivalent of PChar is a pointer to a string (But I used C#'s string).
class Program
{
[DllImport("SAT.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern string AssociarAssinatura(int numeroSessao,
string codigoDeAtivacao, string CNPJvalue, string assinaturaCNPJs);
static void Main(string[] args)
{
Console.WriteLine("Comienza");
int numeroSessao = 111111;
string codigoDeAtivacao = "123123123";
string cnpJvalue = "2222222222222211111111111111";
string assinaturaCnpJs = "lrafwegmqcgvpzpbdmcmcgdvf";
string resposta = AssociarAssinatura(numeroSessao, codigoDeAtivacao,
cnpJvalue, assinaturaCnpJs);
Console.WriteLine(resposta);
}
}
When I call the function, an AccesViolationException is thrown. The code of AssociarAssinatura has some inner prints that show that the code from the function is indeed running well. Due to this I guess the problem is related when the function is returning it's value. My best guess is that somehow I'm having issues with the calling convention. Any thoughts?
Your problem here is most likely related to your PChar type in Delphi. In C#, strings are Unicode by default, and when calling your func, there will actually be a conversion from a PChar to PWideChar, which means a new block of memory will be allocated to hold this new PWideChar. This interop and difference between how strings are handled in .NET and in Delphi is more than likely causing your AccessViolationException.
You can use the MarshalAs attribute to explicitly tell .NET how to handle the specific type:
[DllImport("SAT.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern string AssociarAssinatura(int numeroSessao,
[MarshalAs(UnmanagedType.LPStr)] string codigoDeAtivacao, [MarshalAs(UnmanagedType.LPStr)] string CNPJvalue, [MarshalAs(UnmanagedType.LPStr)] string assinaturaCNPJs);
Which will explicitly specify how the string is handled. After that, your code should be fine.

Mono C# PInvoke - Get CFString from external lib

I have a lib that returns a CFString, and I'm trying to get that string value in C#..
The problem is that I don't know how to do this in C#..
Making the external function return a CFString won't work as it throws an exception "Type MonoMac.CoreFoundation.CFString which is passed to unmanaged code must have a StructLayout attribute."
Then I thought that I could get the string as a byte array and then convert it to a string in C#, but then, I have another problem, I don't know how to convert in C the CFString to a byte array :/
C# Dll Import stuff
[DllImport("lib")]
public static extern MonoMac.CoreFoundation.CFString test();
[DllImport("lib")]
public static extern byte[] test();
C Library sample
CFStringRef test()
{
return CFSTR("test string");
}
If anyone knows a way to do this please help me out ;)
Thanks
Try:
[DllImport("lib")]
public static extern IntPtr test();
Then use:
new CFString (test ()).ToString ();
using UnixMarshal.StringToHeap to convert string to IntPtr.
using UnixMarshal.PtrToString to convert IntPtr to string.

How to import void * C API into C#?

Given this C API declaration how would it be imported to C#?
int _stdcall z4ctyget(CITY_REC *, void *);
I've been able to get this far:
[DllImport(#"zip4_w32.dll",
CallingConvention = CallingConvention.StdCall,
EntryPoint = "z4ctygetSTD",
ExactSpelling = false)]
private extern static int z4ctygetSTD(ref CITY_REC args, void * ptr);
Naturally in C# the "void *" doesn't compile.
Some Googling indicates that it should be translated as "object." Which seems like it should work. But others indicate that "Void * is called a function pointer in C/C++ terms which in C# terms is a delegate". That doesn't make a whole lot of sense here as what would it delegate to? Some similar calls for other APIs found through Googling use other functions in the respective API. But in this API no other call would make sense.
The documentation for the call shows an example:
z4ctyget(&city, “00000”);
Which seems to show that even a static value could be passed.
It will compile with object in place of the void *. I don't know whether this is right and I haven't had an opportunity to test it (licensing issue).
For the void* parameter you can just use an IntPtr
[DllImport(#"zip4_w32.dll",
CallingConvention = CallingConvention.StdCall,
EntryPoint = "z4ctygetSTD",
ExactSpelling = false)]
private extern static int z4ctygetSTD(ref CITY_REC args, IntPtr ptr);
You can also use void* if you mark your class as unsafe.
It really depends on what the API is looking for in that parameter.
You can add IntPtr or Object* to get past compiler, but you will still need to pass it the correct data when you call it.
As far as I can tell the C declaration of z4ctyget is:
int z4ctyget(CITY_REC *cityrec, char *zipcode);
The second parameter is a 5 character ANSI string representing the zip code at which you want to start your search or "00000" to start at the beginning of the file. So your declaration should be:
[DllImport(#"zip4_w32.dll", CharSet = CharSet.Ansi)]
private extern static int z4ctygetSTD(ref CITY_REC args, string zipcode);

Categories