-1

I am using this procedure to execute a Comandline. I have found this piece of code online and it works fine, except a few details. I have read in some forums not to use ProcessMessages and put it into a thread.

When I remove the Application.ProcessMessages line, then it stops working. Then if I keep it, while it's executing I get "Not responding". Could you help me in using a thread in this case?

procedure ExecAndWait(const CommandLine: string);
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  FillChar(StartupInfo, SizeOf(StartupInfo), 0);
  StartupInfo.cb := SizeOf(TStartupInfo);
  StartupInfo.wShowWindow := SW_HIDE;
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;

  //UniqueString(CommandLine);

  if CreateProcess(nil, PChar(CommandLine), nil, nil, False,
    0, nil, nil, StartupInfo, ProcessInfo) then
  begin
    while WaitForSingleObject(ProcessInfo.hProcess, 10) > 0 do
    Application.ProcessMessages;
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end
  else
    RaiseLastOSError;
end;
end.

procedure BuildThread;
var
  myThread: TThread;

begin
  // Create an anonymous thread that calls a method and passes in
  // the fetchURL to that method.
  myThread := TThread.CreateAnonymousThread(
    procedure
    begin
      ExecAndWait();
    end);
end;

I added this:

procedure RunThread(const CommandLine: string);
var
  myThread: TThread;        
begin
  myThread := TThread.CreateAnonymousThread(
    procedure
    begin
      ExecAndWait(CommandLine);
    end). Start;
end;
Zen AM
  • 31
  • 1
  • 6
  • What is your question? – David Heffernan Oct 15 '17 at 16:23
  • @DavidHeffernan, basically I want to know how to prevent the "Not responding" from showing. I read online and they suggest to put the CreateProcess into a thread – Zen AM Oct 15 '17 at 16:30
  • Create a thread to wait on the process handle. When it is signaled, notify the UI thread. – David Heffernan Oct 15 '17 at 16:31
  • Since you haven't created working threads yet, start with simple examples from help, call your `ExecAndWait` from `Execute` method (or anonymous thread function). Seems you don't need to interact with GUI, so synchronization stuff is not needed – MBo Oct 15 '17 at 17:03
  • @MBo thank you for your answer. Delphi is not my language of choice, I have a small project I need to work in it because otherwise I would never dive into it. Since I have never used threads, and if you are a beginner like me, if you try to google you will see there is no simple example. I know what I am trying to do is fairly simple, but I just don't know how to insert a thread. I am not asking for you to do what I need, I would only appreciate if you could show me a simple example. – Zen AM Oct 15 '17 at 17:19
  • @MBo , This is what I am trying to go I think: https://stackoverflow.com/questions/4190872/which-is-the-correct-way-to-wait-for-a-thread-finalization-and-keep-my-applicati – Zen AM Oct 15 '17 at 17:20
  • https://stackoverflow.com/questions/34890222/createanonymousthread-with-parameters – MBo Oct 15 '17 at 17:28
  • @MBo thanks, I modified the question. I tried it but it doesn't solve my issue. While it runs (because the procedure may take a few minutes) I get "Not responding" message. – Zen AM Oct 15 '17 at 17:37
  • You are creating an anonymous thread, but forgot to start it. – LU RD Oct 15 '17 at 17:52
  • @MBo I debugged it and the ExecAndWait never gets executed..., am I missing something? – Zen AM Oct 15 '17 at 17:54
  • @LURD some help on starting it? – Zen AM Oct 15 '17 at 17:55
  • 1
    `myThread.Start;` – LU RD Oct 15 '17 at 17:55
  • @LURD should there also be a myThread.End? – Zen AM Oct 15 '17 at 17:57
  • No, The thread will self destruct when ready. Please slow down and read up on documentation and some examples. – LU RD Oct 15 '17 at 17:58
  • @LURD CANNOT CALL START ON A RUNNING OR SUSPENDED THREAD – Zen AM Oct 15 '17 at 17:59
  • An anonymous thread should not be reporting this error. You also shouldn't keep a reference to an anonymous thread - that's why it's anonymous. You should close up the call to `CreateAnonymousThread` with `.Start;` `TThread.CreateAnonymousThread( ... ).Start;` You also should not be using `Application.ProcessMessages`, *especially* from within the thread. That breaks the VCL thread safety, or lack thereof rather. – Jerry Dodge Oct 15 '17 at 18:30
  • @JerryDodge thank you. If I do that, then I get the error message: Incompatible types: 'TThread' and 'procedure, untyped pointer or untyped parameter' – Zen AM Oct 15 '17 at 18:37
  • That sounds very bizarre - I don't know what to say without seeing your new code. `TThread.CreateAnonymousThread( procedure begin ExecAndWait(CommandLine); end).Start;` That should be *all* the code you need. Don't declare a variable for the thread, don't read the result of `CreateAnonymousThread`, there's no need to. – Jerry Dodge Oct 15 '17 at 18:38
  • If I may, what's the reason to wait for a process inside of a thread when you're not doing anything once it's finished? Do you really need to wait for the process to end? If so, your thread will need some sort of callback to capture the completion. If not, then why wait for it at all in the first place? A simple `ShellExecuteEx` should be all you need in that case. – Jerry Dodge Oct 15 '17 at 19:19
  • @JerryDodge When I do ShellExecute the cmd window opens up, so this is a nice way to not display it. Anyways I do need to wait for it to end because it's a process that can take up to 10 min – Zen AM Oct 15 '17 at 19:23
  • Then use `SW_HIDE`... – Jerry Dodge Oct 15 '17 at 19:24
  • @JerryDodge well, i keep getting the Not responding window and that's really boring, if I switch to ShellExecute.. – Zen AM Oct 15 '17 at 19:35
  • Then I highly suggest opening up another question on that subject. If you really don't need to wait for it to finish, then everything you're doing here is a big waste. – Jerry Dodge Oct 15 '17 at 19:36
  • Frankly that was my main concern, to avoid the Not responding – Zen AM Oct 15 '17 at 19:40
  • You shouldn't get Not Responding, when it's done correctly. Most of the time, applications are launched blindly without waiting for completion. It's not every day that one wishes to wait for a process to end. – Jerry Dodge Oct 15 '17 at 20:58

1 Answers1

0

Anonymous threads are not meant to be referenced. You should not attempt to keep a local variable referencing your thread. Instead, you should directly call Start in-line with the call to CreateAnonymousThread...

procedure RunThread(const CommandLine: string);       
begin
  TThread.CreateAnonymousThread(
    procedure
    begin
      ExecAndWait(CommandLine);
    end).Start;
end;

In addition, you should not be using Application.ProcessMessages, especially from within a thread. Application is part of the VCL, which is not thread-safe, and thus breaks the VCL multi-threading safety rules. And even if that weren't the case, it's still useless since it's only intended for the main UI thread.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327