I am trying to marshal a hid_device_info struct in C#, but I can't figure out how to translate the wchar_t* strings to managed C# strings. I have tried all possible values in the MarshalAs attribute, but all of them returned the first character only and nothing else.
I have tried replacing all the wide strings with pointers so I can manually look at them, this is the struct that I have so far:
public struct HidDeviceInfo
{
public IntPtr path; // This one marshals fine because it's just a regular char_t*
public ushort vendor_id;
public ushort product_id;
public IntPtr serial_number; // wchar_t*
public ushort release_number;
public IntPtr manufacturer_string; // wchar_t*
public IntPtr product_string; // wchar_t*
public ushort usage_page;
public ushort usage;
public int interface_number;
public IntPtr next;
}
When I manually iterate through one of the pointers (serial_number for example), I can see that all the characters have 4 bytes (1 ascii byte followed by 3 zeros). I have tried all the possible Marshal.PtrToString... methods, but none of them are able to retrieve the full string.
I have a suspicion that the strings are being treated as 2 byte characters since I can't specify the character width anywhere in C#, and this is why it stops after the first character. Of course, by knowing this, I could easily write my own string marshaler, but I feel like there must be an existing solution and I'm overlooking something obvious.
This struct is coming from a P/Invoked function and Marshal.PtrToStructure:
[DllImport(LibUsbName, CharSet = CharSet.Unicode)]
public static extern IntPtr hid_enumerate(ushort vendorId, ushort productId);
I've also tried all the possible CharSet values.
This can't be a character type mismatch, as it was in this question, because I've tried all possible combinations of different character types.
I ended up writing this method that works fine for me, but only if all character are ASCII and the char width is guaranteed to be 4 bytes.
private static string ToUcs4String(this IntPtr ptr)
{
var builder = new StringBuilder();
var buffer = new byte[4];
while (true)
{
Marshal.Copy(ptr, buffer, 0, 4);
if (buffer[0] == 0)
break;
builder.Append((char) buffer[0]);
ptr += 4;
}
return builder.ToString();
}
Related
interoping nim dll from c# i could call and execute the code below
if i will add another function (proc) that Calls GetPacks() and try to echo on each element's buffer i could see the output in the C# console correctly
but i could not transfer the data as it is, i tried everything but i could not accomplish the task
proc GetPacksPtrNim(parSze: int, PackArrINOUT: var DataPackArr){.stdcall,exportc,dynlib.} =
PackArrINOUT.newSeq(parSze)
var dummyStr = "abcdefghij"
for i, curDataPack in PackArrINOUT.mpairs:
dummyStr[9] = char(i + int8'0')
curDataPack = DataPack(buffer:dummyStr, intVal: uint32 i)
type
DataPackArr = seq[DataPack]
DataPack = object
buffer: string
intVal: uint32
when i do same in c/c++ the type i am using is either an IntPtr or char*
that is happy to contain returned buffer member
EXPORT_API void __cdecl c_returnDataPack(unsigned int size, dataPack** DpArr)
{
unsigned int dumln, Index;dataPack* CurDp = {NULL};
char dummy[STRMAX];
*DpArr = (dataPack*)malloc( size * sizeof( dataPack ));
CurDp = *DpArr;
strncpy(dummy, "abcdefgHij", STRMAX);
dumln = sizeof(dummy);
for ( Index = 0; Index < size; Index++,CurDp++)
{
CurDp->IVal = Index;
dummy[dumln-1] = '0' + Index % (126 - '0');
CurDp->Sval = (char*) calloc (dumln,sizeof(dummy));
strcpy(CurDp->Sval, dummy);
}
}
c# signature for c code above
[DllImport(#"cdllI.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
private static extern uint c_returnDataPack(uint x, DataPackg.TestC** tcdparr);
C# Struct
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
public IntPtr StrVal;
}
}
finally calling the function like so:
public static unsafe List<DataPackg.TestC> PopulateLstPackC(int ArrL)
{
DataPackg.TestC* PackUArrOut;
List<DataPackg.TestC> RtLstPackU = new List<DataPackg.TestC>(ArrL);
c_returnDataPack((uint)ArrL, &PackUArrOut);
DataPackg.TestC* CurrentPack = PackUArrOut;
for (int i = 0; i < ArrL; i++, CurrentPack++)
{
RtLstPackU.Add(new DataPackg.TestC() { StrVal = CurrentPack->StrVal, Id = CurrentPack->Id });
}
//Console.WriteLine("Res={0}", Marshal.PtrToStringAnsi((IntPtr)RtLstPackU[1].StrVal));//new string(RtLstPackU[0].StrVal));
return RtLstPackU;
}
how could i produce similar c code as above from Nim ?
it doesn't have to be same code, but same effect, that in c# i would be able to read the content of the string. for now, the int is readable but the string is not
Edit:
this is what i tried to make things simple
struct array of int members
Update:
it seem that the problem is to do with my settings of nim in my windows OS.
i will be updating as soon as i discover what exactly is wrong.
The string type in Nim is not equivalent to the C's const char* type. Strings in Nim are represented as pointers, pointing into a heap-allocated chunk of memory, which has the following layout:
NI length; # the length of the stored string
NI capacity; # how much room do we have for growth
NIM_CHAR data[capacity]; # the actual string, zero-terminated
Please beware that these types are architecture specific and they are really an implementation detail of the compiler that can be changed in the future. NI is the architecture-default interger type and NIM_CHAR is usually equivalent to a 8-bit char, since Nim is leaning towards the use of UTF8.
With this in mind, you have several options:
1) You can teach C# about this layout and access the string buffers at their correct location (the above caveats apply). An example implementation of this approach can be found here:
https://gist.github.com/zah/fe8f5956684abee6bec9
2) You can use a different type for the buffer field in your Nim code. Possible candidates are ptr char or the fixed size array[char]. The first one will require you to give up the automatic garbage collection and maintain a little bit of code for manual memory management. The second one will give up a little bit of space efficiency and it will put hard-limits on the size of these buffers.
EDIT:
Using cstring may also look tempting, but it's ultimately dangerous. When you assign a regular string to a cstring, the result will be a normal char * value, pointing to the data buffer of the Nim string described above. Since the Nim garbage collector handles properly interior pointers to allocated values, this will be safe as long as the cstring value is placed in a traced location like the stack. But when you place it inside an object, the cstring won't be traced and nothing prevents the GC from releasing the memory, which may create a dangling pointer in your C# code.
Try to change your struct to:
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
[MarshalAs(UnmanagedType.LPStr)]
public String StrVal;
}
}
I am calling a C++ dll from my C# program. The DLL consists of several functions and I am able to call most of them except this one.
The C++ function is like as below:
__declspec(dllexport) uint8_t* myHash(const char *filename)
{
uint8_t *hash = (unsigned char*)malloc(72*sizeof(uint8_t));
//some processing on hash
return hash;
}
As can be seen in the above code, the hash function stores a character array. I want to receive the values in my C# program but I am not able to do it.
My C# code is like as below:
[DllImport("myHash.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr myHash(string filename);
IntPtr ptr = myHash(fileA);
char[] result = new char[72];
Marshal.Copy(ptr, result, 0, 72);
The problem is that char in C# is a 16 bit character element. Your C++ code returns an array of 8 bit uint8_t values. You should switch to using a byte array instead.
[DllImport("myHash.dll", CallingConvention=CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
public static extern IntPtr myHash(string filename);
....
IntPtr ptr = myHash(fileA);
byte[] result = new byte[72];
Marshal.Copy(ptr, result, 0, 72);
I specified a calling convention because, as written, your function is __cdecl. Perhaps you omitted something in the transcribing of the question, but the declaration above matches the unmanaged code in the question.
This function would be much better designed to allow the caller to allocate the buffer. That avoids you having to export a deallocator from the C++ code. I'd write the C++ like this:
__declspec(dllexport) int myHash(const char *filename, uint8_t* hash)
{
// calculate hash and copy to the provided buffer
return 0; // return value is an error code
}
And the corresponding C# code:
[DllImport("myHash.dll", CallingConvention=CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
public static extern int myHash(string filename, byte[] hash);
....
byte[] hash = new byte[72];
int retval = myHash(fileA, hash);
This function hard-codes in its interface that the buffer is of length 72. That might be reasonable, but it might make sense to pass the length of the buffer too so that the unmanaged code can defend against buffer overruns.
Note that although you refer to the output of this function as a character array, the use of uint8_t* makes it seem more likely to be a byte array. If it really is a character array, then you can use Encoding.GetString() to convert into a string.
I am converting c/c++ structures into C# standards.
C/C++ Structures:
typedef struct _sta_conn_info{
STA_CONNECT_STATE connect_state;//Enum
STA_ASSOC_STATE assoc_state;//Enum
unsigned char bssid[6];
unsigned char ssid[34];
unsigned long channel;
enum mode mode;//Enum
unsigned long signalStrength;
unsigned long noiseLevel;
STA_AUTH_ALG auth_alg;//enum
STA_ENCRYPT_ALG encrypt_alg;//enum
}STA_CONN_INFO;
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
PTCHAR ptcDeviceName;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Respective C# structures:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _sta_conn_info
{
public _sta_connect_state connect_state;
public _sta_assoc_state assoc_state;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public char[] bssid ;//= new char[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public char[] ssid ;//= new char[34]
public uint channel;
public mode mode;
public uint signalStrength;
public uint noiseLevel;
public _sta_auth_alg auth_alg;
public _sta_encrypt_alg encrypt_alg;
}
QUERY STRUCT:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
public byte[] Data;
};
I converted the data types using this >>>reference
Marshal.SizeOf() is working in WIN CE. I tested it.
If my structure conversion is fine then definitely Marshal.SizeOf() will work to get the size of the structure, but it is throwing exceptions and returning error code 87 in DeviceIoControl() API.
Can anyone clarify me about the conversions and let me know If I did anything wrong.
For bssid and ssid the C++ declarations are:
unsigned char bssid[6];
unsigned char ssid[34];
Now, unsigned char is a single byte and is typically used for byte arrays rather than text. So the C# should be:
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public byte[] bssid ;//= new byte[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public byte[] ssid ;//= new byte[34]
Your use of char in the C# is not correct because char is two bytes wide in C#.
In _NDISUIO_QUERY_OID where you have
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = sizeof(uint))]
public byte[] Data;
I believe that you need to use ByValArray rather than ByValTStr. But as we have discussed in many of your recent questions, the exact meaning of this member is unclear. Is it really a fixed length byte array, or is it a variable length buffer? Do you have sample C++ code that works? That would settle the debate once and for all.
OK, from the header nuiouser.h header file I have this:
//
// Structure to go with IOCTL_NDISUIO_QUERY_OID_VALUE.
// The Data part is of variable length, determined by
// the input buffer length passed to DeviceIoControl.
//
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
#ifdef UNDER_CE
//
// In CE land app is allowed to query without having to do
// IOCTL_NDISUIO_OPEN_DEVICE
// Hence the device name to query argument needed..
// For app that does IOCTL_NDISUIO_OPEN_DEVICE this argument
// is then not necessary..
//
PTCHAR ptcDeviceName;
#endif
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Which tells you conclusively that Data is variable length. You'll need to allocate the struct with AllocHGlobal and do all the marshalling by hand I am afraid.
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.
I need to make interop calls to a DLL written in C++. In the C++ code, there are various functions that receive and return strings. These all use a commonly defined type (struct) in C++, which comprises a pointer to a string along with an integer for its size as follows:
struct StringParam
{
int size; // 4 byte integer for the size of the string buffer
LPWSTR buff; // Pointer to a wide char buffer
}
When a string is returned from the DLL and when the string buffer is too small to store the string result, an error code is returned along with the correct size buffer that is required in the integer size field, so that the caller can provide the right size buffer.
Ignoring the struct for the moment, this could be easily accomplished through interop using the combination of a StringBuilder parameter, along with a ref int parameter for the size. However, we are using a struct and StringBuilder fields are not permitted in structs that are marshalled.
Given a C++ function that receives a string as follows:
int __stdcall DoSomethingWithString(StringParam *stringParam)
One can declare the following struct in C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct StringParam
{
public int Size;
[MarshalAs(UnmanagedType.LPWStr)]
public string Text;
public StringParam(string text)
{
this.Text = text;
this.Size = text.Length;
}
}
and can successfully initialize a struct and call a C++ function with the following signature:
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int DoSomethignWithString(ref StringParam stringParam);
However, given a function that needs to return a string as follows, presents a problem:
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetSomeString(ref StringParam stringParam);
When receiving a string from the interop call, we do not know the size of the string and must allocate a buffer sufficient in size for storing the string result. StringBuilder would be ideal, but cannot be used within a struct that is marshalled. We could pre-allocate the string with 2048 dummy characters, which could then be used to store the results. This is in fact recommended in the error message one gets when attempting to use the StringBuilder type:
Cannot marshal field 'Text' of type 'StringParam': Struct or class fields cannot be of type StringBuilder. The same effect can usually be achieved by using a String field and preinitializing it to a string with length matching the length of the appropriate buffer.
This seems kind of messy to pre-initialize a string with dummy values. Is there another/better way to go about this?