[Delphi] Thread in een DLL

Pagina: 1
Acties:

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Is het mogelijk om in een DLL een thread te maken en deze te geruiken?

Ik heb een aparte unit met waar de Thread class in zit, dit werkt perfect in een gewone app, maar zodra ik hem in een DLL stop werkt hij niet echt goed meer. Hij create+execute de thread goed, maar de OnTerminate event wordt niet echt goed uitgevoerd. Pas na een ShowMessage ofzo wordt ie ineens getriggerd. Hoe kan dit? En is dit te verhelpen.

Voor eventuele code hoor ik het wel.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • Delphi32
  • Registratie: Juli 2001
  • Laatst online: 16:08

Delphi32

Heading for the gates of Eden

Kom maar op met die code, want bij mijn beste weten hoort dit gewoon te kunnen...

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Thread unit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
interface

uses
  Windows, Messages, SysUtils, Classes,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP;

type
  TGetThread = class(TThread)
  private
    HTTP: TIdHTTP;
  public
    Result: Byte;
    constructor Create(AOwner: TComponent);
    procedure Execute; override;
  end;

implementation

constructor TGetThread.Create(AOwner: TComponent);
begin
  inherited Create(False);
  HTTP := TIdHTTP.Create(AOwner);
  Result := 0;
end;

procedure TGetThread.Execute;
var
  HTTPData: String;
begin
  try
    HTTPData := HTTP.Get('http://www.tweakers.net/');
    if Pos('1', HTTPData) > 0 then Result := 1 else Result := 0;
  finally
    HTTP.Free;
  end;
end;

end.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
En het aanroepen vanuit een Form van de DLL:
code:
1
2
3
4
5
6
7
8
9
10
procedure TMainForm.Button1Click(Sender: TObject);
begin
  GetThread := TGetThread.Create(Self);
  with GetThread do begin
    OnTerminate := GetThreadTerminate;
    Priority := tpNormal;
    FreeOnTerminate := True;
    Resume;
  end;
end;

En de OnTerminate event:
code:
1
2
3
4
5
6
7
8
9
10
11
procedure TMainForm.GetThreadTerminate(Sender: TObject);
begin
  with GetThread do begin
    if Result = 1 then begin
    StatusBar.Panels.Items[0].Text := 'OK';
    end else begin
    StatusBar.Panels.Items[0].Text := 'ERROR';
    end;
  end;
  GetThread := nil;
end;

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Het werkt allemaal goed, maar hij komt dus nooit bij die OnTerminate event. Dat gebeurt WEL als ik hem in een gewoon programma hang.

Dit is nog vager:
Nadat ik zelf weet wanneer de OnTerminate van de Thread had moeten plaats vinden doe ik dit:
code:
1
2
3
4
procedure TMainForm.Button2Click(Sender: TObject);
begin
  ShowMessage('hallo');
end;

Dan triggerd hij toch de OnTerminate vlak voor de MessageBox.

Daarom dacht ik dat het iets met die DLL te maken moet hebben.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Allereerst maak je je form uit een ander proces eigenaar van het thread in een DLL. Dit is onzinnig. Waarschijnlijk draait dat HTTP component ook zijn eigen thread en lijkt me dus een beetje onzinnig om deze te doen.

B.t.w.: maak je wel gebruik van de Unit ShareMem ?

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Op woensdag 20 februari 2002 09:57 schreef hvdberg het volgende:
Allereerst maak je je form uit een ander proces eigenaar van het thread in een DLL. Dit is onzinnig. Waarschijnlijk draait dat HTTP component ook zijn eigen thread en lijkt me dus een beetje onzinnig om deze te doen.

B.t.w.: maak je wel gebruik van de Unit ShareMem ?
TIdHTTP heeft GEEN eigen thread, daarom doe ik het zo.

Anders zou die hele MainForm + de applicatie die de DLL aanroept even vast lopen als die IdHTTP zijn data ophaalt.

ShareMem heb ik toch niet nodig, omdat de applicatie die de DLL aanroept niks met de inhoud van de DLL heeft te maken. De applicatie die hem aanroept is niet eens een Delphi applicatie.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Op woensdag 20 februari 2002 10:10 schreef jelmervos het volgende:

[..]

TIdHTTP heeft GEEN eigen thread, daarom doe ik het zo.

Anders zou die hele MainForm + de applicatie die de DLL aanroept even vast lopen als die IdHTTP zijn data ophaalt.

ShareMem heb ik toch niet nodig, omdat de applicatie die de DLL aanroept niks met de inhoud van de DLL heeft te maken. De applicatie die hem aanroept is niet eens een Delphi applicatie.
Ok... da's dan duidelijk maar waarom maak je het form dan eigenaar van het thread (via je constructor). TThread stamt namelijk helemaal niet af van TComponent en dus is de AOwner: TComponent constructie onzin (je gebruikt hem volgens mij niet eens, je owner).

In ieder geval heb ik even een projectgroep gemaakt met daarin een DLL en een EXE. De DLL bevat het thread (TMyThread) en één geëxporteerde functie (DoThreading), deze functie geeft een TThread object terug en benodigd een TNotifyEvent als parameter (OnTerminate). De EXE bevat een form met twee buttons (Start en Stop).

Hier volgt de code voor de DLL (Project1)
code:
1
2
3
4
5
6
7
8
9
10
11
library Project1;

uses
  SysUtils,
  Classes,
  Unit1 in 'Unit1.pas';

exports
  DoThreading;

end.

Hier volgt de code voor Unit1 van Project1.dll
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
unit Unit1;

interface

uses
  Classes;

type
  TMyThread = class(TThread)
  private
    FMyInt64: Int64;
  protected
    procedure Execute; override;
    constructor Create(const CreateSuspended: Boolean);
  end;

function DoThreading(const OnTerminateProc: TNotifyEvent): TThread; stdcall;

implementation

function DoThreading;
begin
  Result := TMyThread.Create(True);
  with Result do
  try
    FreeOnTerminate := True;
    OnTerminate := OnTerminateProc;
    Resume;
  except
    raise;
  end;
end;

constructor TMyThread.Create(const CreateSuspended: BOolean);
begin
  inherited Create(CreateSuspended);
  FMyInt64 := 0;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
    inc(FMyInt64);
end;

end.

De code voor project2 (de EXE) is niet zo van belang behalve van Unit2 (Form2). Hier volgt de code van Unit2.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    FThread: TThread;
    procedure ThreadTerminated(Sender: TObject);
  public
    { Public declarations }
  end;

function DoThreading(const OnTerminateProc: TNotifyEvent): TThread; stdcall;

var
  Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.Button1Click(Sender: TObject);
begin
  FThread:=DoThreading(ThreadTerminated);
end;

procedure TForm2.ThreadTerminated(Sender: TObject);
begin
  ShowMessage('Thread beëindigd');
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  FThread.Terminate;
end;

function DoThreading; external 'Project1.dll';

end.

Deze code heb ik getest en deze werkt goed. D.m.v. Start-knop (Button1) start het thread. D.m.v. Knop2 wordt het thread gestopt. Doordat het thread gestopt wordt (Terminate) wordt het OnTerminate-event afgevuurd (Form2.ThreadTerminated).

Suc6!

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Uuhmm, bedankt. Zal er es wat beter naar kijken zo.


Het is bij mij wel het geval dat ik de aanroepende applicatie niet ken, laat staan bewerken. Mijn DLL export 1 functie die de aanroepende applicatie gebruikt.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Oja, het werkt allemaal wel goed als ik aan het einde vande Execute van de Thread dit doe:
code:
1
  Self.OnTerminate(Self);

Dan wordt de OnTerminatie event goed getriggerd en wordt deze uitgevoerd in de MainForm.

Maar da's niet echt een prettige oplossing denk ik.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Op woensdag 20 februari 2002 11:26 schreef jelmervos het volgende:
Oja, het werkt allemaal wel goed als ik aan het einde vande Execute van de Thread dit doe:
code:
1
  Self.OnTerminate(Self);

Dan wordt de OnTerminatie event goed getriggerd en wordt deze uitgevoerd in de MainForm.

Maar da's niet echt een prettige oplossing denk ik.
Inderdaad niet om de volgende 2 redenen:
1) Je controleert niet (wordt niet uit stukje code duidelijk) of OnTerminate wel ge-assigned is, dus daarvan maken:
code:
1
2
if Assigned(OnTerminate) then
  OnTerminate(Self);

2) Je vuurt nu zelf het event af terwijl het best zo kan zijn dat ook het basis (TThread) object ook nog eens het OnTerminate-event afvuurt. Dus daar kun je beter van maken:
code:
1
2
3
if Assigned(OnTerminate) then
  OnTerminate(Self);
OnTerminate := nil;

Hierdoor zal je event uitgevoerd worden en niet nog een keer door de basis TThread.

PS. Netjes is deze manier allerminst!

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Op woensdag 20 februari 2002 11:41 schreef hvdberg het volgende:

[..]

PS. Netjes is deze manier allerminst!
Oke, da's idd de perfect functie voor dit probleem.

Maar je hebt gelijk, het is niet netjes. Maar ik wilde het gewoon testen, en het bleek idd zo te zijn dat het zo wel werkt.


Nu de grote vraag? Waarom triggerd die TThread zelf niet die event na de Execute? Alles lijkt immers goed te werken?

Overigens wordt hij wel degelijk getriggerd, maar da's pas als ik bijvoorbeeld een ShowMessage (zie paar posts hierboven) doe in de MainForm? Waarom???

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Iemand nog een idee?

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Gebruik je een dll of een run-time bpl?

Als je een dll gebruikt, hoe haal je dan de class uit die dll?

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Het is gewoon een DLL.


Maar alles wat je ziet is alleen binnen de DLL hoor. De aanroepende applicatie roept 1 funtie aan en die export mijn DLL dus.

Met de thread wil ik voorkomen dat de boel niet klem zit als hij een HTTP request doet.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Maar TMainForm (= de aanroepende applicatie?) roept TGetThread aan; die in de dll wordt gedefinieerd? Hoe komt TMainForm dan aan TGetThread?

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
TMainForm zit ook in die DLL.

De aanroepende applicatie is in die geval Winamp, het is namelijk een Winamp plugin.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Hang gewoon WinAmp aan je applicatie en ga debuggen!

Run->Parameters, selecteer WinAmp.exe als Host Application en ge kunt debuggen.

B.t.w. Heb je stdcall achter je functie gezet? Dit maakt namelijk behoorlijk uit wat betreft parameters! (zie mijn voorbeeld).

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Heb Winamp aan mijn DLL gehangen voor debuggen, perfect! :)

Maar het lost zo nog niks op natuurlijk. Verder zie ik als enige verschil met jou code en die van mij, dat jij een functie in je thread unit hebt die de thread maakt e.d. Ik doe dat gewoon in de MainForm unit, maakt dat veel uit?
Daardoor is er dus ook nergens plaats voor een stdcall achter een method.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Op donderdag 21 februari 2002 10:45 schreef jelmervos het volgende:
Heb Winamp aan mijn DLL gehangen voor debuggen, perfect! :)

Maar het lost zo nog niks op natuurlijk. Verder zie ik als enige verschil met jou code en die van mij, dat jij een functie in je thread unit hebt die de thread maakt e.d. Ik doe dat gewoon in de MainForm unit, maakt dat veel uit?
Daardoor is er dus ook nergens plaats voor een stdcall achter een method.
Moet niet uitmaken. Stdcall zet je achter de functie welke je exporteert.

Maar ik denk niet dat dit veel uit gaat maken. Natuurlijk kun je wel tot op VCL TThread niveau debuggen door deze Unit (classes) aan je project (pas-file) toe te voegen.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Uuhmm, het helpt allemaal niet.

Ik zit nu nog steeds met deze oplossing: aan het eind van de Execute procedure de OnTerminate van de thread 'handmatig' aanroepen.


Het is wel zo dat de thread PRIMA werkt in een gewone applicatie.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

De OnTerminate van een thread maakt gebruik van Synchronize. Deze is wat anders geimplementeerd in D6 dan in D5. Lees hier er wat over:
Most developers using Synchronize will not need to take any special action to make their code work with Delphi 6. Developers of a projects which do not use TApplication (such as ActiveX libraries), however, will need to call this manually or use something other than Synchronize.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Volgens mij kan ik met Synchronize weinig bereiken omdat dit alleen is om vanuit je Thread een procedure op je MainThread te starten, om bijvoorbeeld een Label.Captio te updaten.
Het gaat hier om een DLL applicatie die door Winamp wordt gebruikt als plugin.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Nee, ik bedoelde dat TThread gebruikt maakt van Synchronize als de OnTerminate procedure wordt aangeroepen.

Delphi 5 source code:
code:
1
2
3
4
5
6
7
8
9
10
11
procedure TThread.CallOnTerminate;
begin
  { Hier wordt de OnTerminate procedure dus aangeroepen }
  if Assigned(FOnTerminate) then FOnTerminate(Self);
end;

procedure TThread.DoTerminate;
begin
  { En hier wordt gebruik gemaakt van Synchronize }
  if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;

Ik weet niet zeker of dit in D6 ook zo is, maar ik denk het wel. En doordat gebruik gemaakt wordt van Synchronize geeft dit problemen bij jou, omdat jij geen gebruik maakt van TApplication [zie de link die ik gaf]

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Aha, ik snap hem.
Maar wat is dat de oplossing, want die Synchronize werkt dus niet. Dat verklaart ook waarom de thread wel werkt in een gewone applicatie.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Is het misschien een idee om een message te sturen vanuit de thread naar de DLL (MainThread)?

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Mogelijke oplossingen:
  • CheckSynchronize aanroepen in de main-thread als de applicatie idle wordt, zodat de Synchronize werkt. Dit is denk ik niet zo simpel.
  • OnTerminate aanroepen zonder Synchronize. Dit is wat je nu doet, geloof ik. Probleem is misschien dat de OnTerminate 2x aangeroepen wordt [2e mislukt omdat Synchronize niet werkt] Maar die 2e komt dus wel in een lijst terecht; en als om wat voor reden [patch van Borland :)] dan ook CheckSynchronize wordt aangeroepen wordt OnTerminate dus 2x aangeroepen.
  • Zelf een nieuw event maken. Die aanroepen zonder Synchronize. Probleem is misschien dat je nu op moet letten wat je in die OnTerminate doet.
  • Met PostMessage een message versturen naar jouw TMainForm. Dit is de veiligste oplossing.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
De laatste maar denk ik. PostMessage.

"The shell stopped unexpectedly and Explorer.exe was restarted."

Pagina: 1