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.
Related
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();
}
I have to access a C DLL from C#. I don't have source for the DLL so I am stuck with the following function signature:
int func(unsigned char* instr, unsigned char* outstr, int inlength);
How do I declare the DllImport for this in C# so that .NET understands that the returned INT is the length of the outstr array. The output char* is not a C-style string. They are byte arrays and thus there can be zeroes embedded in the output.
[DllImport("the.dll")]
int func([In] string instr, [Out] ?????? outstr, int inlength);
EDIT: Please read carefully. The RETURN value is length of the outstr parameter.
Your C API is less than ideal. The function can’t allocate a new buffer and return it, and doesn’t even take the length of the buffer.
API user (you) have to know in advance upper bound of how many bytes the function will write to that array, and supply output buffer of that size.
The DLL import itself is simple:
[DllImport( "the.dll" )]
int func( [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 2 )] byte[] instr, [Out, MarshalAs( UnmanagedType.LPArray)] byte[] outstr, int inlength );
If the function returns count of bytes written, call the function then resize the output array.
I have a C-DLL + header file and try to p/invoke a function from C#. I also have some example C++ code of how to use the function. Here is the function definition:
int GetData(unsigned char* buffer, long bufferSize);
The more interesting part is the example code and how the function can be called:
if(dataSize == 16)
{
unsigned short* usData = new unsigned short[m_numX * m_numY * 3 / 2];
GetData( (unsigned char*)usData, m_numX * m_numY * sizeof(unsigned short) );
}
else if (dataSize == 32)
{
unsigned long* ulData = new unsigned long[m_numX * m_numY];
GetData( (unsigned char*)ulData, m_numX * m_numY * sizeof(unsigned long) );
}
So, depending on the dataSize variable, the actual data array can be an ushort or an ulong. However, it is always passed as an unsigned char pointer.
For the sake of simpleness I just tried to get at least one of the variants to work. Here's the code I tried for dataSize = 16
[DllImport("External.dll", EntryPoint = "GetData", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(ref ushort[] pBuffer, long lBufferSize);
long bufferSize1 = numX * numY * 3 / 2;
long bufferSize2 = numX * numY * sizeof(ushort);
ushort[] data = new ushort[bufferSize1];
GetData(ref data, bufferSize2)
If I run above code, the application quits with an 'Access Violation' Exception. That usually means, that the unsafe code tried to write over the buffer limits or that the p/invoke declaration has an error. I tried huge buffers (which would be able to hold any kind of data I'm expecting) but my guess would be, that my mistake is in the declaration.
I also tried to declare the buffer as byte[] (since the example casts it as unsigned char*) and ulong in the p/invoke declaration. same for the actual buffer I pass as reference. the error remains the same.
How can I make this work?
A couple of mistakes:
The array must not be passed as ref. That is because ref ushort[] matches unsigned short**.
C++ long does not match C# long on Windows. The former is 32 bits, the latter 64 bits.
You need to import like this:
[DllImport("External.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(
[Out] ushort[] pBuffer,
int lBufferSize
);
It would be perfectly reasonable, for convenience, to use an overload for the 32 bit data variant:
[DllImport("External.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(
[Out] uint[] pBuffer,
int lBufferSize
);
Likewise, and overload for an array of byte would also be valid should you need it.
With C/C++ DLL SDK fun,like this:
INT CmdGetAllLog( BYTE *bStream, UINT16 *nCount, const UINT8 nblk )
but in project use c#,I do it with:
[DllImport("C:\\PrBioApi.dll", EntryPoint = "CmdGetAllLog")]
private static extern bool CmdGetAllLog(IntPtr bStream, ref UInt16 nCount, byte nblk);
and I use it with:
int nMallocSize = Marshal.SizeOf(new LOG_RECORD()) * stuSystem.wLogCnt + 4096;
byte[] pRecord = new byte[nMallocSize];
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(nMallocSize));
Marshal.Copy(pRecord, 0, p, pRecord.Length);
bGetSucc = CmdGetAllLog(p, ref nGet, nBlk++);
Marshal.FreeHGlobal(p);
but it did not work.
would anyone can help me ?thanks.
Your code which copies between the managed array, and the unmanaged pointer, is in the wrong place. It would need to be after the call to the unmanaged function.
But you may as well let the p/invoke marshaller do the work for you:
[DllImport(#"C:\PrBioApi.dll")]
private static extern bool CmdGetAllLog(
byte[] bStream,
ref ushort nCount,
byte nblk
);
int nMallocSize = ...;
byte[] pRecord = new byte[nMallocSize];
bool bGetSucc = CmdGetAllLog(pRecord, ref nGet, nBlk++);
Because a byte array is blittable then the marshaller will just pin your array during the call and hand it off to the native code.
I'm assuming that the other two parameters are passed correctly. Since you did not specify any more details of the interface, they could well be wrong. I'd guess that nGet is used to tell the function how big the buffer is, and to return how much was copied to it by the function. I cannot see where you specify nGet in the question. I'm trusting that you got that bit right.
Some other comments:
You may need to specify a calling convention in the DllImport attribute. Is the native code cdecl perhaps?
The return value is INT in the native code but you've mapped it to bool. That probably is fine if the protocol is that non-zero return means success. But if the return value indicates more than that then you'd clearly need to use int. Personally I'd be inclined to use int and stay true to the native.
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.