I'm trying to pass string from c++ to c#.
C++:
extern "C" __declspec(dllexport) void GetSurfaceName(wchar_t* o_name);
void GetSurfaceName(wchar_t* o_name)
{
swprintf(o_name, 20, L"Surface name");
}
C#:
[DllImport("SurfaceReader.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void GetSurfaceName(StringBuilder o_name);
StringBuilder name = new StringBuilder(20);
GetSurfaceName(name);
But only first symbol is passed: name[0] == 'S'. Other symbols are nulls. Could you tell me what is wrong here?
Thanks,
Zhenya
You forgot to tell the pinvoke marshaller that the function is using a Unicode string. The default is CharSet.Ansi, you'll need to use CharSet.Unicode explicitly. Fix:
[DllImport("SurfaceReader.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode)]
private static extern void GetSurfaceName(StringBuilder o_name);
You'll get a single "S" now because the utf-16 encoded value for "S" looks like a C string with one character.
Do in general avoid magic numbers like "20". Just add an argument that say how long the string buffer is. That way you'll never corrupt the GC heap by accident. Pass the StringBuilder.Capacity. And give the function a return value that can indicate success so you also won't ignore a buffer that's too small.
I am not sure, but instead of using StringBuilder, I would pass from C# char (wchar) array to C++, fill it and then operate with this array in C#.
Related
I am given a Delphi DLL that contains functions that I need to call in C#. One of the functions takes two char arrays, where one is an encrypted password and the other is the key.
TCString = array[0..254] of Char;
...
function Decrypt(const S, Key: TCString): TCString; stdcall;
I tried to figure out how to call this function on my own but I keep getting "Cannot marshal 'return value': Invalid managed/unmanaged type combination." I am using byte since the Char type in Delphi is AnsiChar which is 8 bits.
[DllImport("path", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern byte[] Decrypt(byte[] S, byte[] Key);
What is the correct way to call this in C#?
I think I would be inclined to wrap the fixed length array in a C# struct.
public struct CString
{
[UnmanagedType.ByValArray(SizeConst=255)]
byte[] value;
}
This allows the size to be specified in one place only.
The next hurdle is the return value. The Delphi ABI treats a return value that cannot fit into a register as an additional hidden var parameter. I'll translate that as a C# out parameter.
Finally the two input parameters are declared as const. That means that they are passed by reference.
So the function would be:
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
public static extern void Decrypt(
[In] ref CString S,
[In] ref CString Key,
out CString Result
);
I've intentionally avoided any use of text in this because this would appear to be a function that operates on binary data. Many Delphi programmers treat AnsiChar arrays interchangeably with byte arrays in such situations which is often confusing.
I have a problem with calling a C DLL fom C#
The C function is (I don't have a c header or a good spec for this :( )
int knr12_read ( char *kn12, char *ik9, char *wok, char *wlc,
char *plz, char *ort, char *woz );
kn12 is a ref parameter
This is what I've tried in C#
[return: MarshalAs(UnmanagedType.U4)]
[DllImport("Knr12.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "knr12_read", CharSet = CharSet.Ansi)]
unsafe public static extern int knr12_read(out IntPtr buffer, string ik9, string wok, string wlc, string plz, string ort, string woz);
int knr = knr12_read(out pBuffer, knrTemp, "11111", "", "98529", "Suhl", "1");
string data = Marshal.PtrToStringAnsi(pBuffer);
The returning int is always right, how it should be, but I have problems with the ref parameter pBuffer...
Also the sting type for the other variables is working...
When I use a ref,I always get an AccessViolation error knr12_read().In case I use out I get a pointer,but the String is always empty which can't be.I even tried out String as ref for char* but I get an AccessViolation error on knr12_read().
Please guide.
StringBuilder is often a good type to use when P/Invoking to functions with string returning parameters:
static extern int knr12_read(StringBuilder kn12, ...)
You'll need to initialise the string builder before you call the function, something like:
StringBuilder outString = new StringBuilder(100);
You shouldn't need the 'unsafe', and unless the 'C' code holds onto the pointers for longer than the duration of the call, you shouldn't need to worry about pinning - the framework is doing that for you.
Here's a SO question which should help: Marshal "char *" in C#
Probably you have not pinned the buffer. here is the example of how to pin the buffer data.
GCHandle pinnedRawData = GCHandle.Alloc(rawData,
GCHandleType.Pinned);
Pinning the object makes sure that the pointer is valid cause .Net runtime can always reallocate the memory as and when it thinks fit.
Try it out and let me know if it helps you.
I have declared a DLL import in my C# program that looks like this:
[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode",
CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr generateKeyCode(char[] serial, char[] option, char c_type);
It references the function generateKeyCode() inside of my DLL.
Here is the code that is causing an error (used breakpoints):
const char* generateKeyCode(char serial[],
char option[],
char c_type)
{
returnBufferString = "";
SHA1_CTX context;
int optionLength = 0;
#ifdef WIN32
unsigned char buffer[16384] = {0};
#else
unsigned char buffer[256] = {0};
#endif
//char output[80];
char keycode[OPTION_KEY_LENGTH+1] = "";
int digest_array_size = 10; //default value for digest array size
unsigned char digest[20] = {0};
char optx[24] = {0};
char c_type_upper;
// Combine serial # and Option or Version number
char str1[30] = {0};
int i;
int size = 0;
int pos = 0;
...
...
}
Basically, I imported this DLL so I could pass the function parameters and it could do its algorithm and simply return me a result. I used this marshaler function...
public static string genKeyCode_marshal(string serial, string option, char type)
{
return Marshal.PtrToStringAnsi(generateKeyCode(serial.ToCharArray(),
option.ToCharArray(), type));
}
...so I could make the call properly. Inside of my C++ header file, I have defined a string, as indicated is helpful in the answer to this question (it is the returnBufferString variable present at the top of the C/C++ function).
I make this function call several times as I use a NumericUpDown control to go from 1.0 to 9.9 in increments of 0.1 (each up or down accompanies another function call), and then back down again. However, every time I try to do this, the program hitches after a seemingly set number of function calls (stops at 1.9 on the way back down if I just go straight up and down, or earlier if I alternate up and down a bit).
Please note that it works and gives me the value I want, there are no discrepancies there.
I changed the buffer size to some smaller number (5012) and when I tried to run the program, on the first function call it threw the AccessViolationException. However, doubling the buffer size to twice (32768) the original had no effect in comparison to the original -- going straight up to 9.9 from 1.0 and down back again, it stops at 1.9 and throws the exception.
EDIT: Default is ANSI, so it is ANSI. No problems there. Is this a memory allocation issue??
I would suggest trying the following:
[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode",
CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr generateKeyCode(string serial, string option, char c_type);
Note the new CharSet field of DllImport attribute.
Next idea is to use MarshalAs attribute explicitely:
[DllImport("C:\\c_keycode.dll", EntryPoint = "generateKeyCode",
CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
static extern IntPtr generateKeyCode([MarshalAs(UnmanagedType.LPTStr)] string serial, [MarshalAs(UnmanagedType.LPTStr)] string option, char c_type);
I know this may be unsatisfactory, but once I removed the output redirection I was using to debug from within my C/C++ DLL, the problem stopped. Everything works now, so I guess that's essentially equivalent to answering my own question. Thanks to everyone for the replies.
Hey!
I've just begun fiddling with pinvoke and have encountered a problem. I'm getting the AccessViolationException. First of all, is there some way to debug or trace out which field is causing this error? The only thing being written to is the result struct.
The c++ call looks like:
MyFunc(int var1, _tuchar *var2, _tuchar *var3, _tuchar *var4, MyStruct *Result,
_tuchar *var5, _tuchar *var6);
The c++ struct:
typedef struct MyStruct
{
_tuchar *id;
_tuchar *ErrorMessages;
int int1;
_tuchar language[3];
_tuchar *result;
int type;
int number;
int *type2;
_tuchar **blocks;
}
The C# struct:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.LPStr)]
public string Id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=500)]
public char[] ErrorMessages;
public int int1;
[MarshalAs(UnmanagedType.LPStr)]
public string language;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
public char[] result;
public int type;
public int number;
public int type2;
[MarshalAs(UnmanagedType.ByValArray)]
public string[] blocks;
The C# method declaration:
[DllImport(MyPath, EntryPoint = "MyEntryPoint", SetLastError = true,
CharSet = CharSet.Unicode)]
internal static extern int MyFunc(int var1, string var2, string var3,
string var4, ref MyStruct Result, string var5, string var6);
The C# Call:
var result = new MyStruct();
MyFunc(0, "var2", "var3", "var4", ref result, "var5", "var6");
Hope I haven't left anything out.
Thanks for any help!
Ooooh, man! You've picked quite a complex case for your first fiddling experience. I recommend doing something simpler first, and then moving on to the real stuff.
Firstly, CharSet=CharSet.Ansi looks suspicious. All your strings and chars are _tuchar, and I gather the u in there means "Unicode", doesn't it? If that's the case, you need CharSet=CharSet.Unicode.
Secondly, (and this is the most likely culprit) why is the ErrorMessages field marshaled as ByValArray? You know that ByVal here means "by value", don't you? As in, not by reference. And you know that little asterisk thing in C++ means "reference", don't you? So why does your reference field ErrorMessages marshaled as a by-value array? In case you don't know, an array is generally said to be passed "by value" when all of it's content is being passed, instead of just passing a reference (pointer) to a memory location where all that content is stored. In C++ struct definition, you specify _tuchar*, which means "a reference (pointer) to some memory containing one or more of _tuchars", whereas in C# you specify [MarshalAs(UnmanagedType.ByValArray, SizeConst=500)], which means "500 _tuchars are supposed to be here, no more and no less". Seeing how a reference (pointer) usually takes 4 bytes (or 8 bytes on 64bit machines), and 500 unicode characters take 1000 bytes, you have an obvious mismatch right here.
Thirdly and fourthly, same point goes for result and blocks fields.
Fifthly, the language field is exactly reverse situation: the C++ code says "there are 3 _tuchars here", while C# code says "there is a reference (pointer) to a string here" (in case you don't know, LPStr means "Long Pointer to STRing")
And finally, after you have fixed all those problems, I recommend you execute your program and print out the result of call to Marshal.SizeOf( typeof( MyStruct ) ). That will give you exactly how big your struct is, in .NET's opinion. The go on the C++ side and print out sizeof( MyStruct ). That will give you what C++ thinks about the size.
If they turn out different, see what's wrong. Try to remove fields one by one, until they become same. This will give you the culprit field(s). Work with them.
Overall, I suggest you need a better understanding of how things work first. This case is waaaay too complex for a beginner.
Good luck!
This is a bit of a shot in the dark, but have you tried decorating the string parameters with MarshalAs(UnmanagedType.LPWStr):
[DllImport(MyPath, EntryPoint = "MyEntryPoint", SetLastError = true,
CharSet = CharSet.Unicode)]
internal static extern int MyFunc(
int var1,
[MarshalAs(UnmanagedType.LPWStr)]
string var2,
[MarshalAs(UnmanagedType.LPWStr)]
string var3,
[MarshalAs(UnmanagedType.LPWStr)]
string var4,
ref MyStruct Result,
[MarshalAs(UnmanagedType.LPWStr)]
string var5,
[MarshalAs(UnmanagedType.LPWStr)]
string var6);
I believe that the default marshaling chosen for strings is BStr and _tuchar should expand to wchar_t so I'd guess that LPWStr is the correct marshalling method (pointer to a wide character string).
Update: Various things on MyStruct don't look quite right:
ErrorMessages is marked as ByValArray, and so the .Net interop is probably expecting MyStruct to look a bit like this:
typedef struct MyStruct
{
_tuchar *id;
_tuchar ErrorMessages[500];
// Rest of MyStruct
Which is probably going to cause problems - same thing for result.
Also I think that language should be using ByValArray with a size of 3.
Finally blocks should probably be passed using LPArray - ByValArray doesn't seem right.
(This is all mostly guesswork btw - I hope this is pointing you in the right direction but I don't have that much experience with P/Invoke interop)
Another update: On MyStruct you declare the charset to be Ansi, but on MyFunc its Unicode... is the unmanaged dll compiled with Unicode or Ansi? If it uses Unicode then I believe that you should use LPWStr when marshalling strings, and with Ansi it should be LPStr.
I need to call a function from a C api contained in a dll. Function prototype looks as follows....
int func( char* name, void* value );
where the contents of the pointer value can refer to any type dependent on the passed name. I an unsure how to set up the Dll inport to correctly marshall this void *. Ihave been experimenting with IntPtr which seems to work whe the value is an int but I cannot retrieve values correctly for floats etc.
I am trying to import the function like this...
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, ref IntPtr value );
pls note that value is an output. A pointer to a value of any type, i.e. the address in a global region of memory of a value of a known type (known to the caller). In a c prog the caller would then be expected to cast this void * to the desired type and dereference to get the actual value stored there. The answers given so far seem to be based around an assumption that the function will write the result to pointer location passed in. My fault as I haven't been too specific. Sorry. C# is not my bag, and I don't even know if IntPtr is the way to go here...
The best way to tackle this is to provide overloads of the function so everything is squeaky clean on the C# side. Like this:
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func(string name, out int value);
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func(string name, out float value);
// etc, one each for each type
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, IntPtr value );
...
// n - number of bytes which is enough to keep any type used by function
IntPtr ptr = Marshal.AllocHGlobal(n);
func(name, ptr);
// Use Marshal.ReadByte, Marshal.ReadInt32 ... or Marshal.Copy
// to copy from ptr filled by func to managed variable. For example:
byte b = Marshal.ReadByte(ptr);
Marshal.FreeHGlobal(IntPtr);
You do not need the ref - IntPtr is the way to pass void* to native code.
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, IntPtr value );
EDIT:
The C code can use the input to write to the required memory. The problem you face is for the managed code to know how much memory to allocate for each possible return value type. Then an appropriate sized block can be allocated using Marshal.AllocHGlobal or Marshal.AllocCoTaskMem, freed (according to which allocation method you use) use via FreeHGlobal or FreeCoTaskMem, once managed code is done with the output value.
See answer from #Alex Farber for an example.
Sometimes, it might be easier to use this approach.
Declaration:
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func([MarshalAs(UnmanagedType.LPTStr)] string name, [MarshalAs(UnmanagedType.AsAny)] object value);
Example usage to pass a value:
func("some string", (int)12);
func("some string", (double)12);
Example usage to pass a string:
func("some string", "test");
Example usage to pass a variable:
int value = 12;
func("some string", value);