3

我正在尝试使用 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,但无济于事。

有人可以帮忙吗?

4

3 回答 3

2
  1. 你为什么映射UINTushort. 它不是有4个字节吗?
  2. 本机BOOLEAN类型映射到 4 个字节,AFAIK。
  3. 您需要CtTagValueItems通过 ref (作为 class 或ref)。
  4. 检查调用约定。
  5. 检查评论中写的内容。
于 2013-01-24T15:41:58.877 回答
1

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

StructLayout(LayoutKind.Sequential, Pack = 1)
于 2013-01-24T15:41:44.010 回答
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);
}
于 2013-01-24T16:02:54.207 回答