0

I have been randoming getting a Access Violation Exception when calling PInvoke, the struct version seems to work. The class version will randomly give me the Access Violation Exception. Is the issue that I have extra fields in the class version and the struct version matches exactly what the contract is expecting? I would assume extra fields shouldn't matter as the unmanaged code should only be accessing the first 3 UInt32's anyway.

What I'm afraid of is the access violation problem still exists with the struct version but just not as often.

Any help would be appreciated Thanks

[StructLayout(LayoutKind.Sequential)]
public class TStat
{
    private UInt32 bitfield;
    public UInt32 cbInQue;
    public UInt32 cbOutQue;
    private readonly UInt32 fCtsHoldMask = 0x00000001;
    private readonly Int32 fCtsHoldShift = 0;
    private readonly UInt32 fDsrHoldMask = 0x00000002;
    private readonly Int32 fDsrHoldShift = 1;
    private readonly UInt32 fRlsdHoldMask = 0x00000004;
    private readonly Int32 fRlsdHoldShift = 2;
    private readonly UInt32 fXoffHoldMask = 0x00000008;
    private readonly Int32 fXoffHoldShift = 3;
    private readonly UInt32 fXoffSentMask = 0x00000010;
    private readonly Int32 fXoffSentShift = 4;
    private readonly UInt32 fEofMask = 0x00000020;
    private readonly Int32 fEofShift = 5;
    private readonly UInt32 fTximMask = 0x00000040;
    private readonly Int32 fTximShift = 6;

    public bool fCtsHold
    {
       get { return ((bitfield & fCtsHoldMask) != 0); }
       set { bitfield |= (Convert.ToUInt32(value) << fCtsHoldShift); }
    }
    public bool fDsrHold
    {
       get { return ((bitfield & fDsrHoldMask) != 0); }
       set { bitfield |= (Convert.ToUInt32(value) << fDsrHoldShift); }
    }
    public bool fRlsdHold
    {
       get { return ((bitfield & fRlsdHoldMask) != 0); }
       set { bitfield |= (Convert.ToUInt32(value) << fRlsdHoldShift); }
    }
    public bool fXoffHold
    {
       get { return ((bitfield & fXoffHoldMask) != 0); }
       set { bitfield |= (Convert.ToUInt32(value) << fXoffHoldShift); }
    }
    public bool fXoffSent
    {
       get { return ((bitfield & fXoffSentMask) != 0); }
       set { bitfield |= (Convert.ToUInt32(value) << fXoffSentShift); }
    }
    public bool fEof
    {
       get { return ((bitfield & fEofMask) != 0); }
       set { bitfield |= (Convert.ToUInt32(value) << fEofShift); }
    }
    public bool fTxim
    {
       get { return ((bitfield & fTximMask) != 0); }
       set { bitfield |= (Convert.ToUInt32(value) << fTximShift); }
    }
}

[StructLayout(LayoutKind.Sequential)]
public struct TStat
{
    private UInt32 bitfield;
    public UInt32 cbInQue;
    public UInt32 cbOutQue;
}
[DllImport("coredll.dll", EntryPoint = "ClearCommError", SetLastError = true)]
    private static extern Boolean ClearCommError(IntPtr hPort, out UInt32 Errors, out TStat Stat);

PInvoke links:

_http://www.pinvoke.net/default.aspx/kernel32.clearcommerror _http://www.pinvoke.net/default.aspx/Structures/COMSTAT.html

Native signatures from msdn

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363180(v=vs.85).aspx

BOOL WINAPI ClearCommError(
_In_       HANDLE hFile,
_Out_opt_  LPDWORD lpErrors,
_Out_opt_  LPCOMSTAT lpStat
);

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363200(v=vs.85).aspx

typedef struct _COMSTAT {
 DWORD fCtsHold  :1;
 DWORD fDsrHold  :1;
 DWORD fRlsdHold  :1;
 DWORD fXoffHold  :1;
 DWORD fXoffSent  :1;
 DWORD fEof  :1;
 DWORD fTxim  :1;
 DWORD fReserved  :25;
 DWORD cbInQue;
 DWORD cbOutQue;
} COMSTAT, *LPCOMSTAT;

1 Answers1

3

When you use a class type in a PInvoke signature it is essentially passed as a pointer to the value. The same is true for ref / out. Hence out TStat when TStat is a class is passing the value by double pointer. It only makes sense if the native parameter type is TStat**.

I'm guessing that the native signature is actually TStat*. This is why passing as a struct works because out struct has the same data semantics as TStat* in PInvoke

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Wouldn't that cause the method call to never work? It appears to be returning the majority of the time and it populates the cbInQue with data that seems to match what I would be expecting – user3037658 Nov 26 '13 at 18:37
  • There's no guarantee that you get an AV, the pointer has a knack for pointing to writable memory. That cbInQue has a good value is harder to explain, except that it usually ought to be 0 which isn't hard to come by. But it is clearly wrong, use [Out] instead of *out*. – Hans Passant Nov 26 '13 at 18:50