访问冲突 - 我该如何追查原因?
Access Violation - how do I track down the cause?
我在关闭应用程序中的表单时遇到访问冲突。它似乎只有在我访问数据库几次后才会发生,但这似乎没有意义。
我已经追踪并将 outputdebugstring 消息放入所有相关的 OnDestroy() 方法中,但 AV 似乎在我的代码之外。
这是消息的正文:
Access violation at address 00405F7C in module
'MySoopaApplication.exe'. Read of address 00000008.
如何找到 00405F7C 在应用程序中的位置?
Delphi 10.1 Berlin 中有哪些工具可以帮助我解决这个问题?
编辑:添加了更多信息...单击 "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。
感谢您的帮助,非常感谢。
在 IDE 中的 Tools|Options
下转到 Embarcadero Debuggers | Language Exceptions
并确保选中 Notify on Language Exceptions
。同样在 Project Options | Compiling
下,确保选中 Debugging | Use debug DCUs
。
允许异常发生,然后转到View | Debug Windows | Call stack
,您应该能够准确地看到异常发生的位置。它发生在数据库访问之后的事实可能是因为这会导致创建一些对象,当它被销毁时会生成 AV。可能是因为它被 Free()
编辑了两次。
如果这不能解决问题,您可能需要一个异常记录工具,例如 DavidH 提到的 madExcept。
Read of address 00000008.
这个地址是一个小数这一事实表明它是一个对象成员的地址(因为它们通常与对象的基地址相距很低)。
How do I find where in the application 00405F7C is?
使用您的应用 运行,在 IDE 中转到 Search | Go to Address
。如果异常出现在您的应用程序中而不是出现在它正在使用的某个相关模块(如 .DLL)中,这应该会找到它。一旦应用程序处于 IDE 中的 运行 并在断点处停止,就会启用该菜单项。还有一个编译器命令行开关,可以通过地址查找错误。
其他人已经解释了如何诊断 AV。
关于代码本身,它存在问题:
最重要的是,您没有为缓冲区分配足够的内存。 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;
除此之外:
您忽略了来自 GetComputerName()
的错误,因此您不能保证 CompName
甚至可以有效地传递给 Result
。
您应该使用 SetString(Result, CompName, nSize)
而不是 Result := CompName
,因为 GetComputerName()
输出实际的 CompName
长度。当您已经知道长度时,无需浪费处理时间让 RTL 计算要复制的长度。并且由于您不检查错误,因此如果 GetComputerName()
失败,您不能依赖 CompName
被 null 终止。
您应该完全摆脱 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;
我在关闭应用程序中的表单时遇到访问冲突。它似乎只有在我访问数据库几次后才会发生,但这似乎没有意义。
我已经追踪并将 outputdebugstring 消息放入所有相关的 OnDestroy() 方法中,但 AV 似乎在我的代码之外。
这是消息的正文:
Access violation at address 00405F7C in module 'MySoopaApplication.exe'. Read of address 00000008.
如何找到 00405F7C 在应用程序中的位置?
Delphi 10.1 Berlin 中有哪些工具可以帮助我解决这个问题?
编辑:添加了更多信息...单击 "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。
感谢您的帮助,非常感谢。
在 IDE 中的 Tools|Options
下转到 Embarcadero Debuggers | Language Exceptions
并确保选中 Notify on Language Exceptions
。同样在 Project Options | Compiling
下,确保选中 Debugging | Use debug DCUs
。
允许异常发生,然后转到View | Debug Windows | Call stack
,您应该能够准确地看到异常发生的位置。它发生在数据库访问之后的事实可能是因为这会导致创建一些对象,当它被销毁时会生成 AV。可能是因为它被 Free()
编辑了两次。
如果这不能解决问题,您可能需要一个异常记录工具,例如 DavidH 提到的 madExcept。
Read of address 00000008.
这个地址是一个小数这一事实表明它是一个对象成员的地址(因为它们通常与对象的基地址相距很低)。
How do I find where in the application 00405F7C is?
使用您的应用 运行,在 IDE 中转到 Search | Go to Address
。如果异常出现在您的应用程序中而不是出现在它正在使用的某个相关模块(如 .DLL)中,这应该会找到它。一旦应用程序处于 IDE 中的 运行 并在断点处停止,就会启用该菜单项。还有一个编译器命令行开关,可以通过地址查找错误。
其他人已经解释了如何诊断 AV。
关于代码本身,它存在问题:
最重要的是,您没有为缓冲区分配足够的内存。
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;
除此之外:
您忽略了来自
GetComputerName()
的错误,因此您不能保证CompName
甚至可以有效地传递给Result
。您应该使用
SetString(Result, CompName, nSize)
而不是Result := CompName
,因为GetComputerName()
输出实际的CompName
长度。当您已经知道长度时,无需浪费处理时间让 RTL 计算要复制的长度。并且由于您不检查错误,因此如果GetComputerName()
失败,您不能依赖CompName
被 null 终止。您应该完全摆脱
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;