2

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

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

这是消息的文本:

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

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

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

编辑:添加了更多信息...单击“中断”时,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。

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

4

2 回答 2

5

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

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

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

读取地址 00000008。

这个地址是一个低数字的事实表明它是一个对象成员的地址(因为它们通常与对象的基地址有低偏移量)。

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

随着您的应用程序运行,在 IDE 中转到Search | Go to Address. 如果异常出现在您的应用程序中,而不是在某些相关模块(如它正在使用的 .DLL)中,这应该会找到它。一旦应用程序在 IDE 中运行并在断点处停止,菜单项就会启用。还有一个编译器命令行开关可以按地址查找错误。

于 2016-06-22T09:36:30.120 回答
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;
    

在此之上:

  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;
    
于 2016-06-22T18:25:10.957 回答