C# AcessExceptionViolation 与 Marshal.Copy

Posted

技术标签:

【中文标题】C# AcessExceptionViolation 与 Marshal.Copy【英文标题】:C# AcessExceptionViolation with Marshal.Copy 【发布时间】:2015-07-08 17:14:27 【问题描述】:

我正在将一个程序从 VB 翻译成 C#,它与外部程序 MMTTY.EXE 进行通信。这个程序有用于 VB6.0 的 ActiveX 控件,我在 Visual Studio 2013 中添加到我的 C# 项目中。

VB中的代码是这样的:

Dim m_nmmr(63) As Long
Private Sub XMMR_OnNotifyNMMR(pNMMR As Long)
Call CopyMemory(m_nmmr(0), pNMMR, 64 * 4)   'Windows API CopyMemory()*

当 MMTTY.EXE 有数据并且 pNMMR 指向的数据被复制到 m_nmmr(63) 缓冲区时,会产生此事件。

我用 C# 编写的程序是这样的:

[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
    static extern void CopyMemory(Int32[] Destination, IntPtr Source, uint Length);
private void XMMR_OnNotifyNMMR(object sender,      AxXMMTLib._DXMMREvents_OnNotifyNMMREvent e)
                
        IntPtr ptr = (IntPtr)e.pNMMR;
        Int32[] m_nmmr = new Int32[63];
        Marshal.Copy(ptr, m_nmmr, 0, 63);
    *

但是当我执行它时,我得到了一个 AccessViolationException。它告诉我有人尝试在受保护的内存中写入或读取。

我该如何解决这个问题?有什么想法吗?

这是VB6.0的原始方法的帮助:

无效 OnNotifyNMR(long* pNMRR) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 此事件在 TXM_LEVLEL 消息到达时生成。 pNMRR 指向的 NMR 结构体定义为

#pragma pack(push, 1)
typedef struct 
    DWORD   m_markfreq;
    DWORD   m_spacefreq;
    DWORD   m_siglevel;
    DWORD   m_sqlevel;
    DWORD   m_codeswitch;
    DWORD   m_codeview;
    DWORD   m_notch1;
    DWORD   m_notch2;
    DWORD   m_baud;
    DWORD   m_fig;
    DWORD   m_radiofreq;
    DWORD   m_Reserved[53];
NMMR;
#pragma pack(pop)

如果应用程序使用这个结构,它不必响应所有的 XMMR 事件。因为 NMRR 结构简单地由 LONG 变量组成,所以可以将其复制到 VB Long 数组中。数组的索引在 XMMT.ocx 中有几个名称,例如 xr_markfreq。具体请参考XMMR的预定义常量。

[Example]
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Dim m_nmmr(63) As Long

Private Sub XMMR_OnNotifyNMMR(pNMMR As Long)
    Call CopyMemory(m_nmmr(0), pNMMR, 64 * 4)   'Windows API CopyMemory()
    |
    MarkFreq = m_nmmr(xr_markfreq)
    SpaceFreq = m_nmmr(xr_spacefreq)
       |
    'Pass pNMMR to the control for the supplemental control
    Call XMMSpec.UpdateByNMMR(pNMMR)    'Update the frequency property of XMMSpec control
    Call XMMLvl.DrawByNMMR(pNMMR)   'Draw the level indicator
End Sub

【问题讨论】:

为什么这被标记为 C++? 因为NMR结构的定义是用C++做的,还是我错了? 您是否检查过入站事件对象(到您的新事件处理程序)是否在 e.pNMRR 中包含有效的指针值? 它的值是 1519 ,也就是 mmtty.exe 程序中 xr_markfreq 的值,指针的有效值是多少? XMMR_OnNotifyNMR() 方法签名非常可疑,它的参数与 VB6 版本完全不匹配。很难看出这是怎么发生的,我的水晶球说有一个我们看不到的代表参与其中。 【参考方案1】:

63 是问题:

在 VB6 中,63 是顶部索引 (0..63)。

在 C# 中,63 是元素的计数 (0..62)。

所以你错过了 4 个字节。你可以用一个小控制台程序简单地检查一下,在第一次赋值时断点:

    static void Main(string[] args)
    
        int[] test = new int[1];
        test[0] = 1;
        test[1] = 2; // <- this will crash
    

【讨论】:

以上是关于C# AcessExceptionViolation 与 Marshal.Copy的主要内容,如果未能解决你的问题,请参考以下文章

C#进阶C# 泛型

C#进阶C# 匿名方法

C#进阶C# 多线程

C# 教程

[C#教程01]C# 简介

用 C# 编写 C# 编译器,先有鸡还是先有蛋?