프로그래밍 초보 탈출

Libraries/Delphi Library

[System] Window Desktop Library

째즈토끼 2022. 6. 22. 22:54
unit LibDesktop;

interface

uses
  Windows, SysUtils, Messages, ShellApi;

const
  FEATURE_DEBUG_LOG	= FALSE;

function  WINDOW_STATION_FindVisibleStation(var Station: HWINSTA) : BOOL;
function  WINDOW_STATION_AttachToVisibleStation(var Attached: BOOL; var HomeStation: HWINSTA) : BOOL;
function  WINDOW_STATION_AttachToStation(Station: HWINSTA) : BOOL;

//	Window를 가지거나 Desktop Hooks 을 가진 쓰레드는 다른 데스크탑으로 Attach 될 수 없다.

function  DESKTOP_GetInputDesktopName(NameBuf : PChar; BufLen : Integer) : BOOL; // Current Active Desktop
function  DESKTOP_GetThreadDesktopName(NameBuf : PChar; BufLen : Integer; const ThreadID : DWORD = 0) : BOOL;
function  DESKTOP_CheckThreadDesktopIsInputDesktop(var Equal : BOOL; const ThreadID : DWORD = 0) : BOOL;
function  DESKTOP_AttachThreadToInputDesktop(const ThreadID : DWORD = 0) : BOOL;

function  DESKTOP_Select(Handle : HDESK) : HDESK; overload;
function  DESKTOP_Select(Name : PChar) : HDESK; overload;
procedure DESKTOP_ForceCtrlAltDel;

function  DESKTOP_AttachThreadToInputDesktopOfVisibleStation(const ThreadID : DWORD = 0) : BOOL;

function  DESKTOP_RegisterChangeNotification(WND: HWND; MessageID: DWORD) : THandle;
procedure DESKTOP_UnregisterChangeNotification(Handle: THandle);
function  DESKTOP_AttachThreadToInputDesktopOfVisibleStation(const ThreadID : DWORD = 0) : BOOL;
var
  Attached: BOOL;
  OldStation: HWINSTA;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := TRUE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_AttachThreadToInputDesktopOfVisibleStation');
{$IFEND}
  RESULT :=  WINDOW_STATION_AttachToVisibleStation(Attached, OldStation);
  if RESULT and Attached then CloseWindowStation(OldStation);
  if RESULT then RESULT := DESKTOP_AttachThreadToInputDesktop(ThreadID);
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_AttachThreadToInputDesktopOfVisibleStation', RESULT);
{$IFEND}
end;

function EnumWindowStationProc(WindowStationName: PChar; var WS: HWINSTA) : BOOL; stdcall;
var
  CWS   : HWINSTA;
  Flag  : USEROBJECTFLAGS;
  dummy : DWORD;
begin
  RESULT := TRUE; // Continue;
  CWS := OpenWindowStation(WindowStationName, FALSE, GENERIC_ALL);
  if CWS = 0 then Exit;
  if (not GetUserObjectInformation(CWS, UOI_FLAGS, @Flag, Sizeof(Flag), dummy)) or
     ((Flag.dwFlags and WSF_VISIBLE) <> WSF_VISIBLE) then begin
     CloseWindowStation(CWS);
     Exit;
     end;
  WS := CWS;
  RESULT := FALSE;
end;

function  WINDOW_STATION_FindVisibleStation(var Station: HWINSTA) : BOOL;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := FALSE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: WINDOW_STATION_FindVisibleStation');
{$IFEND}
  RESULT  :=  (Win32Platform = VER_PLATFORM_WIN32_NT);
  if RESULT then begin
     Station := 0;
     EnumWindowStations(@EnumWindowStationProc, DWORD(@Station));
     RESULT := Station <> 0;
     end;
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: WINDOW_STATION_FindVisibleStation', RESULT);
{$IFEND}
end;
function  WINDOW_STATION_AttachToVisibleStation(var Attached: BOOL; var HomeStation: HWINSTA) : BOOL;
var
  VWS : HWINSTA;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := TRUE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: WINDOW_STATION_AttachToVisibleStation');
{$IFEND}
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     Attached := FALSE;
     RESULT := TRUE;
     Exit;
     end;

  RESULT := WINDOW_STATION_FindVisibleStation(VWS);
  if not RESULT then Exit;

  HomeStation := GetProcessWindowStation();
  Attached := VWS <> HomeStation;
  if not Attached then Exit;

  RESULT := SetProcessWindowStation(VWS);
  if not RESULT then CloseWindowStation(VWS);
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: WINDOW_STATION_AttachToVisibleStation', RESULT);
{$IFEND}
end;

function  WINDOW_STATION_AttachToStation(Station: HWINSTA) : BOOL;
var
  CWS : HWINSTA;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := TRUE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: WINDOW_STATION_AttachToStation');
{$IFEND}
  CWS := GetProcessWindowStation();
  RESULT := (CWS = Station);
  if not RESULT then begin
     RESULT := SetProcessWindowStation(Station);
     if RESULT then CloseWindowStation(CWS);
     end;
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: WINDOW_STATION_AttachToStation', RESULT);
{$IFEND}
end;
function  DESKTOP_Select(Handle : HDESK) : HDESK; overload;
var
  Buf : Array[0..MAX_PATH] of Char;
  dummy : DWORD;
  Old : HDESK;
begin
  RESULT := 0;
  if Win32Platform <> VER_PLATFORM_WIN32_NT then Exit;

  Old := GetThreadDesktop(GetCurrentThreadId());
  if not GetUserObjectInformation(Handle, UOI_NAME, @(Buf[0]), MAX_PATH, dummy) then Exit;
  if not SetThreadDesktop(Handle) then Exit;
  RESULT := Old;
end;

function  DESKTOP_Select(Name : PChar) : HDESK; overload;
var
  DT : HDESK;
begin
  RESULT := 0;
  if Win32Platform <> VER_PLATFORM_WIN32_NT then Exit;

  if Assigned(Name)
     then DT := OpenDesktop(Name, 0, FALSE,
			    DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or
			    DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or
			    DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or
			    DESKTOP_SWITCHDESKTOP or GENERIC_WRITE)
     else DT := OpenInputDesktop(0, FALSE,
			    DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or
			    DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or
			    DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or
			    DESKTOP_SWITCHDESKTOP or GENERIC_WRITE);

  if DT = 0 then Exit;
  RESULT := DESKTOP_Select(DT);
end;

// 현재 상태가 XP의 새로운 로그온 화면이거나, 데스크탑이 전환중인 상태인 경우 OpenInputDesktop은 실패한다.
function DESKTOP_GetInputDesktopName(NameBuf : PChar; BufLen : Integer) : BOOL; // Current Active Desktop
var
  DT : HDESK;
  dummy : DWORD;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := FALSE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_GetInputDesktopName');
{$IFEND}
  DT := OpenInputDesktop(0, FALSE,
			    DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or
			    DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or
			    DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or
			    DESKTOP_SWITCHDESKTOP or GENERIC_WRITE);
  RESULT := DT <> 0;
  if RESULT then begin
     RESULT := GetUserObjectInformation(DT, UOI_NAME, NameBuf, BufLen, dummy);
     CloseDesktop(DT);
     end;
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_GetInputDesktopName', RESULT);
{$IFEND}
end;
function DESKTOP_GetThreadDesktopHandle(const ThreadID : DWORD = 0) : HDESK;
var
  ID : DWORD;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := 0;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_GetThreadDesktopHandle');
{$IFEND}

  if ThreadID = 0
     then ID := GetCurrentThreadID
     else ID := ThreadID;
  RESULT := GetThreadDesktop(ID);
  // 여기서 리턴되는 값은 CloseDesktop 할 필요없다.
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_GetThreadDesktopHandle', RESULT);
{$IFEND}
end;

function DESKTOP_GetThreadDesktopName(NameBuf : PChar; BufLen : Integer; const ThreadID : DWORD = 0) : BOOL; // Current Active Desktop
var
  DT : HDESK;
  dummy : DWORD;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := FALSE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_GetThreadDesktopName');
{$IFEND}

  DT := DESKTOP_GetThreadDesktopHandle(ThreadID);
  RESULT := (DT <> 0) and GetUserObjectInformation(DT, UOI_NAME, NameBuf, BufLen, dummy);
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_GetThreadDesktopName', RESULT);
{$IFEND}
end;

function DESKTOP_CheckThreadDesktopIsInputDesktop(var Equal : BOOL; const ThreadID : DWORD = 0) : BOOL;
var
  CDESK, IDESK : array[0..MAX_PATH-1] of Char;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := TRUE;
     Equal := TRUE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_CheckThreadDesktopIsInputDesktop');
{$IFEND}
  RESULT :=  DESKTOP_GetInputDesktopName(@IDESK[0], MAX_PATH)
	 and DESKTOP_GetThreadDesktopName(@CDESK[0], MAX_PATH, ThreadID);

  if RESULT then Equal := StrComp(CDESK, IDESK) = 0;
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_CheckThreadDesktopIsInputDesktop', RESULT);
{$IFEND}
end;
function DESKTOP_AttachThreadToInputDesktop(const ThreadID : DWORD = 0) : BOOL;
var
  CH, IH  : HDESK;
  CanSkip : BOOL;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := TRUE;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_AttachThreadToInputDesktop');
{$IFEND}

  RESULT := DESKTOP_CheckThreadDesktopIsInputDesktop(CanSkip, ThreadID);

  if RESULT and (not CanSkip) then begin
     IH := OpenInputDesktop(0, FALSE,
			       DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or
			       DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or
			       DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or
			       DESKTOP_SWITCHDESKTOP or GENERIC_WRITE);
     RESULT := IH <> 0;
     if RESULT then begin
	CH := DESKTOP_GetThreadDesktopHandle(ThreadID);
	RESULT := SetThreadDesktop(IH);
	if RESULT
	   then CloseDesktop(CH)		// old desktop handle
	   else CloseDesktop(IH);
	end;
	
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_AttachThreadToInputDesktop', RESULT);
{$IFEND}
end;

procedure DESKTOP_ForceCtrlAltDel_95;
begin

end;

procedure DESKTOP_ForceCtrlAltDel_NT;
var
  DT : HDESK;
  Buf : String;
begin
  DT := DESKTOP_Select('Winlogon');
  if DT<>0
      then PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT or MOD_CONTROL, VK_DELETE))
      else begin
           SetLength(Buf, MAX_PATH);
           SetLength(Buf, GetSystemDirectory(PChar(Buf), MAX_PATH));
           Buf := Buf + '\taskmgr.exe';
           if FileExists(Buf) then ShellExecute(0, 'open', PChar(Buf), nil, nil, SW_NORMAL);
	   SetLength(Buf, 0);
           end;
  if DT<>0 then DESKTOP_Select(DT);
end;

procedure DESKTOP_ForceCtrlAltDel;
begin
  if Win32Platform = VER_PLATFORM_WIN32_NT
     then DESKTOP_ForceCtrlAltDel_NT
     else DESKTOP_ForceCtrlAltDel_95;
end;
type
  PWaitNotifyStruct = ^TWaitNotifyStruct;
  TWaitNotifyStruct = packed record
    EventHandle	: THandle;
    Receiver	: HWND;
    MessageID	: DWORD;
    ThreadID	: DWORD;
    ThreadHandle: THandle;
    Terminating	: BOOL;
    end;

function __DESKTOP_RegisterChangeNotificationThread(Para : PWaitNotifyStruct) : DWORD; stdcall;
var
  R : DWORD;
begin
  while TRUE do begin
     R := WaitForSingleObject(Para^.EventHandle, 1000);
     if Para^.Terminating then Break;
     if R = WAIT_TIMEOUT then Continue;

     if R = WAIT_OBJECT_0 then begin
	PostMessage(Para^.Receiver, Para^.MessageID, 0, 0);
	Continue;
	end;
	
     Break;
     end;

  RESULT := 0;
end;

function  DESKTOP_RegisterChangeNotification(WND: HWND; MessageID: DWORD) : THandle;
var
  NS : PWaitNotifyStruct;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     RESULT := 0;
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_RegisterChangeNotification');
{$IFEND}
  RESULT := OpenEvent(SYNCHRONIZE, FALSE, 'WinSta0_DesktopSwitch');
  if RESULT = 0 then begin
{$IF FEATURE_DEBUG_LOG}
     TRACE('END: DESKTOP_RegisterChangeNotification', RESULT);
{$IFEND}
     Exit;
     end;

  GetMem(NS, SizeOf(TWaitNotifyStruct));
  NS^.EventHandle	:= RESULT;
  NS^.Receiver		:= WND;
  NS^.MessageID		:= MessageID;
  NS^.Terminating	:= FALSE;

  RESULT := CreateThread(nil, 1024, @__DESKTOP_RegisterChangeNotificationThread, NS, 0, NS^.ThreadID);
{$IF FEATURE_DEBUG_LOG}
  TRACE('---: DESKTOP_RegisterChangeNotification. ThreadHandle', RESULT);
{$IFEND}
  if RESULT = 0 then begin
     CloseHandle(NS^.EventHandle);
     FreeMem(NS);
     end
  else begin
     NS^.ThreadHandle := RESULT;
     RESULT := THandle(NS);
     end;
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_RegisterChangeNotification', RESULT);
{$IFEND}
end;
procedure DESKTOP_UnregisterChangeNotification(Handle: THandle);
var
  NS : PWaitNotifyStruct;
begin
  if Win32Platform <> VER_PLATFORM_WIN32_NT then begin
     Exit;
     end;

{$IF FEATURE_DEBUG_LOG}
  TRACE('BGN: DESKTOP_UnregisterChangeNotification', Handle);
{$IFEND}
  if Handle <> 0 then begin
     NS := Pointer(Handle);
     NS^.Terminating := TRUE;
     CloseHandle(NS^.EventHandle);
     if WaitForSingleObject(NS^.ThreadHandle, 3000) = WAIT_TIMEOUT then begin
{$IF FEATURE_DEBUG_LOG}
	TRACE('---: DESKTOP_UnregisterChangeNotification. Terminate Force!!', NS^.ThreadHandle);
{$IFEND}
	TerminateThread(NS^.ThreadHandle, 0);
	end;
     FreeMem(NS);
     end;
{$IF FEATURE_DEBUG_LOG}
  TRACE('END: DESKTOP_UnregisterChangeNotification', Handle);
{$IFEND}
end;

{$IF FEATURE_DEBUG_LOG}
initialization
  LOG_HANDLE := LOG_OPEN(ExtractFilePath(ParamStr(0)) + 'LibDesktop.LOG.TXT');
finalization
  LOG_CLOSE(LOG_HANDLE);
{$IFEND}

end.

'Libraries > Delphi Library' 카테고리의 다른 글

[WebBrowser] Cookie Library  (0) 2022.06.22
[WebBrowser] Internet Explorer Library  (0) 2022.06.22
[DateTime] Lunar Library  (0) 2022.06.22
[IPC] Critical Section Class  (0) 2022.06.22
[Math] Complex Library  (0) 2022.06.22