unit winlist; //{$DEFINE _DEBUG} interface uses Windows, Classes; type T_MyBounds = record Left, Top, Width, Height: Integer; end; type {T_WinItem} //Pauseの時のすべてのウィンドウの表示情報を保持するためのクラス T_WinItem = class(TObject) private // FbAllowAdd: Boolean; //追加するか FbToplevel: Boolean; //トップレベルウィンドウか FbVisible: Boolean; FrcClientToScreenRect: TRect; //スクリーン座標のClientRect FrgRegion: HRGN; //ウィンドウリージョン FiRegionCode: Integer; //GetWindowRgnの戻り値 FParent: T_WinItem; FChilds: TList; FhHandle: HWND; FsClassName: String; FsText: WideString; FszWindowSize, FszClientSize: T_MyBounds; FptClientPos: TPoint; FrcBoundsRect: TRect; FiControlID, FiStyle, FiStyleEx: LongInt; FbEnabled: Boolean; FiProcessID: DWORD; FsExeName: WideString; FsFileVersion: WideString; function FGetParent: T_WinItem; { F_hParentHandle: HWND; F_sParentClassName: String; F_sParentText: WideString; } function FGetCount: Integer; function FGetChild(iIndex: Integer): T_WinItem; public constructor Create(ANode: T_WinItem; hHandle: HWND; rcBoundsRect: TRect; bTopLevel: Boolean); overload; destructor Destroy; override; procedure Clear; procedure Add(AItem: T_WinItem); procedure Delete(iIndex: Integer); function FindWindowItem(hWindow: HWND): T_WinItem; property Count: Integer read FGetCount; property Items [iIndex: Integer]: T_WinItem read FGetChild; default; property IsToplevel: Boolean read FbToplevel; property Visible: Boolean read FbVisible; property ClientToScreenRect: TRect read FrcClientToScreenRect; // property AllowAdd: Boolean read F_bAllowAdd; property Parent: T_WinItem read FGetParent; property Region: HRGN read FrgRegion; property RegionCode: Integer read FiRegionCode; property Handle: HWND read FhHandle; property WinClassName: String read FsClassName; property Text: WideString read FsText; property WindowSize: T_MyBounds read FszWindowSize; property ClientSize: T_MyBounds read FszClientSize; property ClientPos: TPoint read FptClientPos; property BoundsRect: TRect read FrcBoundsRect; property ControlID: Longint read FiControlID; property Style: Longint read FiStyle; property StyleEx: Longint read FiStyleEx; property Enabled: Boolean read FbEnabled; property ProcessID: DWORD read FiProcessID; property ExeName: WideString read FsExeName; property FileVersion: WideString read FsFileVersion; { property hParentHandle: HWND read F_hParentHandle; property sParentClassName: String read F_sParentClassName; property sParentText: WideString read F_sParentText; } end; T_WinList = class(TObject) private FbAeroThemeEnabled : Boolean; F_Root: T_WinItem; public constructor Create; destructor Destroy; override; procedure Add(AWinItem: T_WinItem); function FindWindowItem(hWindow: HWND): T_WinItem; overload; function FindWindowItem(ptPos: TPoint; bIgnoreLayered: Boolean): T_WinItem; overload; property Items: T_WinItem read F_Root; property AeorThemeEnabled : Boolean read FbAeroThemeEnabled; end; implementation uses {$IFDEF _DEBUG} myDebug, {$ENDIF} SysUtils, Forms, my_monitor, // windowstyle, general; constructor T_WinItem.Create(ANode: T_WinItem; hHandle: HWND; rcBoundsRect: TRect; bTopLevel: Boolean); var i : Integer; lpt_Pos: TPoint; l_Item: T_WinItem; begin inherited Create; FChilds := TList.Create; if (ANode = nil) then begin //ルート。 FParent := Self; //Exit; end else begin FParent := ANode; end; FbVisible := False; FhHandle := hHandle; FbToplevel := bTopLevel; FrcBoundsRect := rcBoundsRect; FrgRegion := 0; if (bToplevel) then begin if (ANode <> nil) then begin //トップレベルウィンドウ for i := 0 to ANode.Count-1 do begin l_Item := ANode.Items[i]; //レイヤードウィンドウの場合自身をキャプチャしないにすると見えなくなるので除外判定からはずす。 if ((l_Item.StyleEx and WS_EX_LAYERED) = 0) then begin //レイヤードウィンドウではない。 //Zオーダーが手前のトップレベルウィンドウと比較する。 if gfnbCompleteRectInRect(FrcBoundsRect, l_Item.BoundsRect) then begin //他のトップレベルウィンドウの下に隠れて見えないので登録しない。 Exit; end; end; end; end; end else begin //子ウィンドウ。 //子ウィンドウは見えなくてもリストに登録はする。 if not(IsWindowVisible(FhHandle)) //自身が見えない or not(ANode.Visible) //親が見えない or not(gfnbRectInRect(FrcBoundsRect, ANode.ClientToScreenRect)) //親ウィンドウの長方形外 then begin Exit; end; for i := ANode.Count-1 downto 0 do begin l_Item := ANode.Items[i]; //兄弟ウィンドウとの比較 if (gfnbCompleteRectInRect(FrcBoundsRect, l_Item.BoundsRect)) then begin //他の兄弟ウィンドウの下に隠れて見えないのでこれ以上情報を取得しない。 Exit; end; end; end; FszWindowSize.Width := gfniRectWidth (FrcBoundsRect); FszWindowSize.Height := gfniRectHeight(FrcBoundsRect); if (FszWindowSize.Width <= 0) or (FszWindowSize.Height <= 0) then begin //サイズが0であるなら見えないのでこれ以上情報を取得しない。 Exit; end; //ClientSize // if (Windows.GetClientRect(FhHandle, lrc_Client)) if (Windows.GetClientRect(FhHandle, FrcClientToScreenRect)) then begin FszClientSize.Width := FrcClientToScreenRect.Right; FszClientSize.Height := FrcClientToScreenRect.Bottom; end else begin FszClientSize.Width := 0; FszClientSize.Height := 0; end; //クライアントの原点のスクリーン座標 FptClientPos := Point(0, 0); Windows.ClientToScreen(FhHandle, FptClientPos); //スクリーン座標のClientRectを算出。 FrcClientToScreenRect.TopLeft := FptClientPos; Windows.ClientToScreen(FhHandle, FrcClientToScreenRect.BottomRight); LogicalToPhysicalPoint(FhHandle, FptClientPos); //ウィンドウのサイズ if (bToplevel) then begin //トップレベルウィンドウ FszWindowSize.Left := FrcBoundsRect.Left; FszWindowSize.Top := FrcBoundsRect.Top; end else begin //子ウィンドウ lpt_Pos := FrcBoundsRect.TopLeft; if (ScreenToClient(ANode.Handle, lpt_Pos)) then begin //子ウィンドウ FszWindowSize.Left := lpt_Pos.X; FszWindowSize.Top := lpt_Pos.Y; end else begin //トップレベルウィンドウ? FszWindowSize.Left := FrcBoundsRect.Left; FszWindowSize.Top := FrcBoundsRect.Top; end; end; //クラス名 FsClassName := gfnsClassNameGet(FhHandle); //ウィンドウテキスト FsText := gfnsWindowTextGet(FhHandle); //コントロールID FiControlID := GetWindowLong(FhHandle, GWL_ID); //ウィンドウスタイル FiStyle := GetWindowLong(FhHandle, GWL_STYLE); //ウィンドウスタイル FiStyleEx := GetWindowLong(FhHandle, GWL_EXSTYLE); //拡張ウィンドウスタイル FbEnabled := IsWindowEnabled(FhHandle); { //親ウィンドウのハンドル //↑で既に取得済み // FhParentHandle := gfnhParentWindowGet(FhHandle); //親ウィンドウのクラス名 FsParentClassName := gfnsClassNameGet(FhParentHandle); //親ウィンドウのウィンドウテキスト FsParentText := gfnsWindowTextGet(FhParentHandle); } //プロセスID GetWindowThreadProcessId(FhHandle, @FiProcessID); //実行ファイル名 FsExeName := gfnsExeNameGet(FhHandle); //ファイルバージョン FsFileVersion := gfnsFileVersionGet(FsExeName); // if (F_bToplevel) then begin if (ANode = nil) then begin // FiRegionCode := MyMonitors.DesktopRegionCode; FrgRegion := MyScreenSize.DesktopRegion; end else begin //この処理に結構時間がかかってしまう。 //ウィンドウリージョンを取得 //まず空のリージョンを作成 FrgRegion := CreateRectRgn(0,0,0,0); //ウィンドウリージョン取得 FiRegionCode := GetWindowRgn(FhHandle, FrgRegion); end; // end else begin // F_rgRegion := 0; // F_iRegionCode := Windows.ERROR; // end; FbVisible := True; end; destructor T_WinItem.Destroy; begin Clear; FChilds.Free; inherited; end; procedure T_WinItem.Clear; var i: Integer; begin for i := FGetCount-1 downto 0 do begin Self.Delete(i); end; FChilds.Clear; DeleteObject(FrgRegion); end; procedure T_WinItem.Delete(iIndex: integer); begin T_WinItem(FChilds[iIndex]).Free; FChilds.Delete(iIndex); end; procedure T_WinItem.Add(AItem: T_WinItem); begin FChilds.Add(AItem); end; function T_WinItem.FindWindowItem(hWindow: HWND): T_WinItem; //自身と自身の子ウィンドウアイテム中からhWindowのハンドルを持つウィンドウの親ウィンドウのアイテムを返す。 var i: Integer; l_Node: T_WinItem; begin if (Self.Handle = hWindow) then begin Result := Self; end else begin // Result := nil; for i := Self.Count-1 downto 0 do begin l_Node := Self.Items[i]; //最後に追加したノードから探す if (l_Node.Handle = hWindow) then begin Result := l_Node; Exit; end else if (l_Node.Count > 0) then begin //子ノードから探す Result := l_Node.FindWindowItem(hWindow); if (Result <> nil) then begin Exit; end; end; end; Result := Self; end; end; function T_WinItem.FGetParent: T_WinItem; begin if (FParent = nil) then begin Result := Self; end else begin Result := FParent; end; end; function T_WinItem.FGetCount: Integer; begin Result := FChilds.Count; end; function T_WinItem.FGetChild(iIndex: Integer): T_WinItem; begin Result := T_WinItem(FChilds.Items[iIndex]); end; //------------------------------------------------------------------------------ { T_WinList } function FEnumChildProc(hWindow: HWND; ANode: T_WinItem): Bool; stdcall; //子ウィンドウを列挙する var lh_Parent: HWND; l_Item, l_ParentNode: T_WinItem; lrc_Rect: TRect; begin { 子ウィンドウの列挙はフラット(孫ウィンドウを子ウィンドウの列挙中に列挙するわけでは ない)なのでトップレベルウィンドウの場合と違い非可視ウィンドウや範囲外のウィンドウ であっても除外できない。 そのためT_WinItemにVisibleプロパティを設けて非可視ウィンドウや範囲外のウィンドウ はVisibleをFalseにしてHandleだけをセットする。 } lh_Parent := gfnhParentWindowGet(hWindow); //myDebug.gpcDebug([lh_Parent, gfnsClassNameGet(lh_Parent), hWindow, gfnsClassNameGet(hWindow)]); l_ParentNode := ANode.FindWindowItem(lh_Parent); // lrc_Rect := gfnrcWindowRectGet(hWindow); lrc_Rect := gfnrcPhysicalWindowRectGet(hWindow); l_Item := T_WinItem.Create(l_ParentNode, hWindow, lrc_Rect, False); l_ParentNode.Add(l_Item); Application.ProcessMessages; Result := True; { myDebug.gpcDebug([ IntToStr(hWindow) ,gfnsClassNameGet(hWindow) ,gfnsWindowTextGet(hWindow) ,gfnbRectInRect(lrc_Rect, l_ParentNode.ClientToScreenRect) //親ウィンドウの長方形外 ,IntToStr(l_ParentNode.ClientToScreenRect.Left) ,IntToStr(l_ParentNode.ClientToScreenRect.Top) ,IntToStr(l_ParentNode.ClientToScreenRect.Right) ,IntToStr(l_ParentNode.ClientToScreenRect.Bottom) ]); } end; function FEnumWindowsProc(hWindow: HWND; ARoot: T_WinItem):BOOL; stdcall; //トップレベルウィンドウを列挙する var l_Item: T_WinItem; lrc_Rect: TRect; begin if (IsWindowVisible(hWindow)) //ウィンドウは可視 // and not(IsIconic(hWindow)) //NG サブウィンドウが最小化されていた場合に対応できない //最小化されていない then begin lrc_Rect := gfnrcWindowRectGet(hWindow); // if (RectInRegion(MyScreenSize.DesktopRegion, lrc_Rect)) // if (gfnbRectInRect(MyScreenSize.DesktopRect, lrc_Rect)) if ((gfniRectWidth(lrc_Rect) > 0) and (gfniRectHeight(lrc_Rect) > 0)) and (MyScreenSize.RectInDesktopRect(lrc_Rect)) then begin { myDebug.gpcDebug([ '**********' , IntToStr(hWindow) ,gfnsClassNameGet(hWindow) ,gfnsWindowTextGet(hWindow) ]); } //デスクトップの範囲内 l_Item := T_WinItem.Create(ARoot, hWindow, lrc_Rect, True); if (l_Item.Visible) then begin ARoot.Add(l_Item); EnumChildWindows(hWindow, @FEnumChildProc, Integer(l_Item)); end else begin l_Item.Free; end; end; end; Application.ProcessMessages; Result := True; end; constructor T_WinList.Create; begin inherited; FbAeroThemeEnabled := gfnbIsAeroThemeEnabled; //とりあえずF_Rootを作っておく F_Root := T_WinItem.Create(nil, GetDesktopWindow, MyScreenSize.DesktopRect, True); EnumWindows(@FEnumWindowsProc, LPARAM(F_Root)); end; destructor T_WinList.Destroy; begin F_Root.Free; inherited; end; procedure T_WinList.Add(AWinItem: T_WinItem); begin F_Root.Add(AWinItem); end; function T_WinList.FindWindowItem(hWindow: HWND): T_WinItem; //ウィンドウハンドルがhWindowであるアイテムをリストから探索して返す。 function lfn_FindItem(ANode: T_WinItem; hHandle: HWND): T_WinItem; var i: Integer; l_Node: T_WinItem; begin Result := nil; if (ANode.Handle = hHandle) then begin Result := ANode; end else begin for i := ANode.Count-1 downto 0 do begin l_Node := ANode.Items[i]; //最後に追加したノードから探す if (l_Node.Handle = hHandle) then begin Result := l_Node; Break; end else if (l_Node.Count > 0) then begin //子ノードから探す Result := lfn_FindItem(l_Node, hHandle); if (Result <> nil) then begin Break; end; end; end; end; end; begin Result := lfn_FindItem(F_Root, hWindow); end; function T_WinList.FindWindowItem(ptPos: TPoint; bIgnoreLayered: Boolean): T_WinItem; //スクリーン座標ptPos上にある一番上のアイテムをリストから探索して返す。 (* function _FindChildItem(ANode: T_WinItem; ptPos: TPoint): T_WinItem; var i: Integer; l_Node: T_WinItem; begin Result := nil; for i := 0 to ANode.Count-1 do begin l_Node := ANode.Items[i]; //ANodeは子ウィンドウ。 //子ウィンドウの探索に入るということは少なくともその親ウィンドウの範囲内 //にかかっているということになるのでResultの初期値はANodeになる。 //myDebug.gpcDebug([i, l_Node.Handle, l_Node.WinClassName, l_Node.Text, l_Node.Visible]); if (l_Node.Visible) then begin if (PtInRect(l_Node.BoundsRect, ptPos)) then begin //ClientToScreenRectではない Result := lfn_FindChildItem(l_Node, ptPos); if (Result = nil) then begin Result := l_Node; end; Exit; end; end; end; end; *) function _FindItem(ANode: T_WinItem; ptPos: TPoint; bIgnore: Boolean): T_WinItem; var i: Integer; l_Node: T_WinItem; lpt_Pos: TPoint; begin Result := nil; for i := 0 to ANode.Count-1 do begin l_Node := ANode.Items[i]; if (bIgnore) //自身をキャプチャしない。 and (gfnbFlagCheck(l_Node.StyleEx, WS_EX_LAYERED)) //ANodeはレイヤードウィンドウ。 then begin //ANodeはトップレベルウィンドウ。 //レイヤードウィンドウは表示されないので判定から除外。 Continue; end; lpt_Pos.X := ptPos.X - l_Node.BoundsRect.Left; lpt_Pos.Y := ptPos.Y - l_Node.BoundsRect.Top; if ((l_Node.RegionCode <> ERROR) and (PtInRegion(l_Node.Region, lpt_Pos.X, lpt_Pos.Y))) //リージョンで判定 or ((l_Node.RegionCode = ERROR) and (PtInRect(l_Node.BoundsRect, ptPos))) //Rectで判定 then begin //ptPosがトップレベルウィンドウ内にあった。 if (l_Node.ClientSize.Width = 0) or (l_Node.ClientSize.Height = 0) then begin //クライアントサイズが0だった。 Result := l_Node; end else begin // Result := lfn_FindChildItem(l_Node, ptPos); Result := _FindItem(l_Node, ptPos, False); if (Result = nil) then begin Result := l_Node; end; end; Exit; end; end; end; begin Result := _FindItem(F_Root, ptPos, bIgnoreLayered); if (Result = nil) then begin //本来ここにくることはない。 Result := F_Root.Items[F_Root.Count-1]; end; end; end.