【问题标题】:Access violation between lines行间访问冲突
【发布时间】:2014-06-24 14:02:44
【问题描述】:

以下代码在我的机器上似乎可以正常工作,但会导致客户访问冲突。

procedure TSummaryThread.Execute;
var
  When: String;
begin
  try
    FCarcassListCS := TCriticalSection.Create;
    FCarcassLists := TObjectList.Create;
    while (not Terminated) do
      begin
        try
          When := 'GetMessage';
          if (GetMessage(Msg, 0, 0, 0))
            then begin
                  if (Msg.message = WM_SUMMARISE)
                  then begin
                        When := 'WM_SUMMARISE';
                        Update(True);
                       end
                else if (Msg.message = WM_UPDATE)
                  then begin
                        When := 'WM_UPDATE';
                        Update(False);
                       end
                else if (Msg.message = WM_RELOAD_SETUP)
                  then begin
                        When := 'WM_RELOAD_SETUP';
                        ReloadSetup(Pointer(Msg.wParam));
                       end
                else if (Msg.message = WM_SEND_TO)
                  then begin
                        When := 'WM_SEND_TO';
                        SendToThread(Pointer(Msg.wParam));
                       end
                else if (Msg.message = WM_UPDATE_ORDER_SUM)
                  then begin
                        When := 'WM_UPDATE_ORDER_SUM';
                        DoOrderSummary;
                       end
                else if (Msg.message = WM_SUMMARISE_LOAD)
                  then begin
                        When := 'WM_SUMMARISE_LOAD';
                        LoadAndSummarise;
                       end
                  else begin
                        When := 'DispatchMessage';
                        DispatchMessage(Msg);
                       end;
                 end;
        except
          on E: Exception do
            Log('Exception (' + E.ClassName + ') in summary  thread at ' + When + ': ' + E.Message);
        end;
      end;
  except
    on E: Exception do
      Log('Exception (' + E.ClassName + ') in summary  thread (2) at ' + When + ': ' + E.Message);
end;

procedure TSummaryThread.Update(Full: Boolean);
var
  CarcassList: TObjectList;
  Where: String;
begin
  try
    Where := 'GetList';
    FCarcassListCS.Enter;
    try
      if (FCarcassLists.Count = 0)
        then raise Exception.Create('No carcass list found');
      CarcassList := FCarcassLists[0] as TObjectList;
      FCarcassLists.Extract(CarcassList);
    finally
      FCarcassListCS.Leave;
    end;
    FSummaryListCS.Enter;
    try
      Where := 'LoadFiles';
      SetFiles;
      try
        Where := 'AddCcs';
        CarcassList := GetShiftCarcassList(CarcassList);
        AddCarcassesToSummaries(CarcassList, Full);
      finally
        CloseFiles;
      end;
      Where := 'Send';
      SendSummaries;
      Where := 'Done';
    finally
      FreeAndNil(CarcassList);
      FSummaryListCS.Leave;
    end;
  except
    on E: Exception do
    begin
      E.Message := 'Update(' + Where + '): ' + E.Message;
      raise;
    end;
  end;
end;

从日志中我可以看出When := 'WM_UPDATE'; 被击中而Where := 'GetList'; 没有。这使我认为当Update() 过程中的本地Where 变量被添加到堆栈时,会发生访问冲突。

我可以添加哪些额外的诊断程序来帮助找出导致问题的原因?有什么方法可以检查堆栈是否已损坏和/或Where 变量被添加到堆栈的位置?

我意识到这是一个相当模糊的问题,但我想不出任何办法来缩小范围。

确切的错误是:

WM_UPDATE 的摘要线程中的异常 (EAccessViolation):模块“Application.exe”中地址 004B6759 的访问冲突。读取地址 00000008

【问题讨论】:

  • (1) 确切的错误信息? (2) 你确定线程对象是有效的并且还没有消失吗?
  • 您如何知道发生了访问冲突?实际的症状/日志是什么?未能将变量放入堆栈不会生成 AV,并且将值(尤其是文字)分配给 String 无论如何都不会将该数据放入堆栈。这段代码中没有任何内容会导致您声明的 AV,除非 AV 来自 RTL 的内存管理器本身,在这种情况下,您的应用程序被搞砸了,您需要重新启动它。
  • 顺便说一句,在跟踪内存问题时,尽量减少内存分配,不要使用分配来跟踪问题。使用OutputDebugString() 进行日志记录,并使用DebugView 之类的工具在IDE 调试器之外运行应用程序时查看这些消息。
  • @JensG 我已经添加了确切的错误消息。上面的代码来自 TSummaryThread.Execute 方法,所以我认为线程对象仍然有效。
  • 把 madexcept 放在这个过程中并获得一些真实的信息。看起来像一个零对象。也许 self 为零。

标签: delphi stack delphi-5


【解决方案1】:

在可执行文件的项目选项中,找到链接器设置。确保将Map file 设置配置为Detailed。在链接器设置页面上,检查Image base 地址。很可能是 $00400000,如果不写,请记住它的设置。

重建您的可执行文件(我假设您的项目是单个可执行文件,它似乎基于您提供的异常文本)。在编译器的输出目录中,您应该找到一个名为“Application.map”的文件。在您喜欢的文本编辑器中打开此文件。

现在,获取异常消息中报告的地址(在本例中为 $004B6759)。从异常的地址中减去可执行文件的基地址 ($00400000)。这给你 $B6759。减去 1000 美元(我很确定它是减去,如果不是有人会指出它应该添加到 cmets 中)。离开 $B5759。这是发生访问冲突的可执行文件中的偏移量。

使用您之前加载的地图文件,查看Detailed map of segments 部分。找到小于或等于 $B5759 的第一个条目,然后是大于 $B5759 的段。与段关联的扫描单元名称(它是标有“M=”的那个)。

在映射文件中搜索单元名称,您会找到标题为“XXX(..) 段 .text 的行号”的部分,其中 XXX 是您要查找的单元的名称。本节将列出行号和该行号代码第一个字节的偏移量。找到小于或等于 $B5759 的第一个行号(基于地址),然后是大于 $B5759 的偏移量。

这将告诉您包含实际导致访问冲突的已编译代码的行号(和源文件)。

正如其他人所说,鉴于异常是读取 8 美元,您可能试图从 nil 指针或对象中读取。可能是记录或对象的第二个字段(假设是 32 位代码)。

祝你好运。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-06
    • 2013-02-26
    • 2015-04-23
    • 2018-04-16
    • 1970-01-01
    相关资源
    最近更新 更多