Le premier site francophone dédié au développement Pocket PC


Comment créer un Plug-In Today ?
 
   


Dans cet article, nous allons créer un plugin pour l'application Today qui affichera simplement l'heure. Une boite de dialogue d'options permettra de changer la taille du plugin. Pour comprendre cet article, il vous faut un minimum de connaissances sur le fonctionnement de Windows et de eVC++ (création de project, ajout de fichiers et de librairies, etc...)

Si vous avez des questions et/ou des problèmes, n'hésitez pas à utiliser le forum "Plug-in Today" de

Création du projet

Les plugins today sont des DLLs, nous devont donc tout d'abord créer un project eVC++ du type WCE Dynamic-Link Library. eVC++ vous demande ensuite quel type de DLL vous voulez créer, choisir A simple Windows CE DLL project et donner TimeToday comme nom. Il nous faut ensuite un fichier DEF pour indiquer à eVC++ quelles fonctions doivent être exportées de la DLL, créons donc ce fichier timetoday.def et ajoutons le au projet. Copions ensuite le bout de code suivant dans ce fichier:

EXPORTS
InitializeCustomItem @ 240 NONAME
CustomItemOptionsDlgProc @ 241 NONAME

Ce fichier est le même pour tous les plugins today, sauf si vous ne voulez pas de boite de dialogue d'options (dans ce cas, ne pas mettre la dernière ligne).

Includes et globales

Le prototype des fonctions nécessaires à l'écriture d'un plugin Today sont dans un fichier .h de eVC++, nous allons donc l'inclure.

#include    <todaycmn.h>

On ajoute ensuite 3 variables globales pour stocker l'instance, le nom de l'application et le nom de la clé de registry utilisée pour stoker les paramètres du plugin. Refresh est utilisée pour optimiser le rafraichissement du plugin, VertSize est la taille verticale de notre plugin, CurrentHour et CurrentTime sont utilisées pour stocker l'heure courante.

TCHAR       AppName [] = TEXT("TimeToday");
TCHAR       RegKey [] = TEXT ("Software\\Microsoft\\Today\\Items\\TimeToday");
HINSTANCE   Instance;

int         Refresh = 1;

int         VertSize = 30;
int         CurrentHour;
int         CurrentMinute;

La variable Instance est initialisée au chargement de la dll dans la fonction DllMain grace au code suivant:

    if (ul_reason_for_call == DLL_PROCESS_ATTACH)
        Instance = (HINSTANCE)hModule;
Ecriture de la fonction d'initialisation du plugin

Cette fonction est appelée par l'application Today à chaque fois qu'elle démarre ou que les options de Today ont été changées. C'est dans celle-ci que l'on doit créer une class pour notre fenêtre ainsi que la fenêtre elle même. Cette fonction doit impérativement s'appeler InitializeCustomItem et son prototype doit être le même que dans le code suivant. C'est également dans cette fonction que l'on peut appeler des fonctions d'initialisations nécéssaires pour certaines fonctionnalitées de Windows, par exemple SHInitExtraControls. Tips: Il est conseillé d'appeler UnregisterClass juste avant le RegisterClass. La fenêtre doit absolument avoir le flag WS_CHILD et avoir comme parent la fenêtre passée en paramètre. La fonction doit retourner le handle de la fenêtre qui vient d'être créée. C'est également dans cette fonction que l'on va lire dans la registry les paramètres permanents du plugin (dans notre cas, la taille verticale de celui-ci).


HWND APIENTRY InitializeCustomItem(TODAYLISTITEM *ptli, HWND hwndParent)
{
WNDCLASS            wc;
HWND                hWnd;

HKEY                hkey;
DWORD               size, type;

    // lecture de la registry
    if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, RegKey, 0, 0, &hkey) == ERROR_SUCCESS)
    {
        size = sizeof (int);
        RegQueryValueEx (hkey, TEXT ("VertSize"), 0, &type, (unsigned char *)&VertSize, &size);
        RegCloseKey (hkey);
    }

    // création de la class
    wc.style            = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc      = (WNDPROC) WndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;
    wc.hInstance        = Instance;
    wc.hIcon            = NULL;
    wc.hCursor          = 0;
    wc.hbrBackground    = (HBRUSH) GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName     = 0;
    wc.lpszClassName    = AppName;

    UnregisterClass (AppName, Instance);
    RegisterClass (&wc);

    // création de la fenêtre
    hWnd = CreateWindow (AppName, AppName, WS_VISIBLE | WS_CHILD, 0, 0, 240, 0, hwndParent, NULL, Instance, NULL);

    return hWnd;
}
Ecriture de la Window Proc

Cette fonction va gérer les évenements provoqués par l'utilisateur ou par l'application Today. C'est dans cette fonction que l'on détermine la taille verticale du plugin et que l'on gère les clicks du stylet. C'est une WindowProc standard, je ne détaille donc par le prototype.
On commence par le message WM_TODAYCUSTOM_CLEARCACHE, ce message est envoyé lorsque l'on quitte la page d'options de Today, dans notre cas, on force un refresh du plugin.


        case WM_TODAYCUSTOM_CLEARCACHE:
            Refresh = 1;
            return 0;

Le deuxième message est beaucoup plus important, c'est WM_TODAYCUSTOM_QUERYREFRESHCACHE. Ce message est envoyé à interval régulier (environ toute les secondes) par l'application today pour connaitre la taille verticale de notre plugin et pour savoir si il a besoin d'être rafraichi à l'écran. Ce message doit retourner 1 si l'affichage doit être refait ou si la taille verticale a changée et 0 si rien n'a changé. Dans cet exemple, je ne retourne pas 1 lorsqu'il n'y a que l'affichage à updater (la taille verticale n'a pas changée) mais je force le réaffichage en faisant un InvalidateRect.

        case WM_TODAYCUSTOM_QUERYREFRESHCACHE:
            {
            SYSTEMTIME  st;

                GetLocalTime (&st);
                CurrentHour = st.wHour;

                if (Refresh)
                {
                TODAYLISTITEM   *ptli;

                    CurrentMinute = st.wMinute;

                    ptli = (TODAYLISTITEM *)wParam;
                    ptli->cyp = VertSize;
                    Refresh = 0;
                    return 1;
                }
                else
                {
                    if (st.wMinute != CurrentMinute)
                    {
                        CurrentMinute = st.wMinute;
                        InvalidateRect (hWnd, NULL, TRUE);
                    }
                }
            }
            return 0;
Le troisième message est le WM_PAINT, c'est le message classique de windows pour afficher notre plugin. Dans cet            exemple, on affiche simplement l'heure au centre de la zone client.
        case WM_PAINT:
            {
            HDC             hdc;
            PAINTSTRUCT     ps;
            RECT            rect;
            TCHAR           buf [20];
                
                hdc = BeginPaint (hWnd, &ps);

                if (hdc)
                {
                    GetClientRect (hWnd, &rect);
                    _stprintf (buf, TEXT ("%d:%d"), CurrentHour, CurrentMinute);
                    DrawText (hdc, buf, _tcslen (buf), &rect, DT_VCENTER | DT_CENTER | DT_SINGLELINE);
                }

                EndPaint (hWnd, &ps);
                return 0;
            }
La boite de dialogue des options

C'est une boite de dialogue standard. On va créer une boite de dialog à l'aide de l'éditeur de resources et mettre nos control dedans. Pour ce qui est de la taille horizontale, je tatonne car je n'ai pas trouvé plus simple. Il faut ensuite associer à cette boite de dialogue l'ID 500, on va changer ça en ouvrant les propriétées de la boite de dialogue et en rajoutant '=500' juste derrière son ID (qui doit être IDD_DIALOG1). Si on ne fait pas ca, l'application today ne pourra pas trouver la boite de dialogue. Il nous faut enfin ajouter la ligne suivante dans notre .cpp pour pouvoir acceder aux controls:

#include    "resource.h"

Il n'est pas utile de mettre un bouton 'OK' car nous en ajouterons un automatiquement dans la barre de titre plus tard. Par contre, si vous voulez que votre plugin soit facilement désinstallable, mettez un bouton 'Uninstall'.

La DialogProc des options

Il nous reste enfin à écrire la DialogProc qui gère la boite de dialogue des options de notre plugin. C'est une DialogProc standard qui doit impérativement s'appeler CustomItemOptionsDlgProc dont voici le source.

BOOL APIENTRY CustomItemOptionsDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
SHINITDLGINFO   shidi;
HKEY            hkey;

    switch (message)
    {
        case WM_INITDIALOG:
            shidi.dwMask = SHIDIM_FLAGS;
            shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLG;
            shidi.hDlg = hDlg;
            SHInitDialog (&shidi);

            SetDlgItemInt (hDlg, IDC_VERTSIZE, VertSize, FALSE);
            return TRUE;

        case WM_COMMAND:
            switch (LOWORD (wParam))
            {
                case IDOK:
                    VertSize = GetDlgItemInt (hDlg, IDC_VERTSIZE, NULL, FALSE);
                    if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, RegKey, 0, 0, &hkey) == ERROR_SUCCESS)
                    {
                        RegSetValueEx (hkey, TEXT ("VertSize"), 0, REG_DWORD, (const unsigned char *)&VertSize, sizeof (int));
                        RegCloseKey (hkey);
                    }
                    EndDialog (hDlg, LOWORD(wParam));
                    return TRUE;

                case IDC_UNINSTALL:
                    RegDeleteKey (HKEY_LOCAL_MACHINE, RegKey);
                    MessageBox (hDlg, TEXT("You must now Soft-Reset your device and uninstall from the control panel"), AppName, MB_OK);
                    break;

            }
            break;
    }
    return FALSE;
}

Dans le message WM_INITDIALOG on commence par initialiser la boite de dialogue grace aux fonctions du shell. Cela permet d'ajouter un bouton 'OK' dans la barre de titre et de resizer la boite à la taille de l'écran. Pour utiliser la fonction SHInitDialog il nous faut inclure un fichier .h, ce que nous faisont grace au code ci-dessous:

#include	<Aygshell.h>

Il faut également ajouter la librairie aygshell.lib dans le projet (dans les options du projet, onglet 'link', ne pas oublier de la mettre pour toutes les platformes)

Installation

Maintenant que notre plugin est près, il faut l'installer. Pour ca, il nous faut un fichier .cab car il ne suffit pas de copier la dll sur le Pocket PC pour l'installer, il faut également créer des clés dans la registry. Je ne vais pas détailler cela dans cet article mais je vous renvois au fichier TimeToday.inf qui est dans le projet et à la doc Microsoft. Il y a également dans le répertoire du projet le fichier build_cabs.bat que j'utilise pour générer tous les .cab d'un coup.

Téléchargement

Vous pouvez maintenant télécharger le projet complet et quelques fichiers annexes :

Sources du Plug-in de cet article

 

Lou

 

 
       
   
 
   
Copyright 2001-2004 - Tous droits réservés
 
   

iPAQ est un produit de COMPAQ.
Visual Tools est un produit de Microsoft Corporation.
Toutes les autres marques et produits présents dans ces pages sont la propriété exclusive de leurs sociétés respectives.