ActiveSync, Windows Mobile Device Center et ThemeGenCE
Auteur Benoît Thonnart
Date Décembre 2007

Benoît Thonnart développe des logiciels PC en Delphi. Il nous explique dans cet article les fonctions qu'il a utilisées dans son logiciel ThemeGenCE (vainqueur aux Best Software Awards 2007).

ActiveSync, Windows Mobile Device Center et ThemeGenCE

L’interface de Microsoft permettant de communiquer avec un ordinateur Windows et nos mobiles sous Windows Mobile est :

·         ActiveSync : pour les ordinateurs dont le système d’exploitation est XP ou en dessous (Windows 2000, Windows 98…).

·         Windows Mobile Device Center : pour les ordinateurs dont le système d’exploitation est Vista.

Nous allons préciser maintenant les éléments utiles apportés par cette interface pour communiquer de PC à Mobile ou de Mobile à PC, puis nos verrons comment le programme ThemeGenCE se sert de cette interface pour augmenter les options de création ou de relecture de Thèmes (TSK), ou donner de multiples renseignements sur le mobile connecté avec ActiveSync ou Windows Mobile Device Center.

Rapi.dll

A l’installation d’ ActiveSync ou Windows Mobile Device Center  une DLL rapi.dll est mise dans les dossiers systèmes de l’ordinateur (Windows/System32). C’est grâce à l’utilisation de cette dll que l’on va pouvoir communiquer entre le PC et le mobile.

Les fonctions déclarées dans rapi.dll sont :

function CeRapiInitEx(RInit: PRAPIINIT): HRESULT;

function CeRapiInit: HRESULT;

function CeRapiUninit: HRESULT;

function CeRapiGetError: HRESULT;

function CeRapiFreeBuffer(buffer: pointer): HRESULT;

function CeRapiInvoke (pDllPath: LPCWSTR; pFunctionName: LPCWSTR;

         cbInput: DWORD; Input: PBYTE; var cbOutput: DWORD; var

         pOutput: PBYTE; pIRAPIStream_:pointer{PIRAPIStream}; dwReserved: DWORD): HRESULT;

 

function CeCreateDatabase (lpszName: LPWSTR; dwDbaseType: DWORD; wNumSortOrder: WORD; rgSortSpecs: PSORTORDERSPEC): CEOID;

function CeDeleteDatabase (oidDbase: CEOID): longbool;

function CeDeleteRecord (hDatabase: THANDLE; oidRecord: CEOID): longbool;

function CeFindFirstDatabase (dwDbaseType: DWORD): THANDLE;

function CeFindNextDatabase (hEnum: THANDLE): CEOID;

function CeOidGetInfo (oid: CEOID; oidInfo: pointer {CEOIDINFO [JP]}): longbool;

function CeOidGetInfoEx (pceguid : PCEGUID; oid: CEOID; oidInfo: pointer): longbool;

function CeOpenDatabase(poid: PCEOID; lpszName: LPWSTR;

                        SortPropID: CEPROPID; Mode: DWORD;

                        hwndNotify: HWND): THANDLE;

function CeReadRecordProps(DBHandle: THANDLE; Mode: DWORD;

                           var PropIDCount: WORD; aCEProps: LPDWORD;

                           var Buf: PChar; var BufSize: DWORD):CEOID;

function CeSeekDatabase (hDatabase: THANDLE; dwSeekType: DWORD; dwValue: DWORD;

         var dwIndex: DWORD): CEOID;

function CeSetDatabaseInfo (oidDbase: CEOID; NewInfo: CEDBASEINFO): longbool;

function CeWriteRecordProps (hDbase: THANDLE; oidRecord: CEOID; cPropID: WORD; rgPropVal: PCEPROPVAL): CEOID;

function CeFindFirstFile (lpFileName: LPCWSTR; var FindFileData: CE_FIND_DATA): THANDLE;

function CeFindNextFile (hFindFile: THANDLE; var FindFileData: CE_FIND_DATA): longbool;

function CeFindClose (hFindFile: THANDLE): longbool;

function CeGetFileAttributes (lpFileName: LPCWSTR): DWORD;

function CeSetFileAttributes  (lpFileName: LPCWSTR; dwFileAttributes: DWORD): longbool;

function CeCreateFile (lpFileName: LPCWSTR; dwDesiredAccess: DWORD;

         dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes;

         dwCreationDistribution: DWORD; dwFlagsAndAttributes: DWORD; hTemplateFile: THANDLE): THANDLE;

function CeReadFile (hFile: THANDLE; Buffer : PByte; nNumberOfBytesToRead: DWORD;

         var lpNumberOfBytesRead: DWORD; lpOverlapped_: POVERLAPPED): longbool;

function CeWriteFile (hFile: THANDLE; Buffer : PByte; nNumberOfBytesToWrite: DWORD;

         var lpNumberOfBytesWritten: DWORD; lpOverlapped_: POVERLAPPED): longbool;

function CeCloseHandle (hObject: THANDLE): longbool;

function CeFindAllFiles (szPath: LPCWSTR; dwFlags: DWORD; var dwFoundCount: DWORD;

         var pFindDataArray: LPCE_FIND_DATA): longbool;

function CeFindAllDatabases (dwDbaseType: DWORD; wFlags: WORD; var cFindData : WORD;

         var pFindData: LPCEDB_FIND_DATA): longbool;

function CeEnumDBVolumes(var pceguid: CEGUID; lpBuf: LPWSTR; dwNumChars: DWORD): longbool;

function CeFindFirstDatabaseEx(pceguid: PCEGUID; dwDbaseType: DWORD): THANDLE;

function CeFindNextDatabaseEx(hEnum : THANDLE; pceguid : PCEGUID): CEOID;

function CeGetLastError: DWORD;

function CeSetFilePointer (hFile: THANDLE; lDistanceToMove: LONG;

         DistanceToMoveHigh: PLONG; dwMoveMethod: DWORD): DWORD;

function CeSetEndOfFile (hFile: THANDLE): longbool;

function CeCreateDirectory (lpPathName: LPCWSTR; lpSecurityAttributes: PSecurityAttributes): longbool;

function CeRemoveDirectory (lpPathName: LPCWSTR): longbool;

function CeCreateProcess (lpApplicationName: LPCWSTR; lpCommandLine: LPCWSTR;

         lpProcessAttributes: PSecurityAttributes; lpThreadAttributes: PSecurityAttributes;

         bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;

         lpCurrentDirectory: LPWSTR; lpStartupInfo: Pointer;

         var lpProcessInformation: TPROCESSINFORMATION): longbool;

function CeMoveFile (lpExistingFileName: LPCWSTR; lpNewFileName: LPCWSTR): longbool;

function CeCopyFile (lpExistingFileName: LPCWSTR; lpNewFileName: LPCWSTR; bFailIfExists: BOOL): longbool;

function CeDeleteFile (lpFileName: LPCWSTR): longbool;

function CeGetFileSize (hFile: THANDLE; lpFileSizeHigh: LPDWORD): DWORD;

function CeRegOpenKeyEx (hKey_: HKEY; lpszSubKey: LPCWSTR; dwReserved: DWORD;

         samDesired: REGSAM; phkResult: PHKEY): LONG;

function CeRegEnumKeyEx (hKey_: HKEY; dwIndex: DWORD; lpszName: LPWSTR; var cchName: DWORD;

         lpReserved: pointer; lpszClass: LPWSTR; lpcchClass: PDWORD;

         lpftLastWrite: PFILETIME): LONG;

function CeRegCreateKeyEx (hKey_: HKEY; lpszSubKey: LPCWSTR; dwReserved: DWORD; lpszClass: LPWSTR;

         dwOptions: DWORD; samDesired: REGSAM; lpSecurityAttributes: PSECURITYATTRIBUTES;

         var hkResult: HKEY; lpdwDisposition: PDWORD): LONG;

function CeRegCloseKey (hKey_: HKEY): LONG;

function CeRegDeleteKey (hKey_: HKEY; lpszSubKey: LPCWSTR): LONG;

function CeRegEnumValue (hKey_: HKEY; dwIndex: DWORD; lpszName: LPWSTR;

         var lpcchName: DWORD; lpReserved: Pointer; lpType: PDWORD; lpData: PBYTE;

         lpcbData: PDWORD): LONG;

function CeRegDeleteValue (hKey_: HKEY; lpValueName: LPCWSTR): LONG;

function CeRegQueryInfoKey (hKey_: HKEY; lpClass: LPWSTR; lpcbClass: LPDWORD;

         lpReserved: Pointer; lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen,

         lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, lpcbSecurityDescriptor: PDWORD;

         lpftLastWriteTime: PFileTime): LONG;

function CeRegQueryValueEx (hKey_: HKEY; lpValueName: LPCWSTR; lpReserved: Pointer;

         lpType: PDWORD; lpData: PBYTE; lpcbData: PDWORD): LONG;

function CeRegSetValueEx (hKey_: HKEY; lpValueName: LPCWSTR; Reserved: DWORD;

         dwType: DWORD; lpData: Pointer; cbData: DWORD): LONG;

function CeGetStoreInformation(var  lpsi: STORE_INFORMATION): longbool;

function CeGetSystemMetrics (nIndex: integer): LONG;

function CeGetDesktopDeviceCaps (nIndex: integer): LONG;

procedure CeGetSystemInfo (var lpsi: TSYSTEMINFO);

function CeSHCreateShortcut (lpszShortcut: LPWSTR; lpszTarget: LPWSTR): DWORD;

function CeSHGetShortcutTarget (lpszShortcut: LPWSTR; lpszTarget: LPWSTR; Arg3: integer): longbool;

function CeCheckPassword (lpszPassword: LPWSTR): longbool;

function CeGetFileTime (hFile: THandle; lpCreationTime, lpLastAccessTime,

         lpLastWriteTime: PFileTime): longbool;

function CeSetFileTime (hFile: THandle; lpCreationTime, lpLastAccessTime,

         lpLastWriteTime: PFileTime): longbool;

function CeGetVersionEx (var lpcov: CEOSVERSIONINFO): longbool;

function CeGetWindow (hWnd: HWND; uCmd: DWORD): HWND;

function CeGetWindowLong (hWnd: HWND; nIndex: integer): LONG;

function CeGetWindowText (hWnd: HWND; lpString: LPWSTR; nMaxCount: integer): LONG;

function CeGetClassName (hWnd: HWND; lpClassName: LPWSTR; nMaxCount: integer): LONG;

function CeGlobalMemoryStatus (var lpms: TMEMORYSTATUS): longbool;

function CeGetSystemPowerStatusEx (var lpsps: SYSTEM_POWER_STATUS_EX; fUpdate: BOOL): longbool;

function CeGetTempPath (nBufferLength: DWORD; lpBuffer: LPWSTR): DWORD;

function CeGetSpecialFolderPath (nFolder: integer; nBufferLength: DWORD; lpBuffer: LPWSTR): DWORD;

function CeQueryInstructionSet(dwInstructionSet : DWORD; lpdwCurrentInstructionSet : LPDWORD): longbool;

 

Ces fonctions ont en principe leurs équivalents – sans le préfixe Ce -  sur le mobile si on les utilise pour des programmes écrits pour Windows Mobile. Celles utilisées – côté PC – sont toutes préfixées par Ce.

 

CeUtil.dll

Une autre dll CeUtil.dll est aussi dans les fichiers Windows système si ActiveSync ou Windows Mobile Device Center sont installés sur le PC.

Les fonctions déclarées dans CeUtil.dll sont :

function CeGetDeviceId: HRESULT;

function CeSvcOpen(uSvc: DWORD; pszPath: LPTSTR; fCreate: boolean; var phSvc: HCESVC): HRESULT;

function CeSvcOpenEx(hSvcRoot: HCESVC; pszPath: LPTSTR; fCreate: boolean; var phSvc : HCESVC): HRESULT;

function CeSvcClose(hSvc: HCESVC): HRESULT;

function CeSvcGetString(hSvc: HCESVC; pszValName: LPCTSTR; pszVal: LPTSTR; cbVal: DWORD): HRESULT;

function CeSvcGetDword(hSvc: HCESVC; pszValName: LPCTSTR; var pdwVal: DWORD): HRESULT;

function CeSvcEnumProfiles(var phSvc: HCESVC; lProfileIndex: DWORD; var plProfile: DWORD): HRESULT;

 

Notifications IDccManSink

L’interface IDccManSink permet de recevoir des notifications sur le PC quand un mobile est connecté ou déconnecté à celui-ci. Cette interface est exécutée dans le thread de Rapi.

Les notifications sont :

function OnLogIpAddr(dwIpAddr : DWORD): HResult; stdcall;

function OnLogTerminated: HResult; stdcall;

function OnLogActive: HResult; stdcall;

function OnLogInactive: HResult; stdcall;

function OnLogAnswered: HResult; stdcall;

function OnLogListen: HResult; stdcall;

function OnLogDisconnection: HResult; stdcall;

function OnLogError: HResult; stdcall;

 

Pour ThemegenCE j’ai écrit un composant (en Delphi) qui reçoit ces notifications en temps réel, ce qui permet d’adapter les options disponibles du programme en fonction de l’état de connexion du mobile.

Pour apprécier le temps réel, lancer ThemeGenCE et connectez ou déconnectez le mobile. Selon l’état, les « gros » boutons de la barre supérieure :

·         « Lire un Thème sur le Pocket PC ».

·         « Transfert du Thème en cours vers le Pocket PC ».

·         « Informations PDA si connecté ».

sont grisés et non « cliquables » si le mobile est déconnecté ou colorés et fonctionnels si le mobile est connecté au PC.

 

ThemeGenCE et ActiveSync ou Windows Mobile Device Center

Voici quelques exemples utilisés par ThemegenCE (en Delphi mais c’est le principe qui compte):

1.       Recenser sur le mobile tous les thèmes (extension « .tsk ») à partir de la racine :

type
  PCEFDArray = ^TCEFDArray;
  TCEFDArray = array[0..32767] of CE_FIND_DATA;

Fonction Recherche ;
var
  foundCount :DWORD;
  findDataArray : PCEFDArray;
  i : integer;

begin
CeFindAllFiles(PWideChar(WideString(‘\*’)),   FAF_ATTRIBUTES or FAF_NAME, foundCount,   LPCE_FIND_DATA(findDataArray));
if foundCount > 0 then
  begin
          for i := 0 to foundCount - 1 do
            begin
              NomFichier := findDataArray[i].cFileName;
              if UpperCase(ExtractFileExt(NomFichier)) = '.TSK' then “mettre ce nom dans la liste”;
            end;
  end;

Cette petite fonction explore tous les dossiers et tous les sous-dossiers à partir de la racine et récupère le chemin complet de chaque fichier ayant pour extension ‘.TSK’.

2.       Ecrire un fichier (en l’occurrence dans  ThemegenCE , un thème) :

function EcrireFichierSurCE(NomFichier, OrigineFichier : string) : boolean;
var
  DestinationHandle : THandle;
  Buf: PByte;
  NumRead, NumWritten: DWORD;
  taille : DWORD;
  FileHandle: Integer;

begin
  Result := false;
  DestinationHandle :=
CeCreateFile(PWideChar(WideString(NomFichier)),
  GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  if DestinationHandle = INVALID_HANDLE_VALUE then Result := false
  else
    begin
      FileHandle := FileOpen(OrigineFichier, fmOpenRead);
      taille := FileSeek(FileHandle,0,2);
      FileSeek(FileHandle,0,0);
      GetMem(Buf, taille);
      try
      NumRead := FileRead(FileHandle, Buf^, taille);
      if NumRead = taille then
        begin
          Result :=
CeWriteFile(DestinationHandle, Buf, taille, NumWritten, nil);
          if Result and (taille <> NumWritten) then Result := false;
        end;
      finally
        FreeMem(Buf);
        FileClose(FileHandle);
       
CeCloseHandle(DestinationHandle);
      end;
    end;
end;

Un fichier est lu sur le PC (par FileOpen) et écrit sur le PDA (par CeWriteFile).

3.       Lire un fichier sur le Mobile :

function LireUnFichierDuPDA(NomFichier, Destination : string) : boolean;
var
  lngFileHandle : THandle;
  lpFindFileData : Ce_Find_Data;
  FileHandle: Integer;
  Buf: PByte;
  NumRead, NumWritten : DWORD;
  taille : DWORD;

begin
  Result := false;
  lngFileHandle :=
CeFindFirstFile(PWideChar(WideString(NomFichier)), lpFindFileData);
  if lngFileHandle <> INVALID_HANDLE_VALUE then //ShowMessage('Le fichier n''existe pas')
    begin
      CeFindClose(lngFileHandle);
      lngFileHandle :=
CeCreateFile(PWideChar(WideString(NomFichier)), GENERIC_READ, FILE_SHARE_READ,
                                   NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
      if lngFileHandle <> INVALID_HANDLE_VALUE then
        begin
          FileHandle := FileCreate(Destination);
          taille := lpFindFileData.nFileSizeLow;
          GetMem(Buf, taille);
          try
            Result :=
CeReadFile(lngFileHandle, Buf, taille, NumRead, NIL);
            if Result and (taille = NumRead) then
              begin
                NumWritten := FileWrite(FileHandle, Buf^, taille);
                if NumWritten <> taille then Result := false;
              end
            else Result := false;
          finally
            FreeMem(Buf);
            FileClose(FileHandle);
           
CeCloseHandle(lngFileHandle);
          end;
        end;
    end;
end;


4.       Rechercher sur le PDA le nom du thème en cours :

Attention, cela nécessite d’aller lire la base de registre du PDA ! Là, ce n’est pas grave car on ne fait que lire la base, mais on peut aussi utiliser des fonctions qui écrivent dans la base de registre…

function NomTSKactuel : string;
var
  cle : HKEY;
  retCode : integer;
  dwType : DWORD;
  bData : PByte;
  cbData : DWORD;
  Chaine : WideString;

begin
  Result := '';
  retCode :=
CeRegOpenKeyEx(HKEY_CURRENT_USER, PWideChar(WideString('Software\Microsoft\Today')), 0, 0, @cle);
  if retCode = ERROR_SUCCESS then
    begin
      Chaine := 'Skin';
      retCode :=
CeRegQueryValueEx(cle, PWideChar(Chaine), NIL, @dwType, NIL, @cbData);
      if ((retCode = ERROR_SUCCESS) or (retCode = ERROR_MORE_DATA)) and (dwType = REG_SZ) then
        begin
          GetMem(bData, cbData);
          try
          if
CeRegQueryValueEx(cle, PWideChar(Chaine), NIL, @dwType, bData, @cbData)
             = ERROR_SUCCESS then Result := String(PWideChar(bData));
          finally
          FreeMem(bData, cbData);
          end;
        end;
     
CeRegCloseKey(cle);
    end;
end;


5.       Rechercher la version de Windows Mobile :

Ici on utilise la fonction CeGetVersionEx :

function MajorVersionCe : integer;
var
  CV : CeOSVersionInfo;

begin
  CV.dwOSVersionInfoSize := SizeOf(CV);
 
CeGetVersionEx(CV);
  Result := CV.dwMajorVersion;
end;


6.       …………..

 

 

Voilà on peut faire beaucoup de choses à partir d’un PC connecté à un mobile, et ce dans les 2 sens. Il est vrai que je ne m’en suis pas privé dans mon programme ThemeGenCE. Pour « me faire plaisir » j’ai rajouté quelques fonctions, qui je l’avoue n’ont pas grand-chose à voir avec la création ou la visualisation de Thèmes Windows Mobile.

 

Benoît Thonnart


Développez pour Windows Mobile
Copyright 2001-2007 - Tous droits réservés
Toutes les marques et produits présents dans ces pages sont la propriété exclusive de leurs sociétés respectives.