ActiveSync, Windows Mobile Device Center et ThemeGenCE (partie 2)
Auteur Benoît Thonnart
Date Juillet 2008

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).

Première partie de cet article

ActiveSync, Windows Mobile Device Center et ThemeGenCE (partie 2)

 

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 notifications que l’on peut recevoir d’un mobile connecté avec ActiveSync ou Windows Mobile Device Center.

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.

Voici le composant écrit en Delphi mais certainement facilement adaptable pour d’autres langages. Je me suis servi des données de MSDN pour le concevoir (http://msdn.microsoft.com/en-us/library/aa912022.aspx).

Le module de Microsoft qui a servi de base à la conception du composant :

/*++

 

Copyright (c) Microsoft Corporation. All rights reserved.

 

Module Name:

 

    dccole2.h

 

Abstract:

 

    This file defines OLE interface to the DCCMAN module (Desktop Only).

    It includes the old interfaces from dccole.h and contains

    a new IDCCManSink2 interface for IPv6 support.

   

Environment:

 

    User Mode - Win32

 

--*/

 

#ifdef __cplusplus

extern "C" {

#endif /* __cplusplus */

 

// {A7B88840-A812-11cf-8011-00A0C90A8F78}

DEFINE_GUID(IID_IDccManSink,

0xa7b88840, 0xa812, 0x11cf, 0x80, 0x11, 0x0, 0xa0, 0xc9, 0xa, 0x8f, 0x78);

// {A7B88841-A812-11cf-8011-00A0C90A8F78}

DEFINE_GUID(IID_IDccMan,

0xa7b88841, 0xa812, 0x11cf, 0x80, 0x11, 0x0, 0xa0, 0xc9, 0xa, 0x8f, 0x78);

// {499C0C20-A766-11cf-8011-00A0C90A8F78}

DEFINE_GUID(CLSID_DccMan,

0x499c0c20, 0xa766, 0x11cf, 0x80, 0x11, 0x0, 0xa0, 0xc9, 0xa, 0x8f, 0x78);

 

// {1317003A-9A62-4040-98F1-9CE9EFD8298B}

DEFINE_GUID(IID_IDccManSink2,

0x1317003a, 0x9a62, 0x4040, 0x98, 0xf1, 0x9c, 0xe9, 0xef, 0xd8, 0x29, 0x8b);

 

#ifndef _DCCOLEH_

#define _DCCOLEH_

 

#undef  INTERFACE

#define INTERFACE   IDccManSink

 

DECLARE_INTERFACE_ (IDccManSink,  IUnknown)

{

                // These methods corespond to GW_LOG messages generated by the Win95 DCC

    STDMETHOD(OnLogIpAddr) (THIS_ DWORD dwIpAddr) PURE;

    STDMETHOD(OnLogTerminated) (THIS) PURE;

    STDMETHOD(OnLogActive) (THIS) PURE;

                STDMETHOD(OnLogInactive) (THIS) PURE;

                STDMETHOD(OnLogAnswered) (THIS) PURE;

                STDMETHOD(OnLogListen) (THIS) PURE;

                STDMETHOD(OnLogDisconnection) (THIS) PURE;

                STDMETHOD(OnLogError) (THIS) PURE;

};

typedef IDccManSink *LPDCCMANSINK;

 

#undef  INTERFACE

#define INTERFACE   IDccMan

 

DECLARE_INTERFACE_ (IDccMan,  IUnknown)

{

    STDMETHOD(Advise) (THIS_

                               IN            IDccManSink * pDccSink,       // The advise sink that is requesting notification

                               OUT DWORD * pdwContext                  // Identifies the context for future calls to the Unadvise method

                ) PURE;

               

                STDMETHOD(Unadvise) (THIS_

                               DWORD dwContext                                                              // As returned by Advise()

                ) PURE;

  

                STDMETHOD(ShowCommSettings) (THIS) PURE;               // Displays the Communication Property Sheet on the screen

                                                                                                                                                                                            // If a connection is active, the sheet is in read-only mode

                STDMETHOD(AutoconnectEnable) (THIS) PURE;

                STDMETHOD(AutoconnectDisable) (THIS) PURE;

 

                STDMETHOD(ConnectNow) (THIS) PURE;                                           // Active only when Autoconnect is Disabled

                STDMETHOD(DisconnectNow) (THIS) PURE;                      // Active only when Autoconnect is Disabled

               

                STDMETHOD(SetIconDataTransferring) (THIS) PURE;

                STDMETHOD(SetIconNoDataTransferring) (THIS) PURE;

                STDMETHOD(SetIconError) (THIS) PURE;

};

typedef IDccMan *LPDCCMAN;

 

#endif /* end, ifdef _DCCOLEH_  */

 

//================ New Sink Interface for IPv6 support ========================

#ifndef _DCCOLE2H_

#define _DCCOLE2H_

 

#include <winsock2.h>

#undef  INTERFACE

#define INTERFACE   IDccManSink2

 

DECLARE_INTERFACE_ (IDccManSink2,  IDccManSink)

{

    STDMETHOD(OnLogIpAddrEx) (THIS_ const SOCKADDR_STORAGE* pIpAddr) PURE;

};

typedef IDccManSink2 *LPDCCMANSINK2;

 

#endif /*_DCCOLE2H_*/

 

 

#ifdef __cplusplus

}

#endif  /* __cplusplus */

 


 

Et maintenant le composant Delphi :

(* -------------------------------------------

           \\\|///

         \\  - -  //

          (  @ @  )

 -------oOOo-(_)-oOOo----------

  Copyright (c)Benoît Thonnart

            2005

          AutoSink

 -----------------Oooo---------

          oooO   (   )

         (   )    ) /

          \ (    (_/

           \_)

 ---------------------------------------------*)

 

unit AutoSink;

 

interface

 

uses

  Windows, Classes, ActiveX, SysUtils, ComObj(*, Dialogs*);

 

const

  CLSID_DccMan    : TGUID = '{499C0C20-A766-11cf-8011-00A0C90A8F78}';

  IID_IDccMan     : TGUID = '{A7B88841-A812-11cf-8011-00A0C90A8F78}';

  IID_IDccManSink : TGUID = '{A7B88840-A812-11cf-8011-00A0C90A8F78}';

 

type

  IDccManSink = interface(IUnknown)

    ['{A7B88840-A812-11cf-8011-00A0C90A8F78}']

    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;

  end;

  LPDCCMANSINK = ^IDccManSink;

 

  IDccMan = interface(IUnknown)

    ['{A7B88841-A812-11cf-8011-00A0C90A8F78}']

    function Advise(pDccSink : LPDCCMANSINK; var pdwContext : DWORD): HResult; stdcall;

    function Unadvise(dwContext : DWORD): HResult; stdcall;

    function ShowCommSettings: HResult; stdcall;

    function AutoconnectEnable: HResult; stdcall;

    function AutoconnectDisable: HResult; stdcall;

    function ConnectNow: HResult; stdcall;

    function DisconnectNow: HResult; stdcall;

    function SetIconDataTransferring: HResult; stdcall;

    function SetIconNoDataTransferring: HResult; stdcall;

    function SetIconError: HResult; stdcall;

  end;

  //LPDCCMAN = ^IDccMan;

 

{$M+}

  { Déclaration forward pour FOwner }

  TDccMan = class;

 

  { Déclaration TDccEventSink }

  TDccEventSink = class(TInterfacedObject, IUnknown, IDccManSink)

  private

    FNbRef : integer;

    FOnLogEtat : string;

    FOnLogIndex : integer;

    FOwner : TDccMan;

  public

    { IUnknown }

    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;

    function _AddRef: Integer; stdcall;

    function _Release: Integer; stdcall;

    { IDccManSink}

    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;

 

    constructor Create(AOwner : TDccMan);

    destructor Destroy; override;

    function RehercheInterface(var p : LPDCCMANSINK) : HResult;

    property OnLogEtat : string read FOnLogEtat stored '';

    property OnLogIndex : integer read FOnLogIndex stored - 1;

  published

  //

  end;

  //pDccEventSink = ^TDccEventSink;

 

  { Déclaration TDccMan }

  TDccMan = class

  private

    FIDccMan : IDccMan;

    FdwContext : DWORD;

    FDccSync : TDccEventSink;

    FpIMS : LPDCCMANSINK;

    FComLib : boolean;

    FQI : boolean;

    FAdvise : boolean;

    FOnChange : TNotifyEvent;

    procedure DoChange(Sender : TObject);

  public

    constructor Create;

    destructor Destroy; override;

    procedure Advise;

    procedure Unadvise;

    procedure ShowCommSettings;

    function LitEtat : string;

    function LitIndex : integer;

    property ComLibOk : boolean read FComLib stored false;

    property InterfaceOk : boolean read FQI stored false;

    property NotificationOk : boolean read FAdvise stored false;

    property Etat : string read LitEtat stored '';

    property IndexOnLog : integer read LitIndex stored - 1;

  published

    property OnChange : TNotifyEvent read FOnChange write FOnChange;

  end;

 

implementation

 

{ TDccMan implementation }

 

constructor TDccMan.Create;

var

  hr : HRESULT;

 

begin

  inherited Create;

  CoInitialize(nil);

  FComLib := false;

  FQI := false;

  FAdvise := false;

  hr := CoCreateInstance(CLSID_DccMan, nil, CLSCTX_INPROC_SERVER or

                         CLSCTX_LOCAL_SERVER, IID_IDccMan, FIDccMan);

  if SUCCEEDED(hr) then

    begin

      FComLib := true;

      FDccSync := TDccEventSink.Create(Self);

      hr := FDccSync.RehercheInterface(FpIMS);

      if SUCCEEDED(hr) then FQI := true;

    end;

end;

 

destructor TDccMan.Destroy;

begin

  Unadvise;

  if Assigned(FDccSync) then FDccSync.Free;

  CoUninitialize;

  inherited Destroy;

end;

 

procedure TDccMan.Advise;

var

  hr : HRESULT;

 

begin

  // apparemment on peut cumuler les appels

  // si déjà Advise redonne l'état actuel (actif ou autre)

  // peut être utile, mais le contexte a changé !!

  if FQI and not FAdvise then

    begin

      hr := FIDccMan.Advise(FpIMS, FdwContext);

      if SUCCEEDED(hr) then FAdvise := true;

    end;

end;

 

procedure TDccMan.Unadvise;

begin

  if FAdvise then

    begin

      FIDccMan.Unadvise(FdwContext);

      FAdvise := false;

    end;

end;

 

procedure TDccMan.ShowCommSettings;

begin

  if FComLib then FIDccMan.ShowCommSettings;

end;

 

function TDccMan.LitEtat : string;

begin

  if Assigned(FDccSync) then Result := FDccSync.FOnLogEtat

  else Result := '';

end;

 

function TDccMan.LitIndex : integer;

begin

  if Assigned(FDccSync) then Result := FDccSync.FOnLogIndex

  else Result := - 1;

end;

 

procedure TDccMan.DoChange(Sender : TObject);

begin

  if Assigned(FOnChange) then FOnChange(Self);

end;

 

 

 

{ TDccEventSink implementation }

 

constructor TDccEventSink.Create(AOwner : TDccMan);

begin

  inherited Create;

  FNbRef := 0;

  FOwner := AOwner;

end;

 

destructor TDccEventSink.Destroy;

begin

  _Release;

  inherited Destroy;

end;

 

function TDccEventSink.QueryInterface(const IID: TGUID; out Obj): HRESULT;

begin

  Result := E_NOINTERFACE;

  if GetInterface(IID, Obj) then Result := S_OK;

end;

 

function TDccEventSink.RehercheInterface(var p : LPDCCMANSINK) : HResult;

begin

  Result := QueryInterface(IID_IDccManSink, p);

end;

 

function TDccEventSink._AddRef: Integer;

begin

  InterLockedIncrement(FNbRef);

{  FOnLogIndex := 200;

  FOnLogEtat := Format('AddRef %d', [FNbRef]);

  FOwner.DoChange(Self); }

  Result := 2;

end;

 

function TDccEventSink._Release: Integer;

begin

  InterLockedDecrement(FNbRef);

{  FOnLogIndex := 100;

  FOnLogEtat := Format('Release %d', [FNbRef]);

  FOwner.DoChange(Self); }

  Result := 1;

end;

 

function TDccEventSink.OnLogIpAddr(dwIpAddr : DWORD): HResult;

begin

  FOnLogIndex := 1;

  FOnLogEtat := Format('IP Adress : %d.%d.%d.%d',

                                           [dwIpAddr and $FF,

                                           (dwIpAddr and $FF00) shr 8,

                                           (dwIpAddr and $FF0000) shr 16,

                                            dwIpAddr shr 24]);

  FOwner.DoChange(Self);

  Result := NO_ERROR;

end;

 

function TDccEventSink.OnLogTerminated: HResult;

begin

  FOnLogIndex := 2;

  FOnLogEtat := 'Closed';

  FOwner.DoChange(Self);

  Result := NO_ERROR;

end;

 

function TDccEventSink.OnLogActive: HResult;

begin

  FOnLogIndex := 3;

  FOnLogEtat := 'Active';

  FOwner.DoChange(Self);

  Result := NO_ERROR;

end;

 

function TDccEventSink.OnLogInActive: HResult;

begin

  FOnLogIndex := 4;

  FOnLogEtat := 'Inactive';

  FOwner.DoChange(Self);

  Result := NO_ERROR;

end;

 

function TDccEventSink.OnLogAnswered: HResult;

begin