确定DLLImport参数并安全地调用非托管C函数

时间:2013-10-04 20:22:58

标签: c# c interop pinvoke

作为我之前问题的后续内容,我终于将C dll导出并在C#中可用,但我仍然试图弄清楚正确的参数类型和调用方法。

我在这里研究了SO,但似乎没有关于如何分配变量类型的模式。

我看到有些人建议StringBuilderuchar*,其他人为byte[],有些人提及“不安全”代码,等等。任何人都可以推荐基于此的解决方案具体用例?

另请注意,在调用C函数之后,代码就会立即生成异常。

C函数导入:

[DllImport("LZFuncs.dll")]
internal static extern long LZDecomp(ref IntPtr outputBuffer, byte[] compressedBuffer, UInt32 compBufferLength); //Originally two uchar*, return is size of uncompressed data.

C函数签名:

long LZDecomp(unsigned char *OutputBuffer, unsigned char *CompressedBuffer, unsigned long CompBufferLength)

使用如下:

for (int dataNum = 0; dataNum < _numEntries; dataNum++)
        {
            br.BaseStream.Position = _dataSizes[dataNum]; //Return to start of data.
            if (_compressedFlags[dataNum] == 1)
            {
                _uncompressedSize = br.ReadInt32();
                byte[] compData = br.ReadBytes(_dataSizes[dataNum] - 4);
                IntPtr outData = IntPtr.Zero;
                LZFuncs.LZDecomp(ref outData, compData, Convert.ToUInt32(compData.Length));
                var uncompData = new byte[_uncompressedSize]; //System.ExecutionEngineException was unhandled
                Marshal.Copy(outData, uncompData, 0, Convert.ToInt32(_uncompressedSize));
                BinaryWriter bw = new BinaryWriter(new FileStream("compData" + dataNum + ".txt", FileMode.CreateNew));
                bw.Write(uncompData);
                bw.Close();
            }
            else
            {
                BinaryWriter bw = new BinaryWriter(new FileStream("uncompData" + dataNum + ".txt", FileMode.CreateNew));
                bw.Write(br.ReadBytes(_dataSizes[dataNum]));
                bw.Close();
            }
        }

我认为,如果C代码破坏了C#调用者,那么C代码会严重破坏内存,但是由于C代码的编写方式,在没有破坏功能的情况下绝对没有办法修改它,它是实际上是一个黑盒子。 (大部分是用大会写的。)

作为参考,我只是为了解决这个问题而阅读了几个问题:

How do I return a byte array from C++ to C#

Correct way to marshall uchar[] from native dll to byte[] in c#

还有其他人,但这些是最新的。

2 个答案:

答案 0 :(得分:1)

好的,这不太难以使用。两个缓冲区参数是字节数组。您应该将它们声明为byte[]。调用约定是Cdecl。请记住,C ++ long在Windows上只有32位宽,所以使用C#int而不是C#long,因为后者是64位宽。

声明这样的函数:

[DllImport("LZFuncs.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int LZDecomp(
    [Out] byte[] outputBuffer, 
    [In] byte[] compressedBuffer, 
    uint compBufferLength
);

您正在将compressedBuffer解压缩为outputBuffer。你需要知道outputBuffer需要多大(问题中的代码表明你已经处理过这个)并分配一个足够大的数组。除此之外,我认为如何称呼它是显而易见的。

调用代码如下所示:

_uncompressedSize = br.ReadInt32();
byte[] compData = br.ReadBytes(_dataSizes[dataNum] - 4);
byte[] outData = new byte[_uncompressedSize];
int len = LZFuncs.LZDecomp(outData, compData, (uint)compData.Length);

答案 1 :(得分:0)

这是一个老问题,但却是一个真正的问题,可能会导致严重的安全问题,所以我想我给它 2 美分

每当我使用 [DllImport] 时,我总是添加您认为安全的位置,一种选择是指定用于 Windows DLL 的位置

[DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]

但是,查看您的选项以使其符合您的需求,您可能会加载位于其他地方的私有 DLL。