我尝试在C#代码中使用C ++ DLL 必须将读取的所有数据作为指向结构的指针传递给dll。我的第一个想法是只保留一些非托管内存,将指针传递给函数并随后提取数据。问题是函数只返回一个错误代码,转换为“参数无效”。
从DLL标题(缩短结果和BusPortId)
typedef enum {
BUS_COM1 = 0x01, // 2 to 15 ommited
BUS_COM16 = 0x10,
BUS_USB1 = 0x101, // 2 to 15 ommited
BUS_USB16 = 0x110
} BusPortId;
typedef enum {
BUS_SUCCESS = 0, //!< The operation was completed successfully
BUS_ERROR = 0x100, //!< An error occured
BUS_INVALIDARG = 0x1000, //!< An argument is not valid
} Result
struct BusPortInfo
{
ULONG portInfoSize;
CHAR portText[64];
BOOL portLocked;
BusPortId portId;
};
Result BUSDRV_API busGetAvailablePortCount( ULONG *retCount );
Result BUSDRV_API busGetAvailablePort( ULONG index, BusPortInfo *portInfo );
到目前为止我的C#程序的相关部分
enum BusPortId
{
BUS_COM1 = 0x01, // 2 to 15 ommited
BUS_COM16 = 0x10,
BUS_USB1 = 0x101, // 2 to 15 ommited
BUS_USB16 = 0x110
};
public enum Result
{
BUS_SUCCESS = 0, //!< The operation was completed successfully
BUS_ERROR = 0x100, //!< An error occured
BUS_INVALIDARG = 0x1000, //!< An argument is not valid
};
struct BusPortInfo
{
public ULONG portInfoSize;
unsafe public fixed char portText[64];
public BOOL portLocked;
public BusPortId portId;
}
[DllImport(DLL_Path)]
unsafe static extern Result busGetAvailablePortCount(ULONG* retCount);
[DllImport(DLL_Path)]
unsafe static extern Result busGetAvailablePort(ULONG index, BusPortInfo* portInfo);
ulong count= 0;
Result res = busGetAvailablePortCount(&count);
ulong index = 0;
BusPortInfo info = new BusPortInfo();
Result res = busGetAvailablePort(0, &info);
对busGetAvailablePortCount(和其他类似函数)的调用没有任何问题。但是当我调用busGetAvailablePort时,我在控制台输出中得到以下内容
Cannot marshal 'parameter #2': Pointers cannot reference marshaled structures. Use ByRef instead.
问题是我可以在C#中更改我的结构以便我可以传递指针,但是DLL中的函数也会返回“参数无效”
我需要对我的结构做什么,以便我可以将指针传递给函数,同时仍然被DLL接受?
P.S。对不起英语不好,我不是母语人士。
答案 0 :(得分:3)
这些声明存在很多问题,当程序员不断破解代码以试图使pinvoke调用工作时,往往会发生这些问题。该结构最可能的正确声明是:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct BusPortInfo {
public int portInfoSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string portText;
public bool portLocked;
public BusPortId portId;
}
强调本机代码中的ULONG是32位类型,而fixed
并不是必需的,而且非常尴尬。由于bool和string成员,这个结构不是blittable,没什么可担心的。
[DllImport]声明需要正确声明第二个参数。 CallingConvention属性总是很重要,我们无法看到BUSDRV_API的含义。撑船:
[DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)]
static extern Result busGetAvailablePortCount(out int retCount );
[DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)]
static extern Result busGetAvailablePort(int index,
[In, Out] ref BusPortInfo portInfo);
电话看起来不正确。当一个结构具有&#34;大小&#34;成员然后api合同通常要求在呼叫之前设置它。这是一种安全措施,它确保当调用者使用错误的结构声明时,api不会破坏内存。当设置错误时,&#34;无效的参数&#34;错误是预期的结果。所以写它类似于:
int count;
Result res = busGetAvailablePortCount(out count);
if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString());
for (int ix = 0; ix < count; ++ix) {
BusPortInfo info;
info.portInfoSize = Marshal.SizeOf(typeof(BusPortInfo)); // Important!
res = busGetAvailablePort(ix, ref info);
if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString());
// etc...
}
当然没有经过测试,应该在球场。如果仍有问题,请验证本机代码中的sizeof(BusPortInfo)是否与C#中的Marshal.SizeOf(typeof(BusPortInfo))的值匹配。如果全部失败则使用C ++ / CLI,这样您就可以直接使用本机声明。并与DLL的所有者讨论正确的使用说明,最好他会为你写一个pinvoke样本。