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

Composants VCL Delphi Discussion :

Comment créer les composants de TWebView4Delphi programmatiquement ?


Sujet :

Composants VCL Delphi

  1. #1
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    425
    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 : 425
    Billets dans le blog
    1
    Par défaut Comment créer les composants de TWebView4Delphi programmatiquement ?
    Je suis sous W11 avec Delphi 6 Personal Edition, service packs 1 à 3 installés

    Je viens d'apprendre à créer et utiliser (petitement...) WebView4Delphi dans un programme D6.
    J'arrive à naviguer, et j'arrive à afficher un fichier SVG et à déplacer la ViewBox à volonté.

    Tout cela fonctionne bien si je place les composants dans ma fiche dans l'EDI, puis je les utilise dans mon code Delphi.

    Maintenant, je veux créer ces composants dynamiquement, et je veux faire cela dans une DLL de service.
    Et ça ne marche pas - rien ne s'affiche.

    J'ai joint un fichier ZIP qui contient 3 projets, complets avec codes source, exécutables et fichiers de données:
    1. le projet opérationnel avec les composants posés directement dans une fiche à partir de l'EDI : projet FirstTest
    2. le projet définissant la DLL de service, avec juste 1 seule fonction CreerWebView : projet WebView4Delphi
    3. le projet contenant un programme Delphi appelant la fonction CreerWebView de la DLL : projet Test_Avec_DLL

    C'est ce dernier (3) qui n'affiche rien, et je ne comprends pas. Voici le code de la dll WebView4Delphi:
    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
    library WebView4Delphi;
     
    {
      WebView4Delphi
    }
     
    uses
      SysUtils,Classes, Forms, Windows, Dialogs, Controls,
     
      uWVBrowserBase, uWVBrowser, uWVWinControl, uWVWindowParent,
      uWVTypes, uWVConstants, uWVTypeLibrary,
      uWVLibFunctions, uWVLoader, uWVInterfaces, uWVCoreWebView2Args;
     
    type
      TDummy = class
        private
        published
          class procedure AfterCreated(Sender: TObject);
      end;
     
    var
      WVWindowParent1: TWVWindowParent;
      WVBrowser1: TWVBrowser;
     
     
    {$R *.res}
     
     
    function CreateWebView(aDest: HWND): integer; stdcall; export;
    begin
      result := 0;
      try
        if assigned(WVWindowParent1) then exit;
        if assigned(WVBrowser1) then exit;
     
        // J'essaie de simuler ce qui se passe lors de la création d'une form contenant ces objets
        WVWindowParent1 := TWVWindowParent.CreateParented(aDest);
        WVWindowParent1.ParentWindow := aDest;
        WVWindowParent1.Left := 10;
        WVWindowParent1.Top := 50;
        WVWindowParent1.Width := 1000;
        WVWindowParent1.Height := 500;
        WVBrowser1 := TWVBrowser.Create(WVWindowParent1);
        WVBrowser1.DefaultURL := 'https://google.fr';
        WVBrowser1.OnAfterCreated := TDummy.AfterCreated;
        WVWindowParent1.Browser := WVBrowser1;
     
        if GlobalWebView2Loader.InitializationError then
          showmessage(GlobalWebView2Loader.ErrorMessage)
         else
          if GlobalWebView2Loader.Initialized then
            WVBrowser1.CreateBrowser(WVWindowParent1.Handle);
     
        result := integer(WVWindowParent1);
      except
        showmessage('Error in CreateWebView');
      end;
    end;
    exports CreateWebView;
     
    class procedure TDummy.AfterCreated(Sender: TObject);
    begin
      WVWindowParent1.UpdateSize;
      WVBrowser1.Navigate(WVBrowser1.DefaultURL);
    end;
     
    begin
      GlobalWebView2Loader                := TWVLoader.Create(nil);
      GlobalWebView2Loader.UserDataFolder := ExtractFileDir(Application.ExeName) + '\CustomCache';
      GlobalWebView2Loader.StartWebView2;
     
     
    end.
    Est-ce que vous pouvez me montrer où j'ai fauté ?
    Fichiers attachés Fichiers attachés

  2. #2
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    714
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 714
    Billets dans le blog
    1
    Par défaut
    Ne pas mélanger Parent VCL entre EXE et DLL : Évitez WVWindowParent1.Parent := TWinControl(...) si l'hôte n'est pas dans le même module VCL.
    S'assurer que WebView2Loader est prêt : GlobalWebView2Loader.Initialized = True avant d'appeler CreateBrowser.

    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
    function CreateWebView(aDest: HWND): integer; stdcall; export;
    begin
      Result := 0;
      try
        WVWindowParent1 := TWVWindowParent.Create(nil);
        // Forcer création du handle
        WVWindowParent1.HandleNeeded;
        // Définir parent Windows explicitement
        SetParent(WVWindowParent1.Handle, aDest);
        ShowWindow(WVWindowParent1.Handle, SW_SHOW);
        SetWindowPos(WVWindowParent1.Handle, 0, 10, 50, 1000, 500, SWP_NOZORDER or SWP_SHOWWINDOW);
     
        WVBrowser1 := TWVBrowser.Create(WVWindowParent1);
        WVBrowser1.DefaultURL := 'https://google.fr';
        WVBrowser1.OnAfterCreated := TDummy.AfterCreated;
        WVWindowParent1.Browser := WVBrowser1;
     
        // Attendre que loader soit initialisé
        if GlobalWebView2Loader.InitializationError then
          ShowMessage(GlobalWebView2Loader.ErrorMessage)
        else if GlobalWebView2Loader.Initialized then
          WVBrowser1.CreateBrowser(WVWindowParent1.Handle) ;
     
        Result := Integer(WVWindowParent1);
      except
        ShowMessage('Error in CreateWebView');
      end;
    end;
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  3. #3
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    425
    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 : 425
    Billets dans le blog
    1
    Par défaut
    Merci, XeGregory !

    Tu dis:
    Ne pas mélanger Parent VCL entre EXE et DLL : Évitez WVWindowParent1.Parent := TWinControl(...) si l'hôte n'est pas dans le même module VCL.
    Je comprends. C'était une des multiples tentatives que j'avais faites, mais c'était nul...

    J'ai installé le code que tu as posté. Mais il y a un plantage sur "HandleNeeded".

    De plus, j'ai constaté qu'après l'arrêt du programme principal Delphi, ce dernier reste en mémoire tout en gardant le lien avec la DLL, et il faut le tuer par le gestionnaire de tâches. C'est certainement lié au code placé dans la section d'initialisation.

    J'ai donc fait plusieurs choses pour tenter d'avancer:
    J'ai ajouté une fonction UnloadWebView que j'appelle dans l'évènement OnClose de la Form1 du programme principal. Voici son code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function UnloadWebView:integer; stdcall; export;
    begin
      result := 0;
      if assigned(GlobalWebView2Loader) then begin
        if assigned(WVWindowParent1) then begin
          WVBrowser1.Free;
          WVWindowParent1.Browser := nil;
          WVWindowParent1.Free;
        end;
        GlobalWebView2Loader.Free;
      end;
    end;
    exports UnloadWebView;
    Résultat: une violation d'accès:
    Nom : Crash en clôture du programme principal après WebView.png
Affichages : 120
Taille : 3,7 Ko

    mais après, le programme est parti. Plus besoin de le supprimer. Mais c'est évidemment à corriger.

    Pour montrer comment je crée habituellement des objets dans ma DLL pour les injecter dans le programme principal, j'ai ajouté un bouton "Panel" dans la Form1. Ce bouton crée un petit TPanel avec une caption, puis l'injecte dans la Form1. 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
    function CreatePanel(aDest: HWND): TPanel; stdcall; export;
    begin
      result := nil;
      try
        PA := TPanel.Create(nil);
        PA.ParentWindow := aDest;
        PA.Top := 50;
        PA.Caption := 'Test de Panel';
        result := PA;
      except
      end;
    end;
    exports CreatePanel;
    C'est très basique, mais ça fonctionne. Voici le résultat:
    Nom : Résultat du bouton Panel.png
Affichages : 119
Taille : 3,2 Ko

    Je n'ai pas réussi du tout à faire la même chose avec WebView...

    Voici le code de ma DLL actuelle (avec un commentaire indiquant le lieu du plantage sur HandleNeeded:
    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
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    library WebView4Delphi;
     
    {
      WebView4Delphi
    }
     
    uses
      SysUtils,Classes, Forms, Windows, Dialogs, Controls, ExtCtrls,
     
      uWVBrowserBase, uWVBrowser, uWVWinControl, uWVWindowParent,
      uWVTypes, uWVConstants, uWVTypeLibrary,
      uWVLibFunctions, uWVLoader, uWVInterfaces, uWVCoreWebView2Args;
     
    type
      TDummy = class
        private
        published
          class procedure AfterCreated(Sender: TObject);
      end;
     
    var
      WVWindowParent1: TWVWindowParent;
      WVBrowser1: TWVBrowser;
      PA: TPanel;
     
    {$R *.res}
     
    function CreateWebView(aDest: HWND): TWVWindowParent; stdcall; export;
    begin
      Result := nil;
      try
       WVWindowParent1 := TWVWindowParent.Create(nil);
        // Forcer création du handle
        WVWindowParent1.HandleNeeded;         // <<<<<<============ plantage ici !
        // Définir parent Windows explicitement
        SetParent(WVWindowParent1.Handle, aDest);
        ShowWindow(WVWindowParent1.Handle, SW_SHOW);
        SetWindowPos(WVWindowParent1.Handle, 0, 10, 50, 1000, 500, SWP_NOZORDER or SWP_SHOWWINDOW);
     
        WVBrowser1 := TWVBrowser.Create(WVWindowParent1);
        WVBrowser1.DefaultURL := 'https://google.fr';
        WVBrowser1.OnAfterCreated := TDummy.AfterCreated;
        WVWindowParent1.Browser := WVBrowser1;
     
        // Attendre que loader soit initialisé
        if GlobalWebView2Loader.InitializationError then
          ShowMessage(GlobalWebView2Loader.ErrorMessage)
        else if GlobalWebView2Loader.Initialized then
          WVBrowser1.CreateBrowser(WVWindowParent1.Handle) ;
     
    //    Result := Integer(WVWindowParent1);
        Result := WVWindowParent1;
      except
        ShowMessage('Error in CreateWebView');
      end;
    end;
    exports CreateWebView;
     
    function UnloadWebView:integer; stdcall; export;
    begin
      result := 0;
      if assigned(GlobalWebView2Loader) then begin
        if assigned(WVWindowParent1) then begin
          WVBrowser1.Free;
          WVWindowParent1.Browser := nil;
          WVWindowParent1.Free;
        end;
        GlobalWebView2Loader.Free;
      end;
    end;
    exports UnloadWebView;
     
    class procedure TDummy.AfterCreated(Sender: TObject);
    begin
      WVWindowParent1.UpdateSize;
      WVBrowser1.Navigate(WVBrowser1.DefaultURL);
    end;
     
    function CreatePanel(aDest: HWND): TPanel; stdcall; export;
    begin
      result := nil;
      try
        PA := TPanel.Create(nil);
        PA.ParentWindow := aDest;
        PA.Top := 50;
        PA.Caption := 'Test de Panel';
        result := PA;
      except
      end;
    end;
    exports CreatePanel;
     
     
    begin
      GlobalWebView2Loader                := TWVLoader.Create(nil);
      GlobalWebView2Loader.UserDataFolder := ExtractFileDir(Application.ExeName) + '\CustomCache';
      GlobalWebView2Loader.StartWebView2;
     
     
    end.
    Le projet global est dans le ZIP ci-joint.

    Remarque supplémentaire:
    Pour créer dynamiquement des composants non-fenêtrées (TImage, ...) programmatiquement dans une DLL, j'ai l'habitude de créer un TPanel que j'injecte dans la Form du programme appelant (ou dans un TAB, dans un Container ou tout autre objet ayant un handle), puis je crée l'objet non fenêtré et je l'injecte dans ce TPanel que j'ai créé. Ca marche très bien.

    C'est pourquoi je supposais que TWVWindowParent pourrait se comporter comme un TPanel, mais ça n'a pas l'air d'être le cas.
    Fichiers attachés Fichiers attachés

  4. #4
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    714
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 714
    Billets dans le blog
    1
    Par défaut
    Tu as testé de créer le parent VCL en le parentant directement au HWND fourni.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    WVWindowParent1 := TWVWindowParent.CreateParented(aDest);
    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
    function CreateWebView(aDest: HWND): TWVWindowParent; stdcall; export;
    begin
      Result := nil;
      try
        // Crée le parent VCL déjà attaché au HWND fourni
        WVWindowParent1 := TWVWindowParent.CreateParented(aDest);
     
        // Positionner et afficher (CreateParented crée déjà le handle)
        WVWindowParent1.SetBounds(10, 50, 1000, 500);
        WVWindowParent1.Show;
     
        // Créer et configurer le browser
        WVBrowser1 := TWVBrowser.Create(WVWindowParent1);
        WVBrowser1.DefaultURL := 'https://google.fr';
        WVBrowser1.OnAfterCreated := TDummy.AfterCreated;
        WVWindowParent1.Browser := WVBrowser1;
     
        // S'assurer que le loader est initialisé avant CreateBrowser
        if GlobalWebView2Loader.InitializationError then
          ShowMessage(GlobalWebView2Loader.ErrorMessage)
        else if GlobalWebView2Loader.Initialized then
          WVBrowser1.CreateBrowser(WVWindowParent1.Handle);
     
        Result := WVWindowParent1;
      except
        ShowMessage('Error in CreateWebView');
      end;
    end;
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  5. #5
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    425
    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 : 425
    Billets dans le blog
    1
    Par défaut
    Oui, je l'avais essayé. Je viens de remettre cette version en place? Résultat: pas de plantage, mais AUCUNE réaction. RIEN ne s'affiche.

  6. #6
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    714
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 714
    Billets dans le blog
    1
    Par défaut Vérifier si le problème vient du HWND
    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
    uses
      Windows, SysUtils, Dialogs;
     
    procedure CheckHWND(aDest: HWND);
    var
      pid: DWORD;
      curPid: DWORD;
      styles: LongInt;
    begin
      if aDest = 0 then
        raise Exception.Create('aDest = 0');
     
      if not IsWindow(aDest) then
        raise Exception.Create('Handle invalide (IsWindow = FALSE)');
     
      GetWindowThreadProcessId(aDest, @pid);
      curPid := GetCurrentProcessId;
     
      styles := GetWindowLong(aDest, GWL_STYLE);
     
      ShowMessage(Format('HWND ok. PID=%d, CurrentPID=%d, Styles=0x%x',
        [pid, curPid, styles]));
     
      if pid <> curPid then
        ShowMessage('HWND appartient à un autre processus : impossible d''attacher un contrôle.');
     
      if (styles and WS_CHILD) = 0 then
        ShowMessage('WS_CHILD absent.');
     
      if (styles and WS_VISIBLE) = 0 then
        ShowMessage('WS_VISIBLE absent.');
    end;
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  7. #7
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    425
    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 : 425
    Billets dans le blog
    1
    Par défaut
    J'ai essayé ta routine de test.

    Tout d'abord une remarque: si le handle appartient à un autre process, cela n'empêche rien ! Je peux y injecter mes contrôles ce que je fais de temps en temps !

    Ceci dit, voici ce que j'ai fait:
    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
    procedure CheckHWND(aDest: HWND);
    var
      pid: DWORD;
      curPid: DWORD;
      styles: LongInt;
    begin
      if aDest = 0 then
        raise Exception.Create('aDest = 0');
     
      if not IsWindow(aDest) then
        raise Exception.Create('Handle invalide (IsWindow = FALSE)');
     
      GetWindowThreadProcessId(aDest, @pid);
      curPid := GetCurrentProcessId;
     
      styles := GetWindowLong(aDest, GWL_STYLE);
     
      ShowMessage(Format('HWND ok. PID=%d, CurrentPID=%d, Styles=0x%x',
        [pid, curPid, styles]));
     
      if pid <> curPid then
        ShowMessage('HWND appartient à un autre processus : impossible d''attacher un contrôle.');
     
      if (styles and WS_CHILD) = 0 then
        ShowMessage('WS_CHILD absent.');
     
      if (styles and WS_VISIBLE) = 0 then
        ShowMessage('WS_VISIBLE absent.');
    end;
     
    function CreateWebView(aDest: HWND): TWVWindowParent; stdcall; export;
    begin
      Result := nil;
      CheckHWND(aDest);
      try
        // Crée le parent VCL déjà attaché au HWND fourni
        WVWindowParent1 := TWVWindowParent.CreateParented(aDest);
     
        // Positionner et afficher (CreateParented crée déjà le handle)
        WVWindowParent1.SetBounds(10, 50, 1000, 500);
        WVWindowParent1.Show;
     
        // Créer et configurer le browser
        WVBrowser1 := TWVBrowser.Create(WVWindowParent1);
        WVBrowser1.DefaultURL := 'https://google.fr';
        WVBrowser1.OnAfterCreated := TDummy.AfterCreated;
        WVWindowParent1.Browser := WVBrowser1;
     
        // S'assurer que le loader est initialisé avant CreateBrowser
        if GlobalWebView2Loader.InitializationError then
          ShowMessage(GlobalWebView2Loader.ErrorMessage)
        else if GlobalWebView2Loader.Initialized then
          WVBrowser1.CreateBrowser(WVWindowParent1.Handle);
     
        Result := WVWindowParent1;
      except
        ShowMessage('Error in CreateWebView');
      end;
    end;
    exports CreateWebView;
    Je reçois 2 messages successifs:
    d'abord:
    Nom : Capture d'écran 2026-03-03 012722.png
Affichages : 100
Taille : 14,8 Ko

    puis:
    Nom : Capture d'écran 2026-03-03 012752.png
Affichages : 99
Taille : 11,9 Ko

  8. #8
    Membre expérimenté
    Avatar de XeGregory
    Homme Profil pro
    Passionné par la programmation
    Inscrit en
    Janvier 2017
    Messages
    714
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Passionné par la programmation
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 714
    Billets dans le blog
    1
    Par défaut
    Votre TPanel fonctionne parce que c’est un composant VCL simple qui n’utilise ni COM, ni runtime externe, et qui accepte d’être parenté à une fenêtre distante quand vous êtes injecté dans le même processus.

    WebView2, lui, n’est pas équivalent : il exige initialisation COM (STA), le WebView2 loader/runtime, des callbacks asynchrones, et une création sur le thread UI du processus qui héberge la fenêtre.

    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
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    function HRESULTToStr(hr: HRESULT): string;
    begin
      Result := Format('0x%x (%d)', [hr, hr]);
    end;
     
    function CreateHostWindowOverTarget(TargetHWND: HWND): HWND;
    var
      r: TRect;
    begin
      Result := 0;
      if not IsWindow(TargetHWND) then Exit;
      GetWindowRect(TargetHWND, r);
     
      Result := CreateWindowEx(
        WS_EX_TOOLWINDOW,
        'STATIC',
        nil,
        WS_POPUP or WS_VISIBLE,
        r.Left, r.Top,
        r.Right - r.Left, r.Bottom - r.Top,
        0, 0, HInstance, nil
      );
     
      if Result = 0 then
      begin
        ShowMessage(Format('Échec CreateWindowEx : code %d', [GetLastError]));
        raise Exception.CreateFmt('CreateWindowEx failed: %d', [GetLastError]);
      end;
     
      SetWindowPos(Result, TargetHWND, r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top,
        SWP_NOACTIVATE or SWP_SHOWWINDOW);
      ShowMessage(Format('Fenêtre hôte créée à %d,%d taille %dx%d', [r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top]));
    end;
     
    procedure EnsureCOMandThread;
    var
      res: HRESULT;
    begin
      if TThread.CurrentThread.ThreadID <> MainThreadID then
        raise Exception.Create('CreateWebView doit être exécuté sur le thread UI principal');
     
      res := CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
      if Failed(res) and (res <> RPC_E_CHANGED_MODE) then
        raise Exception.CreateFmt('CoInitializeEx a échoué : %s', [HRESULTToStr(res)]);
      ShowMessage('CoInitializeEx (STA) effectué');
    end;
     
    function CreateWebView(aDest: HWND): Pointer; stdcall;
    var
      hostHWND: HWND;
      pid, curPid: DWORD;
      styles: LongInt;
      hr: HRESULT;
    begin
      Result := nil;
      try
        if aDest = 0 then
          raise Exception.Create('aDest = 0');
     
        if not IsWindow(aDest) then
          raise Exception.Create('Handle invalide (IsWindow = FALSE)');
     
        GetWindowThreadProcessId(aDest, @pid);
        curPid := GetCurrentProcessId;
        styles := GetWindowLong(aDest, GWL_STYLE);
     
        ShowMessage(Format('HWND cible OK. PID=%d, PID courant=%d, Styles=0x%x', [pid, curPid, styles]));
     
        if pid <> curPid then
        begin
          ShowMessage('La fenêtre cible appartient à un autre processus -> création d''une fenêtre hôte locale');
          hostHWND := CreateHostWindowOverTarget(aDest);
        end
        else
        begin
          if (styles and WS_CHILD) = 0 then
          begin
            ShowMessage('La fenêtre cible n''a pas WS_CHILD -> création d''une fenêtre hôte locale');
            hostHWND := CreateHostWindowOverTarget(aDest);
          end
          else
            hostHWND := aDest;
        end;
     
        EnsureCOMandThread;
     
        // Créer le parent VCL parented sur hostHWND
        WVWindowParent1 := TWVWindowParent.CreateParented(hostHWND);
        if WVWindowParent1.Handle = 0 then
          raise Exception.Create('Handle de WVWindowParent invalide après CreateParented');
     
        WVWindowParent1.SetBounds(0, 0, 1000, 600);
        WVWindowParent1.Show;
        ShowMessage(Format('WVWindowParent créé. Handle=0x%x', [WVWindowParent1.Handle]));
     
        // Créer le browser
        WVBrowser1 := TWVBrowser.Create(WVWindowParent1);
        WVBrowser1.DefaultURL := 'https://google.fr';
        WVBrowser1.OnAfterCreated := TDummy.AfterCreated;
        WVWindowParent1.Browser := WVBrowser1;
     
        // Vérifier le loader WebView2
        if GlobalWebView2Loader.InitializationError then
          raise Exception.Create('Erreur du loader WebView2 : ' + GlobalWebView2Loader.ErrorMessage);
     
        if not GlobalWebView2Loader.Initialized then
          raise Exception.Create('Le loader WebView2 n''est pas initialisé');
     
        // CreateBrowser retourne un HRESULT ; on l'affiche
        hr := WVBrowser1.CreateBrowser(WVWindowParent1.Handle);
        ShowMessage('CreateBrowser a retourné ' + HRESULTToStr(hr));
        if Failed(hr) then
          raise Exception.CreateFmt('CreateBrowser a échoué : %s', [HRESULTToStr(hr)]);
     
        Result := Pointer(WVWindowParent1);
        ShowMessage('CreateWebView réussi');
      except
        on E: Exception do
        begin
          ShowMessage('Exception dans CreateWebView : ' + E.Message);
          try
            if Assigned(WVBrowser1) then FreeAndNil(WVBrowser1);
            if Assigned(WVWindowParent1) then FreeAndNil(WVWindowParent1);
          except end;
          raise;
        end;
      end;
    end;
     
    exports
      CreateWebView;
     
    end.
    On ne peut pas faire confiance à un code qu'on n'a pas entièrement écrit soi‑même, et encore moins à celui qu'on a écrit entièrement. :aie:

  9. #9
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    425
    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 : 425
    Billets dans le blog
    1
    Par défaut
    Dommage - ça ne compile pas avec Delphi 6...

    Voici la section qui pose problème:
    Nom : Capture d'écran 2026-03-03 103350.png
Affichages : 81
Taille : 22,7 Ko
    et la ligne 139 est:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        hr := WVBrowser1.CreateBrowser(WVWindowParent1.Handle);
    J'ai bien ajouté ComObj à la clause "Uses", mais CurrentThread et COINIT_APARTMENTTHREADED n'existent pas en Delphi 6.

  10. #10
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 201
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 201
    Par défaut
    GetCurrentThreadId devrait faire l'affaire en D6
    COINIT_APARTMENTTHREADED = 0x2 et c'est apparu en Windows 2000, trop juste pour que cela existe en D6,
    Je viens de voir que tous mes utilisations de tmApartment c'est côté Server OLE (code généré du TAutoObjectFactory)
    CoInitializeEx retourne un HRESULT, c'est la fonction Failed qui aide pour savoir si c'est une erreur ou pas.

    Surement la variable hr qui n'est pas du bon type, sur git, c'est boolean le retour de CreateBrowser.

    Vérifie ta version si CreateBrowser de TWVBrowser (héritée de TWVBrowserBase) pose problème, XeGregory n'utilise peut-être pas la même version de WebView4Delphi que toi, ou alors c'est du codé généré par de l'IA qui a nous a produit une petite hallucination.
    Regarde ce repo : Delphi 6 VER140 est supporté, cela retourne un boolean
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  11. #11
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    425
    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 : 425
    Billets dans le blog
    1
    Par défaut
    Je vous remercie tous les deux, sincèrement, d'avoir passé du temps pour m'aider.
    Malheureusement, ce n'était pas tout à fait la bonne voie.

    J'ai fini par l'adresser au forum propre au composant WebView4Delphi.
    Dans la documentation, ils disaient que cela devrait marcher pour Delphi 6, mais que ce n'était testé qu'à partir de Delphi 7.

    J'avais réussi à le faire marcher avec Delphi 6 PE, directement dans un programme sans utilisation d'une DLL.

    J'ai présenté ma solution à ce forum et j'ai demandé s'i quelqu'un connaissait la solution pour créer ce composant programmatiquement dans une DLL et l'injecter dans une form d'un programme externe à la DLL.

    Leur réponse était un exemple de création programmatique dans une DLL, dynamiquement, mais sans injection dans une autre form. Le tout était en Delphi 7.

    Après étude de cet exemple, j'ai créé ma propre DLL en D6. J'arrive maintenant à créer ce composant dans la DLL, j'ai trouvé comment l'injecter dans ma form principale d'un programme externe, tout en lui imposant la position dans la form et ses dimensions. La position dans form était curieusement le plus compliqué à obtenir.

    J'ai ajouté le choix d'une URL à charger, et la possibilité d'ouvrir un fichier local. J'ai aussi ajouter des ScrollBar, horizontale et verticale. Maintenant, je peux charer mon fichier SVG de 40.000 pixels de marge, et je peux le faire défiler horizontalement et verticalement, sans problème.

    Vous trouverez en annexe un fichier ZIP avec le projet complet, y compris le fichier dessin.htm qui contient le fichier SVG, insi qu'un fichier JavaScript réalisant certaines opérations sur le fichier SVG (le scrolling par exemple).

    Voilà. Pour moi, me problème est résolu, et je remercie encore une fois les deux spécialistes qui ont bien voulu m'aider. J'ai beaucoup appris, même si, au final, l'indication décisive venait d'ailleurs. Mais cela n'enlève rien au travail accompli pour lequel je reste sincèrement reconnaissant.
    Fichiers attachés Fichiers attachés

  12. #12
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    425
    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 : 425
    Billets dans le blog
    1
    Par défaut
    J'ai réussi à placer les composants de WebView4Delphi dans une Delphi 6 PE DLL et de les utiliser par un programme principal en Delphi 6 PE.

    J'ai posté le projet complet avec sources, binaires et fichiers de démo, dans GitHub.com, accessible par le lien suivant:
    https://github.com/klausgunther/WebV...r-Delphi-6-PE-

    Toutes les fonctionnalités ne sont pas encore disponibles, mais le travail a suffisamment progressé pour montrer l'essentiel.
    L'archive GitHub contient le code source complet, les binaires et des fichiers exemple.

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

Discussions similaires

  1. Réponses: 14
    Dernier message: 24/11/2023, 21h24
  2. Réponses: 4
    Dernier message: 28/12/2018, 09h26
  3. Réponses: 3
    Dernier message: 07/02/2007, 18h39
  4. Réponses: 5
    Dernier message: 01/11/2006, 17h04
  5. Réponses: 6
    Dernier message: 29/06/2006, 14h54

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