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


Une application C++ portable Win32 - WinCE sans les MFC
 
   


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

 

 
       
   
 
   
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.