Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.AccessViolationException when passing struct to unmanaged code

I'm trying to use an unmanaged API from C#, and banging my head against the wall. (I'm rather a beginner when it comes to PInvoke.)

The relevant parts of the header file look like this:

#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 looks like this:

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;

My C# method declaration:

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

The 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;
}

When I call it like this (from a test assembly built as x86), I get a 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);

I've been trying all kinds of things, like using LayoutKind.Explicit and/or CallingConvention = CallingConvention.Cdecl, but to no avail.

Can anybody help?

like image 332
dabide Avatar asked May 06 '26 15:05

dabide


1 Answers

  1. Why did you map UINT as ushort. Doesn't it have 4 bytes?
  2. The native BOOLEAN type maps to 4 bytes, AFAIK.
  3. You need to pass CtTagValueItems by ref (as class or ref).
  4. Check the calling convention.
  5. Check whats written in the comments.
like image 140
usr Avatar answered May 08 '26 04:05

usr