1

我有一些代码在 Delphi 2007 下运行良好,但在 D2010 下中断。它涉及传入一个字符串,将其转换为 PWideChar(特别是 WideString 指针,而不是 UnicodeString 指针),进行一些处理,然后在其上调用 SysFreeString。它工作正常,直到传入一个空白字符串,然后 SysFreeString 中断。它调用了一堆最终Int 3在 NTDLL.DLL 中引发断点的东西。继续超过这一点会导致

项目引发异常类 $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。它是由 StringToOleStr 生成的 PWideChar,传入非空字符串时不会出现 double-free 错误。不幸的是,我无法真正发布代码示例,因为这是受版权保护的第三方组件。(而且我不能向他们寻求支持,因为它不再受支持。基本上,整个事情都是一团糟。)

4

5 回答 5

3

我要试试心理调试。您的应用程序中存在某种堆损坏,而 SysFreeString 是不幸的受害者(如果没有操作系统符号很难判断,您可能应该为您的操作系统安装 MSFT 符号包)。

尝试为您的应用启用应用程序验证程序(特别是页面堆),看看它是否更早崩溃。

于 2010-08-04T01:43:17.470 回答
2

如果没有看到您的实际代码就很难诊断,但是 WideString 在超出范围时会自动调用 SysFreeString()。听起来您的代码可能正在对已释放的内存进行第二次调用 SysFreeString()。WideString 本身在 D2007 和 D2010 之间根本没有改变,但 Delphi 的字符串处理的其他方面有。也许您没有正确管理字符串。你能显示你的实际代码吗?

于 2010-08-03T23:22:29.330 回答
0

这种错误可能是不同的原因:

  1. 您尝试使用SysFreeString分配的内存而不是分配的内存,SysAllocString例如使用CoTaskMemAlloc.
  2. 你有正确的堆。

堆损坏很难定位。该功能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()不同的可疑位置,您可以非常快速地定位腐败的地方。

于 2010-08-07T12:20:13.817 回答
0

+1 拉里奥斯特曼的回答。

一些 Windows 内存函数在调试器下的行为略有不同:如果它们检测到某种滥用 - 它们会触发断点以通知调试器。所以,基本上,你的代码做错了什么。

您可以在 SysAllocString/SysFreeString 上安装挂钩并将它们重定向到您的内存管理器(应该处于完全调试模式)以收集更多信息。或者你可以将这些调用传递给原始函数,只安装一个过滤器,它会监视内存操作。

您也可以安装调试符号以获取更多信息(我不确定 Delphi 调试器是否可以使用它,但 Process Explorer - 可以。您可以将它连接到您的进程并查看调用堆栈)。

于 2010-08-04T09:18:15.353 回答
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',因为内存还没有被覆盖。

——杰伦

于 2010-08-04T11:06:08.000 回答