System.AccessViolation将结构传递给非托管代码时的异常

本文关键字:非托管代码 异常 AccessViolation 结构 System | 更新日期: 2023-09-27 17:56:05

我正在尝试使用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# 结构:

[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,但无济于事。

有人可以帮忙吗?

System.AccessViolation将结构传递给非托管代码时的异常

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

问题可能对齐。 尝试像

StructLayout(LayoutKind.Sequential, Pack = 1)

C# 调用中的 handle 变量从何而来?

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

我已经更改了相当多的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);
}