Thread & dll -> Exception

Résolu
WSTBoss Messages postés 16 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 27 octobre 2011 - 14 nov. 2008 à 11:43
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 - 18 nov. 2008 à 15:24
Bonjour à tous,

j'ai une dll qui contient un thread, tout ce passe bien le thread marche comme il faut mais cependant lors du dechargement de la dll par l'application, cela génère un exception. J'ai fait pas mal de tests dans tout les sens possible mais la je bloque. Donc je suis revenu au basse et j'ai créé un exe et une dll minimum permettant de reporduire le problème. Pourtant même avec ça, je ne vois pas d'ou viens cette fichue exception. Donc je viens ici avec l'immense espoir que quelqu'un puisse m'aider et me dire ce qui ne va pas. c'est surement pas grand chose mais la j'ai beau ouvrir les yeux, je ne vois rien.

Voici le code de l'appli:

unit uTest2;


interface


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


type
  TForm3 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;


var
  Form3: TForm3;


implementation


{$R *.dfm}


procedure TForm3.Button1Click(Sender: TObject);
var
  libHwnd:                  HWnd;
begin
  libHwnd := LoadLibrary('InitDLL.dll');
  if libHwnd = 0 then
  begin
    memo1.Lines.Add('Could not find the dll file!');
    EXIT; {EXIT}
  end
  else
  begin
    memo1.Lines.Add('dll file find!');
  end;
  Sleep(1000);
  Application.ProcessMessages;


  freelibrary(libHwnd);


  memo1.Lines.add('Unload OK');
end;


end.

et celui de la dll :

unit myDLL2;


interface


uses
  Windows,
  SysUtils,
  Classes;




type TDllThread = class(TThread)
  private
  protected
    procedure Execute; override;
end;


var
  DllThread:  TDllThread = NIL;


implementation


procedure TDllThread.Execute();
{-----------------------------------------------------------------------------
  Procedure: Execute
  Date:      13-nov.-2008
  Arguments:
  Result:    None
  Comment:
  Comment
-----------------------------------------------------------------------------}
var
  i:                integer;
begin
  i:=0;
  {TRAITEMENT}
  while (Terminated=False) do
  begin
    Sleep(10);
    Inc(i);
  end;
  {VALEUR DE RETOUR}
  ReturnValue:=1;
end;


(************************************)
(*          INITIALIZATION          *)
(************************************)
initialization
  {Init thread}
  DllThread:=TDllThread.Create(TRUE);
  DllThread.FreeOnTerminate:=TRUE;
  {Execute thread}
  DllThread.Resume;


(************************************)
(*         FINALIZATION             *)
(************************************)
finalization
  Sleep(10)
end.

bon ben je crois qu'on peux pas faire plus simple comme code.
merci à tous pour votre aide.

WSTBoss!

5 réponses

cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
17 nov. 2008 à 15:02
Salut,

Heu !

Il y a un problème évident dans ton code...
1 Tu charges une dll.
2 Tu lances un thread qui fait une boucle infinie dans la dll.
3 Tu décharges la dll.

Ca plante. Logique. Comment veux-tu que le thread continue sa boucle infinie si tu décharges sont code ????

Il faut que tu demandes l'arrêt du thread avant de décharger la dll.

-> Ajout d'une fonction exportée par la dll.
-> Cette fonction demande l'arrêt du thread et attend son arrêt effectif.
-> Il faut que le thread soit libéré par cette fonction : le WaitFor ne fonctionne pas si le FreeOnTerminate est à true.
-> Le .exe doit appeler la fonction exportée par la dll avant de décharger celle-ci.

<hr size="2" width="100%" />unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

type TStopThreadProc = procedure;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  libHwnd:                  HWnd;
  stopThreadProc: TStopThreadProc;
begin
  libHwnd := LoadLibrary('Project2.dll');
  if libHwnd = 0 then
  begin
    memo1.Lines.Add('Could not find the dll file!');
    EXIT; {EXIT}
  end
  else
  begin
    memo1.Lines.Add('dll file find!');
  end;
  Sleep(1000);
  Application.ProcessMessages;

  @stopThreadProc:= GetProcAddress(libHwnd, 'StopThread');
  if not Assigned(stopThreadProc) then
  begin
    memo1.Lines.Add('Could not find StopThread in the dll!');
    EXIT; {EXIT}
  end;
 
  stopThreadProc();

  freelibrary(libHwnd);

  memo1.Lines.add('Unload OK');
end;

end.
<hr size="2" width="100%" />unit Unit2;

interface

uses
  Windows,
  SysUtils,
  Classes,
  Dialogs;

type TDllThread = class(TThread)
  private
  protected
    procedure Execute; override;
end;

var
  DllThread:  TDllThread = NIL;

procedure StopThread;

implementation

procedure StopThread;
begin
ShowMessage('Fin du thread');
  DllThread.Terminate;
  // On attend la fin du thread
  DllThread.WaitFor;
  DllThread.Free;
end;

procedure TDllThread.Execute();
{-----------------------------------------------------------------------------
  Procedure: Execute
  Date:      13-nov.-2008
  Arguments:
  Result:    None
  Comment:
  Comment
-----------------------------------------------------------------------------}
var
  i:                integer;
begin
  i:=0;
  {TRAITEMENT}
  while (Terminated=False) do
  begin
    Sleep(10);
    Inc(i);
  end;
  {VALEUR DE RETOUR}
  ReturnValue:=1;
end;

(************************************)
(*          INITIALIZATION          *)
(************************************)
initialization
  {Init thread}
  DllThread:=TDllThread.Create(TRUE);
  DllThread.FreeOnTerminate:=FALSE;
  {Execute thread}
  DllThread.Resume;

(************************************)
(*         FINALIZATION             *)
(************************************)
finalization
  Sleep(10)
end.
3
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
18 nov. 2008 à 15:24
Non non ça devrait aller.

Windows ferme tous les handles et décharge les modules quand les processus se terminent, que l'arrêt soit violent ou non.

Les cas où la dll reste en mémoire sont bien plus tordus que ça.

Mais bon ça ne t'exempte pas de libérer proprement ta dll.
3
WSTBoss Messages postés 16 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 27 octobre 2011
17 nov. 2008 à 15:41
Salut rt15,

merci pour ta réponse, effectivement, je viens de voir ma boulette. En fait, j'ai tellement fait de test que je me suis mélangé les pinceaux. En plus j'ai essayé de faire un code minimal afin de reproduire le bug et du coup, j'ai du supprimer un peux trop vite la ligne dans la section Finalization qui disait DllThread.Terminate;
Du coup, j'ai généré un autre bug que celui que je traquais. Y'a plus qu'a retourner à la pêche...

Sinon, j'avais une question, vaut il mieux utiliser une fonction exporté qui permet d'arréter le thread (comme tu l'as fait) ou mon DllThread.Terminate dans la section Finalization est suffisant ?

merci pour ton aide précieuse. Je retourne chercher le bug qui m'intéresse...

WSTBoss!
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
17 nov. 2008 à 17:54
Oki pour le terminate dans la finalization, mais avec un WaitFor.

Sinon terminate va revenir avant que le thread se termine (Terminate ne fait que mettre terminated à true) -> Plantage.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
WSTBoss Messages postés 16 Date d'inscription lundi 16 décembre 2002 Statut Membre Dernière intervention 27 octobre 2011
18 nov. 2008 à 14:49
Salut,

Pour info, si l'on met le terminate et le waitfor dans la section finalization, c'est déjà un peu trop tard pour terminer le thread. cela génère une exception ou le programme ne se ferme pas.
Je prends donc ta méthode d'appeler une fonction d'arrêt du thread avant de décharger la dll.
Cependant que se passe t'il si le programme principal plante et qu'il n'appelle pas la fonction StopThread ?
la dll reste t'elle en mémoire avec le thread qui tourne ?

merci

WSTBoss!
0
Rejoignez-nous