Search code examples
delphicreateprocessdisabled-input

CreateProcess , WaitForSingleObject , Disable Input on Calling Application


I'm calling another Program which only displays a webpage like this :

Problem : if I Create a Process with the Button , and while the Created Process is open , I click on a Checkbox on the calling form , I close the created process the Checkbox is checked .

I tried to use the DisableTaskWindows(0) as seen in the .ShowModal function . But it does not work as I expected. While it does disable the form . But after I enable it , it seems like the form processes the click event anyway. Kinda like if it has a message queue or something .

Can anyone tell me what is it that I am doing wrong here?

procedure TForm1.Button1Click(Sender: TObject);
var
  StartupInfo : TStartupInfo;
  ProcessInfo : TProcessInformation;
  ProcessCreated : Boolean;
  CommandLine : string;
  WindowList: TTaskWindowList;
begin
  WindowList := DisableTaskWindows(0);
  CommandLine:='webmodule.exe';
  uniqueString(CommandLine);
  ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
  StartupInfo.cb := SizeOf(StartupInfo);
  ProcessCreated := CreateProcess(PChar(nil), PChar(CommandLine), nil, nil, false, 0, nil, nil, StartupInfo, ProcessInfo);
  if ProcessCreated then
    WaitForSingleObject(ProcessInfo.hProcess, INFINITE)
  else
    ShowMessage('Error : could not execute!');
  CloseHandle(ProcessInfo.hProcess);
  CloseHandle(ProcessInfo.hThread);
  EnableTaskWindows(WindowList);
end;

UPDATE

unfortunately I am not sure how one uses the RegisterWaitForSingleObject function ... I tried this , but is not working . I am missing the CallBack maybe ? But I have no idea how to use it.

  if ProcessCreated then
  begin
//    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    while (RegisterWaitForSingleObject(ProcessInfo.hProcess,ProcessInfo.hProcess,nil,nil,INFINITE,0) = false) do
    begin
      Form1.Color:=RGB(random(255),random(255),random(255));
      Application.ProcessMessages;
    end;

    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end
  else
    ShowMessage('Error : could not execute!');

UPDATE 2 :

I think I might have solved it , I removed the Enable Disable for the Form . Instead I do this after I executed the Process .

  while PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE or PM_NOYIELD) do;
  while PeekMessage(Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE or PM_NOYIELD) do;

Solution

  • The problem is that you are blocking your app's main message loop while waiting for the spawned process to exit, so you are not allowing your app to process user input until after that process ends. You need to let your app process messages normally, don't block them. If you disable your Form while the spawned process is running, user input will automatically be discarded for you.

    Try something more like this:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      StartupInfo : TStartupInfo;
      ProcessInfo : TProcessInformation;
      CommandLine : string;
    begin
      CommandLine := 'webmodule.exe';
      UniqueString(CommandLine);
      ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
      StartupInfo.cb := SizeOf(StartupInfo);
      if not CreateProcess(PChar(nil), PChar(CommandLine), nil, nil, FALSE, 0, nil, nil, StartupInfo, ProcessInfo) then
      begin
        ShowMessage('Error : could not execute!');
        Exit;
      end;
      CloseHandle(ProcessInfo.hThread);
      Enabled := False;
      repeat
        case MsgWaitForMultipleObjects(1, ProcessInfo.hProcess, FALSE, INFINITE, QS_ALLINPUT) of
          WAIT_OBJECT_0: Break;
          WAIT_OBJECT_0+1: Application.ProcessMessages;
        else
          begin
            ShowMessage('Error : could not wait!');
            Break;
          end;
        end;
      until False;
      CloseHandle(ProcessInfo.hProcess);
      Enabled := True;
    end;
    

    Or this:

    type
      TForm1 = class(ToFrm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        ...
      private
        hWaitObj, hWaitProcess: THandle;
        procedure WaitFinished;
        ...
      end;
    
    ... 
    
    procedure WaitCallback(lpParameter: Pointer; WaitFired: Boolean); stdcall;
    begin
      TThread.Queue(nil, TForm1(lpParameter).WaitFinished);
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      StartupInfo : TStartupInfo;
      ProcessInfo : TProcessInformation;
      CommandLine : string;
    begin
      CommandLine := 'webmodule.exe';
      UniqueString(CommandLine);
      ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
      StartupInfo.cb := SizeOf(StartupInfo);
      if not CreateProcess(PChar(nil), PChar(CommandLine), nil, nil, FALSE, 0, nil, nil, StartupInfo, ProcessInfo) then
      begin
        ShowMessage('Error : could not execute!');
        Exit;
      end;
      CloseHandle(ProcessInfo.hThread);
      if not RegisterWaitForSingleObject(hWaitObj, ProcessInfo.hProcess, WaitCallback, Self, INFINITE, WT_EXECUTELONGFUNCTION or WT_EXECUTEONLYONCE) then
      begin
        CloseHandle(ProcessInfo.hProcess);
        ShowMessage('Error : could not wait!');
        Exit;
      end;
      hWaitProcess := ProcessInfo.hProcess;
      Enabled := False;
    end;
    
    procedure TForm1.WaitFinished;
    begin
      UnregisterWait(hWaitObj);
      CloseHandle(hWaitProcess);
      Enabled := True;
    end;