Composant runonce: autoriser un seul lancement d'une application (ou non!)

Soyez le premier à donner votre avis sur cette source.

Vue 9 733 fois - Téléchargée 851 fois

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 / Exemple :


{ 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é.

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

cs_Forman
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1 -
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...
BruNews
Messages postés
21054
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
7 novembre 2014
13 -
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.
cs_Forman
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1 -
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!
cs_Forman
Messages postés
663
Date d'inscription
samedi 8 juin 2002
Statut
Membre
Dernière intervention
6 avril 2010
1 -
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.
cirec
Messages postés
3809
Date d'inscription
vendredi 23 juillet 2004
Statut
Modérateur
Dernière intervention
1 septembre 2019
32 -
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

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.