program Xe3Preloader;

{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}
{$WEAKLINKRTTI ON}

// {$DEFINE VERBOSE}

{$IFDEF VERBOSE}
{$APPTYPE CONSOLE}
{$ELSE}
{$R *.res}
{$ENDIF}

uses
  Winapi.Windows,
  Winapi.Messages,
  System.Classes,
  System.SysUtils,
  System.DateUtils,
  uEnumWindowList in 'uEnumWindowList.pas',
  uRegistoryUtils in 'uRegistoryUtils.pas';

function GetLastErrorMsg: String;
begin
  Result := SysErrorMessage(GetLastError);
end;

procedure WriteLineString(const s: string);
begin
  {$IFDEF VERBOSE}
  Writeln(s);
  {$ELSE}
  OutputDebugString(PWideChar(s));
  {$ENDIF}
end;

procedure Preload;
const
  MAX_WAIT_SECOND = 60 * 5;
  DESKTOP_NAME = 'delphi_desktop';
var
  Path: String;
  Start: TDateTime;
  SI: TStartUpInfo;
  PI: TProcessInformation;
  List: TList;
  i: Integer;
  ProcessId: DWORD;
  EnumWnd: TEnumWindow;
  Posted: Boolean;
  NewDesktop: THandle;
  AccessFlag: DWORD;
  OrgDesktop: DWORD;
begin
  Path :=
    GetRegValue2(HKEY_CURRENT_USER, '\Software\Embarcadero\BDS\9.0\App');

  WriteLineString('');
  WriteLineString('///// XE3 Preloader /////');
  WriteLineString('');
  WriteLineString('target: ' + Path);
  WriteLineString('');

  AccessFlag :=
    DESKTOP_CREATEMENU
    or DESKTOP_CREATEWINDOW
    or DESKTOP_ENUMERATE
    or DESKTOP_HOOKCONTROL
    or DESKTOP_JOURNALPLAYBACK
    or DESKTOP_JOURNALRECORD
    or DESKTOP_READOBJECTS
    or DESKTOP_SWITCHDESKTOP
    or DESKTOP_WRITEOBJECTS;

  OrgDesktop := GetThreadDesktop(GetCurrentThreadId);

  NewDesktop :=
    CreateDesktop(
      DESKTOP_NAME,
      nil,
      nil,
      DF_ALLOWOTHERACCOUNTHOOK,
      AccessFlag,
      nil);
  try
    if (NewDesktop = 0) then begin
      WriteLineString('create desktop, failed ' + GetLastErrorMsg);
      Exit;
    end
    else
      WriteLineString('create desktop.');

    try
      SetThreadDesktop(NewDesktop);

      if (FileExists(Path)) then begin
        ZeroMemory(@SI, SizeOf(SI));
        with SI do begin
          cb := SizeOf(SI);
          lpDesktop := DESKTOP_NAME;
          dwFlags := STARTF_USESHOWWINDOW;
          wShowWindow := SW_HIDE;
        end;

        if (not CreateProcess(
          nil,
          PChar(Path),
          nil,
          nil,
          True,
          0,
          nil,
          nil,
          SI,
          PI))
        then
          Exit;

        with PI do begin
          Posted := False;
          Start := Now;

          while (SecondsBetween(Now, Start) < MAX_WAIT_SECOND) do begin
            List := TList.Create;
            try
              GetDesktopWindowList(List, NewDesktop);
              try
                for i := 0 to List.Count - 1 do begin
                  EnumWnd := List[i];

                  if (EnumWnd.Visible) then begin
                    GetWindowThreadProcessId(EnumWnd.Handle, ProcessId);

                    if (ProcessId = dwProcessId) then begin
                      if (EnumWnd.Name = 'TAppBuilder') then begin
                        if (PostMessage(EnumWnd.Handle, WM_QUIT, 0, 0)) then
                          WriteLineString('post quit: ' + EnumWnd.ToString)
                        else
                          WriteLineString('post failed:  ' + GetLastErrorMsg);

                        Posted := True;
                      end
                      else begin
                        WriteLineString('found: ' + EnumWnd.ToString);
                        ShowWindow(EnumWnd.Handle, SW_HIDE);
                      end;

                      Break;
                    end;
                  end;
                end;
              finally
                FreeWindowList(List);
              end;
            finally
              List.Free;
            end;

            if (Posted) then
              Break;

            Sleep(100);
          end;

          WriteLineString('target check living.');

          Start := Now;
          while
            (SecondsBetween(Now, Start) < MAX_WAIT_SECOND) and
            (WaitForSingleObject(hProcess, 100) = WAIT_TIMEOUT)
          do
            Sleep(100);

          WriteLineString('target dead.');

          CloseHandle(hProcess);
          CloseHandle(hThread);
        end;
      end
      else
        WriteLineString('target not found.');
    finally
      SetThreadDesktop(OrgDesktop);
    end;
  finally
    if (CloseDesktop(NewDesktop)) then
      WriteLineString('close desktop.')
    else
      WriteLineString('close desktop, failed ' + GetLastErrorMsg);
  end;

  WriteLineString('done.');
end;

begin
  try
    Preload;
  except
    on E: Exception do
      WriteLineString(E.ClassName + ': ' + E.Message);
  end;
end.
