IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API, COM et SDKs Delphi Discussion :

Un thread ne démarre pas sans ShowMessage dans le code qui crée le thread


Sujet :

API, COM et SDKs Delphi

  1. #1
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut Un thread ne démarre pas sans ShowMessage dans le code qui crée le thread
    Bonjour,

    Dans une DLL réalisée en Delphi 6 Personal Edition, j'ai une fonction qui, entre autres, doit créer un nouveau thread. Et ce thread ne veut pas démarrer sans que je fasse appel à ShowMessage dans la fonction qui crée ce thread ! Si je le fais, le thread demarre correctement et fonctionne. Sinon, il ne démarre jamais. Voici mon code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    var
      th: TMyThread;
     
    procedure TMyThread.Execute;
    begin
      synchronize(DoProgress);
      FreeOnTerminate := true; 
    end;
     
    procedure TMyThread.DoProgress;
    begin
      showmessage('TMyThread started');
    end;
     
    function TestThread():integer; stdcall; export;
    begin
      th := TMyThread.Create(true);
      th.Resume;
    //  if assigned(th) then showmessage('ok') else showmessage('oups');
      result := integer(th);
    end;
    Tel quel, le message "TMuThread started" ne s'affiche jamais.
    Mais si d'active la ligne "if assigned...", alors mon message apparaît, puis immédiatement, le message "ok" apparaît en se superposant au premier. Je valide "ok", cette boite disparaît et j'ai accès à la boite "TMuThread started" que je peux alors valider à son tour.

    J'ai conscience que dans ce code, aucune disposition n'est prise pour terminer le thread correctement. Ceci est intentionnel car je voulais metttre exclusivement le problèe du démarrage en évidence.

    Qu'est-ce que je n'ai pas conmpris sur le fonctionnement des Threads ? Qu'est-ce qu'il faut faire pour que le thread démarre ?

  2. #2
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 683
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 683
    Points : 13 092
    Points
    13 092
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    J'ai conscience que dans ce code, aucune disposition n'est prise pour terminer le thread correctement. Ceci est intentionnel car je voulais metttre exclusivement le problèe du démarrage en évidence.
    C'est plutôt l'arrêt (la destruction) qu'il faudrait contrôler. Le problème ici est que TMyThread est libéré avant même qu'il ait eu le temps de démarrer.

  3. #3
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Je veux bien, mais je ne comprends pas. Dans mon esprit, le thread lance une procédure par la méthode Synchronize. Et cette procédure excécute un ShowMessage et ne devrait donc logiquement pas terminer avant la validation de la boite de message affichée par ShowMessage. Et ce n'est qu'ensuite que le thread devrait pouvoir continuer, ce qui évidemment va le supprimer, dans ce cas précis, puisqu'il n'y a rien d'autre derrière. Mais je peux certifier que la DLL qui exécute tout cela, reste active et chargée, et le process principal appelant ma fonction TestThread reste actif et ne se termine pas.

    J'ai sûrement un point aveugle quelque part dans mon raisonnement, mais je ne comprends pas pourquoi la méthode Synchronize se terminerait avant que la procedure DoProgress ne soit terminée. Surtout que j'ai bien pris soin de de donner la commande "FreeOnTerminate := true; " qu'une fois la commande Synchronize exécutée, pas avant.

    EDIT

    D'ailleurs, même si je désactive la commande "FreeOnTerminate := true; " dans la procédure "procedure TMyThread.Execute;", le comportement est identique, alors que, dans ce cas, le thread ne devrait pas être supprimé (bien que terminé). En tout cas, la procédure DoProgress devrait être active tant que la boite affichée par ShowMessage n'est pas validée. Le problème, c'est qu'elle ne s'affiche même pas.

  4. #4
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    J'ai poussé les essais plus loin. J'ai fait deux essais distincts:

    1. Un programme Delphi stand-alone, avec deux boutons, l'un appelant appelant localement ma fonctionqui crée et lance le thread, l'autre appelant ma fonction dans la DLL. Résultat: localement, cela marche parfaitement, sans avoir besoin d'activer la ligne "showmessage" dans la fonction Testthread, celle qui crée et lance mon thread. Par contre, la fonction DLL appelée par le programme Delphi ne démarre pas le thread.

    2. Une DLL contenant uniquement ma fonction TestThread, la définition de MyThread et les procédures associées. Puis, j'ai créé un programme dans un autre langage (de type Basic), avec également juste un bouton appelant ma fonction TestThread. Et là, le thread ne démarre pas si la ligne ShowMessage n'est pas activée dans la fonction TestThread. Exactement comme dans ma vraie DLL (celle sur laquelle je travaille et qui est énorme...).

    C'est donc un problème qui est clairement lié au contexte de l'exécution dans une DLL.

    Voici les sources de ces deux projets:

    1. Un programme Delphi stand-alone nommé Project2. Créer un nouveau rojet GUI, placer deux boutons sur la page, et copier cette unité:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
     
    Type
      TMyThread = class(TThread)
      private
      protected
        procedure Execute; override;
        procedure DoProgress;
      public
    end;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    function TestThread(): integer;
      stdcall; external 'Project2.dll' name 'TestThread';
     
    var
      Form1: TForm1;
      th: TMyThread;
     
    implementation
     
    {$R *.dfm}
     
    procedure TMyThread.Execute;
    begin
      synchronize(DoProgress);
      FreeOnTerminate := true; 
    end;
     
    procedure TMyThread.DoProgress;
    begin
      showmessage('TMyThread started');
    end;
     
    function TestThreadLocal():integer; stdcall; export;
    begin
      th := TMyThread.Create(true);
      th.Resume;
    //  if assigned(th) then showmessage('ok') else showmessage('oups');
      result := integer(th);
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      res: integer;
    begin
      res := TestThreadLocal();
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      res: integer;
    begin
      res := TestThread();
    end;
     
    end.
    2. une DLL Delphi nommée Project2 dont voici le code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    49
    50
    library Project2;
     
     
    uses
      SysUtils,
      Dialogs,
      Classes;
     
    {$R *.res}
     
    Type
      TMyThread = class(TThread)
      private
      protected
        procedure Execute; override;
        procedure DoProgress;
      public
    end;
     
     
     
    {$R *.dfm}
     
    var
      th: TMyThread;
     
    procedure TMyThread.Execute;
    begin
      synchronize(DoProgress);
      FreeOnTerminate := true; 
    end;
     
    procedure TMyThread.DoProgress;
    begin
      showmessage('TMyThread started');
    end;
     
    function TestThread():integer; stdcall; export;
    begin
      th := TMyThread.Create(true);
      th.Resume;
    //  if assigned(th) then showmessage('ok') else showmessage('oups');
      result := integer(th);
    end;
     
    exports
      TestThread;
     
    begin
    end.
    Et voici le code source de mon programme en Basic (facilement adaptable à n'importe quel Basic capable d'appeler une DLL):
    Code basic : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ' TestThread.bas
     
    label Click1
    dim res%
     
    button 1 : top 1,10 : left 1,10 : caption 1,"Button1" : on_click 1,Click1
     
    dll_on "Project2.dll"
     
    end
     
    Click1:
      res% = dll_call0("TestThread")
      return

    Qu'est-ce qui fait que, lancé dans une DLL, ma fonction TestThread ne peut pas créer le thread s'il n'y a pas de SendMessage derrière ?

  5. #5
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 683
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 683
    Points : 13 092
    Points
    13 092
    Par défaut
    Premièrement ta DLL ne peut compiler dû à {$R *.dfm}. Ce n'est donc pas vraiment ta DLL de test

    Ensuite après réflexion, DoProgress ne peut tout simplement pas s'exécuter pour la simple et bonne raison que la pile de procédures (remplie par Synchronize) n'est jamais vidée ! Et ajouter un ShowMessage ne devrait rien y changer, il y a quelque chose de pas clair...

    Y a-t-il une différence du fait que l'app ne soit pas en Delphi ? Peut-être ! Peut-être que le handle de TApplication de la DLL est tout de même défini dans ce cas. On retomberait alors sur ma première remarque, le thread n'a pas le temps de démarrer (par exemple si dll_call0 fait un LoadLibrary, TestThread suivi immédiatement d'un FreeLibrary). ShowMessage ajoute une pause permettant ce démarrage.

    En parlant purement Delphi.

    Comment fonctionne Synchronize ?
    Cette procédure ajoute un pointeur de méthode à une liste puis notifie TApplication par message asynchrone. TApplication va ensuite vider cette liste lorsqu'elle traitera sa pile de messages.

    Problème, exe et dll ne partage pas le même objet TApplication et seul celui de l'exe a un handle valide (il est à zéro dans la DLL). Synchronize dans la DLL envoie par conséquent un message par PostMessage sur un handle nul et attend ensuite indéfiniment sur un event qui ne sera jamais signalé puisque son message est parti dans les choux, le thread est donc définitivement verrouillé (mais il s'est exécuté) !

    A l'instar d'une application console (donc sans TApplication) multithreads, il faudrait vider manuellement cette pile de méthodes. Cela se fait par CheckSynchronize.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    function TestThread():integer; stdcall;
    begin
      th := TMyThread.Create(true);
      th.Resume;
      CheckSynchronize(5000);
    end;
    Mais le faire ainsi supprimerait tout intérêt d'un thread secondaire. Le mieux est donc d'exporter une fonction supplémentaire et de la lancer depuis l'exe lorsque l'application est en attente (à l'aide d'un TApplicationEvents). On aurait ainsi quelque chose comme ceci :

    Exe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    type
      TForm1 = class(TForm)
        Button1: TButton;
        ApplicationEvents1: TApplicationEvents;
        procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
        procedure Button1Click(Sender: TObject);
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    function TestThread :integer; stdcall; external 'MaDLL.dll';
    function CheckSynchro :boolean; stdcall; external 'MaDLL.dll';
     
    procedure TForm1.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
    begin
      CheckSynchro;
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      TestThread;
    end;
     
    end.
    DLL :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    Type
      TMyThread = class(TThread)
      private
      protected
        procedure Execute; override;
        procedure DoProgress;
      public
    end;
     
    procedure TMyThread.Execute;
    begin
      synchronize(DoProgress);
      FreeOnTerminate := TRUE;
    end;
     
    procedure TMyThread.DoProgress;
    begin
      showmessage('TMyThread started');
    end;
     
    function TestThread:integer; stdcall;
    begin
      TMyThread.Create;
    end;
     
    function CheckSynchro :boolean; stdcall;
    begin
      CheckSynchronize(INFINITE);
    end;
     
    exports
      TestThread,
      CheckSynchro;
     
    begin
    end.
    Dernier point, si le but de result := integer(th); était de libérer l'objet TThread depuis l'exe, tu peux oublier ! Exe et DLL ne partage pas le même gestionnaire de mémoire, problèmes assurés

    En conclusion, voir comment se comporte la DLL Delphi avec une app VBA. Mais à mon sens, l'approche générale est à revoir !

  6. #6
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Je te remercie de ton commentaire, Andnotor. Tes réflexions sur la différence des piles de messages entre process principal et DLL sont intéressantes, et je vais approfondir dans ce sens.

    Cependant, tu peux me croire: ma DLL compile bien ! El elle s'exécute sans propblème, du moins en ce qui concerne de $R. Et je suis certain qu'aucun FreeLibrary n'est émis - ce serait la commande DLL_OFF dans ce langage, et elle ne figure pas dans le code. La DLL est bien chargée une fois au début du programme et reste chargée et active pendant toute l'exécution du process principal. ET pour finir, ma ligne "result := integer(th);" a un vrai sens, mais certainement pas celui d'obtenir une libération quelconque de mémoire: en effet, les conventions d'appel d'une fonction DLL à partir de ce langage imposent que tout symbole exporté doit être une fonction (et non une procédure) et toute fonction doit retourner une valeur entière sur 32 bits. Et j'en profite pour retourner l'objet thread car j'en aurai besoin ultérieurement.

    Je mets en pièce jointe in fichier ZIP contenant le dossier complet servant pour ces tests, avec les sources et les exécutables de tout: DLL, programme Delphi stand-alone et programme en Basic. Tu verras que la DLL de test, telle que je l'ai présentée, compile bien et joue bien son rôle. Le fichier Project1.dpr est un programme Delphi stand-alone avec deux boutons: le premier appelle ma fonction recopiée localement dans l'unité principale, et le second appelle la même fonction, mais dans la DLL Project1. Le premier bouton produit le bon résultat, le second ne démarre pas le thread. Si, par contre, dans Project2.dpr (la DLL), on active la ligne avec le SHowMessage, alors ça fonctionne sur les deux boutons. Donc, nul besoin de recourir à un programme externe en Basic ou autre, avec Delphi 6 pur, le problème est mis en évidence. J'ajoute que le ShowMessage qui débloque la situation, est émis dans la DLL, tout comme celui que j'aimerais voir pour prouver le démarrage du thread, dans la procédure DoProgress.

    Pour finir, juste un petit mot sur le contexte. Je développe depuis 2010 une vaste DLL contenant des fonctions de support et d'extension à ce langage de type Basic dont j'ai parlé. Ma DLL exporte actuellement près de 700 fonctions, en une vaingtaine de grosses catégories distinctes. Cela va du contrôle des processus jusqu'aux graphismes, en passant par la gestion des fichiers, bases de données, communication entre processus et beaucoup d'autres. La petite DLL montrée pour lettre le problème de thread en évidence, est juste un petit extrait afin de ne pas encombrer ma demande avec la cinquantaine d'unités source et des dizaines de milliers de ligne de code.

    Voilà. Essaie Project1.exe (ou dpr) dans l'IDE. Puis, active la ligne dans Project2.dpr et réessaie - tu verras la différence tout de suite.

    EDIT

    J'ai regardé la solution par "ApplicationEvents1Idle". Intéressant, mais cela doit être appliqué dans le process principal. Qui, par définition, est écrit en un langage quelconque (dans mon cas, le clône de Basic) et je n'ai pas accès à cela. A moins de placer cela dans une fonction DLL, et un tourne en rond.

    Non, il faudrait qu'on puisse trouver une solution applicable directement dans la DLL, sans perdre le bénéfice du thread independant - sinon, je peux tout coder dans une fonction s'exécutant de façon synchrone. Mais pour un besoin particulier, j'ai précisément besoin d'un thread s'exécutant de façon entièrement autonome et asynchrone au proces principal.
    Fichiers attachés Fichiers attachés

  7. #7
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 683
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 683
    Points : 13 092
    Points
    13 092
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Cependant, tu peux me croire: ma DLL compile bien !
    Pas chez moi (Berlin)
    {$R *.dfm} implique l'existence d'un fichier Project2.dfm. Il n'existe pas, la compilation échoue.

    Citation Envoyé par KlausGunther Voir le message
    pour finir, ma ligne "result := integer(th);" a un vrai sens (...) Et j'en profite pour retourner l'objet thread car j'en aurai besoin ultérieurement.
    C'est incompatible avec FreeOnTerminate, donc attention.

    Citation Envoyé par KlausGunther Voir le message
    Le premier bouton produit le bon résultat, le second ne démarre pas le thread. Si, par contre, dans Project2.dpr (la DLL), on active la ligne avec le SHowMessage, alors ça fonctionne sur les deux boutons
    Je te le confirme avec ton projet VBA
    Mais CheckSynchronize fonctionne aussi (la DLL est compilée avec Berlin).

    Citation Envoyé par KlausGunther Voir le message
    avec Delphi 6 pur, le problème est mis en évidence.
    Chez moi, seul CheckSynchronize fonctionne.

    Citation Envoyé par KlausGunther Voir le message
    J'ajoute que le ShowMessage qui débloque la situation, est émis dans la DLL, tout comme celui que j'aimerais voir pour prouver le démarrage du thread, dans la procédure DoProgress.
    Le problème qui se présente ici est un problème de synchronisation entre ce thread et la tâche principale. Si tu veux juste t'assurer que le thread est démarré, nul besoin de synchro.
    ShowMessage fait appel à la VCL, mieux vaut passer par MessageBox
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TMyThread.Execute;
    begin
      MessageBox(0, 'TMyThread started', 'Confirmation', MB_OK);
      FreeOnTerminate := true;
    end;
    ou créer un fichier, ajouter une entrée dans les événements Windows, un OutputDebugString, ...

  8. #8
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    J'ai analysé tout cela et j'ai fait des essais. Frustrant, car je n'ai pas réussi à utiliser MessageBox à la place de SendMessage SANS faire un SendMessage derrière pour le faire apparaître.

    Tant pis. Tu m'as expliqué qu'il s'agit d'un problème de synchronisation et que malgré cela, le thread était lancé et actif. Qu'à cela ne tienne, j'ai parié la-dessus, j'ai supprimé ma procédure OnProgress et l'utilisation de Synchronize et j'ai codé un traitement exemple dans la procédure Execute, dans une boucle infini. J'ai modifié MyThread pour inclure certaines variables internes, et en particulier un boolean fFinish (défaut: false) et qui est testé à chaque itération de boucle. Puis, j'ai donné 2 paramètres à ma fonction TestThread: le premier est le thread sur lequel on veut travailler, et le second est un code action, avec les actions suivantes:
    0 = supprimer le thread
    1 = créer un nouveau thread
    2 = suspendre le thread
    3 = relancer le thread
    4 = tester l'état du thread
    J'ai adapté mon Project1.dpr à cela, ainsi que mon programme TestThread.bas et TestThread.exe. Et là, tout marche à merveille. Je peux créer, suspendre, relancer et supprimer mon thread autant que je veux. Et, si j'utilise des variables différentes dans le programme principal pour mémoriser l'objet Thread retourné par ma fonction avec le code action, je pourrais créer plusieurs threads indépendants et intervenir sélectivement sur un thread particulier. Et c'est bien cela le but de l'opération.

    Donc, je suis content, j'ai une solution à mon problème, et je marque le post comme "résolu", en te remerciant sincèrement de ton aide. Pour ceux que ça intéresse, je mets en pièce jointe un fichier ZIP avec l'ensemble des modules source et exécutables.
    Fichiers attachés Fichiers attachés

  9. #9
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 683
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 683
    Points : 13 092
    Points
    13 092
    Par défaut
    J'ai juste jeté un œil à la DLL, quelques remarques encore :

    Il y a une petite incompréhension, MessageBox devait être dans Execute pour pouvoir supprimer Synchronize(DoProgress) !

    Tu déclares une variable Finish pour conditionner la sortie du thread. Une telle variable existe déjà : Terminated.

    Application.ProcessMessages n'a pas de raison d'être dans un thread, la tâche principale tourne en parallèle et vide sa pile de messages normalement.

    assigned(th) teste que le paramètre th soit différent de nil mais rien ne garanti qu'il pointe sur un objet valide. Il vaudrait mieux gérer une liste et contrôler que ce thread y figure.
    Là, tu présupposes que l'utilisateur de ta DLL a traité correctement le retour de la fonction et reseté sa variable locale. Hasardeux, Violation d'accès en vue !

    Ta façon d'utiliser FreeOnTerminate est très dangereuse. FreeOnTerminate est utilisé lorsque la durée de vie du thread n'a pas besoin d'être contrôlée, ce n'est pas le cas ici.
    Que se passe-t-il si l'objet est libéré alors qu'on tourne dans cette boucle while th.Finish do ? C'est une VA assurée sur un th devenu invalide.
    Si tu veux contrôler l'existence d'un thread, c'est son handle qu'il faut tester et non l'instance d'objet Delphi (on en a parlé dernièrement ici).

    fWindowHandle pourquoi pas si tu est sûr que la fenêtre existe pendant toute la durée de vie du thread. Ne pas oublier qu'un handle libéré peut être réalloué à une autre fenêtre (et même une fenêtre d'un processus différent), WM_SETTEXT se tromperait alors de cible ! Un FindWindow juste avant SendMessage serait plus sûr et permettrait aussi de quitter le thread si la fenêtre n'existait plus (c'est une DLL de test mais autant le signaler).

    Suspend/Resume sont dépréciés depuis un certain temps. Suspend stoppe net le déroulement du thread à l'endroit où se trouve le pointeur de programme. Un verrou en cours (event, section critique, fichier verrouillé, etc.) peut entraîner le dead-lock de l'application !
    Cela dépend donc de ce que fait ce thread (ici pas trop de soucis) mais un WaitForSingleObject sur un TEvent serait plus à mon goût.
    Et en rapport avec la destruction de th, ne pas oublier qu'un thread ne peut s'arrêter proprement que s'il est en cours. Le thread est par conséquent démarré... à la destruction du TThread. Est-ce raisonnable de terminer la tâche dans ce cas ?

    Un thread critique ressemblerait plutôt à quelque chose comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    procedure TMyThread.Execute;
    begin
      while not Terminated do
      begin
        WaitForSingleObject(...);
     
        if not Terminated do
        begin
          ...
        end;
      end;
    end;
    Voilà, j'espère ne trop t'avoir saouler avec mes remarques

  10. #10
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Non, tu ne me saoules pas du tout, avec tes remarques. Je suis avide d'apprendre, et toute amélioration de ma technique est la bienvenue.

    Ceci dit, une partie des points que tu soulèves, concernent des aspects qui ne peuvent pas se produire dans le contexte dans lequel je programme. En effet, par exemple la variable interne fWindowHandle reçoit le handle de la form principale du process ayant chargé la DLL. Et lorsque cette form se ferme, le process est forcément éliminé, la DLL déchagée, le thread (s'il est encore en vie) terminé. Mais bien sûr, je n'ai posé qu'une questioin sur un détail spécifique en publiant un extrait minimaliste de mon code, ce qui ne permet pas de juger du contexte général. Dans l'absolu, tu as bien ûr parfaitement raison.

    De toutes façons, toutes ces explications et conseils, je les stocke dans un dossier avec des informations techniques, afin de pouvoir les ressortir en cas de besoin. Je te suis très reconnaissant de tes avis.

    As-tu regardé le code que je viens de poster ici:
    https://www.developpez.net/forums/d1...o/#post9103830
    C'est parfaitement opérationnel, et j'ai pu le réaliser grâce à tes conseils. Merci encore !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 22
    Dernier message: 16/08/2006, 13h11
  2. Réponses: 4
    Dernier message: 10/08/2006, 13h44
  3. Services Oracle ne démarre pas sans connexion réseau??
    Par dreamanoir dans le forum Oracle
    Réponses: 6
    Dernier message: 22/02/2005, 00h44

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo