| |
|
S'adresse à tout développeur. Pour compiler les programmes il vous faudra
Visual C++ et eVC
Win32 - WinCE késako ?
Beaucoup de mots barbares dans le titre de cet
article, une petite mise au point s'impose.
Une application portable est un projet de développement qui compile sur
plusieurs plates-formes avec les mêmes fichiers sources. Lors de
l'écriture d'une application pour Windows 95/98/NT/2000 nous sommes dans
le monde Win32. Lors de l'écriture pour PocketPC nous sommes en WinCE. Et
nous avons beaucoup de chance car WinCE et un sous ensemble de Win32.
Les MFC , pour ceux qui ne connaissent pas, est un ensemble de classes C++
qui facilitent la vie du développeur et évitent décrire pas mal de
lignes de code. Si vous ne connaissez pas les MFC, cela tombe bien, car
nous n'allons pas les utiliser.
Dans quels cas ne pas utiliser les MFC ?
C'est l'expérience qui permet de savoir quand utiliser
ou non les MFC. L'ensemble des classes MFC à un coût au niveau taille,
apprentissage et maintenance. On peut s'en passer pour de petites applications
style jeux, affichage simple... par contre c'est incontournable dès que
l'on touche aux ActiveX ou pour de grosses applications aux boites de
dialogue multiples.
Pourquoi faire une application portable ?
Nous arrivons au but de cet article. Pourquoi se
compliquer la vie à écrire une application portable Win32 - WinCE. Et
bien il est vraiment plus simple de mettre au point une
application avec Visual C++ (Win32) que Embeded Visual C++ (WinCE). Pour
avoir un programme robuste et sans bugs je conseille souvent de tester
toutes les lignes en pas à pas, pour voir si tout se passe bien.
L'environnement de développement eVC est lent et
moyennement pratique à utiliser en mode pas à pas, aussi bien avec
l'émulateur qu'en déporté sur un PocketPC (je viens de re-tester et
c'est vraiment trop lent ;-(
Avec une application portable vous profitez de toute
la souplesse et de la rapidité de Visual C++ car la majorité du code est
écrit et validé dans ce mode. En plus, et ce n'est pas négligeable, vous pouvez
utiliser des produits tels que BoundChecker
ou autres pour éliminer le maximum de bugs. Une
fois la mise au point terminée il ne reste plus qua valider les
fonctionnalités en mode PocketPC, pas besoin d'utiliser le mode pas à
pas. Ce mode de développement deviens indispensable lors de
l'écriture d'application faisant du client serveur (socket, HTTP...)
|
Faisons un peu de 3D
Comme exemple je vais prendre une application
qui affiche une courbe mathématique en 3D, avec rotation en temps
réel à la souris (win32) ou stylet (wince). Idéal pour frimer et
faire saliver vos copains lors d'une démo PocketPC, ou
encore pour augmenter vos chances avec cette fille en maths sup.
Je vais indiquer maintenant les quelques
principes de 3D utilisés, pour ne plus y revenir par la suite car
ce n'est pas le but de l'article. Nous calculons dans un tableau en
deux dimensions une courbe du type fz(x,y) = cos(sqrt(x*x)+(y*y)).
Ensuite nous effectuons une rotation autour des différents axes.
Enfin nous projetons sur l'écran 2D l'image de la courbe 3D
(rappelez vous, Thales et son triangle). Pour en savoir plus tapez
les mots clés "Thales 3D projection" dans n'importe quel
moteur de recherche. |

La
version Win32 |
|
|
|
|

La version PocketPC |
UNICODE
Les PocketPC ne fonctionnent qu'en Unicode, donc
attention de bien gérer ce mode particulier en utilisant des TCHAR
et non pas des char. Il faut également utiliser la macro TEXT()
pour tout texte statique, pour que le compilateur C++ le transforme
en chaîne Unicode. Je rappelle qu'en Unicode un caractère est
codé sur 2 octets ou plus, ceci pour supporter les alphabets comme
le Mandarin ou le Kanji (=Japonais).
L'autre mode (un caractère = un octet) est appelé ANSI.
La structure d'un programme Windows.
Cette structure est la même depuis Windows 3.0.
Notre point d'entré est WinMain (ici _tWinMain, la version
compatible ANSI/Unicode). Avant d'afficher une fenêtre il faut
créer sa classe avec la fonction RegisterClass, qui défini en
particulier l'adresse de la fonction qui va recevoir les messages de
la fenêtre. Une fois notre classe enregistrée, nous pouvons créer
une fenêtre avec la fonction CreateWindow qui va nous rendre la
main tout de suite. Il ne reste plus qu'a avoir une pompe à
message, c'est une boucle sans fin qui dépile les messages et les redistribues.
Quand GetMessage retourne 0 il est temps de quitter le programme.
|
Définissons une classe C++ pour structurer tout
cela.
Dans ppBase.h j'ai défini une classe C++ ppBase, qui va
gérer toute la vie de l'application. La macro UNDER_CE permet de savoir
avec quel compilateur nous sommes, elle n'est définie qu'en mode WinCE
avec eVC.
class ppBase
{
friend LRESULT CALLBACK ppBaseWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ;
ppInit m_ppInit ;
protected:
#ifdef UNDER_CE
HWND m_hWndCB ;
SHACTIVATEINFO m_sai ;
#endif
HMENU m_hMenu ;
DWORD m_SHMask ;
public:
ppBase() ;
// User functions
virtual LRESULT WindowProc (HWND hWnd, UINT Msg, WPARAM wP, LPARAM lP) = 0 ;
// ppBase functions
int WinMain (ppInit *pppInit) ;
} ; |
Pour utiliser la classe ppBase il faut la dériver et implémenter
WindowProc. Cette méthode reçoit les messages de la fenêtre, ainsi nous
les traitons selon nos besoins.
Le point d'entré est ppBase::WinMain auquel on passe la
structure ppInit
Exemple d'un programme complet très simple:
#include "ppBase.h"
class Pocket3D : public ppBase
{
public:
LRESULT WindowProc (HWND hWnd, UINT Msg, WPARAM wP, LPARAM lP)
{
switch (Msg)
{
case WM_LBUTTONDONW:
PostQuitMessage(0);
break;
default:
lResult = DefWindowProc(hWnd, Msg, wP, lP) ;
break;
}
}
};
int WINAPI _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
ppInit Init ;
Pocket3D App ;
Init.hInstance = hInstance ;
Init.nCmdShow = nCmdShow ;
lstrcpy (Init.WindowClass, TEXT("Pocket3D")) ;
lstrcpy (Init.Title, TEXT("Pocket 3D")) ;
return App.WinMain (&Init) ;
} |
Remarquez le DefWindowProc, il est
nécessaire pour que Windows gère les messages que nous ne traitons pas.
Passons en revu ce qui se passe dans
ppBase::WinMain.
Tout d'abord on regarde si une autre instance
ne tourne pas déjà avec FindWindow, et si c'est la cas on la place au
premier plan et on arrête là. Ceci est une obligation sur PocketPC, car
les programmes tournent toujours mais se cachent les un les autres.
L'utilisateur retourne dans "Aujourd'hui" pour mettre
l'application au premier plan, et non pas pour en lancer une seconde
instance.
C'est le moment d'enregistrer la classe de la fenêtre avec RegisterClass,
puis de créer le fenêtre à la bonne taille. En mode win32 on force la
taille à 240*320, en WinCE c'est plus compliqué car il faut retrouver la
zone utilisable avec SHSipInfo. Cette fonction indique si le menu du bas
est visible ou non, ainsi que la taille de l'écran.
Une fois la fenêtre crée nous arrivons à la pompe à message. C'est un
simple while(GetMessage(...))
ppBaseWndProc
Ceci est notre callback pour les messages de
la fenêtre. Ici la majorité des traitements sont pour gérer les menus,
et en particulier le menu Sip (Soft Input Panel), celui qui apparaît en bas sur PocketPC. Il
faut absolument créer le menu Sip pendant le message WM_CREATE. Ce ne
sont pas des API marrantes à utiliser, et je ne vais pas m'y attarder car
tout ceci fera l'objet d'un autre article.
Les messages Windows exploités dans Pocket 3D
sont les suivants:
WM_MOUSEMOVE est reçu quand l'utilisateur
déplace la souris au dessus de la fenêtre. C'est le même message avec
le stylet. On teste wParam pour voir si le bouton gauche ou le stylet est appuyé ou
non. Les coordonnés de la souris sont mémorisées dans les variables de
classe m_lastx et m_lasty. Pour forcer à redessiner on utilise InvalidateRect, qui va
provoquer
un message WM_PAINT
WM_CLOSE indique la fermeture du programme,
on poste un message WM_QUIT via PostQuitMessage pour terminer proprement.
WM_PAINT c'est l'ordre pour dessiner dans la
fenêtre, je ne vais pas non plus m'y attarder, gardez simplement à
l'esprit qu'il faut absolument appeler BeginPaint et EndPaint pour que
Windows valide notre algorithme de dessin. Je vais ajouter que nous
dessinons la courbe dans un bitmap, puis nous copions le résultat
terminé pour ainsi éviter les tremblements (flicker).
LRESULT Pocket3D::WindowProc (HWND hWnd, UINT Msg, WPARAM wP, LPARAM lP)
{
LRESULT lResult = 0 ;
switch (Msg)
{
case WM_MOUSEMOVE:
if (wP & MK_LBUTTON)
{
m_lastx = HIWORD(lP);
m_lasty = LOWORD(lP);
InvalidateRect (hWnd, NULL, FALSE);
}
break;
case WM_PAINT:
Paint (hWnd);
break;
case WM_CLOSE:
PostQuitMessage (0) ;
break ;
default:
lResult = DefWindowProc(hWnd, Msg, wP, lP) ;
}
return lResult ;
} |
Les fichiers du projet
Téléchargez le code source et les binaires
win32 et wince (ARM, MIPS, SH3) ici: Pocket3D.zip
ppBase.h et cpp définissent et implémentent
notre classe de base.
3DProjection.h et cpp définissent et implémentent les algorithmes de
3D.
3D.cpp est l'application, avec utilisation de ppBase, 3DProjection.
Le projet Visual C++ est 3D\X86\X86.DSW, le projet eVC est 3D\PocketPC\PocketPC.VCW
Si vous voulez démarrer un nouveau projet
qui utilise ppBase, il
faut choisir "Win32 application" avec Visual C++, et "WCE
Pocket PC Application" avec eVC.
Conclusion
Nous avons maintenant les outils pour créer
rapidement un application Win32 et WinCE avec un seul code source. J'espère
que vous gagnerez du temps dans vos développement en faisant la
mise au point avec Visual C++. Il faut espérer que l'émulateur PocketPC prenne
de la bouteille et devienne comparable à l'émulateur Palm. Certainement
que Microsoft .NET réglera tous ces petits inconvénients.
Rémi
THOMAS
|
|