Bewise

Nous développons... votre avance

Windows Media Center et WCF : développez votre maison intelligente

SLF
02/07/2008 - Frédéric Colin
Télécharger les sources

Introduction

Plus l’on avance dans le temps, plus l’on se rapproche de la maison numérique rêvée il y a quelques années (pilotage des volets roulants, réglage du chauffage / Climatisation, ambiances lumineuses et sonores, Media Center remplaçant judicieusement un magnétoscope, une chaine hifi, tuners hybrides, etc.). Mais qu’en est-il réellement pour le développeur souhaitant développer une application en tâche de fond hébergée dans Windows Media Center ? C’est ce que je me propose de vous exposer dans cet article.

 

La lecture de cet article suppose quelques connaissances de base sur Windows Communication Foundation ainsi qu'une bonne connaissance du langage C#. L'exemple a été développé en C# avec Visual Studio Team System 2008, Framework 3.5, Windows Media Center SDK 5.3 sur Windows Vista Ultimate US. L’IPhone utilisé possédait le Firmware 1.1.4.

 

Objectif Fonctionnel

L’objectif fonctionnel de cet article est de développer les fonctionnalités d’une télécommande pour Windows Media Center et de les utiliser à partir d’un IPhone. L’exploitation de ces services via l’IPhone sera réalisé en utilisant les possibilités de requêtage offertes par Safari (et par tous les navigateurs du marché), à savoir l’utilisation de l’API « XmlHttpRequest » attaquant des services REST (« Representational State Transfer »).

Fonctionnel 

Un peu de théorie

Les points clés techniques autour de cet exemple sont :

·       Développement de services REST avec WCF

·       Utilisation du SDK Windows Media Center 5.3

·       Développement d’un client aussi léger que possible en Javascript avec compatibilité Safari IPhone, Safari Windows et Internet Explorer

 

Vous avez dit REST ?

Une architecture REST est simplement l’utilisation du modèle de programmation Web adapté au monde des services répartis. Une architecture REST vient juste décrire les éléments architecturaux d’un système réparti, mais surtout pas comment techniquement l’implémenter. Les concepts fondamentaux sont les suivants :

·       Utilisation des URIs pour l’identification des ressources

·       Utilisations de protocoles standards (HTTP et HTTPS)

·       HTML et XML sont utilisés pour la représenter le contenu des ressources accédées

·       Le ContentType et le type MIME sont utilisés pour connaitre le type des ressources récupérées (images, xml, vidéo, etc.)

·       Utilisation des verbes HTTP pour la gestion des ressources : PUT (Create), GET (Read), POST (Update), DELETE (Delete)

·       Passage de paramètre via la syntaxe d’une URI (http://192.168.0.10:8000/MediaCenter/IWebManagement/GetFile?filename=default.htm&encoding=text/html)

·       Pas d’encodage SOAP, juste du requêtage HTTP

·       Récupération des données structurées au format XML ou JSON. Voici un exemple JSON :

 

{"FirstName":"Frédéric"

,"Id":1

,"LastName":"Colin"

,"TelephoneNumbers":[

    {"Number":"0561751313"

    ,"Type":"pro"}]}

 

La simplicité du modèle, son poids plus faible en volume de données et la facilité de requêtage en font une technologie qui malgré quelques limites (protocole, peu d’outils pour l’instant, etc.) se révèle très intéressante pour les nouvelles applications de type RIA notamment.

 

Dans sa version 3.5, le Framework .NET apporte toutes les évolutions nécessaires pour une implémentation transparente des architectures REST, et ce, dans dénoter par rapport à ce qui était fait avant en gardant le principe du triptyque « Contrat, Binding et Address ». Une nouvelle Assembly du Framework .NET 3.5 fait donc son apparition pour cela : « System.ServiceModel.Web ».

 

Vous avez dit Windows Media Center SDK ?

Windows Media Center est en fait une « SandBox » dans laquelle nous avons la possibilité de développer des addins. Ces derniers peuvent posséder ou non une interface graphique. Le SDK de Windows Media Center en Version 5.3 est assez pauvre pour ce qui est des explications techniques et des exemples fournis que vous sortiez des chantiers battus ou non. On ne peut qu’espérer une amélioration sur ce sujet.

 

Afin d’implémenter correctement les services, les références suivantes peuvent être ajoutées (à partir du répertoire « c:\Windows\ehome ») :

·       ehepg : permet d’accéder aux classes permettant de gérer le guide des programmes

·       ehiProxy : permet d’accéder aux classes en rapport avec la gestion des chaînes TV notamment

·       ehRecObj : permet d’accéder à la classe LineUp sans laquelle le remplissage du guide serait impossible

·       Microsoft.MediaCenter : permet de fournir l’accès à la classe « AddInHost » la clé de voute du modèle objet de Windows Media Center

·       Microsoft.MediaCenter.UI : permet de fournir l’accès à la classe de base « ModelItem » dont hérite la classe « Application »

 

Au démarrage de l’addin, la classe suivante est appelée par Windows Media Center :

Addin

 

L’interface « IAddInModule » permet de s’abonner aux appels de Windows Media Center au démarrage de l’addin et à son arrêt via les méthodes « Initialize » et « Uninitialize » pour respectivement initialiser les variables privées et enregistrer les informations relatives à l’état et libérer les ressources mémoire.

 

L’implémentation de l’interface « IAddInEntryPoint » est obligatoire pour le démarrage de l’application via l’appel de la méthode « Launch ». Le paramètre envoyé par Windows Media Center à la méthode « Launch » permet d’accéder aux services fournis par Windows Media Center dans l’addin ainsi développé.

 

Mise en place de la solution

Voici une Checklist à suivre afin d’installer correctement son environnement de travail et à pouvoir débugger les addins en tâche de fond développés pour Windows Media Center. En pré-requis, j’ai considéré que Visual Studio 2008 était correctement installé et configuré :

 

1.    Télécharger et installer le SDK 5.3 pour Windows Media Center (http://www.microsoft.com/downloads/details.aspx?FamilyID=A43EA0B7-B85F-4612-AA08-3BF128C5873E&displaylang=en)

 

Téléchargement

 

Ce dernier amène tout le nécessaire pour développer. Pour ce qui est de la documentation et des exemples. L’installation du SDK apporte trois nouveaux types de projets au niveau Visual Studio 2008 :

 

NewProject 

 

Dans le cadre de cet exemple, nous utiliserons le type de projet « background » comme processus porteur de nos services WCF (les deux autres permettant de créer des applications avec une interface graphique).

 

2.    Une fois le projet créé, il reste à le paramétrer. Pour cela, un fichier HTML (ReadMe.htm) a été inclus dans le projet et permet de récapituler l’ensemble des actions à réaliser. En résumé, il faut :

·        Signer l’Assembly et en récupérer la clé publique

·        Editer le fichier xml « Registration.xml » qui servira à enregistrer l’addin dans Windows Media Center et y positionner la clé publique. En profiter pour nommer et décrire l’addin de manière correcte. C’est aussi dans ce fichier que l’on paramètre le fait qu’il s’agisse d’un addin de type « Background »

·        Concernant l’installation de l’addin, vous avez la possibilité d’utiliser Windows Installer XML (WiX). Pour cet article, j’ai reproduit les comportements d’installation via des actions pré-compilation et post-compilation que l’expliciterai ci-après

 

Ce qui donne concrètement :

 

<application

title="Programmez.WCF.Sample1.MediaCenterHost"

id="{b560dbd4-244e-490e-8950-2c8b3de18213}">

      <entrypoint

id="{98702d33-b492-47fa-ab67-ce2b4a611494}"

            addin="Programmez.WCF.Sample1.MediaCenterHost.MyAddIn,

                  Programmez.WCF.Sample1.MediaCenterHost,Culture=Neutral,

                  Version=1.0.0.0,PublicKeyToken=7917b0ee6878be4a"

            title="Programmez.WCF.Sample1.MediaCenterHost"

            description="Programmez.WCF.Sample1.MediaCenterHost Description">

            <category category="Background"/>

      </entrypoint>

</application>

 

3.    Modification des actions de pré et post compilation en éditant les propriétés de projet, onglet « Build Events »

·        En Pre-Build. J’ai rencontré quelques soucis de rafraichissement avec Visual Studio qui m’ont obligé à vider le cache applicatif. J’ai donc systématisé cela avant chaque compilation via la commande suivante :

 

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\mage.exe" –cc

 

·        En Post-build, il faut :

o    installer l’addin dans le GAC

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe" /i "$(TargetPath)" /f

 

o   Déregistrer l’addin de Windows Media Center s’il avait été installé au préalable

 

"C:\Windows\ehome\RegisterMCEApp.exe" /u "$(ProjectDir)\Registration.xml"

 

o   Enregistrer l’addin dans Windows Media Center

 

"C:\Windows\ehome\RegisterMCEApp.exe" "$(ProjectDir)\Registration.xml"

 

o   Pour mon exemple, l’accès à la page HTML représentant la télécommande et accédant aux services REST, est réalisé au travers d’un service WCF qui renvoie un ContentType de type « text/html ». J’ai décidé de stocker l’ensemble des pages Web (*.htm, *.png) directement dans le projet afin de bénéficier des facilités offertes par Visual Studio 2008, mais aussi pour éviter toutes les problématiques de paramétrage des appels via XmlHttpRequest « Cross Domain ». La ligne de commande suivante me permet donc de déployer ces pages directement dans le répertoire contenant les fichiers relatifs à Windows Media Center

 

copy  "$(ProjectDir)WebSite\*.*" c:\Windows\ehome

 

·        Signer l’Assembly et en récupérer la clé publique

4.    Modification de la séquence de démarrage du débugage en indiquant que l’on va exécuter un programme externe qui lancera Windows Media Center (C:\Windows\ehome\ehshell.exe).

5.    Toutes ces étapes sont complétées d’une dernière permettant de faciliter la mise au point des addins dans Windows Media Center. Tout se passe au niveau de la base de registre « HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Media Center\Settings\Extensibility »

 

Registry

 

La première valeur permet d’afficher une boite de dialogue indiquant le PID du processus à attacher à Visual Studio pour débugger (l’exécutable associé s’appelle ehexthost.exe). La seconde permet d’afficher le détail d’une erreur provenant d’un addin.

 

Création des projets

La création de ce projet comme toute solution orientée Windows Communication Foundation respecte les 5 étapes habituelles :

1.    Création des contrats de données : il s’agit de la structure des données qui seront échangées entre les clients et les Services

2.    Création des contrats de service : il s’agit des contrats qui seront définis entre les clients et les Services (Notion d’interfaces)

3.    Implémentation des services : développement du métier des Services qui seront exposés

4.    Création du processus porteur : développement du processus porteur qui sera chargé de rendre accessibles les services WCF aux clients via des EndPoints définis précisément

5.    Développement du client : chargé d’utiliser les services

 

La solution mise en place dans cet exemple est la suivante :

 

Solution 

 

Vous noterez qu’il n’y a que quatre projets dans ma solution. En effet, mon objectif initial était de faire héberger la page HTML directement sur l’IPhone. Malheureusement, nativement et en l’état actuel des choses, l’IPhone n’est pas vu comme une unité externe sur laquelle on peut copier directement des fichiers. Il existe des solutions logicielles pour cela, mais je n’ai pas cherché plus loin car tel n’était pas l’objectif de l’article. Plusieurs autres solutions simples s’offraient donc à moi :

·        Envoyer la page HTML en copie d’un mail et m’en servir comme cela à partir de l’IPhone

·        Créer un site Web dédié à cela et gérer les appels Cross Domain

·        Faire héberger ma page HTML et mon image directement dans le processus porteur Windows Media Center et gérer un service WCF chargé d’y accéder

 

J’ai donc choisi la dernière solution, d’où la présence de quatre projets seulement.

 

Les Contrats

Les contrats de données

 

Les contrats de données viennent décrire la structure des messages qui seront échangés entre les clients et les services. Ils sont complètement décorelés de l’implémentation des services qui s’en servent afin d’être sérialisés en fonction d’un schéma précis. Pour l’exemple qui nous intéresse, j’ai simplement reproduit un sous-ensemble du modèle objet fourni par Windows Media Center sur la notion de Guide. Le Guide représente la liste des programmes TV associés aux chaines. Il y a deux types de contrats :

 

·       Le contrat concernant le guide des programmes TV

DataContractGuide

 

Globalement ce qu’il faut retenir de ce contrat de données, c’est que les informations sont regroupées par chaînes TV (« Channel »), qu’une partie des informations d’une chaîne est contenue dans une classé composée (« Service »). Ensuite, une chaîne contient une liste de planifications (« ScheduleEntry »), chaque planification étant composée d’un programme (« Program »).

 

D’un point de vue technique, les contrats de données sont rendus sérialisables dans le sens WCF du terme par décoration avec les attributs « DataContract » et « DataMember » issus de l’Assembly « System.RuntimeSerialization » :

 

    [DataContract(Namespace = "http://www.bewise.fr/2008/05/Programmez")]

    public class Channel

    {

        [DataMember()]

        public string Id

        { get; set; }

 

        [DataMember()]

        public bool IsActive

        { get; set; }

 

        Etc.

    }

 

·       Le contrat de données concernant la gestion de la télécommande permet de décrire la liste des commandes du contrôle à distance acceptées via un type énuméré qui sera décoré avec les attributs « DataContract » et « EnumMember » :

 

    [DataContract()]

    public enum CommandListOption : ushort

    {

        [EnumMember]

        NAVIGATE_GUIDE = 0x47,

        [EnumMember]

        NAVIGATE_LIVE_TV = 0x54,

        [EnumMember]

        NAVIGATE_MUSIC = 0x4d,

        [EnumMember]

 

        Etc.

      }

 CommandListOption

Les contrats de service

Les contrats de services viennent décrire les contrats dans le sens « Interface » du terme. Ils seront implémentés par la couche service et seront rendus accessibles via WCF. Pour l’exemple, j’ai retenu deux contrats :

 

·       Le contrat de gestion de Windows Media Center :

ServiceContractWMCE 

 

Les noms des services ont des méthodes éponymes qui ne méritent pas plus de description. La seule information à donner concerne le service « GetGuide » qui est surchargé et qui renvoie l’ensemble du guide ou bien une partie du guide en fonction d’une plage de date. Le service « StandBy » permet de mettre en veille le PC.

 

·       Le contrat permettant de récupérer via un service WCF la page HTML contenant l’interface de la télécommande.

ServiceContractWeb

C’est  à cet endroit que la notion de service REST est définie. Effectivement, le Framework .NET 3.5 ajoute de nouveaux attributs à positionner, rendant accessibles les services via une architecture de type REST. Encore une fois, nous nous contentons de décorer plutôt que de coder la tuyauterie et c’est le processus porteur qui se chargera d’exposer les services et ce, de manière transparente pour le développeur.

 

Ce qui nous donne pour le contrat de gestion du Media Center :

 

CodeServiceWMC 

 

Au niveau REST, J’ai volontairement choisi d’utiliser l’attribut « WebInvoke » en lieu et place du classique « WebGet » afin de réaliser les appels en HTTP / POST plutôt qu’en HTTP / GET, ce afin d’éviter toute problématique de cache des navigateurs Web du marché.

 

En effet, les requêtes HTTP / GET  sont souvent mises en cache du côté du navigateur afin d’éviter les allers-retours inutiles sur le serveur Web pour les requêtes HTTP / GET identiques d’un point de vue de l’URL. Ce qui n’est pas sans poser un réel problème avec XMLHttpRequest qui bénéficie lui aussi de ce cache lorsque l’on souhaite exécuter une requête avec la même URL en accédant toujours aux services WCF (par exemple augmenter le volume qui doit se faire  à chaque appel).

 

Certains contournent ce problème en greffant à l’URL de base un n° aléatoire ou un guid. Je n’aime pas particulièrement cette manière de faire et je lui préfère donc un requêtage HTTP / POST qui n’a pas cette problématique de cache du navigateur.

 

Vous noterez aussi sur le service de récupération du guide (« GetGuide ») deux choses importantes :

·       D’un point de vue objet au niveau de l’interface, j’ai défini deux surcharges de la méthode « GetGuide », ce qui n’est pas possible d’un point de vue contrat de service, d’où l’attribut « OperationContract » paramétré avec un renommage (« GetGuideByDate ») du service visible des clients

·       Le format de la réponse est paramétré au niveau de l’attribut « WebInvoke » en XML. J’aurai tout aussi bien pu utiliser un formatage de type JSON


L’implémentation

Le métier associé aux services est globalement divisé en 4 grands domaines :

·       Gestion du son

·       Gestion du guide

·       Gestion de la mise en veille

·       Gestion des autres touches

·       Gestion de l’IHM

 

Toute cette gestion est implémentée dans le schéma de classe suivant :

ServiceWMCE

 

La classe qui supporte l’implémentation des contrats de services définis précédemment est « MediaCenterManagement ». La classe « Application » sert à faire le lien entre les informations fournies par l’addin MediaCenter (donc le modèle objet de Windows Media Center) et la couche Services. Les structures « KEYBDINPUT », « INPUT » et la classe « SendInputCommand » servent à gérer les autres commandes liés à la navigation dans les menus. Enfin, la classe « ChannelEqualityComparer » servira dans les requêtages Linq To Object afin de déterminer une manière de comparer les chaînes TV (« Channel »).

 

Gestion du son

La classe « Application » fournit un propriété « MediaCenterEnvironment.AudioMixer » afin de gérer tous les services en relation avec le son (Mute, VolumeUp, VolumeDown, Volume). Par exemple voici l’implémentation du service permettant de monter le volume :

 

public void VolumeUp()

{

CurrentApplication.MediaCenterEnvironment.AudioMixer.VolumeUp();

}

 

Gestion du guide

Le modèle objet correspondant au guide des programmes est assez curieux dans son ensemble (relations 1-1, redondance, nombreuses informations non remplies, etc.) et j’ai cherché à le représenter de manière fidèle du point de vue des classes mais incomplète du point de vue des propriétés dans le contrat de données.

 

J’ai construit l’instance du contrat de données par un ensemble de requête Linq To Object.

 

1.    La première requête permet de récupérer la liste des chaines :

 

(from chan

in Guide.CurrentEPG.Channels.Distinct(new ChannelEqualityComparer())

where chan.PrimaryService != null

orderby chan.Number

select createChannel(chan, fromDate, toDate)).ToList();

 

Vous noterez l’utilisation de la méthode d’extension « Distinct » avec une classe « comparer » permettant de comparer les  chaine une à une. La méthode de comparaison employée est basée sur le « CallSign » (nom en clair de la chaîne)

 

public bool Equals(ehiProxy.Channel x, ehiProxy.Channel y)

{

    if (x.PrimaryService == null || y.PrimaryService == null)

        return (x.PrimaryService == y.PrimaryService);

               

    return (x.PrimaryService.CallSign == y.PrimaryService.CallSign);

}

 

La sélection distincte a été rendue obligatoire du fait que le modèle objet de Windows Media Center renvoyait des chaines dupliquées (probablement sur des fréquences différentes à cause des deux émetteurs proches de chez moi).

 

La méthode « createChannel » étant spécialisée dans la création des instances de type « Channel «  de mon contrat de données

 

2.    Création de la chaîne

 

private DataContracts.Channel createChannel

(ehiProxy.Channel channel, DateTime fromDate, DateTime toDate)

{

    return

        new DataContracts.Channel()

        {

            Id = channel.Id,

            Number = channel.Number,

            IsActive = channel.IsActive,

            PrimaryService = createService(channel.PrimaryService),

            Scheduling = (

                from s in channel.ShowsAt(fromDate.ToUniversalTime(),

                    toDate.ToUniversalTime())

                where s.Program != null

                select createScheduleEntry(s)

            ).ToList()

        };

}

 

Les points intéressants :

1.    Utilisation des initialiseurs par défaut

2.    Délégation de la création de l’instance du service à une méthode spécialisée

3.    La récupération de la grille des programmes (« Scheduling ») via une requête Linq To Object

4.    Le requêtage des éléments d’un grille via l’utilisation de la méthode « ShowAt »

5.    Délégation de la création de l’instance d’un élément de  la grille à une méthode spécialisée

 

3.    Création du service

 

private DataContracts.Service createService(ehiProxy.Service service)

{

    return

        new DataContracts.Service()

        {

            Affiliation = service.Affiliation,

            CallSign = service.CallSign,

            Id = service.Id,

            IsDigital = service.IsDigital,

            IsDisabled = service.IsDisabled,

            IsPayPerView = service.IsPayPerView,

            Name = service.Name,

            VirtualChannelNumber = service.VirtualChannelNumber

        };

}

 

4.    Création d’un élément de la grille des programmes

 

private DataContracts.ScheduleEntry createScheduleEntry(

ehiProxy.ScheduleEntry scheduleEntry)

{

    return new DataContracts.ScheduleEntry()

    {

        Dolby = scheduleEntry.Dolby,

        EndTime = scheduleEntry.EndTime,

        GenericId = scheduleEntry.GenericId,

        HDTV = scheduleEntry.HDTV,

        Id = scheduleEntry.Id,

        Live = scheduleEntry.Live,

        StartTime = scheduleEntry.StartTime,

        SubTitled = scheduleEntry.SubTitled,

        Program = createProgram(scheduleEntry.Program)

    };

}

 

5.    Création d’un programme

 

private DataContracts.Program createProgram(ehiProxy.Program program)

{

    return

        new DataContracts.Program()

        {

            Description = program.Description,

            DirectorStrs = new List<string>(program.DirectorStrs),

            EpisodeId = program.EpisodeId,

            EpisodeTitle = program.EpisodeTitle,

            ActorStrs = new List<string>(program.ActorStrs),

            GuestsStrs = new List<string>(program.GuestsStrs),

            HasMultipleScheduleEntries

                = program.HasMultipleScheduleEntries,

            HostsStrs = new List<string>(program.HostsStrs),

            Id = program.Id,

            Language = program.Language,

            StoreId = program.StoreId,

            Title = program.Title,

            Year = program.Year

        };

}

 

En bref, rien de bien compliqué si ce n’est qu’il faut penser à initialiser correctement le modèle objet avant de pouvoir requêter correctement :

 

1.    Utilisation de la classe « LineUp » afin d’initialiser la liste des services et de la récupérer

 

Microsoft.MediaCenter.TV.Epg.Lineup lu

= new Microsoft.MediaCenter.TV.Epg.Lineup();

 

_programs = lu.GetServiceIds();

 

2.    Paramétrage du répertoire de travail et initialisation de la grille des programmes

 

DirectoryInfo info = new DirectoryInfo(

Environment.GetFolderPath(Environment.SpecialFolder.System));

           

if (Directory.Exists(info.Parent.FullName + @"\ehome"))

{

Environment.CurrentDirectory = info.Parent.FullName + @"\ehome";

Microsoft.Ehome.Epg.Guide.Initialize();

 

Etc.

}

 

Sans les deux paramétrages précédents, la grille des programmes par chaîne n’est pas remontée.

 

Enfin si vous vous demandez où sont stockées les informations du Guide une fois téléchargées depuis le Web, vous pouvez réflecter l’assembly « ehepg.dll », regarder le contenu de la classe « Guide » et notamment la méthode statique « Initialize() » utilisée précédemment. Cette dernière se sert d’une autre classe interne, via l’une de ses propriétés : « epgFileHelper.CurrentEpgFile ».

 

En poursuivant l’étude minutieuse du code, il devrait être possible de « calculer » ce chemin d’accès. Toutefois pour des raisons de facilité, j’ai fait un peu de « reflection » afin de l’afficher et voici le code correspondant :

 

System.Reflection.Assembly assemb

    = System.Reflection.Assembly.Load("ehepg");

 

String s =

    assemb.GetType("Microsoft.Ehome.Epg.Helper.EpgFileHelper",

            false,true).GetProperty("CurrentEpgFile").GetValue(null, null);

 

La valeur ainsi récupérée pour ma machine est :

 

« C:\ProgramData\Microsoft\eHome\EPG\f1b2556dee484c7f8a3ff50ebd0acfc2.sdf »

 

Il s’agit d’une base « SQLLite » dont les moyens d’accès ont été développés dans l’assembly « ehepg.dll » (espace de nom « Microsoft.Ehome.Epg.Database ») spécifiquement par Microsoft en implémentant les interfaces ADO.NET qui vont bien :

 

SQLLite

Gestion de la mise en veille

Le métier en rapport avec cette partie utilise pleinement la classe « Application » :

Application.SetSuspendState(System.Windows.Forms.PowerState.Suspend, true, true);

 

Le premier paramètre permet de choisir entre le mode suspendu et le mode hibernation. La première valeur booléenne permet de forcer la machine à passer dans l’état suspendu immédiatement et la deuxième valeur pour désactiver la possibilité de réveil événementiel de la machine.

 

Gestion des autres touches

Cette partie a de loin été la plus complexe à élaborer. En effet, le modèle objet de Media Center n’offre pas ces services en standard (déplacement Haut, bas, gauche, droit, ok, menu TV, etc.). Il a donc fallu trouver une solution spécifique pour y arriver.

 

La solution qui m’avait initialement effleuré l’esprit était d’utiliser la classe « SendKeys » afin de simuler les touches du clavier. Malheureusement, ce fut impossible du fait que c’est l’addin Média Center qui se chargerait d’envoyer les ordres (puisque cet addin est le processus porteur WCF) et non pas directement une fenêtre Windows. Il m’a donc fallu recréer une classe permettant de réaliser cette opération directement en appelant les APIs bas-niveau du système d’exploitation.

 

La logique fut donc la suivante :

1.    Etape 1 : définir les combinaisons de touche gérées au travers d’un contrat de données afin de faciliter l’utilisation et la description

2.    Récupérer le handle de la fenêtre Windows supportant le processus porteur de Windows Media Center

3.    Eventuellement restaurer la fenêtre à l’écran si elle avait été iconisée dans la barre de tâche. Pour cela, j’utilise l’API :

 

[DllImport("user32.dll")]

private static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);

 

4.    Eventuellement passer la fenêtre au premier plan si elle ne l’était pas afin que les ordres clavier lui soient transmis. Pour cela, j’utilise l’API :

 

[DllImport("user32.dll")]

public static extern bool SetForegroundWindow(IntPtr hWnd);

 

5.    Ensuite, il reste à gérer les combinaisons de touches de types, Shift, Ctrl, Alt, Win. Le tout étant encapsulé dans une classe dédiée à cela appelée « SendInputCommand ». Pour cette dernière, je me suis basé sur une application développée en 2005 (http://sourceforge.net/projects/mcecontroller/) et qui fournissait une partie du service que je recherchais. J’ai donc fait évoluer cette classe afin qu’elle réponde complètement à mon besoin initial.

 

A partir de là, le métier du service « SendKey » du contrat de service « IMediaCenterManagement » est simplement d’utiliser correctement la classe « SendInputCommand » qui encapsule correctement cette gestion.

 

La gestion de l’IHM

Afin d’éviter tout problème d’appel Cross Domain entre la page HTML qui exécute les appels HTTP et le Host hébergeant les services WCF, j’ai embarqué la page HTML ainsi que l’image afférente directement dans le service WCF et l’ai rendu accessible au travers du contrat de service suivant « IWebManagement » décrit précédemment.

 

Globalement, le métier de ce service est des plus simples. Il s’agit de retourner le contenu d’un fichier stocké côte à côte de Media Center et de le renvoyer dans le flux http en changeant le ContentType. Ce qui nous donne :

 

public Stream GetFile(String fileName, String encoding)

{

    DirectoryInfo info

        = new DirectoryInfo(

Environment.GetFolderPath(Environment.SpecialFolder.System));

 

    WebOperationContext.Current.OutgoingResponse.ContentType = encoding;

 

    if (Directory.Exists(info.Parent.FullName + @"\ehome"))

    {

        return

File.OpenRead(

String.Format(@"{0}\{1}\{2}",

info.Parent.FullName, "ehome", fileName));

    }

 

    return null;

}

 

Dans le cadre d’une architecture REST, vous noterez que le contexte opérationnel est accessible au travers de la classe « WebOperationContext ». Cette dernière est une classe helper permettant d’accéder aux propriétés contextuelles des appels WCF courant et fournit notamment un accès aux requêtes et réponses web. Ce qui nous permet de changer le ContentType du flux renvoyé au client. Voici un exemple d’appel de ce service :

 

http://192.168.0.10:8000/MediaCenter/IWebManagement/GetFile?filename=default.htm&encoding=text/html

 

Le Hosting WCF

Le processus porteur des services WCF est directement géré par l’addin en tâche de fond créé pour la circonstance. J’ai donc réalisé ce traitement lors du lancement de l’addin, c’est à dire lors de l’appel de la Méthode « Launch » créé par la classe qui sert de point d’entrée.

 

Contrairement aux Hosts WCF classiques, je n’ai pas utilisé la classe « ServiceHost » pour gérer l’écoute des services exposés. En effet, lorsque l’on met en place une architecture REST avec WCF, nous utilisons un nouvel espace de nom (« System.ServiceModel.Web ») et une nouvelle classe « WebServiceHost » qui n’est de toute façon qu’une classe fille de « ServiceHost » :

 

TheHost

= new System.ServiceModel.Web.WebServiceHost(

typeof(Programmez.WCF.Sample1.Services.MediaCenterManagement));

 

TheHost.Open();

 

Tout le paramétrage du service en lui-même étant réalisé en fichier de configuration. Ensuite, il reste à savoir quel fichier de configuration puisque je vous rappelle que nous sommes hébergés dans l’application Windows Media Center. Les choses étant bien faites l’exécutable « ehexthost.exe » possède son propre fichier de configuration (« ehexthost.exe.config ») stocké bien évidemment côte à côte. Voici donc la configuration associée :

 

HostConfigFile 

 

Par défaut, vous n’aurez probablement pas les droits d’écrire dans ce fichier. Il suffira pour cela (UAC active sous Vista) de modifier les droits d’écriture et y associer précisément votre compte.

 

Je préfère de très loin un paramétrage de type déclaratif (par fichier de configuration) à un paramétrage de type impératif (par le code) pour des raisons évidentes de facilité d’administration.

 

Le client de test

Comme indiqué précédemment et n’étant absolument pas versé dans l’utilisation du SDK natif IPhone, le plus simple pour moi était d’utiliser les standards du Web afin d’implémenter un client Javascript de mes services REST et fonctionnant aussi bien sur Internet Explorer, Safari et Safari Mobile.

 

Pour cela, je devais résoudre deux problématiques :

  1. Utiliser le minimum syndical en Javascript pour des raisons évidentes de portabilité
  2. Utiliser « XmlHttpRequest » pour les requêtage aux services REST

 

Globalement, je ne me suis pas trop focalisé sur la qualité de cette IHM. Tout juste me suis-je quelque peu frotté à Microsoft Expression Design pour générer l’image de ma télécommande en me faisant expliquer par un graphiste le B.A. BA des rudiments de cet outil ! On retrouve les fonctionnalités de base d’une télécommande basique. J’ai aussi ajouté une interface orientée développement. Voici ce que cela donne Safari pour Windows (mes excuses par avance pour les yeux qui risquent de piquer !) :

 

 IHMSafari

 

IHMSafariTest 

 

Voici l’URI REST utilisée et paramétrée pour récupérer la page html.

 http://192.168.0.10:8000/MediaCenter/IWebManagement/GetFile?filename=default.htm&encoding=text/html

 

Ensuite, je me suis servi d’un image map afin de rendre cliquable certaines zones de l’image et le tour était joué pour cette IHM simplifiée.

 

Le code javascript développé pour l’occasion est somme toute assez simple. Voici une partie de la définition de l’image map utilisée, vous noterez, l’utilisation d’une fonction Javascript unique et paramétrée avec l’url du service accédé :

 

<map id="RemoteControl" name="RemoteControl">

    <area alt="Stop" shape="circle" coords="48,32,20" title="Stop"

href="javascript:Execute('/SendKey?command=178');" />

    <area alt="Previous" shape="circle" coords="48,92,20" title="Previous"

href="javascript:Execute('/SendKey?command=177');" />

    <area alt="Play" shape="circle" coords="162,46,26" title="Play"

href="javascript:Execute('/SendKey?command=179');" />

    <area alt="Sleep" shape="circle" coords="272,32,20" title="Sleep"

href="javascript:Execute('/StandBy');" />

    <area alt="VolPlus" shape="circle" coords="42,266,20" title="VolPlus"

href="javascript:Execute('/VolumeUp');" />

    <area alt="VolMinus" shape="circle" coords="40,334,20" title="VolMinus"

href="javascript:Execute('/VolumeDown');" />

 

…/…

 

</map>

 

Et voici le code de la fonction Javascript « Execute » ainsi utilisée :

 

function Execute(serviceUri)

{

var proxy = null;

              

if (window.ActiveXObject)

proxy = new ActiveXObject("Microsoft.XMLHTTP");

else if (window.XMLHttpRequest)

proxy = new XMLHttpRequest("");       

           

           

// POST to avoid Explorer Cache Problem

proxy.open('POST',

window.document.getElementById("txtServiceURL").value

+ serviceUri, false);

 

proxy.send(null);

 

// Store Request Result        

window.document.getElementById("divStatus").innerHTML = proxy.status;

window.document.getElementById("divStatusText").innerHTML

= proxy.statusText;

           

window.document.getElementById("divResponse").innerHTML

= proxy.responseText;

}

 

Vous noterez l’utilisation d’un requêtage de type POST pour éviter les problématiques de mise en cache des navigateurs. Le paramètre « false » dans l’utilisation de la méthode « Open » permet d’indiquer que les appels seront synchrones. L’appel à la méthode « send » permet d’envoyer la requête http vers le serveur. Le paramètre « null » indique qu’on ne passe rien en temps que corps du message.

 

Voici un exemple d’url pour la récupération du guide :

 

« http://MonServeur:8000/MediaCenter/IWebManagement/GetGuideByDate?fromDate=05/08/2008&toDate=05/09/2008 »

 

Conclusion

Au travers de cet article, je me suis attaché à démontrer plusieurs choses :

·       La capacité offerte par le SDK de Windows Media Center à développer des addins

·       La capacité à créer un host WCF différent de ceux que l’on trouve habituellement

·       La facilité offerte par WCF à mettre en place et gérer des services via une architecture REST

·       Accessoirement à développer les services d’une télécommande accessibles via un IPhone

 

L’utilisation d’une architecture REST permet de simplifier au maximum les protocoles utilisés entre un client et un service. Ces derniers ne viennent pas remplacer les Services WCF d’avant, mais plutôt les compléter judicieusement. Enfin, l’unification WCF offre au développeur .NET une nouvelle corde à son arc en intégrant les architectures REST de manière très simple.

 

Pour ce qui est du développement de la maison numérique de demain, Microsoft offre une première brique à tout développeur Microsoft .NET pour développer des applications au sein de Windows Media Center. Encore un nouvel axe d’unification de la plateforme .NET après l’unification de l’exécution, des langages, des modèles de développement, de l’IDE de développement et de la bibliothèque de classe.

 

> Tous les articles

Commentaires

aucun commentaire
Page 1/1
   
Connexion
  • Accueil
  • Plan du site
  • Contact
Bewise TV, Blog technique, Webcasts...

Votre carrière et nous

  • Nos offres
  • Votre candidature
Ignorer les liens de navigation > Accueil > Nos Métiers > Solutions Langages et Framework > Détail Article
Ignorer les liens de navigation
Nous
Nos Métiers
Vous Former
Nos Evènements
Nos Références
Nos Activités
Nos Certifications
Nos Chiffres
Le Groupe
Nos Partenaires
On Parle de Nous
Nous contacter
Votre Carrière et Nous
Défiler vers le haut
Défiler vers le bas
Administration, Système et Communication
Architecture, Méthodes, Industrialisation
Décisionnel et Gestion des Données
Nouvelles Interfaces Utilisateurs
Portail et Travail Collaboratif
Solutions Langages et Framework
Solutions Web Avancées
Défiler vers le haut
Défiler vers le bas
Nos cours
Le Planning
Offres promotionnelles
Défiler vers le haut
Défiler vers le bas
Bewise Day Conference 2011
Nos Expresso
Défiler vers le haut
Défiler vers le bas
  • Infos légales
  • Lettre du Regional Director
  • Revue de presse