Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

COMPOSANT RUNONCE: AUTORISER UN SEUL LANCEMENT D'UNE APPLICATION (OU NON!)


Information sur la source

Catégorie :Système Classé sous : runonce, mutex, pipe Niveau : Initié Date de création : 05/10/2006 Date de mise à jour : 05/10/2006 22:17:19 Vu / téléchargé: 5 296 / 627

Note :
9 / 10 - par 2 personnes
9,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (9)
Ajouter un commentaire et/ou une note

Description

Un petit composant (un de plus!) pour n'autoriser qu'un seul lancement d'une application. Je sais qu'il en existe déjà plein, mais celui-ci a un design particulier que je n'ai pas trouvé dans les autres: il est possible, lors du lancement d'une 2ème instance, de récupérer sa ligne de commande depuis la première instance, et de choisir si on en autorise le lancement ou non.

Un scénario tout bête: on imagine qu'on a programmé un éditeur multi-document associé au type de fichier *.xxx
-L'utilisateur double-clique sur le fichier a.xxx (donc une première instance de l'éditeur se lance et ouvre le fichier a.xxx).
-L'utilisateur double-clique sur le fichier b.xxx, une 2ème instance de l'éditeur est bloquée par la première qui, en récupérant la ligne de commande de l'instance bloquée, ouvre un 2ème fichier: b.xxx
-L'utilisateur lance manuellement l'éditeur (sans paramètre, donc). Cette fois-ci, on décide que la 2ème instance ne sera pas bloquée (en effet, si l'utilisateur choisit de le faire explicitement, c'est qu'il a une bonne raison de le faire a priori...)

Mon composant permet donc de faire tout cela simplement par l'intermédiaire de con événement OnBlockInstance. Avant la création de la fiche principale (c'est à dire dans le source du programme même, avant la ligne Application.Initialize) il faut rajoutter ceci:

  if not TestRunOnce('Nom') then
    Exit;

A la place de 'Nom' il faut mettre un identificateur unique pour votre application. Voilà la définition de la classe TRunonce:

  TBlockInstanceEvent=procedure(Sender:TObject;Params:TStrings;var Allow:Boolean) of object;

  TRunOnce=class(TComponent)
  public
    constructor Create(AOwner:TComponent);override;

    function IsMaster: Boolean;
    
    destructor Destroy;override;
  published
    property OnBlockInstance:TBlockInstanceEvent read FOnBlockInstance write SetOnBlockInstance;
    property OnAcquireLock:TNotifyEvent read FOnAcquireLock write SetOnAcquireLock;
  end;





REGLES D'UTILISATION:
+ Une seule instance du composant peut être créée par application.
+ Avant toute création d'une instance du composant, la fonction TestRunOnce doit être appelée. Sinon, une exception est déclenchée.
+ La fonction TestRunOnce ne peut être appelée qu'une seule fois.

Le non-respect d'une seule de ces règle entraîne une exception EInvalidOperation
 

Source

  • { Code-source du programme }
  • program Project1;
  • uses
  • Forms,
  • RunOnce,
  • Unit1 in 'Unit1.pas' {Form1};
  • {$R *.res}
  • begin
  • { Il faut ABSOLUMENT que la fonction TestRunOnce soit appelée avant la création du composant TRunOnce. Sinon, une exception est déclenchée.
  • Son résultat permet de savoir si on est la première instance lancée, ou si la première instance a autorisé le lancement }
  • if not TestRunOnce('TestRunOnce') then
  • Exit;
  • Application.Initialize;
  • Application.CreateForm(TForm1, Form1);
  • Application.Run;
  • end.
  • { Code-source de la fiche principale (celle qui crée le composant) }
  • unit Unit1;
  • interface
  • uses
  • Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  • Dialogs, RunOnce;
  • type
  • TForm1 = class(TForm)
  • procedure RunOnce1BlockInstance(Sender: TObject; Params: TStrings;
  • var Allow: Boolean);
  • procedure RunOnce1AcquireLock(Sender: TObject);
  • procedure FormCreate(Sender: TObject);
  • private
  • public
  • RunOnce1: TRunOnce;
  • end;
  • var
  • Form1: TForm1;
  • implementation
  • {$R *.dfm}
  • procedure TForm1.RunOnce1BlockInstance(Sender: TObject; Params: TStrings;
  • var Allow: Boolean);
  • begin
  • { événement appelé lors du lancement d'une 2ème instance du programme. On pose la question à l'utilisateur pour autoriser
  • ou non le 2ème lancement. Params contient la ligne de commande qui a servi à lancer cette 2ème instance }
  • if MessageBox(0,
  • PChar('Voulez vous autoriser le 2ème lancement de "'+Params[0]+'" ?'),
  • 'Confirmation',
  • MB_YESNO or MB_ICONQUESTION or MB_SYSTEMMODAL)=ID_YES then
  • Allow:=True;
  • end;
  • procedure TForm1.RunOnce1AcquireLock(Sender: TObject);
  • begin
  • { événement appelé lorsque l'instance en cours devient celle qui décide si d'autres ont le droit de se lancer ou non.
  • Ce cas peut se produire si une ou plusieurs instances ont été autorisées, et si la première instance
  • (celle qui les a autorisées) a été fermée avant les autres. }
  • Caption:='INSTANCE MAITRE';
  • end;
  • procedure TForm1.FormCreate(Sender: TObject);
  • begin
  • { Le composant est créé à l'exécution pour ne pas être obligé d'installer le package pour compiler l'exemple }
  • RunOnce1:=TRunOnce.Create(Self);
  • RunOnce1.OnBlockInstance:=RunOnce1BlockInstance;
  • RunOnce1.OnAcquireLock:=RunOnce1AcquireLock;
  • if RunOnce1.IsMaster then
  • Caption:='INSTANCE MAITRE';
  • end;
  • end.
{ Code-source du programme }

program Project1;

uses
  Forms,
  RunOnce,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  { Il faut ABSOLUMENT que la fonction TestRunOnce soit appelée avant la création du composant TRunOnce. Sinon, une exception est déclenchée.
    Son résultat permet de savoir si on est la première instance lancée, ou si la première instance a autorisé le lancement }
  if not TestRunOnce('TestRunOnce') then
    Exit;

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.




{ Code-source de la fiche principale (celle qui crée le composant) }

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, RunOnce;

type
  TForm1 = class(TForm)
    procedure RunOnce1BlockInstance(Sender: TObject; Params: TStrings;
      var Allow: Boolean);
    procedure RunOnce1AcquireLock(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  public
    RunOnce1: TRunOnce;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.RunOnce1BlockInstance(Sender: TObject; Params: TStrings;
  var Allow: Boolean);
begin
  { événement appelé lors du lancement d'une 2ème instance du programme. On pose la question à l'utilisateur pour autoriser 
    ou non le 2ème lancement. Params contient la ligne de commande qui a servi à lancer cette 2ème instance }
  if MessageBox(0,
                PChar('Voulez vous autoriser le 2ème lancement de "'+Params[0]+'" ?'),
                'Confirmation',
                MB_YESNO or MB_ICONQUESTION or MB_SYSTEMMODAL)=ID_YES then
    Allow:=True;
end;

procedure TForm1.RunOnce1AcquireLock(Sender: TObject);
begin
  { événement appelé lorsque l'instance en cours devient celle qui décide si d'autres ont le droit de se lancer ou non.
    Ce cas peut se produire si une ou plusieurs instances ont été autorisées, et si la première instance
    (celle qui les a autorisées) a été fermée avant les autres. }
  Caption:='INSTANCE MAITRE';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  { Le composant est créé à l'exécution pour ne pas être obligé d'installer le package pour compiler l'exemple }
  RunOnce1:=TRunOnce.Create(Self);
  RunOnce1.OnBlockInstance:=RunOnce1BlockInstance;
  RunOnce1.OnAcquireLock:=RunOnce1AcquireLock;
  if RunOnce1.IsMaster then
    Caption:='INSTANCE MAITRE';
end;

end.

Conclusion

Commentaires bienvenus         ;-)

Quelques mots sur la façon dont le tout est gèré:
La synchronisation entre processus se fait par l'intermédiaire de 2 mutex nommés (leurs noms incluent la chaîne passée à TestRunOnce).
La récupération des paramètres de la ligne de commande se fait par l'intermédiaire d'un pipe nommé.
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Historique

05 octobre 2006 17:46:47 :
Typo
05 octobre 2006 17:49:04 :
typo2
05 octobre 2006 22:17:19 :
Zip

Commentaires et avis

signaler à un administrateur
Commentaire de cirec le 05/10/2006 20:54:09 administrateur CS

Salut,

un petit commentaire (c'est pas celui que tu attendais :-) pas encore) :
Il y a beaucoup de fichiers inutile dans ton archive !

Pour le code je regarde ça ...

@+
Cirec

signaler à un administrateur
Commentaire de cirec le 05/10/2006 21:21:35 administrateur CS

j'ai un problème ... je n'arrive pas à compiler le code :

ConnectionBuffer.pas(239)
Connection.pas(210)
PipeConnection.pas(118)
Server.pas(173)
PipeServer.pas(97)
Mutex.pas(91)
RunOnce.pas(78) Erreur: E2250 Aucune version surchargée de 'Create' ne peut être
appelée avec ces arguments
RunOnce.pas(79) Erreur: E2003 Identificateur non déclaré : 'Locked'
RunOnce.pas(80) Erreur: E2003 Identificateur non déclaré : 'Enter'

je n'ai rien trouvé de spécial pourtant il ne semble pas accepter la chose ?

@+
Cirec

signaler à un administrateur
Commentaire de Forman le 05/10/2006 22:18:27

Merci Cirec, effectivement j'avais oublié de nettoyer le zip.

Je ne vois pas d'explication à ton problème pour l'instant. Tu utilises quelle version de Delphi?

signaler à un administrateur
Commentaire de cirec le 05/10/2006 23:28:40 administrateur CS

ça y est j'ai trouvé le problème :

Sous D2005 dans l'unité SyncObjs.pas il y a déjà une Classe TMutex de déclaré

J'ai donc modifié dans Mutex.pas ainsi que dans RunOnce.pas:
TMutex en TFMutex

F pour Forman :-)

et ça fonctionne


bon maintenant je peut me mettre à explorer le code :)
@+
Cirec

signaler à un administrateur
Commentaire de cirec le 06/10/2006 00:41:58 administrateur CS

Bon le code est, comme toujours, très propre (pas de surprises) ;-)
On apprend toujours beaucoup de choses avec tes codes. (Merci de cette nourriture pour neurones)

Je n’ai pas encore tout bien compris :-)
Mais je ne désespère pas (je vais avoir besoin d'un peut plus de temps) pour bien comprendre le tout (surtout l'utilisation de Interface pour passer les infos d'une appli à l'autre)

Par contre je pense que le niveau initié est un peut "léger"

Ce qui serait bien ce serait de pouvoir s'affranchir de la boite de dialogue par un switch dans la ligne de commande (Ex: /F /S  [F:Force S:SendParam])

En tous cas 10/10 très bien
@+
Cirec

signaler à un administrateur
Commentaire de Forman le 06/10/2006 10:13:02

La boîte de dialogue est juste là pour l'exemple, et ne fait pas partie du composant. Tout dépend de ce que tu mets dans l'événement OnBlockInstance:

procedure TForm1.RunOnce1BlockInstance(Sender: TObject; Params: TStrings;
  var Allow: Boolean);
begin
  if (Params.IndexOf('/F')>-1) or (MessageBox(0,
                PChar('Voulez vous autoriser le 2ème lancement de "'+Params[0]+'" ?'),
                'Confirmation',
                MB_YESNO or MB_ICONQUESTION or MB_SYSTEMMODAL)=ID_YES) then
    Allow:=True;
end;

Avec cette version-là, lorsque tu mets le switch /F dans sa ligne de commande, la 2ème instance est toujours autorisée.

signaler à un administrateur
Commentaire de Forman le 06/10/2006 10:18:30

J'oubliais: avec la version plus haut, le switch /F empèche l'affichage de la boîte de dialogue. Avec /S:

if (Params.IndexOf('/S')=-1) and ( (Params.IndexOf('/F')>-1) or (MessageBox(...)=ID_YES) ) then
  Allow:=True;

Ici, /S est prioritaire sur /F et empèche la 2ème instance à tous les coups (et là encore empèchera la boîte de dialogue de s'afficher). Pour que ça marche, il faut bien sûr que l'option "évaluation booléenne complète" du compilateur soit désactivée (mais c'est le cas par défaut).

En tout cas merci pour les notes!

signaler à un administrateur
Commentaire de BruNews le 06/10/2006 11:30:36 administrateur CS

MB_SYSTEMMODAL est obsolète depuis win95, le système ne lit plus ce flag.
Le principe du "system modal" a disparu (pour les progs mode user 'normaux') avec win3.1 et le 16 bits en général.

signaler à un administrateur
Commentaire de Forman le 06/10/2006 12:58:14

Si tu mets MB_SYSTEMMODAL, ça permet à la boîte de dialogue de passer par-dessus toutes les autres fenêtres (utile si le programme est en arrière-plan). Mais effectivement, la boîte n'est pas system-modale...

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

DOS par Pipe [ par Filipe35 ] bonjour:comment faire du DOS par Pipe ?Filipe partager de la mémoire [ par mounjetado ] bonjour,existe-t-il un cours complet et détaillé, avec des exemples, sur l'utilisation des objets de synchronisation , tels que mutex, sémaphores, sec


Nos sponsors

Sondage...

CalendriCode

Octobre 2008
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel BAÏSE, Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,281 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.