访问违规 - 如何追查原因?

时间:2016-06-22 08:58:36

标签: delphi debugging access-violation delphi-10.1-berlin

我在应用程序中关闭表单时遇到访问冲突。它似乎只有在我访问数据库几次之后才会发生,但这似乎没有意义。

我已经跟踪并将outputdebugstring消息放在所有相关的OnDestroy()方法中,但AV似乎不在我的代码中。

这是消息的文字:

  

模块中地址00405F7C的访问冲突   'MySoopaApplication.exe'。读取地址00000008。

如何找到应用程序00405F7C中的位置?

Delphi 10.1柏林有哪些工具可以帮助我解决这个问题?

编辑:添加了更多信息...当点击“Break”时,IDE总是带我到GETMEM.INC中的这段代码:

@SmallPoolWasFull:
  {Insert this as the first partially free pool for the block size}
  mov ecx, TSmallBlockType[ebx].NextPartiallyFreePool

进一步编辑:好吧,我找到了罪魁祸首,虽然我不能说老实说调试工具让我在那里 - 他们似乎只是表明它不在我的代码中。

我曾使用网络中的代码来查找Windows登录用户 - 就是这样:

function GetThisComputerName: string;
var
  CompName: PChar;
  maxlen: cardinal;
begin
  maxlen := MAX_COMPUTERNAME_LENGTH +1;
  GetMem(CompName, maxlen);
  try
    GetComputerName(CompName, maxlen);
    Result := CompName;
  finally
    FreeMem(CompName);
  end;
end;

一旦我用简单的结果替换了代码:='12345'AV停止了。我没有改变它到这个代码:

function GetThisComputerName: string;
var
  nSize: DWord;
  CompName: PChar;
begin
  nSize := 1024;
  GetMem(CompName, nSize);
  try
    GetComputerName(CompName, nSize);
    Result := CompName;
  finally
    FreeMem(CompName);
  end;
end;

这似乎有用,作为奖励,不会导致AV。

感谢您的帮助,非常感谢。

2 个答案:

答案 0 :(得分:5)

在IDE中的Tools|Options下,转到Embarcadero Debuggers | Language Exceptions并确保选中Notify on Language Exceptions。同样在Project Options | Compiling下,请确保选中Debugging | Use debug DCUs

允许异常发生,然后转到View | Debug Windows | Call stack,您应该能够确切地看到它发生的位置。它在db访问之后发生的事实可能是因为它导致创建一些在销毁时生成AV的对象。可能是因为它被Free()两次。

如果这不能解决问题,您可能需要像DavidH提到的madExcept之类的异常记录工具。

  

读取地址00000008。

这个地址数量较少这一事实暗示它是一个对象成员的地址(因为它们通常与对象的基地址偏差较小)。

  

如何找到应用程序00405F7C中的位置?

在您的应用运行时,在IDE中转到Search | Go to Address。如果异常在您的应用程序中而不是在某个相关模块(例如它正在使用的.DLL)中,则应该找到它。应用程序在IDE中运行并在断点处停止后,将启用该菜单项。还有一个编译器命令行开关,用于按地址查找错误。

答案 1 :(得分:3)

其他人已经解释了如何诊断AV。

关于代码本身,它存在问题:

  1. 最重要的是,您没有为缓冲区分配足够的内存。 GetMem()对字节进行操作,但GetComputetName()对字符进行操作,在这种情况下,SizeOf (Char)为2个字节。因此,您实际上将要报告的字节数的一半分配给GetComputerName(),因此如果它写入的次数超过您分配的次数,则会损坏堆内存。当你过度分配缓冲区时,腐败就消失了。因此,在分配时请考虑SizeOf(Char)

    function GetThisComputerName: string;
    var
      CompName: PChar;
      maxlen: cardinal;
    begin
      maxlen := MAX_COMPUTERNAME_LENGTH +1;
      GetMem(CompName, maxlen * SizeOf(Char)); // <-- here
      try
        GetComputerName(CompName, maxlen);
        Result := CompName;
      finally
        FreeMem(CompName);
      end;
    end;
    
  2. 除此之外:

    1. 您忽略了GetComputerName()的错误,因此您无法保证CompName首先有效传递给Result

    2. 您应该使用SetString(Result, CompName, nSize)代替Result := CompName,因为GetComputerName()会输出实际的CompName长度。当您已经知道长度时,RTL无需浪费处理时间来计算要复制的长度。由于您没有检查错误,因此如果CompName失败,您无法依赖GetComputerName()无效终止。

    3. 你应该完全摆脱GetMem()而只是在堆栈上使用静态数组:

      function GetThisComputerName: string;
      var
        CompName: array[0..MAX_COMPUTERNAME_LENGTH] of Char;
        nSize: DWORD;
      begin
        nSize := Length(CompName);
        if GetComputerName(CompName, nSize) then
          SetString(Result, CompName, nSize)
        else
          Result := '';
      end;