Bar de progression lors de l'installation d'un MSI

Résolu
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009 - 30 janv. 2007 à 11:15
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009 - 2 févr. 2007 à 11:19
Bonjour à tous, c'est mon premier post ici...


Voilà ma question, je suis en train de réaliser un setup avec plusieurs
pages: bienvenue, saisie d'information, acceptation de licence, etc...

Puis ma derniere doit representer une bar de progression qui tourne
jusqu'à ce que l'installation du .msi qui va avec soit terminée.

Pour executer le .msi je passe une execution de console comme dans cmd avec la commande msiexec, la ça marche!


Mon soucis est je ne sais pas trop comment réaliser cette bar de
progression qui tourne jusqu'à ce que l'installation soit terminée...


Pouvez vous s'il vous plait m'indiquer la marche à suivre, ou me dirigez vers des liens qui pourraient m'aider.

Merci d'avance.

38 réponses

Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
30 janv. 2007 à 13:37
en fait pour que ça fonctionne il faut impérativement avoir le paramètre "/c"  et pas le /k je me suis planté sur ce coup la

Ici tu démares ta ProbressBar
ShellExecute(Handle, Nil, 'CMD' , '/c "start /wait Ici le nom du prog a lancer"', Nil , SW_HIDE);
Ici tu la termines

donc tu lance la ProgressBar ensuite la commande
et grâce au paramètres '/c "start /wait
la ligne suivante ne sera pas executé avant la fin de la commande
tout simplement

 
@+
Cirec

<hr size="2" />
3
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
30 janv. 2007 à 17:56
Salut,

Déjà, bienvenue sur Codes-Sources !

Pour ton problème de CreateProcess(), tu en as un très bon exemple ici (http://delphi.developpez.com/faq/?page=interexecution#lancercontroleapplication) qui fait justement ce que tu veux: attendre la fin d'execution du programme.

Après, pour avoir une progressbar qui "tourne en rond", Windows propose déjà une façon très simple de le faire, mais malheureusement, Delphi ne le gère pas nativement, il va falloir ajouter cette fonctionnalité.

Explications:
Tu crée une nouvelle unité contenant :
-------------------------------
unit NewProgressBar;

interface

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

type
TProgressBar = class(ComCtrls.TProgressBar)
public
procedure CreateParams(var Params: TCreateParams); override;
procedure BeginAnim(Delay: Integer);
procedure StopAnim;
end;

implementation

const
PBS_MARQUEE = $08;
PBM_SETMARQUEE = WM_USER + 10;

procedure TProgressBar.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or PBS_MARQUEE;
end;

procedure TProgressBar.BeginAnim(Delay: Integer);
begin
SendMessage(Handle, PBM_SETMARQUEE, 1, Delay);
end;

procedure TProgressBar.StopAnim;
begin
SendMessage(Handle, PBM_SETMARQUEE, 0, 0);
end;
-------------------------------

Cette unité te parmet d'ajouter en RunTime la fonctionnalité que tu recherches. (Attention: elle doit être la DERNIERE unité de ta clause uses ET dans la partie interface)

Tu appelles ProgressB.BeginAnim(200) après avoir executé CreateProcess() et ProgressB.StopAnim() après que le .msi se soit executé pour stopper l'animation.

Voila, j'èspère avoir été clair.
A+
Flo
3
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
31 janv. 2007 à 14:10
Salut,

Déjà, il faut savoir que ce "truc" pour coder une progressbar spéciale est une actuce pour rajouter des fonctionalités dans Delphi. En *RunTime* signifie qu'il n'y a aucun changement en conception (lorsque tu ajoutes et modifies les composants de ta fiche): la seule différence est à l'execution. Mais à la limite, ce n'est pas le plus important.

Alors ensuite, pour placer ton code: tout dépend comment tu organises cela.
Si ta fiche n°4 (celle de scr4.pas) ne sert qu'a afficher l'avancement, tu mets ProgressB.BeginAnim(200) dans le OnShow de la fiche et ProgressB.StopAnim dans le OnHide (évenements accessibles depuis l'éditeur de propriétés par double click dessus). "ProgressB" étant un TProgressBar dans la fiche Form4.

Après, dans main1.pas, à l'endroit voulu:
- tu appelles scr4.Form4.Show()
- tu effectues ton CreateProcess() et tu attends la fin de l'exécution de l'application (voir lien que je t'ai donné sur la FAQ Delphi)
- et enfin, tu appelles scr4.Form4.Hide pour arreter l'animation.

L'unité "NewProgressBar" doit être ajoutée en fin des uses de l'unité qui contient la fiche qui contient la ProgressBar, évidemment.

Voila ! Bonne chance !
3
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
31 janv. 2007 à 16:53
hmmmm, bon code, c'est bien ce que je t'avais dit ...
Par contre, j'avais soublié un truc: en faisant WaitForSingleObject(), tu freeze l'application, c'est donc normal que rien ne s'affiche.

Il faut changer cela et appeler Application.ProcessMessages régulièrement (toutes les 100 ms).

Il faut donc que tu remplaces WaitForSingleObject(proc_info.hProcess, INFINITE) par un bout de code un peu plus long (nécéssite "Forms" dans les "uses"):

-----------
while True do
begin
Application.ProcessMessages;
if WaitForSingleObject(proc_info.hProcess, 100) <> WAIT_TIMEOUT then
Break;
end;
----------

Et là, ça devrait marcher. Le reste me semble tout à fait correct.

Une autre solution consisterait à ne plus utiliser WaitForSingleObject mais un timer qui teste périodiquement si l'application est en cours mais dans ton cas, comme tu n'as pas l'air de devoir pouvoir abandonner l'execution, ç'est OK.

A+
Flo
3

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

Posez votre question
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
31 janv. 2007 à 17:27
je confirme un seul appel à Application.ProcessMessages ne suffit pas

il faut donc utiliser la dernière méthode de Florenth et la ça fonctionne parfaitement
 
@+
Cirec

<hr size="2" />
3
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
31 janv. 2007 à 18:56
le code fonctionne aussi bien avec un XPManifest qu'avec la ressource

 
@+
Cirec

<hr size="2" />
3
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
1 févr. 2007 à 17:11
Bon j'ai résolu mon probléme, j'indique la marche à suivre que j'ai suivi au cas où certains rencontrent le même probleme que moi.

Donc dans ma procedure qui execute le processus grâce à createprocess nous avons :

aCmdLine := PChar('cmd /c "start /wait '+Variables.Values['run']); // aCmdLine de type PChar

// ainsi c'est comme si j'executais dans démarrer/executer cmd /c "la ligne de commande"

// et le createProcess ressemble donc par la suite à ça :

CreateProcess(Nil, aCmdLine, Nil, Nil, False, 0, Nil, Nil, StartInfo,Proc_Info)

//*********************************

Voilà bon merci en tout cas à Florenth et Cirec pour l'aide que vous m'avez apporté, c'est génial un grand MERCI à tous les deux !!!!
Pfiou tout marche (pour le moment), j'ai cru que je n'en verrais jamais le bout!!!

PS: au cas où vous sauriez comment masquer (ou même cacher à l'utilisateur) la fenetre cmd toute vide qui s'ouvre lors de l'execution de ma ligne de commande, je suis preneur!!!  Encore merci
3
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
1 févr. 2007 à 17:48
donc sur le même principe avec CreateProcess

pour l'exemple je démarre la calculatrice par l'intermédiaire de CMD
procedure TForm1.Button1Click(Sender: TObject);
var
Si :STARTUPINFO;
Pi : PROCESS_INFORMATION    ;
begin
  zeromemory(@si,sizeof(STARTUPINFO));
// les deux lignes suivantes empêche de voir la fenêtre dos
  Si.dwFlags:= STARTF_USESHOWWINDOW;
  Si.wShowWindow:=SW_HIDE;
// Attention le paramètre /c est indispensable c'est grâce à lui que CMD se referme après l'utilisation

//de la calculatrice ou tout autre commande
  If CreateProcess(Nil,'cmd /c "start /wait %systemroot%\System32\calc.exe"',nil,nil,True, 0,nil,nil,Si,Pi) Then
  Begin
    while True do
    begin
      Application.ProcessMessages;
      if WaitForSingleObject(pi.hProcess, 100) <> WAIT_TIMEOUT then
        Break;
    end;
    CloseHandle(pi.hProcess);
  End;
end;
 
@+
Cirec

<hr size ="2" />
3
cs_Loda Messages postés 814 Date d'inscription vendredi 3 novembre 2000 Statut Membre Dernière intervention 30 juillet 2009 3
30 janv. 2007 à 11:22
salut,

bienvenue sur delphifr - Codes-Sources !

Pour ton problème, je pense pas que tu puisse faire un bar de progression qui indique l'avancement (%) de ton install si l'installation elle-même est faite par un programm tier (msiexec).

Tu peux "tricher" en faissant une bar de progression qui tourne en boucle (comme au démarrage de certain version de windows). ou juste afficher une animation image par image (gif), genre indiquant que le programme est pas planté.

Mais pour avoir une bar indiquant le % restant, je pense pas que cela soit possible si tu lance msiexec. Peut-être qqun d'autre personne aurront une solution magique pour toi...

bonne continuation,

Loda

PS: un très bon (et gratuit) installer : inno setup

<hr size="2" width="100%" />Se poser les bonnes questions est le premier pas pour trouver les bonnes réponses.
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
30 janv. 2007 à 12:03
Oui en effet je ne cherche pas à realiser une progress bar qui indique le pourcentage de l'installation, mais plutot comme tu l'as dit une barre qui tourne en boucle jusqu'à ce que l'installation soit finie.

Mais comment savoir que l'installation est finie, et quand est ce qu'elle part pour démarrer la progressbar??? Là est mon gros probleme (ainsi que faire la progress bar qui tourne indéfiniment, je sais pas faire :s)

Désolé si je n'ai pas était plus clair.
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
30 janv. 2007 à 13:22
Justement, Cirec, je mettais servi de ton code source, qui en passant est trés bien fait chapeau bas :D.
Je l'ai adapté à mon code.

En fait j'extrais la commande à executer dans un .ini que je stock dans une chaine de caractère "run". Cette ligne ressemble à ça :
msiexec /I Agent.msi /qn ...

Puis dans mon code (quand j'arrive au form avec la progress bar) je met :

aCmdLine := PChar(cmdSwitch + run); // aCmdLine étant de type PChar
ShellExecute(Handle, nil, 'CMD', aCmdLine, nil, sw_Hide);

Je n'ai pas encore testé mais ça devrait marché...

Ma question maintenant est que je devrais connaître mon point de départ de mon msiexec (comme tu me la signalé), or je n'en ai aucune idée...:s
Par ailleurs tu dis que CMD me permet également de savoir quand l'éxecution est arrivé à son terme, et ça je ne sais pas comment le savoir...

Désolé de vous embetter mais là je bloque depuis ce matin !!!
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
30 janv. 2007 à 13:42
Ok merci bien, je m'en va essayer tout de suite!
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
30 janv. 2007 à 16:58
Bon... J'ai un nouveau probleme!

Ta commande ShellExecute marche niquel... Mais le soucis est que pour appeler ma page je fais:

scr4.show // scr4 étant le nom de la page où il y a la progressbar

labelscr4:
     scr4.progressbar1.Position:=0;
     scr4.timer1.Enabled:=true;

     aCmdLine := PChar('/c "start /wait '+Variables.Values['run']); //Variables.Values['run'] renvoie la ligne de commande msiexec avec les parametres nécessaires.

     with scr4 do
     begin
        progressbar1.Position:=progressbar1.Position+1;;//mise à jour toutes les secondes en 5 secondes
        timer1.Enabled:=true;

        ShellExecute(Handle, Nil, 'CMD' , aCmdLine, Nil , SW_HIDE);
        if progressbar1.Position>= progressbar1.max  then timer1.Enabled:=false;// on arrete le timer à la fin
    end;
ShowMessage('ok!');

Donc deja comme vous pouvez le constater je ne SAIS PAS faire tourner la progressbar en boucle, et le msi s'execute ailleurs, ce qui fait que je vais direct au ShowMessage('ok!'); donc la progressbar n'a pas le temps de tourner en boucle...

C'est la galère....
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
30 janv. 2007 à 17:15
Bon puisque l'execmsi se lance dans un nouveau processus et que je doit attendre qu'il se finisse, peut être que je vais passer par un createprocess pour pouvoir attendre la fin et ainsi arréter la progressbar.

Mais je ne sais toujours pas comment faire tourner la progressbar indéfiniment, et j'hésite pour utiliser le createprocess car il faut que je passe des infos lorsque j'execute le msi, je m'explique par un exemple, la ligne de commande que je doit rentrer ressemble à ça:

msiexec /I AgentSetup.msi /qn FIRSTBACKUP=0 EMAILADRESS=%UserMail% PSWD=%UserPassword%

Or je ne sais pas s'il est possible de passer FIRSTBACKUP / EMAILADRESS / PSWD avec un createprocess... Si oui comment on fait siouplé...????
0
florenth Messages postés 1023 Date d'inscription dimanche 1 août 2004 Statut Membre Dernière intervention 17 août 2008 3
30 janv. 2007 à 17:57
Ah, un détail cependant: sous Windows XP et Vista uniquement. Il me semble pas que cela fonctionne sous un autre NT.

A+
0
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
30 janv. 2007 à 19:54
@ Florenth : chouette cette astuce .. encore une que ne connaissais pas

Mais j'ai remarqué que ce code ne fonctionne qu'avec un XPManifest

@ ObitSkater :  ton problème d'affichage viens d'un mauvais  "placement" de ton code 

la mise à jour de ta PBar devrait se trouver dans l'évennement OnTimer dans la Form4
dans le OnShow de la Form4 tu mets Timer1.Enabled := True;
et dans le OnHide : Timer1.Enabled := False;

Ensuite tu fais l'appel depuis ta form principale

aCmdLine := PChar('/c "start /wait '+Variables.Values['run']);
Scr4.Show;
Application.ProcessMessages;
ShellExecute(Handle, Nil, 'CMD' , aCmdLine, Nil , SW_HIDE);
Src4.Hide;

Mais la solution du CreateProcess est quand même meilleur
 
@+
Cirec

<hr size="2" />
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
31 janv. 2007 à 12:42
@Flo : Merci bien pour ton aide, ce code a l'air trés bien, mais il y a quelques points sur lesquels je bloque...
J'ai créé cette nouvelle unité NewProgressBar (comme tu m'as dit), mais je ne sais pas vraiment à quel endroit faire les appels. Je m'explique, Toutes mes forms sont créées, mais il m'a était demandé de mettre tout le code dans une unité indépendante : main1.pas, dans lequel je fais les appels successifs de chaque form.

Ma Progress Bar se trouve (comme précisé plus haut) dans le 4° form : scr4. Ainsi je compte, lors de l'appel de scr4 (par un scr4.Show) dans main1.pas faire mon create process, et je souhaite faire les appels nécessaires à ton unité à ce moment là.
Mais je ne sais pas si je dois faire ces appels dans main1.pas OU le form scr4 ???

Par ailleurs je ne vois pas trop comment bien "syntaxer" ces appels. Tu me dis d'appeler comme ceci: ProgressB.BeginAnim(200) par exemple, mais est ce que ProgressB correspond au nom de la progress bar de scr4??? Donc suivant où je dois faire mes appels, pourrais tu m'indiquer la bonne syntaxe que je dois adopter s'il te plaît.

Par ailleurs tu me parles de RunTime et ceci est un peu (voir beaucoup :p) flou pour moi (j'ai commencé le delphi il y a un mois dans le cadre de mon stage). Pourrais tu également s'il te plait m'éclairer à ce sujet.

De plus tu me dis que l'unité NewProgressBar doit être la DERNIERE unité de ta clause uses ET dans la partie interface, mais dans quel .pas??? Main.pas ou scr4???????

je tourne en rond j'en peu plus :'(

En tout cas merci énormément à tous pour l'aide que vous m'avez apporté jusque là!!!
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
31 janv. 2007 à 14:20
Trés bien je te remercie, là ça va mieux, pfiou....

Reste plus qu'à attendre que delphi.developpez.com/ remarche (car inaccessible pour le moment) pour voir les explications de CreateProcess et je pourrais tester tout ça.

En attendant je me balade sur delphi3000.com voir ce qu'il propose sur le CreateProcess.

En tout cas merci c'est génial j'ai bon espoir!!!
0
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
31 janv. 2007 à 14:33
N'oublis pas de déposer un XPManifest sur ta form sinon le "truc" de Florenth ne fonctionnera pas

 
@+
Cirec

<hr size="2" />
0
obitskater Messages postés 45 Date d'inscription lundi 29 janvier 2007 Statut Membre Dernière intervention 13 mars 2009
31 janv. 2007 à 14:35
XPManifest??? Qu'est ce et comment faire...???

PS: merci de la patience dont vous faites preuve avec moi :D
0
Rejoignez-nous