|
|
|||||
|
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 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). 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;
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;
}
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. 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;
}
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'. 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) 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.
Vous pouvez maintenant télécharger le projet complet et quelques fichiers annexes :
|
|||||
|
|
|||||
|
Copyright 2001-2004 - Tous droits réservés
|
|||||
|
iPAQ
est un produit de COMPAQ. |