unit DMainHook; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, CtrlWndActiveHook, Vcl.StdCtrls, Vcl.Menus, VirtualTrees, Vcl.ExtCtrls, Vcl.ImgList; type PProcessWndEntry = ^TProcessWndEntry; TProcessWndEntry = record pNode: PVirtualNode; WindowEntry: TWindowEntry; nIcon: Integer; end; TDlgMainHook = class(TForm) Memo1: TMemo; mmMain: TMainMenu; miFile: TMenuItem; miExit: TMenuItem; pnMain: TPanel; vtWindowEntry: TVirtualStringTree; Splitter1: TSplitter; Splitter2: TSplitter; vtLogEntry: TVirtualStringTree; lstState: TImageList; miDebug: TMenuItem; miReloadDLL: TMenuItem; miRemoveHook: TMenuItem; procedure miExitClick(Sender: TObject); procedure vtWindowEntryGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); procedure vtWindowEntryGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure vtWindowEntryFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); procedure vtLogEntryGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); procedure vtLogEntryGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure vtLogEntryFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); procedure vtWindowEntryPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); procedure vtWindowEntryCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); procedure vtLogEntryHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo); procedure vtWindowEntryHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo); procedure vtLogEntryCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); procedure vtWindowEntryGetImageIndexEx(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer; var ImageList: TCustomImageList); procedure vtLogEntryGetImageIndexEx(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer; var ImageList: TCustomImageList); procedure miReloadDLLClick(Sender: TObject); procedure miRemoveHookClick(Sender: TObject); private { Private declarations } WndHook_: TCtrlWndActiveHook; lstShellImg_: TImageList; CurrentViewEntry_: TWindowEntry; RootNodeCreate_, RootNodeDestroy_: PVirtualNode; public { Public declarations } Constructor Create(aOwner: TComponent); override; Destructor Destroy; override; procedure AddWindowLog(pLog: PWindowLogEntry); procedure OutLog(const sLog: String); overload; procedure OutLog(const Format: string; const Args: array of const); overload; procedure AddWindowEntry(aEntry: TWindowEntry; bDestroy: Boolean = false); procedure DeleteWindowEntry(aEntry: TWindowEntry); procedure OnActiveWindow(Sender: TCtrlWndActiveHook; aWindowEntry: TWindowEntry); procedure OnDeActiveWindow(Sender: TCtrlWndActiveHook; aWindowEntry: TWindowEntry); procedure process_WM_WNDHOOK_RELOAD(var msg: TMessage); Message WM_WNDHOOK_RELOAD; procedure process_WM_WNDHOOK_NOTIFY(var msg: TMessage); Message WM_WNDHOOK_NOTIFY; end; var DlgMainHook: TDlgMainHook; implementation uses Sunk.Shell, System.DateUtils, Sunk.DateTime, Sunk.Debug, Sunk.Process; {$R *.dfm} Constructor TDlgMainHook.Create(aOwner: TComponent); procedure InitRootNode; var pData: PProcessWndEntry; begin RootNodeCreate_ := vtWindowEntry.AddChild(nil); Include(RootNodeCreate_.States, vsInitialized); Include(RootNodeCreate_.States, vsExpanded); pData := vtWindowEntry.GetNodeData(RootNodeCreate_); pData.nIcon := -1; RootNodeDestroy_ := vtWindowEntry.AddChild(nil); Include(RootNodeDestroy_.States, vsInitialized); Include(RootNodeDestroy_.States, vsExpanded); pData := vtWindowEntry.GetNodeData(RootNodeDestroy_); pData.nIcon := -2; end; begin Inherited Create(aOwner); WndHook_ := TCtrlWndActiveHook.Create(Handle, true); WndHook_.OnActive := OnActiveWindow; WndHook_.OnDeActive := OnDeActiveWindow; lstShellImg_ := TImageList.Create(self); lstShellImg_.Handle := GetShellImageHandle; lstShellImg_.ShareImages := true; vtWindowEntry.Images := lstShellImg_; vtLogEntry.Images := lstShellImg_; CurrentViewEntry_ := nil; InitRootNode; end; Destructor TDlgMainHook.Destroy; begin FreeAndNil(lstShellImg_); FreeAndNil(WndHook_); Inherited; end; procedure TDlgMainHook.AddWindowLog(pLog: PWindowLogEntry); var pNode: PVirtualNode; pData: PWindowLogEntry; begin if CurrentViewEntry_ = pLog.OwnerWindow then begin pNode := vtLogEntry.AddChild(nil); Include(pNode.States, vsInitialized); pData := vtLogEntry.GetNodeData(pNode); pData^ := pLog^; end; end; procedure TDlgMainHook.miExitClick(Sender: TObject); begin Close; end; procedure TDlgMainHook.miReloadDLLClick(Sender: TObject); begin ShowMessage('±â´É¾øÀ½'); // WndHook_.LoadHookDll(Handle); end; procedure TDlgMainHook.miRemoveHookClick(Sender: TObject); begin WndHook_.UnHookDll; end; procedure TDlgMainHook.OutLog(const sLog: String); begin Memo1.Lines.Add(Format('[%s] %s', [DateTimeToStr(now), sLog])); end; procedure TDlgMainHook.OutLog(const Format: string; const Args: array of const); var str: String; begin FmtStr(str, Format, Args); OutLog(str); // test vtWindowEntry.Repaint; end; procedure TDlgMainHook.AddWindowEntry(aEntry: TWindowEntry; bDestroy: Boolean = false); var pNode: PVirtualNode; pData: PProcessWndEntry; begin if Assigned(aEntry) then begin if bDestroy then pNode := vtWindowEntry.AddChild(RootNodeDestroy_) else pNode := vtWindowEntry.AddChild(RootNodeCreate_); Include(pNode.States, vsInitialized); pData := vtWindowEntry.GetNodeData(pNode); pData.WindowEntry := aEntry; pData.nIcon := GetShellImageIndex(aEntry.ModulePath); end; end; procedure TDlgMainHook.DeleteWindowEntry(aEntry: TWindowEntry); var pNode: PVirtualNode; pData: PProcessWndEntry; begin pNode := vtWindowEntry.GetFirst; while pNode <> nil do begin pData := vtWindowEntry.GetNodeData(pNode); if pData.WindowEntry = aEntry then begin vtWindowEntry.DeleteNode(pNode); exit; end; pNode := vtWindowEntry.GetNext(pNode); end; end; procedure TDlgMainHook.OnActiveWindow(Sender: TCtrlWndActiveHook; aWindowEntry: TWindowEntry); begin OutLog('(%s:%d) ÇÁ·Î±×·¥ Ȱ¼ºÈ­', [aWindowEntry.ModuleName, aWindowEntry.PID]); vtWindowEntry.Repaint; end; procedure TDlgMainHook.OnDeActiveWindow(Sender: TCtrlWndActiveHook; aWindowEntry: TWindowEntry); begin OutLog('(%s:%d) ÇÁ·Î±×·¥ ºñȰ¼ºÈ­ (ÃÑ È°¼ºÈ­ ½Ã°£ = %d)', [aWindowEntry.ModuleName, aWindowEntry.PID, aWindowEntry.ActiveSec]); vtWindowEntry.Repaint; end; procedure TDlgMainHook.vtLogEntryCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); var pData1, pData2: PWindowLogEntry; begin pData1 := Sender.GetNodeData(Node1); pData2 := Sender.GetNodeData(Node2); case Column of 1 : Result := Integer(pData1.WindowLogState) - Integer(pData2.WindowLogState); 2 : Result := CompareDateTime(pData1.dtLog, pData2.dtLog); 3 : Result := CompareStr(pData1.OwnerWindow.ModuleName, pData2.OwnerWindow.ModuleName); 4 : Result := CompareText(pData1.sTitle, pData2.sTitle); end; end; procedure TDlgMainHook.vtLogEntryFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); var pData: PWindowLogEntry; begin pData := Sender.GetNodeData(Node); Finalize(pData^); end; procedure TDlgMainHook.vtLogEntryGetImageIndexEx(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer; var ImageList: TCustomImageList); var pData: PWindowLogEntry; begin pData := Sender.GetNodeData(Node); case Kind of ikNormal, ikSelected: case Column of 1 : begin ImageList := lstState; ImageIndex := Integer(pData.WindowLogState); end; 3 : begin ImageList := lstShellImg_; ImageIndex := GetShellImageIndex(pData.OwnerWindow.ModulePath); end; end; end; end; procedure TDlgMainHook.vtLogEntryGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); begin NodeDataSize := SizeOf(TWindowLogEntry); end; procedure TDlgMainHook.vtLogEntryGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var pData: PWindowLogEntry; begin pData := Sender.GetNodeData(Node); case Column of 0 : CellText := IntToStr(Node.Index + 1); 1 : case pData.WindowLogState of wlUnknown : CellText := '¾Ë¼ö¾øÀ½'; wlCreate : CellText := '»ý¼º'; wlDestroy : CellText := '´Ý±â'; wlActive : CellText := 'Ȱ¼º'; wlMin : CellText := 'ÃÖ¼ÒÈ­'; wlMax : CellText := 'ÃÖ´ëÈ­'; wlRestore : CellText := 'º¹±Í'; wlMoveSize : CellText := 'À̵¿/Å©±â'; wlRedrawTitle : CellText := 'Á¦¸ñº¯°æ'; wlAttach : ; wlDetach : CellText := 'Á¾·á'; end; 2 : CellText := DateTimeToStr(pData.dtLog); 3 : CellText := pData.OwnerWindow.ModuleName; 4 : CellText := pData.sTitle; 5 : CellText := pData.sSubTitle; end; end; procedure TDlgMainHook.vtLogEntryHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo); begin if HitInfo.Button = mbLeft then begin with Sender, Treeview, HitInfo do begin if HitInfo.Column < 0 then exit; if SortColumn > NoColumn then Columns[SortColumn].Options := Columns[SortColumn].Options + [coParentColor]; if HitInfo.Column = 0 then SortColumn := NoColumn else begin if (SortColumn = NoColumn) or (SortColumn <> Column) then begin SortColumn := Column; SortDirection := sdAscending; end else if SortDirection = sdAscending then SortDirection := sdDescending else SortDirection := sdAscending; Columns[SortColumn].Color := $00F7F0F0; vtLogEntry.BeginUpdate; try SortTree(SortColumn, SortDirection, False); finally vtLogEntry.EndUpdate; end; end; end; end; end; procedure TDlgMainHook.vtWindowEntryCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer); var pEntry1, pEntry2: PProcessWndEntry; begin pEntry1 := Sender.GetNodeData(Node1); pEntry2 := Sender.GetNodeData(Node2); if Assigned(pEntry1.WindowEntry) and Assigned(pEntry2.WindowEntry) then case Column of 0 : Result := CompareText(pEntry1.WindowEntry.ModuleName, pEntry2.WindowEntry.ModuleName); 1 : Result := CompareDateTime(pEntry1.WindowEntry.CreateDateTime, pEntry2.WindowEntry.CreateDateTime); 2 : Result := CompareDateTime(pEntry1.WindowEntry.DestroyDateTime, pEntry2.WindowEntry.DestroyDateTime); end; end; procedure TDlgMainHook.vtWindowEntryFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); procedure ClearSortHighlight; begin // Á¤·Ä ÇÏÀ̶óÀÌÆ® ²ô±â with vtLogEntry.Header do begin if SortColumn <> -1 then begin Columns[SortColumn].Options := Columns[SortColumn].Options + [coParentColor]; SortColumn := -1; end; end; end; procedure FillLog(pNode: PVirtualNode); var i: Integer; pEntry: PProcessWndEntry; begin pEntry := Sender.GetNodeData(pNode); CurrentViewEntry_ := pEntry.WindowEntry; vtLogEntry.BeginUpdate; try for i := 0 to pEntry.WindowEntry.CountLog - 1 do AddWindowLog(pEntry.WindowEntry[i]); finally vtLogEntry.EndUpdate; end; end; var pNode: PVirtualNode; begin ClearSortHighlight; vtLogEntry.Clear; if not Assigned(Node) then exit; if (RootNodeCreate_ = Node) or (RootNodeDestroy_ = Node) then begin pNode := Node.FirstChild; while pNode <> nil do begin FillLog(pNode); pNode := pNode.NextSibling; end; vtLogEntry.Sort(vtLogEntry.RootNode, 2, sdAscending); end else FillLog(Node); end; procedure TDlgMainHook.vtWindowEntryGetImageIndexEx(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer; var ImageList: TCustomImageList); var pData: PProcessWndEntry; begin pData := Sender.GetNodeData(Node); case Kind of ikNormal, ikSelected: case Column of 0 : if pData.nIcon < 0 then begin ImageList := lstState; case pData.nIcon of -1 : ImageIndex := 9; -2 : ImageIndex := 10; end; end else begin ImageList := lstShellImg_; ImageIndex := pData.nIcon; end; end; end; end; procedure TDlgMainHook.vtWindowEntryGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); begin NodeDataSize := SizeOf(TProcessWndEntry); end; procedure TDlgMainHook.vtWindowEntryGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var pData: PProcessWndEntry; begin pData := Sender.GetNodeData(Node); if pData.WindowEntry = nil then begin case Column of 0 : if Node = RootNodeCreate_ then CellText := '½ÇÇàµÈ ÇÁ·Î¼¼½º' else if Node = RootNodeDestroy_ then CellText := 'Á¾·áµÈ ÇÁ·Î¼¼½º'; end; end else case Column of 0 : CellText := Format('%s (%d)', [pData.WindowEntry.Description, pData.WindowEntry.CountLog]); 1 : CellText := GetTicktocktime(pData.WindowEntry.ActiveSec); 2 : CellText := GetTicktocktime(pData.WindowEntry.IdleSec); 3 : if Node.Parent = RootNodeCreate_ then CellText := Format('%s:%d', [pData.WindowEntry.ModuleName, pData.WindowEntry.PID]) else CellText := Format('%s', [pData.WindowEntry.ModuleName]); 4 : CellText := DateTimeToStr(pData.WindowEntry.CreateDateTime); 5 : if pData.WindowEntry.DestroyDateTime = 0 then CellText := '½ÇÇàÁß..' else CellText := DateTimeToStr(pData.WindowEntry.DestroyDateTime); end; end; procedure TDlgMainHook.vtWindowEntryHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo); begin exit; if HitInfo.Button = mbLeft then begin with Sender, Treeview, HitInfo do begin if SortColumn > NoColumn then Columns[SortColumn].Options := Columns[SortColumn].Options + [coParentColor]; if (SortColumn = NoColumn) or (SortColumn <> Column) then begin SortColumn := Column; SortDirection := sdAscending; end else if SortDirection = sdAscending then SortDirection := sdDescending else SortDirection := sdAscending; Columns[SortColumn].Color := $00F7F0F0; vtWindowEntry.BeginUpdate; try SortTree(SortColumn, SortDirection, False); finally vtWindowEntry.EndUpdate; end; end; end; end; procedure TDlgMainHook.vtWindowEntryPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); var pData: PProcessWndEntry; begin case Column of 0 : begin if (Node.Parent = RootNodeDestroy_) and not (vsSelected in Node.States) then TargetCanvas.Font.Color := clGray; pData := Sender.GetNodeData(Node); if Assigned(pData.WindowEntry) and pData.WindowEntry.IsActive then TargetCanvas.Font.Style := TargetCanvas.Font.Style + [fsBold] else TargetCanvas.Font.Style := TargetCanvas.Font.Style - [fsBold]; end; end; end; procedure TDlgMainHook.process_WM_WNDHOOK_RELOAD(var msg: TMessage); function CheckUnHook(dwPID: DWORD): Boolean; var dwTick: DWORD; sOld, sCur: String; begin TSunkTrace.t('TDlgMainHook >> process_WM_WNDHOOK_RELOAD >> CheckUnHook()'); Result := true; dwTick := GetTickCount; while (GetTickCount - dwTick) < 1000 do begin sOld := GetProcessNameFromPID(dwPID); Sleep(100); sCur := GetProcessNameFromPID(dwPID); if (sCur = '') or (sCur <> sOld) then begin Result := false; exit; end; Application.ProcessMessages; end; end; begin // if Assigned(WndHook_) then // begin // TSunkTrace.t('TDlgMainHook >> process_WM_WNDHOOK_RELOAD >> PID = %d', [msg.WParam]); // if CheckUnHook(msg.WParam) then // begin // TSunkTrace.t('TDlgMainHook >> process_WM_WNDHOOK_RELOAD >> CheckUnHook() >> UnHook!!!'); //// WndHook_.LoadHookDll(Handle); // end; // end; end; procedure TDlgMainHook.process_WM_WNDHOOK_NOTIFY(var msg: TMessage); var l: TWindowLogEntry; e: TWindowEntry; begin if msg.WParamLo = WND_STATE_DETACH_HOOK then begin // ÀÌ °æ¿ì´Â UIÀû¿ë ¶§¹®¿¡ Ư¼ö »óȲÀÌ´Ù. e := WndHook_[msg.WParamHi]; if Assigned(e) then begin l := e.AddWindowLog(msg.LParam, wlDetach); if l.dtLog <> 0 then AddWindowLog(@l); OutLog('(%s:%d) ÇÁ·Î¼¼½º Á¾·á (Begin = %s, End = %s)', [e.ModuleName, e.PID, DateTimeToStr(e.CreateDateTime), DateTimeToStr(now)]); DeleteWindowEntry(e); end; e := WndHook_.DeleteWindowEntry(msg.WParamHi); if Assigned(e) then AddWindowEntry(e, true); exit; end; l := WndHook_.ProcessHookNotify(msg); if l.dtLog <> 0 then begin AddWindowLog(@l); case msg.WParamLo of WND_STATE_ACTIVATE : OutLog('(%s:%d) Ȱ¼ºÈ­ - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); WND_STATE_WINDOW_MIN : OutLog('(%s:%d) ÃÖ¼ÒÈ­ - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); WND_STATE_WINDOW_MAX : OutLog('(%s:%d) ÃÖ´ëÈ­ - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); WND_STATE_WINDOW_MOVESIZE : OutLog('(%s:%d) À̵¿ / Å©±âÁ¶Àý - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); WND_STATE_REDRAW_TITLE : OutLog('(%s:%d) ĸ¼Çº¯°æ - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); WND_STATE_CREATE_MAIN : OutLog('(%s:%d) ¸ÞÀÎÆû »ý¼º - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); WND_STATE_DESTROY_MAIN : OutLog('(%s:%d) ¸ÞÀÎÆû Á¾·á - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); WND_STATE_WINDOW_NORMAL : OutLog('(%s:%d) ÃÖ¼Ò / ÃÖ´ëÈ­ º¹±Í - %s', [l.OwnerWindow.ModuleName, l.OwnerWindow.PID, l.sTitle]); end; end else if msg.WParamLo = WND_STATE_ATTACH_HOOK then AddWindowEntry(WndHook_[msg.WParamHi]); end; end.