1

Delphi 2007では正常に機能するが、D2010では機能しないコードがいくつかあります。これには、文字列を渡し、それをPWideChar(具体的には、UnicodeStringポインターではなく、WideStringポインター)に変換し、何らかの処理を行ってから、その文字列に対してSysFreeStringを呼び出すことが含まれます。空白の文字列が渡されるまでは正常に機能し、その後SysFreeStringが壊れます。それは、Int 3NTDLL.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であり、空白以外の文字列が渡されてもダブルフリーエラーは発生しません。残念ながら、これは著作権で保護されているサードパーティコンポーネントであるため、コードサンプルを投稿することはできません。(サポートが終了したため、サポートを依頼することはできません。基本的に、全体が1つの大きな混乱です。)

4

5 に答える 5

3

サイキックデバッギングをやってみます。アプリケーションである種のヒープ破損があり、SysFreeString が不幸な犠牲者です (OS シンボルがないとわかりにくいため、OS 用の MSFT シンボル パッケージをインストールする必要があります)。

アプリのアプリケーション検証ツール (特にページヒープ) を有効にしてみて、以前にクラッシュするかどうかを確認してください。

于 2010-08-04T01:43:17.470 に答える
2

実際のコードを見ずに診断するのは困難ですが、範囲外になると、WideString は自動的に SysFreeString() を呼び出します。あなたのコードは、すでに解放されているメモリで SysFreeString() への 2 回目の呼び出しを行っている可能性があります。WideString 自体は D2007 と D2010 の間でまったく変更されていませんが、Delphi の文字列処理の他の側面は変更されています。文字列を正しく管理していない可能性があります。実際のコードを見せていただけますか?

于 2010-08-03T23:22:29.330 に答える
0

ラリー・オスターマンの答えは+1。

一部のWindowsメモリ関数は、デバッガーでの動作がわずかに異なります。ある種の誤用を検出すると、ブレークポイントをトリガーしてデバッガーに通知します。したがって、基本的に、コードは何か間違ったことをしています。

SysAllocString / SysFreeStringにフックをインストールし、それらをメモリマネージャー(完全デバッグモードである必要があります)にリダイレクトして、詳細情報を収集できます。または、これらの呼び出しを元の関数に渡して、メモリアクションを監視するフィルターのみをインストールすることもできます。

また、デバッグシンボルをインストールして、より多くの情報を取得することもできます(Delphiデバッガーが使用できるかどうかはわかりませんが、Process Explorerは使用できます。プロセスに接続して呼び出しスタックを確認できます)。

于 2010-08-04T09:18:15.353 に答える
0

この種のエラーのさまざまな理由が考えられます。

  1. ではなく、たとえば でSysFreeString割り当てられたメモリで解放しようとしています。SysAllocStringCoTaskMemAlloc
  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

簡単なテストは、何をどの順序で行うかについて本当に注意する必要があることを示しています。

ですから、小さな例を投稿することはできませんが、何をしているのかもう少し詳しく教えていただけますか?

デバッグが悪い。以下のことは無視してください。コメントを参照してください。

はPWideChar を返しますがSysFreeString()、呼び出しの最後に呼び出されています。Allocate()

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

于 2010-08-04T11:06:08.000 に答える