C# PInvoke 结构与类访问冲突

Posted

技术标签:

【中文标题】C# PInvoke 结构与类访问冲突【英文标题】:C# PInvoke struct vs class access violation 【发布时间】:2013-11-26 18:16:20 【问题描述】:

我在调用 PInvoke 时随机出现访问冲突异常,结构版本似乎可以正常工作。课程版本会随机给我访问冲突异常。我在类版本中有额外字段并且结构版本与合同所期望的完全匹配的问题吗?我认为额外的字段应该无关紧要,因为非托管代码应该只访问前 3 个 UInt32。

我担心的是结构版本仍然存在访问冲突问题,但不那么频繁了。

任何帮助将不胜感激 谢谢

[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 链接:

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

来自 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;

【问题讨论】:

你能不能把界面的另一边,非托管的一面展示出来 我添加了 pinvoke 链接以及来自 msdn 的 struct def 和来自 msdn 的非托管调用定义 【参考方案1】:

当您在 PInvoke 签名中使用 class 类型时,它本质上是作为指向该值的指针传递的。 ref / out 也是如此。因此,当TStatclass 时,out TStat 通过双指针传递值。仅当本机参数类型为 TStat** 时才有意义。

我猜原生签名实际上是TStat*。这就是为什么作为 struct 传递有效的原因,因为 out struct 与 PInvoke 中的 TStat* 具有相同的数据语义

【讨论】:

这不会导致方法调用永远无法工作吗?它似乎大部分时间都在返回,它用似乎与我期望的数据相匹配的数据填充 cbInQue 不能保证你得到一个 AV,指针有指向可写内存的诀窍。 cbInQue 有一个好的值很难解释,只是它通常应该是 0,这并不难得到。但这显然是错误的,使用[Out]而不是out

以上是关于C# PInvoke 结构与类访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

将带有结构字段的结构从 c++ 返回到 c# pinvoke

通过 PInvoke 从 C# 到 C++ 的简单结构中的垃圾数据

C# 编组、不平衡堆栈和正确获取 PInvoke 签名

PInvoke 在返回结构时仅适用于 64 位

使用协议缓冲区/Protobuf 的 PInvoke 通用字符串格式

PInvoke 与指针 - C++ 到 C#