我的程序需要控制硬件。供应商提供了一个为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;
}
}
答案 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);
}
}