BSOne.SFC/eCrmHE/DLL_eCrmHeHelper/WindowFinderThread.pas

242 lines
6.4 KiB
Plaintext

unit WindowFinderThread;
interface
uses
System.Classes, System.SysUtils, Winapi.Windows, System.Generics.Collections;
type
TWindowFinderThread = class(TThread)
private
FTargetPID: DWORD;
FIsBlocked: Boolean;
CapBlockWndList_: TList<HWND>;
sProcessName_: string;
procedure ApplyCaptureBlock(h: HWND); // 차단 로직 (구현 필요)
protected
procedure Execute; override;
public
constructor Create(TargetPID: DWORD; sProcessName: string);
destructor Destroy; override;
procedure ReleaseCaptureBlock(); // 해제 로직 (구현 필요)
function GetTopWindowHwndListByPID_Enum(TargetPID: DWORD; HwndList: TList<HWND>): Boolean;
end;
implementation
{ TWindowFinderThread 구현 }
constructor TWindowFinderThread.Create(TargetPID: DWORD; sProcessName: string);
begin
inherited Create(True);
FTargetPID := TargetPID;
FIsBlocked := False;
FreeOnTerminate := False;
sProcessName_:= sProcessName;
CapBlockWndList_ := TList<HWND>.Create;
Resume;
end;
destructor TWindowFinderThread.Destroy;
begin
// 스레드가 파괴될 때 차단 해제 로직이 필요하다면 여기서 수행
// (하지만 보통 Stop 로직에서 명시적으로 부르는 게 더 안전함)
FreeAndNil(CapBlockWndList_);
inherited;
end;
type
// EnumWindowsProc에 넘겨줄 데이터 구조체
PEnumWindowParams = ^TEnumWindowParams;
TEnumWindowParams = record
TargetPID: DWORD;
List: TList<HWND>;
end;
function EnumWindowsProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
pParams: PEnumWindowParams;
dwWndPID: DWORD;
wp: TWindowPlacement;
begin
Result := True; // True를 반환해야 다음 윈도우를 계속 검색합니다.
// lParam을 우리가 정의한 구조체 포인터로 변환
pParams := PEnumWindowParams(lParam);
// 1. PID 검사
GetWindowThreadProcessId(hwnd, dwWndPID);
if dwWndPID = pParams.TargetPID then
begin
// 2. 부모 윈도우가 없는 최상위 윈도우인지 검사
if GetParent(hwnd) = 0 then
begin
// 3. 보이는 윈도우인지 검사
if IsWindowVisible(hwnd) then
begin
// 4. 최소화 상태 검사
wp.length := SizeOf(TWindowPlacement);
if GetWindowPlacement(hwnd, @wp) then
begin
if wp.showCmd <> SW_SHOWMINIMIZED then
begin
// 조건을 모두 만족하면 리스트에 추가
// EnumWindows는 Z-Order 상위(화면 앞)부터 순서대로 호출해줍니다.
if Assigned(pParams.List) then
begin
// C++ 코드(push_front)와 동일한 역순 저장을 원하면: pParams.List.Insert(0, hwnd);
// Z-Order 순서대로(Top -> Bottom) 저장을 원하면:
pParams.List.Add(hwnd);
end;
end;
end;
end;
end;
end;
end;
// -----------------------------------------------------------------------------
// 메인 함수: EnumWindows를 실행하는 래퍼 함수
// -----------------------------------------------------------------------------
function TWindowFinderThread.GetTopWindowHwndListByPID_Enum(TargetPID: DWORD; HwndList: TList<HWND>): Boolean;
var
Params: TEnumWindowParams;
begin
Result := False;
if HwndList = nil then Exit;
// 콜백 함수에 넘길 파라미터 세팅
Params.TargetPID := TargetPID;
Params.List := HwndList;
// EnumWindows 호출 (파라미터의 포인터를 lParam으로 전달)
if EnumWindows(@EnumWindowsProc, LPARAM(@Params)) then
begin
Result := True;
end;
end;
procedure TWindowFinderThread.Execute;
var
List: TList<HWND>;
h: HWND;
className: array[0..255] of Char;
Title: array[0..255] of Char;
begin
List := TList<HWND>.Create;
try
// 스레드가 종료(Terminated) 신호를 받을 때까지 무한 반복
while not Terminated do
begin
List.Clear;
// 앞서 만든 EnumWindows 기반 윈도우 검색 함수 호출
if GetTopWindowHwndListByPID_Enum(FTargetPID, List) then
begin
for h in List do
begin
GetClassName(h, className, Length(className));
GetWindowText(h, Title, Length(Title));
OutputDebugString(PChar(Format('[BSONE] TWindowFinderThread.Execute, h(%p), className(%s), Title(%s)', [Pointer(h), className, Title])));
if CompareText(sProcessName_, 'excel.exe') = 0 then
begin
if className = 'XLMAIN' then
begin
FIsBlocked := True;
end;
end
else if CompareText(sProcessName_, 'winword.exe') = 0 then
begin
if className = 'OpusApp' then
begin
FIsBlocked := True;
end;
end
else if CompareText(sProcessName_, 'powerpnt.exe') = 0 then
begin
if className = 'PPTFrameClass' then
begin
FIsBlocked := True;
end;
end
else if CompareText(sProcessName_, 'acrobat.exe') = 0 then
begin
if className = 'AcrobatSDIWindow' then
begin
FIsBlocked := True;
end;
end
else
begin
FIsBlocked := True;
end;
if FIsBlocked then
begin
OutputDebugString(PChar(Format('[BSONE] TWindowFinderThread.FIsBlocked, h(%p)', [Pointer(h)])));
ApplyCaptureBlock(h);
break;
end;
end;
end;
if FIsBlocked then Break;
// 0.5초 대기 (CPU 점유율 방지)
// Sleep을 쪼개서 하면 Terminated 반응 속도를 높일 수 있음
if Terminated and FIsBlocked then Break;
Sleep(500);
end;
finally
// 루프를 빠져나오면(Stop 요청 시) 차단 해제 수행
if FIsBlocked then
begin
// 필요하다면 마지막으로 검색해서 해제하거나,
// 저장해둔 핸들로 해제
// ReleaseCaptureBlock(...);
end;
List.Free;
end;
end;
procedure TWindowFinderThread.ApplyCaptureBlock(h: HWND);
begin
// [여기에 캡처 차단 API 호출 코드 작성]
if h = 0 then
exit;
if CapBlockWndList_.IndexOf(h) = -1 then
CapBlockWndList_.Add(h);
SetWindowDisplayAffinity(h, WDA_MONITOR);
OutputDebugString(PChar(Format('[BSONE] Capture Block Applying to HWND: %p', [Pointer(h)])));
end;
procedure TWindowFinderThread.ReleaseCaptureBlock();
var
i: Integer;
begin
// [여기에 캡처 차단 해제 API 호출 코드 작성]
try
if CapBlockWndList_.Count = 0 then
exit;
for i := 0 to CapBlockWndList_.Count - 1 do
begin
OutputDebugString(PChar(Format('[BSONE] Capture Releasing HWND: %p', [Pointer(CapBlockWndList_[i])])));
SetWindowDisplayAffinity(CapBlockWndList_[i], WDA_NONE);
end;
CapBlockWndList_.Clear;
except
// ..
end;
end;
end.