在C#中使用C ++ DLL

时间:2014-07-17 06:05:53

标签: c# c pinvoke

我的程序需要控制硬件。供应商提供了一个为C / C ++语言设计的DLL。 大多数函数都通过我的测试旁边的函数:

int32 ni845xSpiWriteRead(NiHandle DeviceHandle,
                         NiHandle ConfigurationHandle,
                         uInt32   WriteSize,
                         uInt8  * pWriteData,
                         uInt32 * pReadSize,
                         uInt8  * pReadData);

此功能的文件在这里:

  • 输入NiHandle DeviceHandle

    从ni845xOpen返回设备句柄。

  • NiHandle ConfigurationHandle

    从ni845xSpiConfigurationOpen返回的配置句柄。

  • uInt32 WriteSize

    要写入的字节数。这必须是非零的。

  • uInt8 * pWriteData

    要写入的数据字节。

  • 输出uInt32 * pReadSize

    指向读取的字节数的指针。

  • uInt8 * pReadData

    指向已读取字节的字节数组的指针 存储。

我在C#中的代码在这里:

[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
                                                long DeviceHandle, //In 
                                                long ConfigurationHandle, //In
                                                int WriteSize, //In
                                                IntPtr pWriteData, //In
                                                IntPtr pReadSize, //Out
                                                IntPtr pReadData //Out
                                                );

我总是遇到AccessViolationException例外。我猜这是由指针输入/输出参数引起的。

我在这里调用Swrapper.ni845xSpiWriteRead()的代码:

public void WriteData(int length, int[] writeArray)
        {
            byte[] writeDate = new byte[8];

            int writeSize = writeDate.Length;

            try
            {
                //Define pointers
                IntPtr writeDataPointer = Marshal.AllocHGlobal(writeDate.Length);
                IntPtr readDataSizePointer = Marshal.AllocHGlobal(writeDate.Length);
                IntPtr readDataPointer = Marshal.AllocHGlobal(writeDate.Length);

                //Copy value to write data pointer
                Marshal.Copy(writeDate, 0, writeDataPointer, writeDate.Length);

                int state = Ni845xNativeMethods.ni845xSpiWriteRead(_niHandle, _niConfigrationHandle, writeSize, writeDataPointer,readDataSizePointer,readDataPointer);

                this.CheckStatus(state);

            }
            catch (Exception)
            {
                throw;
            }
        }

3 个答案:

答案 0 :(得分:2)

p / invoke最有可能是:

[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
    IntPtr DeviceHandle,
    IntPtr ConfigurationHandle,
    uint WriteSize,
    [In] byte[] WriteData,
    out uint ReadSize,
    [Out] byte[] ReadData
);

您需要分配足够长度的byte[]作为ReadData参数。大概你知道怎么做。

我从表面看了ReadSize是输出参数的陈述。但是,如果它同时进入,则将其声明为ref

答案 1 :(得分:0)

最可能的是它只是Out size参数。所以:

[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
                                            long DeviceHandle, //In 
                                            long ConfigurationHandle, //In
                                            int WriteSize, //In
                                            IntPtr pWriteData, //In
                                            out uint ReadSize, //Out
                                            IntPtr pReadData //Out
                                            );

这类似于@Marius,但句柄应该是整数。诀窍是将大小返回为整数,因此要么“out uint”或“ref uint”来执行此操作。

如果我们能看到调用代码,那就好多了。


看过代码后,你也可以试试这样的东西。

[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
  long DeviceHandle, //In 
  long ConfigurationHandle, //In
  int WriteSize, //In
  [MarshalAs(UnmanagedType.LPArray)] long [] writeData, //In
  out uint ReadSize, //Out
  [MarshalAs(UnmanagedType.LPArray), Out] readData //Out
);

这避免了所有Marshal.Copy的东西。确保readData缓冲区足够大。

答案 2 :(得分:0)

我认为现有的两个答案都有些不正确,所以我会添加另一个(可能也是不正确的)答案。

如果DeviceHandle和ConfigurationHandle是真正的HANDLE类型(或其他指针类型),那么你应该使用IntPtr作为P / Invoke的类型。

给定C函数声明,ReadData缓冲区必须由调用者分配。此外,可能必须将ReadSize初始化为缓冲区的大小,并将其设置为函数调用期间读取的实际字节数。

我认为这可能接近工作:

class Program
{
    [DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
    public static extern int ni845xSpiWriteRead(
        IntPtr DeviceHandle,
        IntPtr ConfigurationHandle,
        UInt32 WriteSize,
        [MarshalAs(UnmanagedType.LPArray)][In] Byte[] pWriteData,
        ref UInt32 ReadSize,
        [MarshalAs(UnmanagedType.LPArray)][Out] Byte[] pReadData);

    static void Main(string[] args)
    {
        IntPtr DeviceHandle = (IntPtr)123;
        IntPtr ConfigurationHandle = (IntPtr)456;
        Byte[] WriteData = new Byte[2324];
        Byte[] ReadData = new Byte[8274];
        UInt32 ReadSize = (UInt32)ReadData.Length;

        int result = ni845xSpiWriteRead(DeviceHandle,
                                        ConfigurationHandle,
                                        (UInt32) WriteData.Length,
                                        WriteData,
                                        ref ReadSize,
                                        ReadData);
    }
}