我在应用程序中关闭表单时遇到访问冲突。它似乎只有在我访问数据库几次之后才会发生,但这似乎没有意义。
我已经跟踪并将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。
感谢您的帮助,非常感谢。
答案 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。
关于代码本身,它存在问题:
最重要的是,您没有为缓冲区分配足够的内存。 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无需浪费处理时间来计算要复制的长度。由于您没有检查错误,因此如果CompName
失败,您无法依赖GetComputerName()
无效终止。
你应该完全摆脱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;