BSOne.SFC/Tocsg.Lib/VCL/Tocsg.WndUtil.pas

441 lines
11 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.WndUtil }
{ }
{ Copyright (C) 2022 kku }
{ }
{*******************************************************}
unit Tocsg.WndUtil;
interface
uses
System.SysUtils, Winapi.Windows, Winapi.Messages, Vcl.Forms, Tocsg.Thread,
System.Generics.Collections;
type
TEvActiveWndNotify = procedure(aSender: TObject; hActiveWnd: HWND) of object;
TThdActiveWndMon = class(TTgThread)
protected
bSync_: Boolean;
evNotify_: TEvActiveWndNotify;
hActiveWnd_: HWND;
procedure ProcessNotify;
procedure Execute; override;
public
Constructor Create(bSync: Boolean = true);
property OnActiveWndNotify: TEvActiveWndNotify write evNotify_;
end;
const
// GWL_STYLE
WS_VAL: array [0..19] of DWORD = (
WS_OVERLAPPED, WS_POPUP, WS_CHILD, WS_MINIMIZE, WS_VISIBLE, WS_DISABLED,
WS_CLIPSIBLINGS, WS_CLIPCHILDREN, WS_MAXIMIZE, WS_CAPTION, WS_BORDER,
WS_DLGFRAME, WS_VSCROLL, WS_HSCROLL, WS_SYSMENU, WS_THICKFRAME, WS_GROUP,
WS_TABSTOP, WS_MINIMIZEBOX, WS_MAXIMIZEBOX
);
WS_STR: array [0..19] of String = (
'WS_OVERLAPPED', 'WS_POPUP', 'WS_CHILD', 'WS_MINIMIZE', 'WS_VISIBLE', 'WS_DISABLED',
'WS_CLIPSIBLINGS', 'WS_CLIPCHILDREN', 'WS_MAXIMIZE', 'WS_CAPTION', 'WS_BORDER',
'WS_DLGFRAME', 'WS_VSCROLL', 'WS_HSCROLL', 'WS_SYSMENU', 'WS_THICKFRAME', 'WS_GROUP',
'WS_TABSTOP', 'WS_MINIMIZEBOX', 'WS_MAXIMIZEBOX'
);
// GWL_EXSTYLE
WS_EX_VAL: array [0..23] of LongInt = (
WS_EX_DLGMODALFRAME, WS_EX_NOPARENTNOTIFY, WS_EX_TOPMOST, WS_EX_ACCEPTFILES,
WS_EX_TRANSPARENT, WS_EX_MDICHILD, WS_EX_TOOLWINDOW, WS_EX_WINDOWEDGE,
WS_EX_CLIENTEDGE, WS_EX_CONTEXTHELP, WS_EX_RIGHT, WS_EX_LEFT, WS_EX_RTLREADING,
WS_EX_LTRREADING, WS_EX_LEFTSCROLLBAR, WS_EX_RIGHTSCROLLBAR, WS_EX_CONTROLPARENT,
WS_EX_STATICEDGE, WS_EX_APPWINDOW, WS_EX_LAYERED, WS_EX_NOINHERITLAYOUT,
WS_EX_LAYOUTRTL, WS_EX_COMPOSITED, WS_EX_NOACTIVATE
);
WS_EX_STR: array [0..23] of String = (
'WS_EX_DLGMODALFRAME', 'WS_EX_NOPARENTNOTIFY', 'WS_EX_TOPMOST', 'WS_EX_ACCEPTFILES',
'WS_EX_TRANSPARENT', 'WS_EX_MDICHILD', 'WS_EX_TOOLWINDOW', 'WS_EX_WINDOWEDGE',
'WS_EX_CLIENTEDGE', 'WS_EX_CONTEXTHELP', 'WS_EX_RIGHT', 'WS_EX_LEFT', 'WS_EX_RTLREADING',
'WS_EX_LTRREADING', 'WS_EX_LEFTSCROLLBAR', 'WS_EX_RIGHTSCROLLBAR', 'WS_EX_CONTROLPARENT',
'WS_EX_STATICEDGE', 'WS_EX_APPWINDOW', 'WS_EX_LAYERED', 'WS_EX_NOINHERITLAYOUT',
'WS_EX_LAYOUTRTL', 'WS_EX_COMPOSITED', 'WS_EX_NOACTIVATE'
);
function GetWindowStyle(h: HWND): NativeInt;
function GetWindowStyleStr(h: HWND): String;
function GetWindowExStyle(h: HWND): NativeInt;
function GetWindowExStyleStr(h: HWND): String;
function GetWindowCaption(h: HWND): String;
function GetEditText(h: HWND): String;
procedure SetEditText(h: HWND; const sText: String);
function GetTopParentHWND(h: HWND): HWND;
function GetWndChildClass(h: HWND; const sClassName: String; hNextChild: HWND = 0): HWND;
function GetWndChildByCaption(h: HWND; const sFindCaption: String; hNextChild: HWND = 0): HWND;
function GetWndClassName(h: HWND): String;
function HasWndChild(hParent, hSrc: HWND): Boolean;
function GetProgressMax(h: HWND): Integer; // for "msctls_progress32" class
function GetProgressPos(h: HWND): Integer; // for "msctls_progress32" class
procedure SetScreenCenterForm(aForm: TForm);
function FindWindowFromProcessName(sPName: String; bIncInvisible: Boolean = false): HWND;
function SendData(h: HWND; dwCmd: DWORD; const sData: String): LONGLONG;
implementation
uses
Tocsg.Trace, Tocsg.Exception, Tocsg.Process;
{ TThdActiveWndMon }
Constructor TThdActiveWndMon.Create(bSync: Boolean = true);
begin
Inherited Create;
bSync_ := bSync;
hActiveWnd_ := 0;
@evNotify_ := nil;
end;
procedure TThdActiveWndMon.ProcessNotify;
begin
if Assigned(evNotify_) and (hActiveWnd_ <> 0) then
evNotify_(Self, hActiveWnd_);
end;
procedure TThdActiveWndMon.Execute;
var
h, hRecentWnd: HWND;
begin
hRecentWnd := 0;
while not Terminated and not GetWorkStop do
begin
h := GetForegroundWindow;
if h <> hRecentWnd then
begin
hRecentWnd := h;
if bSync_ then
begin
hActiveWnd_ := h;
Synchronize(ProcessNotify);
end else
if Assigned(evNotify_) then
evNotify_(Self, h);
end;
Sleep(500);
end;
end;
{ Other }
// GWL_STYLE
function GetWindowStyle(h: HWND): NativeInt;
begin
Result := 0;
if h <> 0 then
Result := GetWindowLong(h, GWL_STYLE);
end;
function GetWindowStyleStr(h: HWND): String;
var
i: Integer;
wStyle: LongInt;
begin
Result := '';
wStyle := GetWindowStyle(h);
if wStyle <> 0 then
for i := 0 to Length(WS_VAL) - 1 do
if (WS_VAL[i] and wStyle) = WS_VAL[i] then
begin
if Length(Result) > 0 then
Result := Result + '|' + WS_STR[i]
else
Result := WS_STR[i];
end;
end;
// GWL_EXSTYLE
function GetWindowExStyle(h: HWND): NativeInt;
begin
Result := 0;
if h <> 0 then
Result := GetWindowLong(h, GWL_EXSTYLE);
end;
function GetWindowExStyleStr(h: HWND): String;
var
i: Integer;
wExStyle: LongInt;
begin
Result := '';
wExStyle := GetWindowExStyle(h);
if wExStyle <> 0 then
begin
for i := 0 to Length(WS_EX_VAL) - 1 do
begin
if (WS_EX_VAL[i] and wExStyle) = WS_EX_VAL[i] then
begin
if Length(Result) > 0 then
Result := Result + '|' + WS_EX_STR[i]
else
Result := WS_EX_STR[i];
end;
end;
end;
end;
function GetWindowCaption(h: HWND): String;
var
nLen: Integer;
str: array of Char;
begin
Result := '';
nLen := GetWindowTextLength(h);
if nLen > 0 then
begin
Inc(nLen); // GetWindowText()에서 1 더해줘야 정상 처리 되는 경우 있음 24_1209 13:39:07 kku
SetLength(str, nLen);
ZeroMemory(@str[0], nLen * 2);
if GetWindowText(h, @str[0], nLen) <> 0 then
Result := String(PChar(@str[0]));
end;
end;
function GetEditText(h: HWND): String;
var
str: array of Char;
dwResult: DWORD;
begin
Result := '';
SetLength(str, 1024);
try
// SendMessageTimeout(h, WM_GETTEXT, 1024, LPARAM(str), SMTO_NORMAL, 3000, @dwResult);
SendMessage(h, WM_GETTEXT, 1024, LPARAM(str));
Result := PChar(@str[0]);
finally
SetLength(str, 0);
end;
end;
procedure SetEditText(h: HWND; const sText: String);
begin
SendMessage(h, WM_SETTEXT, 0, LPARAM(@sText[1]));
end;
function GetTopParentHWND(h: HWND): HWND;
begin
Result := h;
while Result <> 0 do
begin
if GetParent(Result) = 0 then
exit;
Result:= GetParent(Result);
end;
end;
function GetWndChildClass(h: HWND; const sClassName: String; hNextChild: HWND = 0): HWND;
var
hChild: HWND;
arrClassName: array [0..255] of Char;
begin
Result := 0;
hChild := GetWindow(h, GW_CHILD);
if hChild <> 0 then
begin
if hNextChild <> 0 then
hChild := GetWindow(hNextChild, GW_HWNDNEXT);
while hChild <> 0 do
begin
if GetClassName(hChild, arrClassName, SizeOf(arrClassName)) > 0 then
begin
if CompareMem(@arrClassName[0], @sClassName[1], Length(sClassName)) then
begin
Result := hChild;
exit;
end;
end;
if GetWindow(hChild, GW_CHILD) <> 0 then
begin
Result := GetWndChildClass(hChild, sClassName);
if Result <> 0 then
exit;
end;
hChild := GetWindow(hChild, GW_HWNDNEXT);
end;
end;
end;
function GetWndChildByCaption(h: HWND; const sFindCaption: String; hNextChild: HWND = 0): HWND;
var
hChild: HWND;
sCaption: String;
begin
Result := 0;
hChild := GetWindow(h, GW_CHILD);
if hChild <> 0 then
begin
if hNextChild <> 0 then
while hNextChild <> hChild do
hChild := GetWindow(hChild, GW_HWNDNEXT);
while hChild <> 0 do
begin
sCaption := GetWindowCaption(hChild);
if sCaption = sFindCaption then
begin
Result := hChild;
exit;
end;
if GetWindow(hChild, GW_CHILD) <> 0 then
begin
Result := GetWndChildClass(hChild, sFindCaption);
if Result <> 0 then
exit;
end;
hChild := GetWindow(hChild, GW_HWNDNEXT);
end;
end;
end;
function GetWndClassName(h: HWND): String;
var
arrClassName: array [0..255] of Char;
begin
Result := '';
if h <> 0 then
if GetClassName(h, arrClassName, SizeOf(arrClassName)) > 0 then
Result := String(arrClassName);
end;
function HasWndChild(hParent, hSrc: HWND): Boolean;
var
hChild: HWND;
begin
Result := false;
hChild := GetWindow(hParent, GW_CHILD);
while hChild <> 0 do
begin
if hChild = hSrc then
begin
Result := true;
exit;
end;
if HasWndChild(hChild, hSrc) then
begin
Result := true;
exit;
end;
hChild := GetWindow(hChild, GW_HWNDNEXT);
end;
end;
function GetProgressMax(h: HWND): Integer; // for "msctls_progress32" class
const
PBM_GETRANGE = $0407;
begin
Result := SendMessage(h, PBM_GETRANGE, 0, 0);
end;
function GetProgressPos(h: HWND): Integer; // for "msctls_progress32" class
const
PBM_GETPOS = $0408;
begin
Result := SendMessage(h, PBM_GETPOS, 0, 0);
end;
procedure SetScreenCenterForm(aForm: TForm);
var
i: Integer;
begin
for i := 0 to Screen.MonitorCount - 1 do
if Screen.Monitors[i].Primary then
begin
if (Screen.Monitors[i].Width > aForm.Width) and
(Screen.Monitors[i].Height > aForm.Height) then
begin
aForm.Left := (Screen.Monitors[i].Width div 2) - (aForm.Width div 2);
aForm.Top := (Screen.Monitors[i].Height div 2) - (aForm.Height div 2);
end else begin
aForm.Left := 0;
aForm.Top := 0;
end;
break;
end;
end;
function FindWindowFromProcessName(sPName: String; bIncInvisible: Boolean = false): HWND;
var
h: HWND;
llStyle: LONGLONG;
sTemp: String;
begin
Result := 0;
try
h := FindWindow(nil, nil);
while h <> 0 do
begin
llStyle := GetWindowStyle(h);
if bIncInvisible or
( ((llStyle and WS_VISIBLE) <> 0) and
((llStyle and WS_MINIMIZE) = 0) ) then
begin
sTemp := GetWindowCaption(h);
if sTemp <> '' then
begin
sTemp := GetProcessNameFromWndHandle(h);
if sTemp = sPName then
begin
Result := h;
exit;
end;
end;
end;
h := GetWindow(h, GW_HWNDNEXT);
end;
except
on E: ETgException do
ETgException.TraceException(E, 'Fail .. FindWindowFromProcessName()');
end;
end;
function SendData(h: HWND; dwCmd: DWORD; const sData: String): LONGLONG;
var
CopyData: TCopyDataStruct;
begin
CopyData.dwData := dwCmd;
CopyData.cbData := (Length(sData) + 1) * 2;
CopyData.lpData := PChar(sData);
Result := SendMessage(h, WM_COPYDATA, 0, NativeInt(@CopyData));
Application.ProcessMessages;
end;
end.