使用C#中的C ++头调用dll

时间:2011-01-12 08:57:03

标签: c# c++ marshalling

我正在尝试在dll中调用一个方法,我有C ++标题。我正在用C#调用dll。输入是一个字符串,输出是二进制数据。以下3种方法中的任何一种都可能有效,我只是不知道如何使它们中的任何一种都能正常工作。 C#声明是由我做出的,所以它们可能不正确

1:我能够获得hGlobal,但我不知道如何从句柄中获取数据。

//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);

更新了,这是我认为最终会得到的代码。

    [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);
        }
    }

2 个答案:

答案 0 :(得分:1)

第一个应该是最容易上手的,因为它会让被调用者知道所需的大小。然而,您应该如何知道分配的大小并不明显。也许是回报值。您始终可以通过pinvoke GlobalSize()来获取HGLOBAL句柄的大小。你应该pinvoke GlobalLock()将句柄转换为指针,然后Marshal.CopyMemory()将其复制到byte []。通过调用GlobalUnlock()和Marshal.FreeHGlobal()来释放内存进行清理,将其放在finally块中,这样就不会泄漏。

对于第二个,你应该将第二个参数声明为byte [](不是ref)。麻烦的是你必须预先猜出阵列的大小。失败模式会猜测尺寸太小。

第三个需要COM IStream。将其声明为System.Runtime.InteropServices.ComTypes.IStream。它的行为很像.NET Stream,你可以调用Seek来寻找开头,然后读取读取数据。

我会选择第一个,最不可能在你脸上爆炸。

答案 1 :(得分:-1)

1: 您应该具有二进制数据输出的长度,然后使用Marshal.Copy方法如下: byte [] data = new byte [length]; Marshal.Copy(句柄,数据,0,长度);

2& 3:你的问题是什么?