Introduction
Dans cet article, nous allons exploiter le serveur web de Windows CE
pour intéragir avec votre Pocket PC.
La première question qui vient à l'esprit, quand on entend
parler de l'existence d'un serveur web pour Windows CE, est : "
Quel est l'intérêt d'avoir un serveur web sur un appareil
CE qui est généralement limité en terme de puissance
(CPU mémoire ...) ?
Voici quelques arguments en faveur de l'utilisation d'un serveur web
sous Windows CE.
Un
serveur web proposant du contenu dynamique est un bon intermédiaire
pour effectuer de l'accès à distance à des informations
détenues par un appareil CE.
Si
votre appareil CE ne possède pas d'écran (systême
embarqué par exemple), le serveur web vous permet de déporter
l'affichage sur un PC au travers d'un navigateur.
Un
affichage HTML est indépendant du client. On n'a pas par exemple
à se soucier de la résolution de l'affichage, c'est le
navigateur qui va prendre en charge l'affichage de la page en fonction
des capacités de l'appareil. D'un certain point de vue, un serveur
web pourrait vous permettre de développer un applicatif dont
l'interface graphique s'adapte au client.
Vous l'aurez compris, un serveur web pour Windows CE présente
un intérêt certain.
Installation
du serveur Web sur votre appareil Windows CE.
Vous trouverez le fichier d'installation du serveur web, dans le répertoire
support du SDK de Pocket PC 20002. Sur ma machine, le répertoire
est le suivant : c:\windows ce tools\pocket pc 2002\support\HTTPServer.
Vous y trouverez le fichier HTTPServer.PPC2002ARM.CAB.
Pour l'installer, suivez cette procédure :
Copiez
le fichier .CAB manuellement dans un répertoire de votre pocket
Sur
votre Pocket, lancez l'explorateur de fichier et cliquez sur le fichier
.CAB
Lorsque
l'installation est terminée, exécutez un reset de l'appareil
Le
serveur web sera automatiquement activé au démarrage de
l'appareil.
Afin de vérifier son bon fonctionnement, démarrez Pocket
Internet Explorer et naviguez à l'adresse suivante : http://localhost/
Si tout s'est bien passé, vous devriez voir un message du style
: " the server is correctly installed and running "
Configuration
du serveur Web
Le but du présent article n'est pas de vous expliquer en détail
la procédure de configuration du serveur web. Je vous conseille
de jetter un oeil dans la documentation du MSDN en effectuant une recherche
sur le mot-clé " HTTPD ".
Sur votre Pocket PC, vous devriez maintenant avoir un répertoire
" \windows\www\wwwPub ". Ce répertoire est le répertoire
raçine du serveur web. Pour faire simple, vous installerez le
code de cet article dans ce répertoire.
La
technologie ISAPI
ISAPI est l'acronyme de Internet Server Application
Programming Interface. Le but de ISAPI est de permettre d'augmenter
les fonctionnalités du serveur web au moyen de DLLs d'extension.
Il existe deux types de DLL ISAPI : les filtres ISAPI et les extensions
ISAPI. Celles qui nous intéressent ici sont les DLLs d'extension.
Une DLL d'extension ISAPI est une DLL qui peut être appellée
directement au travers d'une URL. Par exemple, si je possède
une DLL d'extension appellée " webext.dll " et que
je l'ai installé dans le répertoire racine du serveur
web. Je vais pouvoir l'appeller simplement, à partir de Pocket
Internet Explorer, en naviguant à l'URL suivante : http://localhost/webext.dll
Je pourrais même lui passer des informations, un peu à
la manière des paramètres d'une ligne de commande.
Exemple : http://localhost/webext.dll?username=
ldo ;e-mail=ldocquir@hotmail.com
Un
peu de pratique: Web Process Killer (webpkill.dll)
L'exemple que j'ai choisi pour illustrer mon article est une dll qui
va vous permettre de lister les processus actifs de votre pocket et
de les tuer si le coeur vous en dit. Je vous met en garde directement
: vous pourrez tuer n'importe quelle tâche y compris les tâches
système, ce qui pourrait rendre le système instable. Pas
de panique cependant, un soft reset remttra tout en ordre.
Pour tester cette application web, vous pourrez soit utiliser le Pocket
Internet Explorer local ou prendre la main à distance à
partir de IE sur votre PC.
Architecture
de l'application webpkill

Analyse du flux d'information en prenant le point de vue du serveur
web.
- Il reçoit la requête d'un navigateur internet (Pocket
IE, IE, Netscape ou autre).
- Si l'URL fait référence à webpkill.dll, il transmet
la requête à la webpkill.dll
- Webpkill analyse les paramètres de l'URL et exécute
la tâche correspondante. Evidemment la tâche en question
devra générer et renvoyer une page HTML au navigateur
à l'origine de la requête.
- Le navigateur affiche la page HTML. La boucle est bouclée.
Les fonctionnalités offertes par webpkill.
Webpkill propose deux services : le premier permet de lister les processus
actifs tandis que le second permet d'arrêter le processus de votre
choix.
Chacune de ces tâches doit être activée au travers
d'une URL particulière. J'ai donc défini deux paramètres
d'URL :
- ?list déclenchera la tâche liste des processus
- ?kill=process_id déclenchera l'arrêt du processus
dont l'identifiant est 'process_id'
Ainsi donc l'URL http://localhost/webpkill?list
vous permettra d'afficher la liste des processus actifs dans Pocket
IE.
Ossature
d'une DLL d'extension ISAPI
Une dll d'extension ISAPI est très simple d'un point de vue
structurelle : elle exporte deux fonctions dont une seule est réellement
intéressante.
La première fonction à implémenter est GetExtensionVersion.
Elle permet au serveur de connaître la version ISAPI de votre
DLL. Ci-dessous une implémentation typique de GetExtensionVersion
:
BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVer)
{
pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
strcpy(pVer->lpszExtensionDesc,"HTTP Web Process Killer Application");
return TRUE;
}
|
La seconde fonction exportée est HttpExtensionProc. Elle
constitue le principal point d'entrée pour le serveur web : toutes
les requêtes HTTP destinées à votre extension donneront
lieu à un appel à cette fonction. L'unique paramètre,
un pointeur sur une structure EXTENSION_CONTROL_BLOCK, vous livre l'ensemble
des informations.
Voici la définition de cette structure telle qu'elle apparaît
dans httpext.h :
//
// structure passed to extension procedure on a new request
//
typedef struct _EXTENSION_CONTROL_BLOCK {
DWORD cbSize; // size of this struct.
DWORD dwVersion; // version info of this spec
HCONN ConnID; // Context number not to be modified!
DWORD dwHttpStatusCode; // HTTP Status code
CHAR lpszLogData[HSE_LOG_BUFFER_LEN];// null terminated log info
specific to this Extension DLL
LPSTR lpszMethod; // REQUEST_METHOD
LPSTR lpszQueryString; // QUERY_STRING
LPSTR lpszPathInfo; // PATH_INFO
LPSTR lpszPathTranslated; // PATH_TRANSLATED
DWORD cbTotalBytes; // Total bytes indicated from client
DWORD cbAvailable; // Available number of bytes
LPBYTE lpbData; // pointer to cbAvailable bytes
LPSTR lpszContentType; // Content type of client data
BOOL (WINAPI * GetServerVariable) ( HCONN hConn,
LPSTR lpszVariableName,
LPVOID lpvBuffer,
LPDWORD lpdwSize );
BOOL (WINAPI * WriteClient) ( HCONN ConnID,
LPVOID Buffer,
LPDWORD lpdwBytes,
DWORD dwReserved );
BOOL (WINAPI * ReadClient) ( HCONN ConnID,
LPVOID lpvBuffer,
LPDWORD lpdwSize );
BOOL (WINAPI * ServerSupportFunction)( HCONN hConn,
DWORD dwHSERequest,
LPVOID lpvBuffer,
LPDWORD lpdwSize,
LPDWORD lpdwDataType );
} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
|
De cette structure, nous allons utiliser principalement 4 membres :
- ConnId est l'identifiant de la connexion du client. Cette valeur
sera utilisée dans la plupart des appels ISAPI.
- lpszQueryString qui pointe sur une chaîne de caractères
contenant les paramètres de l'URL.
- ServerSupportFunction est un pointeur sur une function qui
vous permet d'activer des services du serveur web
- WriteClient est un pointeur sur une fonction qui vous permet
de renvoyer des informations au navigateur à l'origine de la
requête.
Cette structure contient d'autres informations et fonctions intéressantes.
Je vous renvoie vers la documentation ISAPI pour plus de détails.
Déterminer
le service demandé par le client.
Les fonctions SendProcessList et KillProcess sont déclenchées
par des paramètres de l'URL. Pour déterminer laquelle
des deux fonctions il faut appeller, il suffit d'analyser la chaîne
des paramètres de l'URL. Cette dernière est pointée
par la variable membre lpszQueryString de la structure EXTENSION_CONTROL_BLOCK.
Char szFunction[5];
strncpy(szFunction,pECB->lpszQueryString,4);
szFunction[4] = 0;
if (_stricmp(szFunction,"list") == 0)
{
SendProcessList(pECB);
}
else if (_stricmp(szFunction,"kill") == 0)
{
KillProcess(pECB);
}
else
{
SendDefaultResponse(pECB);
}
|
Renvoyer
une page au client
Il incombe à une DLL ISAPI de renvoyer une réponse HTTP
au client. Par réponse HTTP, j'entends non seulement le contenu
de la page HTML mais aussi les informations de protocole HTTP (le HTTP
Header).
Renvoyer un header HTTP
Rassurez-vous, il n'est pas nécessaire de connaître le
protocole HTTP à fond pour s'en sortir. En effet, le serveur
web vous fournit une fonction qui génère un HTTP header
standard. Un pointeur sur cette fonction est fourni par le membre ServerSupportFunction
de la structure EXTENSION_CONTROL_BLOCK.
Le premier paramètre à passer est l'identifiant de connexion
(ConnId).
ServerSupportFunction offre un certain nombre de services. A chacun
de ces services est associé une constante. Cette constante est
passée en second paramètre de l'appel. Dans le cas présent,
on utilisera la constante HSE_REQ_SEND_RESPONSE_HEADER pour demander
au serveur web de renvoyer un header HTTP standard.
Le troisième paramètre " 200 OK " donne le
return code HTTP que vous désirez associer à la requête.
200 OK signifie que la requête est valable et qu'une réponse
sera renvoyée. Il existe bien évidemment d'autres return
code HTTP qui sont documentés dans la spec du protocole HTTP.
Voici un exemple d'utilisation de ServerSupportFunction :
pECB -> ServerSupportFunction(
pECB -> ConnID, // id de la connexion
HSE_REQ_SEND_RESPONSE_HEADER, // générer header
"200 OK", // code http ok
&dwLen,
(LPDWORD) NULL); // pas de paramètres HTTP que je désire ajouter
|
Renvoyer le contenu de la page HTML
Pour envoyer le contenu de la page HTML, il faut utiliser une autre
fonction de la structure EXTENSION_CONTROL_BLOCK : WriteClient.
WriteClient peut être appellée à plusieurs reprises
pour envoyer le contenu blocs par blocs.
Voici un exemple d'utilisation de WriteClient
char szBuff[MAX_PAGE_SIZE];
DWORD dwLen;
sprintf(szBuff, "<br><font size=1 color=blue face=\"verdana\">Laurent
Docquir</font><br><font size=1 color=red face=\"verdana\">EZOS
2003<br></font></body></html>");
dwLen = strlen ( szBuff );
pECB -> WriteClient (pECB -> ConnID, szBuff, &dwLen,
0);
|
Dresser
une liste des processus avec Toolhelp.
Sous Windows CE il existe un API particulier pour obtenir des informations
diverses sur les processus : il s'agit de l'API ToolHelp.
La première opération consiste à sauvegarder une
" photo instantanée " du contexte d'exécution
de Windows CE. La fonction CreateToolhelp32Snapshot est prévue
à cet effet. Elle renvoie un handle sur un snapshot.
La seconde opération consiste à boucler dans cette prise
de vue (snapshot) pour en retirer la liste des processus actifs. La
fonction Process32First sert à initier la boucle, Process32Next
permet de sauter au processus suivant.
Voici le code de la fonction SendProcessList :
char szBuff[MAX_PAGE_SIZE];
DWORD dwLen;
HANDLE hSnap;
PROCESSENTRY32 pe;
BOOL fOk;
char *szServerName = NULL;
char *szUrl = NULL;
char *szTemp;
szServerName = GetServerVariable(pECB,"SERVER_NAME");
szUrl = GetServerVariable(pECB,"URL");
szTemp = GetLocalIPAddress();
// Envoi de l'entête de page
sprintf(szBuff, "<html><title>Web Process Killer
par Laurent Docquir (EZOS)</title><body><h1><u>Liste des processus
tournant sur %s</u></h1><table border=1>",szServerName);
dwLen = strlen(szBuff);
pECB ->WriteClient(pECB -> ConnID, szBuff, &dwLen, 0);
// Envoi de la liste des process
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
pe.dwSize = sizeof(PROCESSENTRY32);
fOk = Process32First(hSnap,&pe);
while(fOk)
{
sprintf(szBuff, "<tr><td><a href=\"http://%s%s?kill=%d\">%S</a></td></tr>",
szServerName,
szUrl,
pe.th32ProcessID,
pe.szExeFile);
dwLen = strlen ( szBuff );
pECB -> WriteClient (pECB -> ConnID, szBuff, &dwLen, 0);
pe.dwSize = sizeof(PROCESSENTRY32);
fOk = Process32Next(hSnap,&pe);
}
sprintf ( szBuff, "</table><br>");
dwLen = strlen ( szBuff );
pECB -> WriteClient (pECB -> ConnID, szBuff, &dwLen, 0);
// Nettoyage des resources
CloseToolhelp32Snapshot(hSnap);
if (szServerName)
free(szServerName);
if (szUrl)
free(szUrl);
|
Conclusion
Sous Windows CE, la technologie ISAPI vous permet d'exposer du code
C au travers du serveur web. Les intérêts sont multiples
:
Tout
d'abord, la DLL ISAPI est encore assez simple à écrire.
Sa structure n'est pas si complexe pour qui veut bien s'y intéresser.
Il
n'y a rien à coder au niveau de l'application client puisque
le client est un navigateur.
ISAPI
est tout indiqué si vous désirez exécuter une application
graphique sur un appareil qui ne possède pas d'écran.
Dans ce cas, votre application WCE est la DLL ISAPI et votre client
est un navigateur tournant sur une autre machine.
ISAPI
permet de développer du code dont l'interface graphique s'adapte
au possibilité du navigateur du client.
Le code exemple de cet article peut servir comme une modeste base de
départ pour une application plus conséquente. Vous vous
rendrez néanmoins très vite compte que le contenu de ce
document ne vous montre que la partie visible de l'iceberg.
Sources
de cet article (16 ko)
Laurent
Docquir