我有一些代码在Delphi 2007下运行良好但在D2010下有所突破。它涉及传入一个字符串,将其转换为PWideChar(特别是WideString指针,而不是UnicodeString指针),进行一些处理,然后在其上调用SysFreeString。它可以正常工作,直到传入空字符串,然后SysFreeString中断。它调用了一堆最终在NTDLL.DLL中引发Int 3
断点的东西。继续超过这一点导致
项目引发了异常类 $ C0000005,带有消息'访问权限 违反0x7747206e:读取 地址0x539b8dba'。
如果仔细观察,这不是标准的访问冲突消息。
当它到达Int 3
时,堆栈跟踪的顶部如下所示:
:774e475d ; ntdll.dll
:774afad0 ; ntdll.dll
:774e5de9 ; ntdll.dll
:774a6dff ; ntdll.dll
:76fc1075 ; C:\Windows\system32\ole32.dll
:770e443a ; C:\Windows\system32\oleaut32.dll
:770e3ea3 oleaut32.SysFreeString + 0x4a
有谁知道这里发生了什么?
修改(来自评论):
但这不是WideString。它的 一个PWideChar生成的 StringToOleStr,并没有 非空白时双重免费错误 字符串传入。不幸的是,我 无法真正发布代码示例 因为这是第三方 受版权保护的组件。 (和 我不能要求他们支持,因为 它不再受支持。基本上, 整件事情是一团糟。)
答案 0 :(得分:3)
我要尝试通灵调试。您的应用程序中存在某种堆损坏,并且SysFreeString是不幸的受害者(如果没有OS符号,很难说,您应该为您的操作系统安装MSFT符号包)。
尝试为您的应用启用应用验证程序(特别是pageheap),看看它是否先崩溃。
答案 1 :(得分:2)
如果没有看到您的实际代码,很难诊断,但是当WideString超出范围时,WideString会自动调用SysFreeString()。听起来你的代码可能在已经释放的内存上再次调用SysFreeString()。在D2007和D2010之间,WideString本身并没有改变,但是Delphi的字符串处理的其他方面都有。也许你没有正确管理字符串。你能告诉我你的实际代码吗?
答案 2 :(得分:0)
某些Windows内存函数在调试器下表现略有不同:如果它们检测到某种误用 - 它们会触发断点来通知调试器。所以,基本上,你的代码做错了。
您可以在SysAllocString / SysFreeString上安装挂钩,并将它们重定向到您的内存管理器(应该处于完全调试模式)以收集更多信息。或者你可以将这些调用传递给原始函数,只安装一个监视内存操作的过滤器。
你可以install debug symbols获取更多信息(我不确定Delphi调试器是否可以使用它,但Process Explorer - 可以。你可以将它连接到你的进程并查看调用堆栈。)
答案 3 :(得分:0)
一个简单的测试表明,你需要非常小心你以什么顺序做什么。
所以:即使你不能发布一个小例子,你能否更详细地说明你在做什么?
调试不好;忽略下面的事情;见评论。
在 SysFreeString()
调用结束时调用Allocate()
,即使它返回PWideChar:
program ShowStringToOleStrBehaviourProject;
{$APPTYPE CONSOLE}
uses
SysUtils;
function Allocate(const Value: UnicodeString): PWideChar;
begin
Result := StringToOleStr(Value);
// implicit SysFreeString(WideChars);
end;
procedure Run;
var
WideChars: PWideChar;
begin
WideChars := Allocate('Foo');
Writeln(WideChars);
end;
begin
try
Run();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
请注意,控制台仍会输出'Foo',因为内存尚未被覆盖。
击><击> - 的Jeroen 撞击>
答案 4 :(得分:0)
这种错误的原因可能不同:
SysFreeString
内存,而这些内存不是SysAllocString
分配的,而是CoTaskMemAlloc
。堆腐败很难本地化。函数HeapSetInformation
非常有用。例如,您可以使用
HeapSetInformation(NULL,HeapEnableTerminationOnCorruption,NULL,0);
其他好方法是使用HeapValidate
函数。例如,您可以定义一个函数来验证进程中的所有堆(C中的代码,可以在Delphi中轻松重写):
BOOL MyHeapValidate (void)
{
HANDLE hProcessHeaps[1024];
DWORD i;
DWORD dwNumberOfHeaps;
BOOL bSuccess = FALSE;
dwNumberOfHeaps = GetProcessHeaps (sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0]),
hProcessHeaps);
if (dwNumberOfHeaps > sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0])) {
MessageBox(NULL, TEXT("GetProcessHeaps()"),
TEXT("Error in MyHeapValidate()"), MB_OK);
return FALSE;
}
for (i=0; i<dwNumberOfHeaps; i++) {
bSuccess = HeapValidate (hProcessHeaps[i], 0, NULL);
if (!bSuccess)
return bSuccess;
}
return bSuccess;
}
此功能的用法如下:
void BadFunction(BSTR bstr)
{
LPOLESTR psz = OLESTR("Test12");
lstrcpy (bstr, psz);
}
int main()
{
LPOLESTR psz = OLESTR("Test");
BSTR bstr = SysAllocString (psz);
// verify that before call of BadFunction() all process heaps are OK!
if (!MyHeapValidate()) {
_tprintf(TEXT("heap is corrupted after the step 1.\n"));
return 1;
}
BadFunction(bstr);
if (!MyHeapValidate()) {
_tprintf(TEXT("heap is corrupted after the step 1.\n"));
return 1;
}
SysFreeString (bstr);
return 0;
}
关于在不同的疑似地点插入MyHeapValidate()
,你可以很快找到腐败的地方。