4

私の Delphi XE2 32 ビット アプリケーション (Update 4 Hotfix 1 バージョン 16.0.4504.48759) では、Format() ルーチンを使用してポインター値をログに記録しています。

例えば:

Format('MyObject (%p)', [Pointer(MyObject)]);

ただし、結果の文字列に不要な文字が含まれることがあります (たとえば、この場合、16 進数の代わりに '?' または '|' が使用されます)。

MyObject (4E?|2010)

次のように「%p」を「%x」に置き換えても、同じ結果が得られます。

Format('MyObject (%x)', [Integer(MyObject)]);

ただし、整数値を使用すると常に機能します。

Format('MyObject (%d)', [Integer(MyObject)]);

MyObject (1291453120)

私が気付いていないバグはありますか、それともここで経験した問題に関連している可能性がありますか?

バリアントで "%s" 以外を使用すると Format がクラッシュするのはなぜですか?

アップデート

Jeroen の回答は、消去法による解決策につながったので、受け入れました。F7 経由でアプリを起動する状況の後 (コメントによると)、プロセスのかなり早い段階で何か問題が発生しているに違いないと考えました。直感で、IDE メニューから madExcept を無効にし、アプリを再構築したところ、問題は解消されました。明らかに、madExcept が私のアプリケーションにリンクしているコードが何であれ、SysUtils 定数 TwoHexLookup を上書きしていました。madExcept の再有効化と再構築 (他に変更を加えることなく) も機能したため、リンク段階で何らかの破損があったに違いありません。

Jeroen が概説したメモリ破損を検出するための戦略は有用な演習であり、同様の状況に遭遇した場合に役立つはずです。

4

2 に答える 2

4

私の最良の仮説は、おそらく初期化されていないポインタを逆参照することによって、コードが変更すべきではないメモリを変更しているということです。この可能性を示す再現可能なケースを作成しました。少なくとも、私のバージョンのコンパイラを使用して、私のマシンで再現可能です。まったく同じコードは、別の状況では同じことを行わない可能性があります。

procedure TForm1.Button1Click(Sender: TObject);
var
  P : pbyte;
  S : string;
  T : ansistring;
begin
  // There's nothing special about HexDisplayPrefix.
  // It just happens to be one of the last global variables
  // declared in SysUtils.

  P := @ ( HexDisplayPrefix );

  // A few bytes beyond that is TwoHexLookUp.
  // This is a static array of unicode characters used by the IntToHex routine,
  // which is in turn used by Format when %p or %x are used.

  // I'll add an offset to P so that it points into that array.
  // I'll make the offset odd so that it points to the high byte of a character.
  // Of course, I can't guarantee that the same offset will work for you

  P := P + 5763;

  // Change the lookup table.
  // Of course, you would never do this on purpose.

  P ^ := 39;

  // Now let's call IntToHex

  S := IntToHex ( $E0, 2 );

  // Show the value on the screen.
  // Hey look, the zero has been replaced with a star.

  memo1 . lines . add ( S );

  // Convert the unicode string to an ansistring

  T := ansistring ( S );

  // Show the value on the screen.
  // When converting to Ansi, the system doesn't know what to do with the star,
  // so it replaces it with a question mark.

  memo1 . lines . add ( unicodestring(T) );

end;

星付きのEと疑問符付きのEを表示します

于 2013-02-15T12:30:59.217 に答える
2

これはメモリの上書きのように見えるため(user1008646へのコメントを参照)、次の手順を実行してみてください。

  1. まず、どのメモリアドレスが上書きされるかを調べてみてください。あなたはそれが失敗すると言いs := IntToHex(2129827392, 8);ます。正しい値を見つけて、それが内にあるかどうかを調べますTwoHexLookUp
  2. TwoHexLookUpにある場合は、データ変更されたブレークポイントを設定します(オブジェクトフィールドの値が変更されるたびにブレークポイントを定義する方法と、これを行う方法についてはデータブレークポイントを追加するを参照してください)。
  3. ブレークポイントが発生するまでアプリを実行します。

広告1:おそらく最も簡単な方法は、実行時に観察したときTwoHexLookUpに、どの値の変更が間違った結果を得るのと同じ効果があるかを調べることです。s := IntToHex(2129827392, 8);

木曜日はクライアントでDelphiの作業をしているので、もう少し深く掘り下げる時間があるかもしれません。

編集
F7を使用してプロセスを進めると、実際にSysInit最初のプロセスに入ります。

そこでできることは、すでにTwoHexLookup配列にブレークポイントが設定されていることです。
次に、F9 / F8 / F7(必要な粒度に応じて)のいずれかを実行し、ウォッチウィンドウでアレイを監視します。それはあなたを動かすはずです。

于 2013-02-19T19:37:54.470 に答える