multithreadingdelphidlldelphi-2010tthread

Creating MainForm on a TThread


I have a Delphi 2010 application that exports a DLL and has the library header. It creates its MainForm in a TThread, like so:

var
  ActiveThread: TActive;
  
type
  TActive= class(TThread)
  protected
    procedure Execute; override;
  end;

procedure TActive.Execute;
begin
      Application.Initialize;
      Application.CreateForm(MyForm, form);
      Application.Run;
end;

begin
  ActiveThread := TActive.Create(true);
  ActiveThread.FreeOnTerminate := true;
  ActiveThread.Resume;
end.

Whenever I load this DLL through the LoadLibrary function, the application runs fine. (Apparently it uses the thread that I passed to LoadLibrary as the main thread and has no issues)

But if I attempt to export this DLL to an actual EXE, by changing the generated output in Options -> Application. and changing the header from library to program and then build it and execute the output EXE instead of loading the DLL through the windows api, the application hangs when attempting to create the form, specifically at Application.CreateForm(MyForm, form);. If I remove the Application initialization from the thread and place it on the main routine, it runs just fine.

The form I'm trying to render is just an empty form. Any ideas?


Solution

  • When compiling this code as a program, at runtime it will try to terminate itself when end. is reached, before the worker thread even has a chance to run, which could possibly (and likely) happen after the Application object has been destroyed. You would have to wait for the worker thread to finish its work before letting the program exit, eg:

    program MyProgram;
    
    uses
      Classes, Forms, MyForm;
    
    type
      TActive = class(TThread)
      protected
        procedure Execute; override;
      end;
    
    procedure TActive.Execute;
    begin
      Application.Initialize;
      Application.CreateForm(TMyForm, MyForm);
      Application.Run;
    end;
    
    var
      ActiveThread: TActive;
    begin
      ActiveThread := TActive.Create(False);
      ActiveThread.WaitFor;
      ActiveThread.Free;
    end.
    

    But, there is really no good reason to ever use a worker thread like this, this defeats the whole purpose of using a thread, so you may as well just get rid of it altogether:

    program MyProgram;
    
    uses
      Forms, MyForm;
    
    begin
      Application.Initialize;
      Application.CreateForm(TMyForm, MyForm);
      Application.Run;
    end.
    

    On the other hand, if you are trying to share common code between program and library projects, then you can wrap the Application code inside of a function and let the project decide which thread calls the function, eg:

    unit MyApp;
    
    interface
    
    procedure RunMyApp;
    
    implementation
    
    uses
      Forms, MyForm;
    
    procedure RunMyApp;
    begin
      Application.Initialize;
      Application.CreateForm(TMyForm, MyForm);
      Application.Run;
    end;
    
    end.
    
    program MyProgram;
    
    uses
      MyApp;
    
    begin
      RunMyApp;
    end.
    
    library MyLibrary
    
    uses
      Classes, MyApp;
      
    type
      TActive = class(TThread)
      protected
        procedure Execute; override;
      end;
    
    procedure TActive.Execute;
    begin
      RunMyApp;
    end;
    
    var
      ActiveThread: TActive;
    begin
      ActiveThread := TActive.Create(True);
      ActiveThread.FreeOnTerminate := True;
      ActiveThread.Resume;
    end.