C# marshalling char* to StringBuilder get always empty string - c#

I am developing a C++ library and a C# application that should consume it.
The library takes two numeric input arguments and one string output parameter.
My problem is that in the C# application i get always an empty string for this parameter. Here is my code.
C++ side:
typedef struct sharedItem{
unsigned int tagId;
unsigned char tagValue[256];
}sharedItem;
extern "C" {
int getSharedMemoryVariable(char* value, unsigned int variableTagId, int foundVariables)
{
sharedItem *item;
set item properly...
strcpy(value, (char *)item->tagValue);
check result and return properly...
}
}
C# side
[DllImport("C:\\SharedMemory.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
public static extern int getSharedMemoryVariable(StringBuilder variableValue, UInt16 variableTagId, Int16 foundVariables);
StringBuilder value = new StringBuilder(256);
res = SharedMemory.getSharedMemoryVariable(value, 45, 14730);
My problem is that variable value is always an empty string. Please note that, in C++ side, if I replace
strcpy(value, (char *)item->tagValue);
with
strcpy(value, "test");
the application works fine.
I hope somebody can help me.
Thank you

EDIT:
[DllImport] already pins parameters; and there's no need for unsafe code
Thanks #dan
Anyway that can be fixed by doing a memset(item->tagValue, '\0', 256*sizeof(char));

Related

Having trouble marshalling an array of structs from c to c#

I'm trying to marshal an array of c structs into C# (Using Unity) but, no matter the method I use, I always get an exception or a crash.
I'm loading dlls (libretro cores) that conform (or should...) to the Libretro API, the c/c++ side is not available to me (more precisely, not allowed to be modified by me), which means I have to handle the data I get back from that dll no matter how it is laid out.
The C API structs are defined as follow (RETRO_NUM_CORE_OPTION_VALUES_MAX is a constant with a value of 128):
struct retro_core_option_value
{
const char *value;
const char *label;
};
struct retro_core_option_definition
{
const char *key;
const char *desc;
const char *info;
struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX];
const char *default_value;
};
struct retro_core_options_intl
{
struct retro_core_option_definition *us;
struct retro_core_option_definition *local;
};
My C# mappings look like this at the moment:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct retro_core_option_value
{
public char* value;
public char* label;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct retro_core_option_definition
{
public char* key;
public char* desc;
public char* info;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = RETRO_NUM_CORE_OPTION_VALUES_MAX)]
public retro_core_option_value[] values;
public char* default_value;
}
[StructLayout(LayoutKind.Sequential)]
public struct retro_core_options_intl
{
public IntPtr us;
public IntPtr local;
}
The callback function has the following signature on the C# side:
public unsafe bool Callback(retro_environment cmd, void* data)
retro_environment is an unsigned int converted to an enum, a switch is performed on it and then dictates how to handle the void* data pointer appropriately. Here data is a retro_core_options_intl*.
I'm able to do the void* conversion in 2 ways:
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
or
retro_core_options_intl* intl = (retro_core_options_intl*)data;
I get a readable address with both approaches (intl.us for the first and intl->us for the second), the "local" part is empty in my particular case but the "us" part is defined as mandatory by the API. intl->us points to an array of retro_core_option_definition of variable length.
The issue I'm having is trying to read the values inside of this mandatory construct.
The array I'm trying to load right now can be seen here: https://github.com/visualboyadvance-m/visualboyadvance-m/blob/master/src/libretro/libretro_core_options.h at line 51.
The API defines a fixed size for the "struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]" struct member, but code that comes in is almost always defined as an array where the last element is "{ NULL, NULL }" to indicate the end, so they don't always (almost never) contain 128 values.
I tried:
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition us = Marshal.PtrToStructure<retro_core_option_definition>(intl.us);
This gives a NullReferenceException.
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition[] us = Marshal.PtrToStructure<retro_core_option_definition[]>(intl.us);
This gives a retro_core_option_definition array of 0 length.
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition us = new retro_core_option_definition();
Marshal.PtrToStructure(intl.us, us);
This gives a "destination is a boxed value".
That's basically where I'm at...
Any help would be much appreciated :)
The entire codebase can be found here: https://github.com/Skurdt/LibretroUnityFE
First thing I see is that you either need to use wchar_t types instead of char types in you C code, or you can use byte instead of char in C#. System.Char in C# is two bytes. char in C code is 1 byte.
You can also use System.String in the C# code and annotate it with a MarshalAs attribute to tell it what type of char data is coming in, such as Ansi or Unicode C strings.

Native C-DLL in C# AccessViolation/Marshalling

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.

Marshalling C struct containing arrays to C#

With great help of the stackoverflow community, I've managed to call a native DLL function. However, I can't modify the values of ID or intersects array. No matter what I do with it on the DLL side, the old value remains. It seems read-only.
Here are some code fragments:
C++ struct:
typedef struct _Face {
int ID;
int intersects[625];
} Face;
C# mapping:
[StructLayout(LayoutKind.Sequential)]
public struct Face {
public int ID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 625)]
public int[] intersects;
}
C++ method (type set to DLL in VS2010):
extern "C" int __declspec(dllexport) __stdcall
solve(Face *faces, int n){
for(int i =0; i<n; i++){
for(int r=0; r<625; r++){
faces[i].intersects[r] = 333;
faces[i].ID = 666;
}
}
C# method signature:
[DllImport("lib.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int solve(Face[] faces, int len);
C# method invocation:
Face[] faces = new Face[10];
faces[0].intersects = new int[625];
faces[0].ID = -1; //.. and add 9 more ..
solve(faces, faces.Length);
// faces[0].ID still equals -1 and not 666
Kindest regards,
e.
You have to tell the pinvoke marshaller explicitly that the array needs to be marshaled back. You do this with the [In] and [Out] attributes. Like this:
[DllImport("...")]
public static extern int solve([In, Out] Face[] faces, int len);
This is an output field only? To get to the bottom of this, I'd try substituting your Face[] parameter with a large-enough a byte[] and see if the byte array gets filled with anything (you'll have to change your [DllExport] a bit).
Also, one other thing I used to experience when doing this with char*'s is that I had to pre-allocate the buffer in C#. For example:
StringBuilder theString=new StringBuilder();
MyUnmanagedFunction(theString);
would not work. But assuming that returned theString was a max 256 characters, I would do this:
StringBuilder theString=new StringBuilder(256);
MyUnmanagedFunction(theString);
And I'd be in business. I'd recommend trying the byte[] substitution, if that doesn't work, try the pre-allocated byte array. Once you are seeing the byte array actually get changed by your C++ code, then you can figure out how to marshal that thing into your C# struct.

Pinvoke struct marshalling help needed - System.AccessViolationException

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.

How to map a C array to C#?

My question has to do with trying to call a function written in C from C#. I've looked in a header file that came with the C library to understand the functions as they exist in the C dll. Here's what I see:
C code (for a function called "LocGetLocations"):
typedef enum {
eLocNoError,
eLocInvalidCriteria,
eLocNoMatch,
eLocNoMoreLocations,
eLocConnectionError,
eLocContextError,
eLocMemoryError
} tLocGetStatus;
typedef void *tLocFindCtx;
typedef void *tLocation;
PREFIX unsigned int POSTFIX LocGetLocations
(
tLocFindCtx pCtx,
tLocation *pLoc,
unsigned int pNumLocations,
tLocGetStatus *pStatus
);
In C#, I have this:
[DllImport(#"VertexNative\Location.dll")]
public static extern uint LocGetLocations(IntPtr findContext, out byte[] locations, uint numberLocations, out int status);
The problem is that I don't quite know how to handle the pLoc parameter in C#. I'm bringing it over as a byte array, although I'm not sure if that is correct. The C library's documentation says that that parameter is a pointer to an array of handles.
How can I get an array back on the C# side and access its data?
The example I was given in C, looks like this:
tLocation lLocation[20];
// other stuff
LocGetLocations(lCtx, lLocation, 20, &lStatus)
Any help would be much appreciated!
Generally, the only thing that matters is the size of the parameters. As I recall enums are integers in C, so you can simply use that. Or better, recreate the same enum in C#, I think it would work. One thing to remember is that when dealing with complex structs, one needs to use attributes to tell the framework about the desired alignment of members.
In the end, this signature works:
[DllImport(#"VertexNative\Location.dll")]
public static extern uint LocGetLocations(IntPtr findContext, [Out] IntPtr[] locations, uint numberLocations, out int status);
And I can call it like this (some refactoring needed):
IntPtr[] locations = new IntPtr[20];
int status;
// findContext is gotten from another method invocation
uint result = GeoCodesNative.LocGetLocations(findContext, locations, 20, out status);
Thanks for the help!

Categories