C# 在C中包含枚举的封送结构#

C# 在C中包含枚举的封送结构#,c#,marshalling,wrapper,i2c,C#,Marshalling,Wrapper,I2c,简介:我有一个带有一些函数的.dll,我正试图在C#应用程序中使用它。其中一个函数工作不正常,返回“无效参数”状态 我猜这是一个编组问题 以下是.dll标头: typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned long long uint64; typedef signed char int8; typedef signed short int16; typedef signed

简介:我有一个带有一些函数的.dll,我正试图在C#应用程序中使用它。其中一个函数工作不正常,返回“无效参数”状态

我猜这是一个编组问题

以下是.dll标头:

typedef unsigned char   uint8;
typedef unsigned short  uint16;
typedef unsigned long long uint64;

typedef signed char   int8;
typedef signed short  int16;
typedef signed long long int64;

#ifndef _MSC_VER
typedef unsigned char   bool;
#endif

#ifdef __x86_64__  
    typedef unsigned int   uint32;
    typedef signed int   int32;
#else
    typedef unsigned long   uint32;
    typedef signed long   int32;
#endif

typedef enum I2C_ClockRate_t{
    I2C_CLOCK_STANDARD_MODE = 100000,                       /* 100kb/sec */
    I2C_CLOCK_FAST_MODE = 400000,                           /* 400kb/sec */
    I2C_CLOCK_FAST_MODE_PLUS = 1000000,                     /* 1000kb/sec */
    I2C_CLOCK_HIGH_SPEED_MODE = 3400000                     /* 3.4Mb/sec */
}I2C_CLOCKRATE;


/* Channel configuration information */
typedef struct ChannelConfig_t
{
    I2C_CLOCKRATE   ClockRate; 
    uint8           LatencyTimer;
    uint32          Options;
}ChannelConfig;

/******************************************************************************/

/******************************************************************************/
/*                              Function declarations                         */
/******************************************************************************/
FTDI_API FT_STATUS I2C_GetNumChannels(uint32 *numChannels);
FTDI_API FT_STATUS I2C_GetChannelInfo(uint32 index, 
    FT_DEVICE_LIST_INFO_NODE *chanInfo);
FTDI_API FT_STATUS I2C_OpenChannel(uint32 index, FT_HANDLE *handle);
FTDI_API FT_STATUS I2C_InitChannel(FT_HANDLE handle, ChannelConfig *config);
FTDI_API FT_STATUS I2C_CloseChannel(FT_HANDLE handle);
FTDI_API FT_STATUS I2C_DeviceRead(FT_HANDLE handle, uint32 deviceAddress, 
uint32 sizeToTransfer, uint8 *buffer, uint32 *sizeTransfered, uint32 options);
FTDI_API FT_STATUS I2C_DeviceWrite(FT_HANDLE handle, uint32 deviceAddress, 
uint32 sizeToTransfer, uint8 *buffer, uint32 *sizeTransfered, uint32 options);
FTDI_API void Init_libMPSSE(void);
FTDI_API void Cleanup_libMPSSE(void);
FTDI_API FT_STATUS FT_WriteGPIO(FT_HANDLE handle, uint8 dir, uint8 value);
FTDI_API FT_STATUS FT_ReadGPIO(FT_HANDLE handle,uint8 *value);
因此,我制作了一个C#包装器来使用这些函数:

    #region Declaration of general drivers methods

    /// <summary>
    /// This function gets the number of I2C channels that are connected to the host system. The number of ports available in each of these chips is different.
    /// </summary>
    /// <param name="numChannels">The number of channels connected to the host</param>
    /// <returns>Returns status code of type FT_STATUS</returns>
    [DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_GetNumChannels(
            ref uint numChannels
    );

    /// <summary>
    /// This function takes a channel index (valid values are from 0 to the value returned by I2C_GetNumChannels – 1 ) and provides information about the channel in the form of a populated FT_DEVICE_LIST_INFO_NODE structure
    /// </summary>
    /// <param name="index">Index of the channel </param>
    /// <param name="chanInfo">Pointer to FT_DEVICE_LIST_INFO_NODE structure</param>
    /// <returns>Returns status code of type FT_STATUS</returns>
    [DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_GetChannelInfo(
            uint index,
            ref FTDI.FT_DEVICE_INFO_NODE chanInfo
    );

    /// <summary>
    /// This function opens the indexed channel and provides a handle to it. Valid values for the index of channel can be from 0 to the value obtained using I2C_GetNumChannels – 1).
    /// </summary>
    /// <param name="index">Index of the channel</param>
    /// <param name="handle">Pointer to the handle of type FT_HANDLE</param>
    /// <returns>Returns status code of type FT_STATUS</returns>
    [DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_OpenChannel(
            uint index,
            ref ulong handle                
    );

    /// <summary>
    /// This function initializes the channel and the communication parameters associated with it.
    /// </summary>
    /// <param name="handle">Handle of the channel </param>
    /// <param name="config">Pointer to ChannelConfig structure. Members of ChannelConfig structure contains the values for I2C master clock, latency timer and Options</param>
    /// <returns>Returns status code of type FT_STATUS</returns>
    [DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_InitChannel(
            ulong handle,
            [MarshalAs(UnmanagedType.Struct)] ref C_Types.ChannelConfig config
    );

    /// <summary>
    /// Closes a channel and frees all resources that were used by it
    /// </summary>
    /// <param name="handle">Handle of the channel </param>
    /// <returns>Returns status code of type FT_STATUS</returns>
    [DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_CloseChannel(
            ulong handle
    );

    /// Options summary
    /// This parameter specifies data transfer options. The bit positions defined for each of these options are: 
    /// BIT0: if set then a start condition is generated in the I2C bus before the transfer begins. A bit mask is defined for this options in file ftdi_i2c.h as I2C_TRANSFER_OPTIONS_START_BIT
    /// BIT1: if set then a stop condition is generated in the I2C bus after the transfer ends. A bit mask is defined for this options in file ftdi_i2c.h as I2C_TRANSFER_OPTIONS_STOP_BIT
    /// BIT2: reserved (only used in I2C_DeviceWrite)
    /// BIT3: some I2C slaves require the I2C master to generate a NAK for the last data byte read. Setting this bit enables working with such I2C slaves. The bit mask defined for this bit is I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE
    /// BIT4: setting this bit will invoke a multi byte I2C transfer without having delays between the START, ADDRESS, DATA and STOP phases. Size of the transfer in parameters sizeToTransfer and sizeTransfer red are in bytes. The bit mask defined for this bit is I2C_TRANSFER_OPTIONS_FAST_TRANSFER_BYTES*
    /// BIT5: setting this bit would invoke a multi bit transfer without having delays between the START, ADDRESS, DATA and STOP phases. Size of the transfer in parameters sizeToTransfer and sizeTransfer red are in bytes. The bit mask defined for this bit is I2C_TRANSFER_OPTIONS_FAST_TRANSFER_BITS*
    /// BIT6: the deviceAddress parameter is ignored if this bit is set. This feature may be useful in generating a special I2C bus conditions that do not require any address to be passed. Setting this bit is effective only when either 2C_TRANSFER_OPTIONS_FAST_TRANSFER_BYTES or I2C_TRANSFER_OPTIONS_FAST_TRANSFER_BITS is set. The bit mask defined for this bit is I2C_TRANSFER_OPTIONS_NO_ADDRESS*
    /// BIT7–BIT31:reserved
    /// For more information see the AN_177_User_Guide_For_LibMPSSE-I2C.pdf

    /// <summary>
    /// This function reads the specified number of bytes from an addressed I2C slave
    /// </summary>
    /// <param name="handle">Handle of the channel </param>
    /// <param name="deviceAddress">Address of the I2C slave. This is a 7bit value and it should not contain the data direction bit, i.e. the decimal value passed should be always less than 128</param>
    /// <param name="sizeToTransfer">Number of bytes to be read </param>
    /// <param name="buffer">Pointer to the buffer where data is to be read</param>
    /// <param name="sizeTransfered">Pointer to variable containing the number of bytes read</param>
    /// <param name="options">cf Options</param>
    /// <returns>Returns status code of type FT_STATUS</returns>
    [DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_DeviceRead(
            ulong handle,
            uint deviceAddress,
            uint sizeToTransfer,
            ref byte buffer,
            ref uint sizeTransfered,
            uint options
    );

    /// <summary>
    /// This function writes the specified number of bytes to an addressed I2C slave.
    /// </summary>
    /// <param name="handle">Handle of the channel</param>
    /// <param name="deviceAddress">Address of the I2C slave. This is a 7bit value and it should not contain the data direction bit, i.e. the decimal value passed should be always less than 128</param>
    /// <param name="sizeToTransfer">Number of bytes to be written</param>
    /// <param name="buffer">Pointer to the buffer from where data is to be written</param>
    /// <param name="sizeTransfered">Pointer to variable containing the number of bytes written</param>
    /// <param name="options">cf Options</param>
    /// <returns></returns>
    [DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_DeviceWrite(
            ulong handle,
            uint deviceAddress,
            uint sizeToTransfer,
            ref byte[] buffer,
            ref uint sizeTransfered,
            uint options
    );
    #endregion
包装器:

[DllImport("libMPSSE.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public extern static FTDI.FT_STATUS I2C_InitChannel(
            ulong handle,
            [MarshalAs(UnmanagedType.Struct)] ref C_Types.ChannelConfig config
    );
下面是C#结构的实现:

    /// <summary>
    /// I2C Clockrate enum for PC I2C Connection
    /// </summary>
    public enum I2C_ClockRate
    {
        I2C_CLOCK_STANDARD_MODE = 100000,                       /* 100kb/sec */
        I2C_CLOCK_FAST_MODE = 400000,                           /* 400kb/sec */
        I2C_CLOCK_FAST_MODE_PLUS = 1000000,                     /* 1000kb/sec */
        I2C_CLOCK_HIGH_SPEED_MODE = 3400000                     /* 3.4Mb/sec */
    };

    /// <summary>
    /// Channel configuration information for PC I2C Connection
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct ChannelConfig
    {
        public I2C_ClockRate ClockRate;
        public byte LatencyTimer;
        public uint Options;

        public ChannelConfig(I2C_ClockRate pClockRate, byte pLatencyTimer, uint pOptions)
        {
            ClockRate = pClockRate;
            LatencyTimer = pLatencyTimer;
            Options = pOptions;
        }

        public ChannelConfig(I2C_ClockRate pClockRate, byte pLatencyTimer)
        {
            ClockRate = pClockRate;
            LatencyTimer = pLatencyTimer;
            Options = 0x00000000;
        }
    }
//
///用于PC I2C连接的I2C时钟速率枚举
/// 
公共枚举I2C_时钟速率
{
I2C时钟标准模式=100000,/*100kb/秒*/
I2C时钟快速模式=400000,/*400kb/秒*/
I2C时钟快速模式+1000000,/*1000kb/秒*/
I2C时钟高速模式=3400000/*3.4Mb/秒*/
};
/// 
///PC I2C连接的通道配置信息
/// 
[StructLayout(LayoutKind.Sequential)]
公共结构ChannelConfig
{
公共I2C_时钟频率时钟频率;
公共字节延迟体;
公共uint选项;
公共信道配置(I2C_时钟速率pClockRate、字节平台计数器、uint端口)
{
时钟速率=pClockRate;
延迟体=延迟体;
选项=选项;
}
公共信道配置(I2C_时钟速率pClockRate,字节平台计数器)
{
时钟速率=pClockRate;
延迟体=延迟体;
选项=0x00000000;
}
}
问题是dll函数返回一个ftStatus,表示“无效参数”


我猜这是因为我没有正确地封送我的函数,但我无法确定我的错误在哪里。

ulong handle
是错误的,它是
IntPtr
。您需要将CallingConvention=CallingConvention.Cdecl添加到DllImport声明中。与之相比,这只是对上面代码的观察。在dll中,“LatencyTimer”和“Options”是uint8和uint32,但在C#中是byte和uint;这会导致uint8到一个字节的问题吗?@HansPassant:谢谢你提供的链接,我将对此进行研究@杰根:我在网上找的,似乎uint8->byte是正确的。@HansPassant:IntPtr就是问题所在。再次感谢你!
    /// <summary>
    /// I2C Clockrate enum for PC I2C Connection
    /// </summary>
    public enum I2C_ClockRate
    {
        I2C_CLOCK_STANDARD_MODE = 100000,                       /* 100kb/sec */
        I2C_CLOCK_FAST_MODE = 400000,                           /* 400kb/sec */
        I2C_CLOCK_FAST_MODE_PLUS = 1000000,                     /* 1000kb/sec */
        I2C_CLOCK_HIGH_SPEED_MODE = 3400000                     /* 3.4Mb/sec */
    };

    /// <summary>
    /// Channel configuration information for PC I2C Connection
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct ChannelConfig
    {
        public I2C_ClockRate ClockRate;
        public byte LatencyTimer;
        public uint Options;

        public ChannelConfig(I2C_ClockRate pClockRate, byte pLatencyTimer, uint pOptions)
        {
            ClockRate = pClockRate;
            LatencyTimer = pLatencyTimer;
            Options = pOptions;
        }

        public ChannelConfig(I2C_ClockRate pClockRate, byte pLatencyTimer)
        {
            ClockRate = pClockRate;
            LatencyTimer = pLatencyTimer;
            Options = 0x00000000;
        }
    }