我需要从C#代码调用一个本机DLL。由于我对C/C++不太熟悉,所以我不知道如何在C#中声明在C中定义的结构以便调用它。问题是,两个参数似乎是一个结构数组,我不知道如何在C#中声明它(参见最后一个代码块):
c++头文件:
typedef enum
{
OK = 0,
//others
} RES
typedef struct
{
unsigned char* pData;
unsigned int length;
} Buffer;
RES SendReceive(uint32 deviceIndex
Buffer* pReq,
Buffer* pResp,
unsigned int* pReceivedLen,
unsigned int* pStatus);
c#声明:
enum
{
OK = 0,
//others
} RES
struct Buffer
{
public uint Length;
public ??? Data; // <-- I guess it's byte[]
}
[DllImport("somemodule.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint SendReceive(
uint hsmIndex,
uint originatorId,
ushort fmNumber,
??? pReq, // <-- should this be ref Buffer[] ?
uint reserved,
??? pResp, // <-- should this be ref Buffer[] ?
ref uint pReceivedLen,
ref uint pFmStatus);
在一个等价的java客户机中,我发现参数不仅仅是一个缓冲区,而是一个缓冲区数组。在C#中,它看起来如下所示:
var pReq = new Buffer[]
{
new Buffer { Data = new byte[] { 1, 0 }, Length = (uint)2 },
new Buffer {Data = requestStream.ToArray(), Length = (uint)requestStream.ToArray().Length },
//according to the header file, the last item must be {NULL, 0}
new Buffer { Data = null, Length = 0 }
};
var pResp = new Buffer[]
{
new Buffer { Data = new byte[0x1000], Length = 0x1000 },
//according to the header file, the last item must be {NULL, 0}
new Buffer { Data = null, Length = 0x0 }
};
这对我来说很奇怪,因为extern方法确实有一个指向缓冲区结构( Buffer *)的指针,而不是一个指向缓冲区数组(Buffer[]*)的指针。如何在C#中定义结构和extern方法的参数类型?
任何帮助都很感谢,谢谢。
发布于 2013-04-24 23:34:35
首先,您的结构以错误的顺序获得参数。需要将字节数组声明为IntPtr
,并手动编组:
struct Buffer
{
public IntPtr Data;
public uint Length;
}
p/invoke应是:
[DllImport("MyNativeDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern RES SendReceive(
uint deviceIndex,
[In] Buffer[] pReq,
[In, Out] Buffer[] pResp,
out uint pReceivedLen,
out uint pStatus
);
字节数组需要是IntPtr
,这样结构才是可闪烁的。这是必需的,这样数组参数就可以声明为Buffer[]
。
做字节数组的编组工作会有点痛苦。您将希望使用GCHandle
来插入托管字节数组,并调用AddrOfPinnedObject()
来获取结构数组中每个结构的固定数组的地址。编写一些帮助函数以减轻任务的痛苦是值得的。
发布于 2013-04-24 23:28:49
c#中的方法签名应该如下所示:
[DllImport("MyNativeDll.dll")]
public static extern RES SendReceive (uint32 deviceIndex, ref Buffer pReq, ref Buffer pResp, ref uint pReceivedLen, ref uint pStatus);
请参阅此项目,将来您可能会从.net http://clrinterop.codeplex.com/releases/view/14120生成本机调用。
发布于 2013-04-24 23:52:20
基于C++头,但不进行测试,请查看以下代码:
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace WindowsFormsApplication1
{
public class Class1
{
public struct Buffer
{
[MarshalAs(UnmanagedType.LPStr)]
public StringBuilder pData;
public uint length;
}
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
static extern int LoadLibrary(string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
static extern IntPtr GetProcAddress(int hModule, string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
static extern bool FreeLibrary(int hModule);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal delegate IntPtr SendReceive(
uint deviceIndex,
ref Buffer pReq,
ref Buffer pResp,
uint pReceivedLen,
uint pStatus);
public void ExecuteExternalDllFunction()
{
int dll = 0;
try
{
dll = LoadLibrary(@"somemodule.dll");
IntPtr address = GetProcAddress(dll, "SendReceive");
uint deviceIndex = 0;
Buffer pReq = new Buffer() { length = 0, pData = new StringBuilder() };
Buffer pResp = new Buffer() { length = 0, pData = new StringBuilder() };
uint pReceivedLen = 0;
uint pStatus = 0;
if (address != IntPtr.Zero)
{
SendReceive sendReceive = (SendReceive)Marshal.GetDelegateForFunctionPointer(address, typeof(SendReceive));
IntPtr ret = sendReceive(deviceIndex, ref pReq, ref pResp, pReceivedLen, pStatus);
}
}
catch (Exception Ex)
{
//handle exception...
}
finally
{
if (dll > 0)
{
FreeLibrary(dll);
}
}
}
}
}
https://stackoverflow.com/questions/16208592
复制