The Delphi Bug List

Entry No.
686
VCL - 一般 - Controls
FindControl はアクセス違反 "Read of address 00098053" を起こし、無効な結果を返す可能性がある
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 4.03 5.0 5.01 6.0 6.01 6.02 Kylix 1.0
Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists N/A
解説
Reported by many on newsgroups; checked by Jordan Russell, others
FindControl はアクセス違反 "Read of address 00098053" を起こし、無効な結果を返す可能性があります。これは、ユーザがコンボボックス(例 TComboBox)をドロップダウンし、そのドロップダウンリスト上にマウスを移動した場合に良く見られます。

これは Microsoft Identity Manager (msident.dll) (MS Outlook Express や、おそらくその他の MS アプリでも使用されている)との競合です。この Identity Manager は、デスクトップ上のウィンドウのプロパティをセットするために、値 $00098053 を使って SetProp を呼び出します。不運にもこのプロパティのアトム ID は、VCL が自身のプロパティにアクセスするときに内部的に使用するのと同じアトム ID を持つ場合があります。そうなった場合、VCL はデスクトップウィンドウのハンドルに対して FindControl を呼び出しますが、それは Identity Manager によってセットされる値 $00098053 に戻ってしまうでしょう。これは有効なポインタではないため、アクセス違反が発生します。

より詳細は、thread on Google Groups を参照してください。
解決策 / 回避方法
私は、この問題は Controls.pas を修正することによってのみ対応可能だと考えています。以下に示すのはひとつの解決方法です - FindControl と IsDelphiHandle を、ハンドルのプロセス ID がデスクトップウィンドウや別のプロセスのウィンドウではないことを確認するように修正しています。

Delphi 6
function FindControl(Handle: HWnd): TWinControl;
var
  PID: DWORD;
begin
  Result := nil;
  if (Handle <> 0) then
  begin
    if (GetWindowThreadProcessId(Handle, @PID) <> 0) and
       (PID = GetCurrentProcessId) then
    begin
      if GlobalFindAtom(PChar(ControlAtomString)) = ControlAtom then
        Result := Pointer(GetProp(Handle, MakeIntAtom(ControlAtom)))
      else
        Result := ObjectFromHWnd(Handle);
    end;
  end;
end;

...

function IsDelphiHandle(Handle: HWND): Boolean;
var
  PID: DWORD;
begin
  Result := False;
  if Handle <> 0 then
  begin
    if (GetWindowThreadProcessId(Handle, @PID) <> 0) and
       (PID = GetCurrentProcessId) then
    begin
      if GlobalFindAtom(PChar(WindowAtomString)) = WindowAtom then
        Result := GetProp(Handle, MakeIntAtom(WindowAtom)) <> 0
      else
        Result := ObjectFromHWnd(Handle) <> nil;
    end;
  end;
end;
Delphi 2 〜 5
function FindControl(Handle: HWnd): TWinControl;
var
  PID: DWORD;
begin
  Result := nil;
  if Handle <> 0 then
  begin
    if (GetWindowThreadProcessId(Handle, @PID) <> 0) and
       (PID = GetCurrentProcessId) then
      Result := Pointer(GetProp(Handle, MakeIntAtom(ControlAtom)));
  end;
end;

...
 
function IsDelphiHandle(Handle: HWND): Boolean;
var
  PID: DWORD;
begin
  Result := (Handle <> 0) and
    (GetWindowThreadProcessId(Handle, @PID) <> 0) and
    (PID = GetCurrentProcessId) and
    (GetProp(Handle, MakeIntAtom(WindowAtom)) <> 0);
end;
Latest update of this entry: 2002-03-19
本家 The Delphi Bug List のエントリーはこちら
The Delphi Bug List 日本語訳 へ