Créer un applet du panneau de configuration en PureBasic

LEFEVRE Franck

Ce tutoriel a pour objectif de vous apprendre à créer un applet pour le panneau de configuration en PureBasic.
Commentez

- Titre : Créer un applet du panneau de configuration en PureBasic

- Auteur : LEFEVRE Franck

- Parution : 14 octobre 2009

- Mise à jour : 14 octobre 2009

- Licence : Copyright ® 2009 LEFEVRE Franck. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Pré-Requis

- III. Principe

- IV. Fonctions

- IV-A. La procédure principale : CplApplet()

- IV-B. Le message #CPL_INIT

- IV-C. Le message #CPL_GETCOUNT

- IV-D. Les messages #CPL_INQUIRE et #CPL_NEWINQUIRE

- IV-E. Le message #CPL_SELECT

- IV-F. Le message #CPL_DBLCLK

- IV-G. Le message #CPL_STOP

- IV-H. Le message #CPL_EXIT

- IV-I. Le message #CPL_STARTWPARMS

- V. Questions / Réponses

- VI. Compilation & Exemple

- VII. Conclusion

- VIII. Remerciements

I - Introduction

Le panneau de configuration est une partie de l'IHM de Microsoft Windows. Il permet de manipuler la configuration du système et d'applications via des applets. Ainsi, il est possible d'ajouter du matériel, de gérer les imprimantes et les comptes utilisateurs.

Sous Windows XP, en affichage classique :

Sous Windows Vista, en affichage par catégories :

Deux méthodes sont possibles pour faire apparaître un applet dans le panneau de configuration :

- Première méthode : mettre un fichier .cpl dans le dossier SYSTEM32 de Windows (par défaut, 'C:\Windows\System32').

- Seconde méthode : utiliser la base de registres :

Ainsi, lorsque vous voulez supprimer l'applet du panneau de configuration, il suffit juste de supprimer le fichier avec l'extension .cpl.

Mais comment créer un fichier .cpl ?

II - Pré-Requis

Au début du code, certaines constantes sont à initialiser. Elles seront utiles dans les différentes procédures.

- #MyApplet_Name : Nom de l'applet

- #MyApplet_Description : Description de l'applet

- #MyApplet_NumSubPrograms : Nombre de sous-programmes de l'applet

III - Principe

Un applet est un fichier d'extension CPL. C'est en fait une DLL renommée en .cpl. Il a une seule procédure exportée : CPLApplet.
Cette procédure reçoit des messages qui sont envoyés par le panneau de configuration. Ces messages sont :

- #CPL_DBLCLK

- #CPL_EXIT

- #CPL_GETCOUNT

- #CPL_INIT

- #CPL_INQUIRE

- #CPL_NEWINQUIRE

- #CPL_SELECT

- #CPL_STARTWPARMS

- #CPL_STOP

Ces messages seront expliqués dans le chapitre suivant.

IV - Fonctions

IV-A - La procédure principale : CplApplet()

La procédure CplApplet() est le point d'entrée pour un applet pour le panneau de configuration.
C'est une procédure de type callback, c'est-à-dire que cette fonction gérera tous les messages envoyés à un applet par Windows via la variable iMsg. En fonction du message, un ou deux paramètres peuvent être utilisés. Ces deux paramètres sont les variables wParam et lParam.

ProcedureDLL CPlApplet(hWnd.l, iMsg.l, wParam.l, lParam.l)
Select iMsg
Case #CPL_INIT          : ProcedureReturn MyCPApplet_Init()
Case #CPL_GETCOUNT      : ProcedureReturn MyCPApplet_GetCount()
Case #CPL_INQUIRE       : ProcedureReturn MyCPApplet_Inquire(wParam, lParam)
Case #CPL_SELECT        : ProcedureReturn MyCPApplet_Select()
Case #CPL_DBLCLK        : ProcedureReturn MyCPApplet_DblClk(wParam, lParam)
Case #CPL_STOP          : ProcedureReturn MyCPApplet_Stop(wParam, lParam)
Case #CPL_EXIT          : ProcedureReturn MyCPApplet_Exit()
Case #CPL_NEWINQUIRE    : ProcedureReturn MyCPApplet_NewInquire(wParam, lParam)
Case #CPL_STARTWPARMS   : ProcedureReturn MyCPApplet_StartWParams(wParam, lParam)
EndSelect
EndProcedure

IV-B - Le message #CPL_INIT

Ce message est envoyé à l'applet pour son initialisation de variables et de zones mémoires, aussitôt aprés le chargement de la DLL.

La fonction retourne un nombre différent de 0 en cas de succès, sinon 0.

; http://msdn.microsoft.com/en-us/library/bb787978%28VS.85%29.aspx
Procedure MyCPApplet_Init()
;MessageRequester("CPlApplet", "#CPL_INIT")
ProcedureReturn -1
EndProcedure

IV-C - Le message #CPL_GETCOUNT

Ce message est envoyé à l'applet pour savoir quel est le nombre de sous-programmes supportés par l'applet. Ainsi, un applet peut gérer plusieurs boîtes de dialogue différentes qui seront accessibles dans les messages #CPL_INQUIRE et #CPL_NEWINQUIRE via le paramètre uAppNum.

; http://msdn.microsoft.com/en-us/library/bb787976%28VS.85%29.aspx
Procedure MyCPApplet_GetCount()
;MessageRequester("CPlApplet", "#CPL_GETCOUNT")
ProcedureReturn #MyApplet_NumSubPrograms
EndProcedure

IV-D - Les messages #CPL_INQUIRE et #CPL_NEWINQUIRE

Ces messages sont envoyés à l'applet pour demander des informations à propos du sous-programme de l'applet. Donc ces messages seront appelés autant de fois qu'il y a de sous-programmes. Les informations demandées sont l'icône, le nom, la description du sous-programme ainsi que des données qui seront envoyées lors du message CPL_DBLCLK ou CPL_STOP.

La différence entre les deux messages se trouve dans les performances. En fait, le message #CPL_NEWINQUIRE retourne des informations que le système ne peut mettre en cache. Par conséquent, les applications doivent charger les informations à chaque fois que le système les demande.

Les fonctions attendent deux paramètres :

- le premier est uAppNum, qui correspond à l'identifiant du sous-programme (compris entre 0 et la valeur retournée par #CPL_GETCOUNT moins 1)

- le second est *pInfo, une zone mémoire qui contient une structure CPLINFO ou une structure NEWCPLINFO

La fonction retourne 0 en cas de succès.

; http://msdn.microsoft.com/en-us/library/bb787980%28VS.85%29.aspx
Procedure MyCPApplet_Inquire(uAppNum.l, *pInfo.CPLINFO)
;MessageRequester("CPlApplet > SubProgram : "+Str(uAppNum), "#CPL_INQUIRE")
;*pInfo\idIcon = #IDI_APPLICATION
*pInfo\idIcon = 3
*pInfo\idName = 1
*pInfo\idInfo = 2
*pInfo\lpData = 0
ProcedureReturn 0
EndProcedure
; http://msdn.microsoft.com/en-us/library/bb787982%28VS.85%29.aspx
Procedure MyCPApplet_NewInquire(uAppNum.l, *pInfo.NEWCPLINFO)
;MessageRequester("CPlApplet > SubProgram : "+Str(uAppNum), "#CPL_NEWINQUIRE")
*pInfo\dwSize = SizeOf(NEWCPLINFO)
*pInfo\dwHelpContext = 0
*pInfo\lpData = 0
*pInfo\hIcon = 3
*pInfo\szName = #MyApplet_Name
*pInfo\szInfo = #MyApplet_Description
*pInfo\szHelpFile = #NULL$
ProcedureReturn 0
EndProcedure

Mais que cachent donc les éléments de ces deux structures CPLINFO et NEWCPLINFO ?
Commencons par la structure CPLINFO...

- idIcon.i : l'identifiant de la ressource de type ICON ; ainsi #IDI_APPLICATION correspond à l'icône standard d'une application Windows

- idName.i, idInfo.i : les identifiants de la ressource de type STRING ; ainsi dans notre exemple, l'identifiant correspond à un élément d'une STRINGTABLE d'un fichier de ressources à joindre au fichier PureBasic

- *lpData : un pointeur de zone mémoire lié à l'application ; ce pointeur sera envoyé lors des messages CPL_DBLCLK ou CPL_STOP

Puis finissons par la structure NEWCPLINFO...

- dwSize.l : la taille de la structure NEWCPLINFO, donc un SizeOf(NEWCPLINFO) correspond.

- dwFlags.l : définir à 0, vu que cet élément est ignoré

- dwHelpContext.l : définir à 0, vu que cet élément est ignoré

- *lpData : un pointeur de zone mémoire lié à l'application ; ce pointeur sera envoyé lors des messages CPL_DBLCLK ou CPL_STOP

- hIcon.l : l'identifiant de la ressource de type ICON

- szName.s{32} : une chaîne de longueur fixe (32 caractères) contenant le nom de l'applet

- szInfo.s{64} : une chaîne de longueur fixe (64 caractères) contenant la description de l'applet

- szHelpFile.s{128} : définir à "", vu que cet élément est ignoré

Et ci-joint, le fichier de ressources à lier lors de la compilation du fichier et utile au message #CPL_INQUIRE :

STRINGTABLE
{
1, "MyApplet Name"
2, "MyApplet Description"
}
3 ICON K:\Source\MyApplet.ico

N'oubliez de changer le chemin de l'icône !

IV-E - Le message #CPL_SELECT

Ce message est obsolète donc renvoyez 0 quand ce message est appelé.

; http://msdn.microsoft.com/en-us/library/bb787984%28VS.85%29.aspx
Procedure MyCPApplet_Select()
;MessageRequester("CPlApplet", "#CPL_SELECT")
ProcedureReturn 0
EndProcedure

IV-F - Le message #CPL_DBLCLK

Ce message est envoyé à l'applet pour indiquer que l'utilisateur a exécuté un double-clic sur l'icône de l'applet. En fait, cela permet de lancer la boucle principale d'exécution du sous-programme.
La fonction attend deux paramètres :

- le premier est uAppNum, qui correspond à l'identifiant du sous-programme (compris entre 0 et la valeur retournée par #CPL_GETCOUNT moins 1)

- le second est *lpData, une zone mémoire qui contient l'élément lpData d'une structure CPLINFO ou d'une structure NEWCPLINFO

La fonction retourne 0 en cas de succès, sinon un nombre différent de 0.

; http://msdn.microsoft.com/en-us/library/bb787973%28VS.85%29.aspx
Procedure MyCPApplet_DblClk(uAppNum.l, *lpData)
Protected plEvent.l, plQuit.l
;MessageRequester("CPlApplet > SubProgram : "+Str(uAppNum), "#CPL_DBLCLK")
If OpenWindow(0, 100, 200, 300, 200, #MyApplet_Name, #PB_Window_SystemMenu)
EditorGadget(0, 5, 5, 290, 190)
SetGadgetText(0, #MyApplet_Description)
Repeat
plEvent = WaitWindowEvent()
If plEvent = #PB_Event_CloseWindow
plQuit = 1
EndIf
Until plQuit = 1
EndIf
ProcedureReturn 0
EndProcedure

IV-G - Le message #CPL_STOP

Ce message est envoyé quand l'applet du panneau de configuration est fermé, et cela à chaque fois que la fenêtre du sous-programme est fermée.
La fonction attend deux paramètres :

- le premier est uAppNum, qui correspond à l'identifiant du sous-programme (compris entre 0 et la valeur retournée par #CPL_GETCOUNT moins 1)

- le second est *lData, une zone mémoire qui contient l'élément *lpData d'une structure CPLINFO ou d'une structure NEWCPLINFO

La fonction retourne 0 en cas de succès.

; http://msdn.microsoft.com/en-us/library/bb787988%28VS.85%29.aspx
Procedure MyCPApplet_Stop(uAppNum.l, *lData)
;MessageRequester("CPlApplet > SubProgram : "+Str(uAppNum), "#CPL_STOP")
ProcedureReturn 0
EndProcedure

IV-H - Le message #CPL_EXIT

Ce message est envoyé une seule fois à l'applet avant la libération de la DLL afin de vider les zones mémoires et les variables.

La fonction retourne 0 en cas de succès.

; http://msdn.microsoft.com/en-us/library/bb787975%28VS.85%29.aspx
Procedure MyCPApplet_Exit()
;MessageRequester("CPlApplet", "#CPL_EXIT")
ProcedureReturn 0
EndProcedure

IV-I - Le message #CPL_STARTWPARMS

Ce message est similaire au message #CPL_DBLCLK, mais avec quelques informations additionnelles.
La fonction attend deux paramètres :

- le premier est uAppNum, qui correspond à l'identifiant du sous-programme (compris entre 0 et la valeur retournée par #CPL_GETCOUNT moins 1)

- le second est lpszExtra, qui correspond à une chaîne avec des paramètres additionnels d'exécution

La fonction retourne 1 en cas de succès, sinon 0.

; http://msdn.microsoft.com/en-us/library/bb787986%28VS.85%29.aspx
Procedure MyCPApplet_StartWParams(uAppNum.l, lpszExtra.l)
; MessageRequester("CPlApplet > SubProgram : "+Str(uAppNum), "#CPL_STARTWPARMS")
MessageRequester("CPlApplet > SubProgram : "+Str(uAppNum), PeekS(lpszExtra))
ProcedureReturn #True
EndProcedure

V - Questions / Réponses

Quel est la différence entre les messages #CPL_INQUIRE et #CPL_NEWINQUIRE ?

Le message CPL_INQUIRE fournit des informations de manière que Windows puisse les mettre en cache. Le message CPL_NEWINQUIRE est utilisé seulement si vous avez besoin de changer l'icône de votre élément (sous-programme de l'applet) ou d'afficher des chaînes basées sur l'état de l'ordinateur.

Les structures CPLINFO et NEWCPLINFO ne sont pas déclarées dans PureBasic. Où puis-je les trouver?

Ici-même...

;- Structures
Structure CPLINFO
idIcon.i
idName.i
idInfo.i
*lpData
EndStructure
Structure NEWCPLINFO
dwSize.l
dwFlags.l
dwHelpContext.l
*lpData
hIcon.l
szName.s{32}
szInfo.s{64}
szHelpFile.s{128}
EndStructure

Les messages #CPL_* ne sont pas déclarés dans PureBasic. Où puis-je les trouver?

Ici-même...

;- Constantes
#CPL_INIT         = 1
#CPL_GETCOUNT     = 2
#CPL_INQUIRE      = 3
#CPL_SELECT       = 4
#CPL_DBLCLK       = 5
#CPL_STOP         = 6
#CPL_EXIT         = 7
#CPL_NEWINQUIRE   = 8
#CPL_STARTWPARMS  = 9
#CPL_SETUP        = 200

VI - Compilation & Exemple

Téléchargez la source du tutoriel :
Source du tutoriel
Par défaut, toutes les options sont enregistrées dans le fichier.
Sinon voici les options à définir :

- Format de l'exécutable : Shared DLL

- Ressources : Ajouter le fichier "MyApplet.rc"

Aprés avoir compilé le fichier DLL, lancez le batch afin de copier le fichier MyApplet.dll vers le fichier C:\Windows\System32\MyApplet.cpl.
Ensuite, ouvrez le panneau de configuration et vous verrez apparaître un élément nommé "MyApplet".

VII - Conclusion

Voilà, nous arrivons à la fin de cet article. L'avantage est que ce code est fonctionnel sous de nombreuses versions de Windows. Mais sous Windows Vista, une autre technique basée sur XML et la base de registres existe.

J'espère que cet article permettra aux gens d'avoir un nouveau point de vue sur PureBasic.
Et maintenant à vos claviers !

Références :

- MSDN : Control Panel Applets

- Developpez.com : Créer une application dans le panneau de configuration sous Windows Vista

VIII - Remerciements

Merci à Comtois et jacques_jean pour la relecture.
Merci à Louis-Guillaume MORAND (Page perso) pour son article sur les applets du panneau de configuration sous Windows Vista.
Enfin je dis un grand merci à l'équipe de DVP pour leur travail.