2017-02-13 97 views
1

我尝试在我的C#代码以使用C++ DLL。
所读取的所有数据必须被传递到DLL作为一个指针结构。我的第一个想法是只保留一些非托管内存,传递函数指针和事后提取数据。问题是这些函数只会返回一个错误代码,该代码会转换为“参数无效”。C#通指向struct(含有非Blittable型)非托管C++ 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.对不起英语不好,我不是母语的人。

回答

3

这些声明有很多问题,往往发生在程序员不断尝试使代码调用工作的黑客代码时。对结构最可能是正确的声明是:

[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手段。 Punting:

[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); 

而且通话看起来不正确。当一个struct有一个“size”成员时,那么api协议通常会要求它在调用之前被设置。这是一个安全措施,确保API时,呼叫方使用了错误的结构声明不能破坏内存。当设置错误时,“无效参数”错误是预期的结果。所以写它类似于:

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示例。