Call dll with C++ header from C#
I'm trying to call a method in a dll which I have the C++ header for. I'm calling the dll from C#. The input is a string and the output is binary data. Any of the following 3 methods would probably work, I just don't know how to make any of them w开发者_如何学Goork all the way. The C# declaration is made by me, so they might not be correct
1: I'm able to get the hGlobal, but I don't know how to get data from the handle.
//CMBT_LL_WINAPI INT DLLPROC LlConvertStringToHGLOBALW(LPCWSTR pszText, _PHGLOBAL phMemory);
[DllImport("cmll15.dll", EntryPoint = "LlConvertStringToHGLOBALW", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern int _LlConvertStringToHGlobal32(string text, ref IntPtr handle);
2:
[DllImport("cmll15.dll", EntryPoint = "LlConvertStringToBLOBW", CharSet = CharSet.Unicode, ExactSpelling = true)]
//CMBT_LL_WINAPI INT DLLPROC LlConvertStringToBLOBW(LPCWSTR pszText, _PUINT8 pBytes, UINT nBytes);
private static extern int _LlConvertStringToBLOBW(string text, ref IntPtr pBytes, UInt32 nBytes);
3:
[DllImport("cmll15.dll", EntryPoint = "LlConvertStringToStreamW", CharSet = CharSet.Unicode, ExactSpelling = true)]
//CMBT_LL_WINAPI INT DLLPROC LlConvertStringToStreamW(LPCWSTR pszText, _PISTREAM pStream);
private static extern int _LlConvertStringToStreamW(string text, ref IntPtr pStream);
Updated, here is the code I think I will end up with.
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern UIntPtr GlobalSize(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern IntPtr GlobalUnlock(IntPtr handle);
[DllImport("cmll15.dll", EntryPoint = "LlConvertStringToHGLOBALW", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern int _LlConvertStringToHGlobal32(string text, ref IntPtr handle);
private static void Main(string[] args)
{
IntPtr dataHandle = IntPtr.Zero;
_LlConvertStringToHGlobal32(Contents, ref dataHandle);
try
{
var size = (uint) GlobalSize(dataHandle);
var array = new byte[size];
IntPtr dataPtr = GlobalLock(dataHandle);
try
{
Marshal.Copy(dataPtr, array, 0, array.Length);
}
finally
{
GlobalUnlock(dataPtr);
}
using (var fs = new FileStream("c:\\file.dat", FileMode.Create))
{
fs.Write(array, 0, array.Length);
}
}
finally
{
Marshal.FreeHGlobal(dataHandle);
}
}
The first one ought to be easiest to get going because that leaves it up to the callee to figure out the required size. It is however not obvious how you are supposed to know the size of the allocation. Maybe the return value. You can always pinvoke GlobalSize() to get the size from the HGLOBAL handle. You should pinvoke GlobalLock() to convert the handle to a pointer, then Marshal.CopyMemory() to copy it into a byte[]. Cleanup by calling GlobalUnlock() and Marshal.FreeHGlobal() to release the memory, put it inside a finally block so you cannot leak.
For the second one, you should declare the 2nd argument as byte[] (not ref). Trouble is that you'll have to guess the size of the array up front. A failure mode would be guessing the size too small.
The third one requires a COM IStream. Declare it as out System.Runtime.InteropServices.ComTypes.IStream. It behaves a lot like a .NET Stream, you'd call Seek to seek to the beginning and Read to read the data.
I'd go for the first one, least likely to blow up in your face.
For 1: You should have the length of binary data output and then using Marshal.Copy method as following: byte[] data = new byte[length]; Marshal.Copy(handle, data, 0, length);
For 2 & 3: what's your problem?
精彩评论