242 lines
6.4 KiB
Plaintext
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.
|