是什么导致SysFreeString击中Int 3断点?

时间:2010-08-03 23:09:11

标签: delphi winapi delphi-2010 delphi-2007 widestring

我有一些代码在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,并没有   非空白时双重免费错误   字符串传入。不幸的是,我   无法真正发布代码示例   因为这是第三方   受版权保护的组件。 (和   我不能要求他们支持,因为   它不再受支持。基本上,   整件事情是一团糟。)

5 个答案:

答案 0 :(得分:3)

我要尝试通灵调试。您的应用程序中存在某种堆损坏,并且SysFreeString是不幸的受害者(如果没有OS符号,很难说,您应该为您的操作系统安装MSFT符号包)。

尝试为您的应用启用应用验证程序(特别是pageheap),看看它是否先崩溃。

答案 1 :(得分:2)

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

答案 2 :(得分:0)

拉里奥斯特曼回答+1。

某些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)

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

  1. 您尝试免费使用SysFreeString内存,而这些内存不是SysAllocString分配的,而是CoTaskMemAlloc
  2. 你的堆正确。
  3. 堆腐败很难本地化。函数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(),你可以很快找到腐败的地方。