2015-12-18 10 views
6

このコードは、AVを作成する:関数の中身がフリーで破壊されるのを避けるには?

function PAIsMainAppWindow(Wnd: THandle): Boolean; 
var 
    ParentWnd: THandle; 
    ExStyle: DWORD; 
begin 
    if IsWindowVisible(Wnd) then 
    begin 
    ParentWnd := THandle(GetWindowLongPtr(Wnd, GWLP_HWNDPARENT)); 
    ExStyle := GetWindowLongPtr(Wnd, GWL_EXSTYLE); 
    Result := ((ParentWnd = 0) or (ParentWnd = GetDesktopWindow)) and 
     ((ExStyle and WS_EX_TOOLWINDOW = 0) or (ExStyle and WS_EX_APPWINDOW <> 0)); 
    end 
    else 
    Result := False; 
end; 

function PAEnumTaskWindowsProc(Wnd: THandle; List: TStrings): Boolean; stdcall; 
var 
    Caption: array [0..1024] of Char; 
begin 
    if PAIsMainAppWindow(Wnd) and (GetWindowText(Wnd, Caption, SizeOf(Caption)) > 0) then 
    List.AddObject(ExtractFileName(GetProcessNameFromWnd(Wnd)), Pointer(Wnd)); 
    Result := True; 
end; 

function PAGetTaskWindowHandleFromProcess(const AProcessName: string): THandle; 
var 
    sl: TStringList; 
    i: Integer; 
begin 
    Result := 0; 

    sl := TStringList.Create(True); // stringlist owns objects 
    try 
    if EnumWindows(@PAEnumTaskWindowsProc, LPARAM(sl)) then 
    begin 
     for i := 0 to sl.Count - 1 do 
     begin 
     if SameText(AProcessName, sl[i]) then 
     begin 
      Result := THandle(sl.Objects[i]); 
      BREAK; 
     end; 
     end; 
    end; 
    finally 
    sl.Free; // AV! 
    end; 
end; 

ChromeHandle := PAGetTaskWindowHandleFromProcess('chrome.exe'); 

もSTRINGLISTを解放すると、関数の結果を破壊するので、AVが発生することは明らかです。しかし、どのようにこれを避けることができますか?

+0

オブジェクトを所有する必要はありません。それらはオブジェクトではなく、ヒープメモリが割り当てられていません。 – MBo

答えて

9

まず、実際のコードを見てみましょう。文字列リストはオブジェクトを保持しません。ウィンドウハンドルを保持します。したがって、OwnsObjectsは単に適切ではありません。それはObjects[]のものがDelphiインスタンスのクラスであると仮定し、これらのインスタンスでFreeを呼び出します。これが障害の発生場所です。

これらのウィンドウハンドルは所有していないため、破棄しようとしないでください。

したがって、OwnsObjectsTrueを設定しないと問題が解決しません。これで

sl := TStringList.Create(True); // stringlist owns objects 

:さらに

sl := TStringList.Create; 

、あなたがTHandleにこれらのオブジェクトをキャストしていることは、この行を置き換えています。それは間違っています、実際に重要ではありません。意味的には、これらはウィンドウハンドルなので、HWNDにキャストします。実際、THandleを使用している場合は、HWNDを使用してください。

他にもエラーがあります。 GetWindowTextを使用すると、長さではなくバッファのサイズが渡されます。つまり、バッファーの長さについてはあなたが嘘をついています。これらはワイド文字なので、バッファの長さは半分になります。デスクトップウィンドウの親であるウィンドウを探していると間違っていると感じます。


引数のために、文字列リストに実際にオブジェクトが含まれていたとしましょう。その場合、そして理想的な世界では、文字列リストクラスは、そのオブジェクトを破壊することなく所有コンテナからオブジェクトを削除する従来の方法であるExtractメソッドを提供します。代わりにOwnsObjectsシャッフルを実行できます。

if SameText(AProcessName, sl[i]) then 
begin 
    sl.OwnsObjects := False; 
    Result := TSomeObject(sl.Objects[i]); 
    sl.Objects[i] := nil; 
    sl.OwnsObjects := True; 
    BREAK; 
end; 

あなたではなく、あなたは文字列のリストを作成するときにFalseをするOwnsObjectsを設定することができ、そしてあなたがそれにFreeを呼び出す直前にのみTrueに設定したい場合。

+0

David、広範な説明のおかげで!どのようにGetWindowTextの問題を解決しますか? – user1580348

+0

その機能については、ドキュメントで調べることができます。バッファの長さを渡すように指示します。あなたの長さは1025です。 –

関連する問題