将struct传递给非托管代码时出现System.AccessViolationException

时间:2013-01-24 15:39:26

标签: c# pinvoke marshalling unmanaged

我正在尝试使用来自C#的非托管API,并将我的头撞在墙上。 (当谈到PInvoke时,我相当初学者。)

头文件的相关部分如下所示:

#define CTAPICALL       __stdcall
#ifdef __cplusplus
    extern "C" {
#endif

extern  BOOL    CTAPICALL   ctTagReadEx(HANDLE,LPCSTR,LPSTR,DWORD,CT_TAGVALUE_ITEMS*);      /* read extended data from tag          */

#ifdef __cplusplus
}
#endif

CT_TAGVALUE_ITEMS看起来像这样:

typedef struct
{
    DWORD                   dwLength;                           /* size, in bytes, of this structure    */
    unsigned __int64        nTimestamp;                         /*  timestamp                           */
    unsigned __int64        nValueTimestamp;                    /*  value timestamp                     */
    unsigned __int64        nQualityTimestamp;                  /*  quality timestamp                   */
    BYTE                    bQualityGeneral;                    /*  quality general                     */
    BYTE                    bQualitySubstatus;                  /*  quality substatus                   */
    BYTE                    bQualityLimit;                      /*  quality limit                       */
    BYTE                    bQualityExtendedSubstatus;          /*  quality extended substatus          */
    UINT                    nQualityDatasourceErrorCode;        /*  quality datasource error            */
    BOOLEAN                 bOverride;                          /*  quality override flag               */
    BOOLEAN                 bControlMode;                       /*  quality control mode flag           */
}   CT_TAGVALUE_ITEMS;

我的C#方法声明:

    [DllImport("ctapi.dll", SetLastError = true)]
    public static extern bool ctTagReadEx(
        IntPtr hCTAPI,
        [MarshalAs(UnmanagedType.LPStr)] string tag,
        [MarshalAs(UnmanagedType.LPStr)] System.Text.StringBuilder value,
        int length,
        CtTagValueItems tagValueItems);

C#struct:

[StructLayout(LayoutKind.Sequential)]
public struct CtTagValueItems
{
    public int dwLength;
    public ulong nTimestamp;
    public ulong nValueTimestamp;
    public ulong nQualityTimestamp;
    public byte bQualityGeneral
    public byte bQualitySubstatus;
    public byte bQualityLimit;
    public byte bQualityExtendedSubstatus;
    public uint nQualityDatasourceErrorCode;
    public uint bOverride;
    public uint bControlMode;
}

当我将其称为(来自构建为x86的测试程序集)时,我得到System.AccessViolationException : Attempted to read or write protected memory

StringBuilder valueBuilder = new StringBuilder(300);
CtTagValueItems tagValueItems = new CtTagValueItems {dwLength = Marshal.SizeOf(typeof (CtTagValueItems))};
bool ok = CTAPI.ctTagReadEx(new IntPtr(handle), "TIC_Hold_PV", valueBuilder, valueBuilder.Capacity, tagValueItems);

我一直在尝试各种各样的事情,例如使用LayoutKind.Explicit和/或CallingConvention = CallingConvention.Cdecl,但无济于事。

有人可以帮忙吗?

3 个答案:

答案 0 :(得分:2)

  1. 为什么要将UINT映射为ushort。它有4个字节吗?
  2. 原生BOOLEAN类型映射到4个字节,AFAIK。
  3. 您需要通过引用传递CtTagValueItems(作为类或ref)。
  4. 检查调用约定。
  5. 查看评论中的内容。

答案 1 :(得分:1)

问题可能是对齐的。尝试像

这样的东西
StructLayout(LayoutKind.Sequential, Pack = 1)

答案 2 :(得分:1)

C#调用中的handle变量来自哪里?

我更喜欢在IntPtr方法定义中使用DllImport。通过这种方式管理和编组似乎更容易。

我已经改变了struct定义的相当一部分,因为我没有和你一样的定义。我的ctTagReadEx函数中也没有太多的正文(我会尝试充实它以确保传入的参数与收到的参数相匹配)。但这对我有用。

更新:看起来所有参数和结构值都正确传递。

<强> C

typedef struct
{
    int                     dwLength;                           /* size, in bytes, of this structure    */
    unsigned long           nTimestamp;                         /*  timestamp                           */
    unsigned long           nValueTimestamp;                    /*  value timestamp                     */
    unsigned long           nQualityTimestamp;                  /*  quality timestamp                   */
    int                     bQualityGeneral;                    /*  quality general                     */
    int                     bQualitySubstatus;                  /*  quality substatus                   */
    int                     bQualityLimit;                      /*  quality limit                       */
    int                     bQualityExtendedSubstatus;          /*  quality extended substatus          */
    unsigned int            nQualityDatasourceErrorCode;        /*  quality datasource error            */
    int                     bOverride;                          /*  quality override flag               */
    int                     bControlMode;                       /*  quality control mode flag           */
}   CT_TAGVALUE_ITEMS;


CTAPICALL int ctTagReadEx(void *, const char *, char *, int, CT_TAGVALUE_ITEMS *);

int ctTagReadEx(void * hCTAPI, const char * tag, char * value, int length, CT_TAGVALUE_ITEMS *tagValueItems) {
    return 15;
}

<强> C#

[StructLayout(LayoutKind.Sequential)]
public struct CtTagValueItems {
    public int dwLength;
    public ulong nTimestamp;
    public ulong nValueTimestamp;
    public ulong nQualityTimestamp;
    public int bQualityGeneral;
    public int bQualitySubstatus;
    public int bQualityLimit;
    public int bQualityExtendedSubstatus;
    public uint nQualityDatasourceErrorCode;
    public int bOverride;
    public int bControlMode;
}

[DllImport("ctapi.dll")]
static extern int ctTagReadEx(IntPtr hCTAPI, IntPtr tag, IntPtr value, int length, IntPtr tagValueItems);

public void TestMe() {

    var tagValueItems = new CtTagValueItems();
    var tagValueItemsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CtTagValueItems)));
    Marshal.StructureToPtr(tagValueItems, tagValueItemsPtr, true);

    var tag = "tag";
    var tagPtr = Marshal.StringToHGlobalAnsi(tag);

    var value = "value";
    var valuePtr = Marshal.StringToHGlobalAnsi(value);

    int length = value.Length;

    var result = ctTagReadEx(IntPtr.Zero, tagPtr, valuePtr, length, tagValueItemsPtr);
    if (result != 15) throw new Exception();

    Marshal.FreeHGlobal(tagValueItemsPtr);
    Marshal.FreeHGlobal(tagPtr);
    Marshal.FreeHGlobal(valuePtr);
}