Étiquette : .NET

Vincent Lecomte
[.NET] ITextSharp : document protégé

[.NET] ITextSharp : document protégé

Nous avons vu il y a déjà quelques mois comment appliquer un filigrane sur toutes les pages d’un fichier PDF (voir liens suivants) :

J’avais utilisé cette astuce provenant d’un utilisateur des forums PC SOFT, afin de pouvoir réimprimer des factures avec un joli “DUPLICATA” affiché en oblique. Mais ce que je n’avais pas pris en compte, ce sont les fichiers auxquels j’avais mis un mot de passe pour bloquer la modification. Ces documents pouvaient être ouverts, imprimés mais leur contenu était verrouillé…

Du coup, lors de l’appel de la procédure qui ajoute le petit filigrane, cela me générait une jolie exception avec l’erreur PdfReader not opened with owner password.

Zut alors ! Mais comment faire pour que ça marche sans pour autant spécifier de mot de passe. En fait j’ai simplement spécifié un paramètre, juste après l’initialisation de ma variable MonFichierPDF de type PdfReader, qui permet d’apporter des modifications au flux chargé.

PdfReader.unethicalreading = true;

Et bim ! Je relance la procédure en passant comme source un fichier protégé par mot de passe (pour la modification) et cette fois ça marche. Super ! Notez enfin que cette syntaxe fonctionne dans la procédure Windev qui avait été détaillée dans les deux articles ci-dessus.

[.NET] ITextSharp : lire les métadonnées d’un PDF

[.NET] ITextSharp : lire les métadonnées d’un PDF

En C# .NET, à l’aide de la librairie ITextSharp, on peut facilement lire les métadonnées d’un document. Pour rappel il s’agit d’une DLL qui permet de manipuler des documents PDF : parsing, ajout de filigranes, etc. Vous pouvez l’obtenir sur Sourceforge.

PdfReader p = new PdfReader(
	System.IO.File.ReadAllBytes("file.pdf") 
); 

string s = p.Info["CreationDate"];

A l’aide de Windev, on peut aussi effectuer un appel à ces fonctions .NET. Chargez l’assemblage dans votre projet, ensuite utilisez le code suivant. Attention : les tableaux d’objets ne sont pas pris en charge, il faudra donc utiliser une petite variante.

pclRead est un PdfReader dynamique <- allouer un PdfReader(File.ReadAllBytes(sInFile))
clInf est un Hashtable <- pclRead.Info
sTest = clInf.get_Item("CreationDate")

Et le tour est joué ! Dans notre exemple nous avons récupéré la date de création. Il est retourné sous cette forme : “D:AAAAMMJJ” suivi de l’heure. Bon développement !

[.NET] ITextSharp : IOException à l’ouverture

[.NET] ITextSharp : IOException à l’ouverture

Si vous obtenez une exception d’E/S en tenant d’ouvrir un PDF, cela peut être dû à un problème de permissions, soit au niveau de la DLL de l’assemblage, soit au niveau du fichier que l’on souhaite charger.

Cela se produit en utilisant le code suivant :

PdfReader p = new PdfReader(CHEMIN);

Le message de l’exception levée indique :

Exception Details: System.IO.IOException: 
 C:Fichier.pdf not found as file or resource.

Cela peut être causé par :

  • Un problème au niveau de la propriété “copie locale” de la référence (voir la documentation MSDN en ligne : article # t1zz5y8c)
  • Un fichier considéré comme “bloqué” par Windows : effectuez un clic droit sur le fichier, option “Propriétés” puis choisissez “Débloquer”.
  • Un problème de permissions sur le fichier. Celui-ci est peut-être stocké à un emplacement auquel l’application n’a pas accès.

On peut contourner le problème avec le code suivant.

PdfReader p = new PdfReader( 
	System.IO.File.ReadAllBytes(CHEMIN) 
);

L’erreur disparait alors.

Bon développement !

[.NET] Accès à DB2 UDB iSeries + DataGridView

[.NET] Accès à DB2 UDB iSeries + DataGridView

On retrouve dans .NET les mêmes possibilités qu’en Java afin de se connecter à une base de données DB2 UDB sur un AS/400. En effet, lorsqu’on installe IBM i Access sur le PC, un kit de développement est inclus. Dedans on retrouve les assemblages nécessaires pour développer en .NET. Dans le menu démarrer vous devriez retrouver, dans le dossier IBM i, l’aide “Boite à outils de programmation“.

Start Menu > Programs > IBM i Access for Windows > Boîte à outils de programmation.

Intégrer l’assemblage dans le projet

Pour effectuer cette opération il faut ouvrir l’explorateur de solutions, effectuer un clic droit sur la catégorie “Références” et choisir l’option “Ajouter une référence” dans le menu contextuel.

Ensuite, il faut cliquer sur “Extensions” et chercher après “IBM for DB2 i .NET Provider“. On peut s’aider du champ de recherche afin de filtrer les résultats. Une fois celui-ci repéré il faut le cocher puis cliquer sur OK.

Lorsqu’on a terminé, si on déroule l’arborescence, on pourra constater qu’un nouvel élément est venu rejoindre les références existantes, à savoir “IBM.Data.DB2.iSeries“. On va dès lors pouvoir utiliser les différents objets de l’API.

Connexion et requête

Tout d’abord, il faudra indiquer, à l’aide de la directive “using“, qu’on souhaite utiliser des objets et types de l’espace de noms “IBM.Data.DB2.iSeries” sans avoir à le qualifier à nouveau. Pour information nous programmons ici en C#.

using IBM.Data.DB2.iSeries;

Ensuite on va, dans le code d’un bouton par exemple, initialiser la connexion, récupérer des données dans une table, puis fermer cette connexion. On va aussi afficher le nom du job démarré sur la machine AS/400.

C’est en instanciant un objet de la classe “iDB2Connection” qu’on va pouvoir se connecter. Pour cela, on devra renseigner dans le paramètre du constructeur une chaine de connexion (ConnectionString). Pour savoir quelles sont les propriétés qu’on peut renseigner, consultez l’aide ou la documentation en ligne.

iDB2Connection cn2 = new iDB2Connection("DataSource=ADR_IP" +
  ";UserID=USER;Password=PASSWD;Naming=SQL"); 
cn2.Open();

Dans l’exemple ci-dessus, on a spécifié :

  • DataSource : le nom ou l’adresse du serveur à contacter.
  • UserID : le nom d’utilisateur avec lequel on veut se connecter.
  • Password : le mot de passe associé.
  • Naming : indique si on veut utiliser les conventions de nommage SQL.

Ensuite on a utilisé la fonction “Open()” de l’objet créé afin d’ouvrir explicitement la connexion. L’étape suivante consiste à créer une “commande” de type “select” qui sera liée à la connexion précédente.

iDB2Command myCmd = cn2.CreateCommand(); 
myCmd.CommandText = "SELECT * FROM myLib.myTable";

La propriété “CommandText” contiendra le code SQL de la requête à exécuter. On va remplir ce qu’on appelle un “DataGridView“. C’est un contrôle personnalisable qui permet d’afficher assez facilement un tableau, lié à une source de données, ou non.

iDB2DataAdapter da = new iDB2DataAdapter(myCmd);
DataTable ds = new DataTable(); da.Fill(ds);
dataGridView1.DataSource = ds;

Pour cela on va utiliser deux autres types de variables, un “iDB2DataAdapter” et un “DataTable“. Le second est une représentation des données de la table. Le premier quant à lui va servir de “pont” entre la base de données et notre objet “DataTable” en question.

  • On crée donc une instance de iDB2DataAdapter en passant en paramètre la commande.
  • On crée un objet DataTable, sans spécifier de paramètre au constructeur.
  • On utilise la fonctionne “Fill()” de notre objet iDB2DataAdapter avec en paramètre, l’objet DataTable.
  • Ensuite on lie cet objet DataTable à notre contrôle grâce à la propriété DataSource.
  • Il n’y a rien à faire ; le tableau apparaitra automatiquement.

Voilà ce que ça donne à l’exécution.

Et bien sûr il ne faudra pas oublier de se déconnecter de la base de données une fois que c’est terminé (tout dépend de ce que vous souhaitez faire ensuite).

Afficher le nom du job

Si on veut afficher le nom du job qui a été créé pour la connexion en cours, on peut par exemple écrire le code suivant.

MessageBox.Show(cn2.JobName);

Cela affichera, dans une boite de dialogue, son nom complet (travail, utilisateur et numéro).

Déconnexion

C’est très simple ! Il suffit d’appeler la fonction “Close()” de l’objet Connexion créé au début de ce billet. Cela aura pour effet de mettre fin au job créé sur l’AS/400.

[WD20] Récupérer l’e-mail dans l’AD avec .NET

[WD20] Récupérer l’e-mail dans l’AD avec .NET

Si vous avez besoin, pour une raison une autre, de récupérer des informations depuis l’Active Directory pour l’utilisateur déjà connecté sans utiliser les fonctions de la famille LDAP*, alors vous pouvez utiliser l’assemblage “System.DirectoryServices” de .NET. Dans l’explorateur de projets, effectuez un clic droit sur “Assemblages .NET” puis choisissez l’option “Utiliser un assemblage” du menu contextuel.

La première étape consiste à initialiser les variables pour lancer la recherche. Pour cela on a besoin de trois types d’objets :

  • DirectoryEntry : qui encapsule un nœud/un objet dans la hiérarchie de l’AD.
  • DirectorySearcher : pour rechercher à partir d’une position spécifique.
  • Environment.UserName : nom de l’utilisateur authentifié.

Ensuite il faut initialiser le filtre de la recherche et indiquer les propriétés que l’on souhaite récupérer. Dans notre exemple nous souhaitons obtenir l’e-mail (“mail”) mais également d’autres informations (comme le prénom et le nom).

On doit ensuite lancer la recherche à l’aide de la fonction “FindOne”, qui va simplement renvoyer le premier objet répondant aux critères. Si la variable qui en résulte est différente de Null, alors on peut parcourir les propriétés. Pour rappel, Windev ne gère pas les tableaux .NET, il va donc être impossible de récupérer un objet en indiquant la clé ou l’index.

Pour contourner nous avons utilisé une boucle de type “POUR TOUT” (équivalent à un “foreach” en C#) afin de parcourir l’ensemble des noms de propriétés. Ensuite, nous avons récupéré une collection d’objets correspondants à la clé “mail” grâce à la procédure “get_Item()“.

Il ne reste plus qu’à parcourir cette collection pour récupérer la valeur. Normalement il n’y en a qu’une puisqu’on a demandé à ne reprendre que le premier objet correspondant à nos critères de recherche. On stocke donc la valeur dans une simple variable chaine.

Bon développement !

[WD19] Filigrane sur un PDF déjà généré

[WD19] Filigrane sur un PDF déjà généré

Ayant des fichiers PDF déjà générés, je cherchais une solution pour rajouter un filigrane du style “Duplicata” pour une réimpression. Travaillant en Windev, je suis tombé sur la solution de Jurassic Pork sur les forums de PC SOFT, où il conseille d’utiliser ITextSharp, un assemblage .NET qui permet en gros de générer des PDF ou de les manipuler (séparer en plusieurs documents, fusionner, ajouter un filigrane ou un tampon, remplir des formulaires, etc).

Pour une question de licence, il recommande d’utiliser une ancienne version, soit la 4.1.6 (qu’on peut télécharger à l’adresse suivante via nuget : https://www.nuget.org/packages/itextsharp.4.1.6.0) et qui est sous licence MGPL/LGPL… Mais attention, d’après plusieurs sources, celle-ci est deprecated (considérée comme obsolète). Si vous souhaitez travailler avec une version plus récente, vous retrouverez l’assemblage sur Sourceforge (projet ITextSharp). Notez que la version 5 est passée sous licence AGPL. Pour savoir ce que cela implique, consultez le site GNU.org, section “Licences”.

Intégrer l’assemblage .NET

Si vous ne vous rappelez pas comment intégrer un assemblage dans votre projet, voici comment il faut procéder :

  • Dans l’explorateur de projets, clic droit sur “Assemblages .NET“.
  • Choisissez l’option “Utiliser un assemblage .NET dans le projet“.
  • Cliquez sur le bouton “Parcourir” dans la fenêtre qui s’ouvre.
  • Sélectionnez le fichier “itextsharp.dll” puis cliquez sur OK.
  • Validez et recompilez le projet.

Procédure pour ajouter le filigrane

La procédure écrite par “JP” recevra en paramètre trois éléments : l’emplacement du fichier source, l’emplacement (complet – avec le nom de fichier et l’extension de celui-ci) pour le fichier de destination, et le texte qu’on souhaite afficher.

En début de procédure on va déclarer les premières variables :

  • Une variable de type entier pour contenir l’index de page, qui sera en fait utilisée dans une boucle en partant de la valeur 1. Il sera nommé “pageIndex“.
  • Un objet de type PdfReader, qui va servir à charger le fichier source (on passe l’emplacement du fichier en paramètre du constructeur). Il sera nommé “MonFichierPDF“.
  • Un objet de type FileStream pour initialiser un flux pour la création du fichier de destination (les paramètres du constructeur, sont, dans l’ordre : l’emplacement du futur fichier, et le mode de création – ex : FileMode.OpenOrCreate). Il sera nommé “fluxSortie“.
  • Un objet de type PdfStamper qui sera utilisé pour ajouter du contenu supplémentaire au fichier source. Les paramètres du constructeur sont : l’objet PdfReader précédemment instancié, et le flux de sortie). Il sera nommé tout simplement “pdfStamper“.

La fameuse boucle dont il était question ci-dessus est de type “POUR variable = x _A_ y”. Dans ce cas-ci on réutilise notre entier – nommé “pageIndex” qu’on initialise à 1 en début de boucle, pour aller jusqu’à “MonFichierPDF.NumberOfPages“(inclus).

Ensuite dans la boucle il va falloir effectuer plusieurs choses :

  • D’abord la création d’un objet dynamique de type “itextsharp.text.rectangle“. Il s’agit d’une représentation d’une forme géométrique. Dans ce cas-ci l’objet contiendra la représentation géométrique d’une page. Cela nous permettra de récupérer sa longueur et sa largeur afin de plus tard, placer le filigrane au bon endroit.
  • On va instancier un objet dynamique de type “PdfContentByte“. Il va en fait contenir les images et le contenu textuel de la page renvoyée par notre objet “pdfStamper“. Pour obtenir cela on appelle la fonction GetOverContent() en passant en paramètre le numéro de la page.
  • On va configurer la police et la taille pour le futur filigrane.

Mais ce n’est pas tout !

Toujours dans la boucle qui parcourt les différentes pages de notre document PDF, on va commencer à créer le filigrane. Tout d’abord à l’aide d’un objet PdfGState – qu’on va nommer “graphicsState” -, on va définir son opacité (à 0.4 dans l’exemple). Pour cela il faut écrire :

graphicsState.FillOpacity = 0.4

Ensuite, à l’aide de la fonction SetGState() de l’objet “PdfContentByte“, on va appliquer cette caractéristique d’opacité au contenu existant. On définit ensuite la couleur de l’écriture – à l’aide de SetColorFill() -, puis on indique qu’on va commencer à saisir du texte – en utilisant BeginText().La méthode suivante – ShowTextAligned() – va recevoir en paramètre la position du texte (ex : Element.ALIGN_CENTER), le texte à afficher, sa position sur la page – qui sera calculée en divisant respectivement la hauteur et la largeur par 2, et la valeur de la rotation. Si vous avez bien compté, cela fait au total 5 paramètres.

Dernière ligne de la boucle, il faut appeler la fonction EndText() de notre objet “PdfContentByte“. Tous ces traitements se répéteront donc pour chacune des pages présentes dans le PDF. Cela peut prendre du temps en fonction du nombre de pages à traiter.

Ensuite il faut appeler la méthode Close() sur nos deux objets, “pdfStamper” et “fluxSortie“. C’est très important pour que le fichier soit correctement sauvegardé et pour que le programme libère tous les descripteurs de fichiers ouverts.

Bon développement !

Sources

Forums PC-SOFT

[WD19] Utiliser l’API Exchange Web Services (III)

[WD19] Utiliser l’API Exchange Web Services (III)

Avec EWS Managed API 2.0, il est possible de réaliser des choses vraiment intéressantes. Il serait clairement plus simple de travailler directement en .NET mais nous allons voir ensemble ce qu’il est possible de faire dans Windev. Dans ce nouvel exemple nous allons récupérer la liste de dossiers à partir d’un dossier source.

Récupérer une liste de dossiers

N’oubliez pas qu’avant de récupérer une liste de dossiers vous devez au préalable récupérer l’URL du service Web. Nous avons vu ensemble comment réaliser cette opération dans les précédents articles. Si vous ne savez plus comment faire, suivez le lien: voir partie 1.

La première étape consiste à initialiser la vue et un filtre de recherche. Il faut tout d’abord déclarer les différentes variables, des types suivants : FindItemResults, FolderView, et SearchFilter. Ces deux dernières doivent être initialisées à la valeur Null. On peut aussi déclarer des entiers qui serviront de compteurs.

Ensuite, voici ce que nous avons réalisé :

  • Création d’un filtre avec l’initialisation de la variable “scFilt” (qui est notre filtre de recherche). On l’instancie avec new SearchFilter.IsGreaterThan(). Les paramètres sont, dans l’ordre, la constante FolderSchema.TotalCount et 0. Cela permettra d’obtenir les dossiers qui contiennent un ou plusieurs objets.
  • Initialisation de la vue : new FolderView(TAILLE, OFFSET). Comme pour récupérer des e-mails, nous spécifions une taille (dite “de page”) et éventuellement un offset si la recherche retourne de multiples éléments.
  • Dans la vue, on doit définir l’ensemble des propriétés à récupérer. Il faut donc alimenter le contenu de la propriété View.PropertySet. Remarquez que cela est réalisé en deux étapes car les constructeurs requièrent des collections d’objet, chose qui n’est pas forcément pratique à mettre en place.
    • new PropertySet(BasePropertySet.IdOnly).
    • vi.PropertySet.Add(xxxx) : ajouter d’autres propriétés qu’on souhaite récupérer et lire (comme FolderSchema.DisplayName pour le nom ou FolderSchema.FolderClass pour la classe du dossier – ex : “IPF.Note” pour les mails).
  • Méthode de recherche : avec les dossiers il est possible d’aller plus loin et donc de rechercher les sous-dossiers de manière récursive. La propriété Traversal de notre vue peut être initialisée avec la constante FolderTraversal.Deep. Dans notre code d’exemple cela donne tout simplement la syntaxe suivante : vi.Traversal = FolderTraversal.Deep.

Notre filtre et notre vue sont désormais prêts, on peut passer à l’étape suivante.

Dans une boucle où l’on teste qu’il reste des éléments, nous récupérons à chaque fois jusqu’à 10 objets, en modifiant à chaque fois l’offset dans la vue. Le principe est le même que pour récupérer les e-mails. Dans cette étape on a donc réalisé les opérations suivantes :

  • Tester la variable bMoreItems. Si elle vaut “true” on continue à traiter les éléments.
  • Initialiser notre variable de type FindItemResults cette fois avec la méthode ExchangeService.FindFolders(). 3 paramètres doivent être donnés à la fonction : le dossier à partir duquel on souhaite effectuer la recherche (ex : WellKnownFolderName.Root), le filtre (dans notre exemple il s’agit de la variable “scFilt“) et enfin la vue (variable “vi“).
  • Est-ce qu’il y a encore des objets ? bMoreItems = clDrFindResults.MoreAvailable.
  • Déplacer l’offset si bMoreItems vaut Vrai : vi.Offset = vi.Offset + iPageSize.
  • Initialiser une variable de type entier à 0 en guise de compteur.
  • Initialiser le nombre d’objets affichables, c’est-à-dire le nombre d’objets que la collection a retourné. Dans notre exemple le code est le suivant : iDspCnt = clDrFindResults.Folders.Count.
  • Dans une boucle POUR TOUT on traite chaque objet de la collection clDrFindResults.Folders. Ici nous décidons de sauvegarder le nom et l’ID de chaque dossier dans un objet de classe. On sort de cette boucle lorsque notre compteur a atteint le nombre d’objets affichables (voir ci-dessus). A vous de trouver le traitement adéquat à réaliser…

Voilà, vous pouvez désormais récupérer une liste de dossiers avec l’API et ce en utilisant Windev. N’est-ce pas magnifique ?

[WD19] Utiliser l’API Exchange Web Services (II)

[WD19] Utiliser l’API Exchange Web Services (II)

Il y a quelques mois nous avions vu comment utiliser l’API Exchange Web Services 2.0 depuis Windev. Grâce aux bibliothèques .NET fournies il est possible d’interroger un serveur Exchange et de récupérer les e-mails d’un utilisateur.

Nous avons fait l’expérience en créant une petite fonction qui récupère 10 objets à la fois dans la boite de réception de l’utilisateur, et ce en travaillant avec la notion d’offset, ce qui permet à terme d’effectuer des opérations de pagination (ex : l’utilisateur appuie sur un bouton pour afficher la deuxième page).

Télécharger les bibliothèques

L’API est fournie par Microsoft et peut être téléchargée sur le site officiel à l’adresse suivante : https://www.microsoft.com/en-us/…/details.aspx?id=35371. Sélectionnez la version qui convient (32 ou 64 bits) puis installez-là sur votre poste de développement. Les DLL sont alors disponibles dans le répertoire %ProgramFiles%MicrosoftExchangeWeb Services2.0.

Importez-les dans votre projet Windev.

Recherche avec pagination

Dans l’exemple qui suit, nous allons effectuer une récupération de 10 éléments en modifiant à chaque fois l’offset. Nous nous basons sur le tutoriel proposé sur MSDN qui explique comment fonctionne cette méthode. Nous avons alors repris la procédure d’exemple en fin de page mais nous avons fait une croix sur l’ “ancre” qui permet de déterminer si un élément a changé de place ou non.

Il est évidemment important d’allouer un objet Credentials pour permettre la connexion à chaque requête. Le Web Service EWS étant stateless, la connexion n’est pas constante et chaque requête est traitée indépendamment des autres. Avant toute chose, il faut donc instancier un objet global de type ExchangeService, qu’on pourra réutiliser dans chaque fenêtre. Dans le code qui suit, l’objet fait plutôt partie d’une classe qu’on pourra déclarer dans la section “initialisation du projet”.

Voici ce qui a été réalisé :

  • Instanciation de la classe ExchangeService. Le constructeur reçoit en paramètre le numéro de version Exchange qui doit correspondre à celui utilisé sur le serveur, sinon les requêtes ne seront pas toujours bien gérées (MSDN#FF597939).
  • On se connecte en utilisant le nom d’utilisateur, le mot de passe et le domaine (en allouant une nouvelle instance d’un objet de type WebCredentials).
  • La fonction “AutodiscoverUrl()” permet de localiser le fichier Exchange.asmx sur le serveur pour pouvoir envoyer les futures requêtes. On obtient alors l’URL du “point d’entrée” (endpoint) du web service.

Si la connexion a abouti il faut ensuite réaliser ces opérations :

Dans l’ordre, voici ce qui a été fait :

  • On crée un nouvel objet de type ItemView permettant de stocker un nombre d’objet défini par la variable “iPageSize“. L’offset – stocké dans la variable “iOffset” – indique l’endroit à partir duquel on souhaite récupérer les informations (de base 0 pour commencer au début). Il devra être progressivement modifié pour récupérer les éléments suivants.
  • On initialise l’ensemble des propriétés à récupérer. Dans ce cas-ci, on récupère simplement l’ID mais on peut imaginer récupérer la date/heure de réception ou le sujet. A partir de cet instant on peut utiliser la syntaxe suivante : maVue.PropertySet.Add(xxxxx). A noter que la fonction FindItems() ne récupère pas les adresses des destinataires ou de l’expéditeur.
  • On ajoute une condition de tri : maVue.OrderBy.Add(champ, ordre du tri). Ici, on souhaite trier sur la date/heure de réception (ItemSchema.DateTimeReceived) de manière descendante (la plus récente d’abord soit SortDirection.Descending).
  • On indique la méthode de recherche.

Pourquoi ne pas avoir indiqué d’autres propriétés dans le constructeur de la classe PropertySet ? Si on souhaite en rajouter d’autres, on peut se servir de la méthode Add() car le second constructeur de la classe PropertySet demande une collection d’objets. En .NET, lorsqu’on indique plusieurs objets à la suite, il considère cela comme un tableau d’objets ou une collection IEnumerable, et reconnait quel constructeur doit être appelé. Mais à ce stade Windev n’est pas capable d’effectuer la conversion de types.

On entre alors dans le vif du sujet : la récupération des e-mails.

Voici ce qu’on a réalisé dans cette étape (le tout étant englobé dans une boucle) :

  • On a englobé le code dans une boucle qui teste “bMoreItems“. Tant que cette variable est à “true“, on continue le traitement. Lorsqu’elle vaut “false“, on sort de la boucle.
  • A chaque tour de boucle on initialise l’objet de type ‘FindItemsResults’ (à déclarer comme tel en début de fonction) en appelant simplement la fonction FindItems() qui reçoit en paramètre le dossier (ex : WellKnownFolderName.Inbox) ainsi que la vue qui a été précédemment paramétrée. Seul l’offset changera au cours de l’opération.
  • S’il y a d’autres résultats alors on va augmenter l’offset pour le prochain tour de boucle en rajoutant à celui-ci la taille d’une page (ici, on ajoute donc 10). Si on avait utilisé le système d’ancre, il aurait fallu récupérer à chaque fois un objet de plus pour permettre de déterminer s’il y avait eu un changement entre deux sets de résultats.
  • Ensuite on charge l’ensemble des propriétés de “première classe”, il s’agit notamment des champs sujet, date/heure réception, expéditeur, destinataire(s), etc. Dans ce cas s’il y a 10 objets récupérés on charge en mémoire les propriétés de ceux-ci.

Pour parcourir la collection il faut utiliser une boucle de type POUR TOUT. On arrête cette boucle lorsqu’on a atteint le nombre d’objets retourné. Pour en sortir, il faudra le faire de force avec le mot-clé adéquat. On y traite les e-mails un par un et là, c’est à vous de décider ce que vous faites. Dans l’exemple ci-dessous, on enregistre simplement dans des membres d’une classe. On peut imaginer d’ajouter chaque élément dans un champ Table.

Ce qu’il faut faire :

  • Initialiser la variable de comptage à 0.
  • Initialiser le nombre maximum d’objets à afficher.
  • Traiter chacun des objets de la collection monResultset.Items.
  • Incrémenter la variable d’itération.
  • Sortir lorsqu’elle est égale à la variable qui contient le nombre de résultats à afficher.

Contraintes rencontrées

Lorsqu’on utilise une API .NET, on peut faire face à certains soucis…

  • Windev ne prend pas en charge les tableaux d’objets en .NET. Dans notre cas on ne peut pas écrire “clDrFindResults.Items[x]“.
  • Certaines fonctions comme FindItems.Items.First() ou Last() tentent d’effectuer des copies d’objet. Cela n’est pas géré par Windev et donc les fonctions ne peuvent pas être utilisées telles quelles.
  • La conversion d’objets pose problème dans le cas des constructeurs, comme c’est le cas lorsqu’on effectue une allocation d’un objet de la classe PropertySet. Dans ce cas il faut trouver une alternative…
  • Certains objets doivent être convertis en chaine pour l’affichage avec les méthodes ToString(). En général, cela se fait automatiquement en .NET, sauf dans certains cas bien précis.

Bon développement à tous !

[WD19] Utiliser l’API Exchange Web Services (I)

[WD19] Utiliser l’API Exchange Web Services (I)

Plutôt que d’utiliser les fonctions natives pour l’accès à Exchange, nous avons essayé d’accéder à un compte e-mail par l’intermédiaire de l’API “Exchange Web Services” version 2.0. Les bibliothèques sont proposées par Microsoft et peuvent être téléchargées sur le site web.

Dans cet article, nous verrons comment nous connecter à Exchange, comment récupérer dix éléments de la boite de réception, et comment enregistrer une pièce jointe sur le disque dur, dans un répertoire temporaire. Les documentations MSDN et PC SOFT, ainsi que les différents sujets retrouvés sur de nombreux forums, ont permis d’aboutir à un résultat intéressant.

Télécharger les bibliothèques

Cette API est fournie par Microsoft et peut être téléchargée via le lien suivant : https://www.microsoft.com/e…s.aspx?id=35371. Le site vous proposera de télécharger le fichier adéquat en fonction de la version du système d’exploitation que vous utilisez (32 ou 64 bits).

Une fois l’API installée sur votre machine, deux bibliothèques DLL sont disponibles dans le dossier suivant : %ProgramFiles%\Microsoft\Exchange\Web Services\2.0. Ces assemblys pourront être importés dans le projet Windev de votre choix. Les deux fichiers “.dll” devront également être copiés ou installés sur la machine cliente.

Utiliser les assemblages

Dans votre projet Windev, ouvrez l’explorateur de projets, puis descendez jusqu’à l’arborescence “Assemblages .NET”. Effectuez ensuite un clic droit sur le titre de la catégorie, puis choisissez l’option “Utiliser un assemblage (…)”. Une nouvelle fenêtre s’ouvre alors.

Cliquez sur “Parcourir” puis sélectionnez “Microsoft.Exchange.WebServices.Auth” et “Microsoft.Exchange.WebServices” depuis le répertoire d’installation de l’API. Cliquez sur OK : l’arborescence est mise à jour et on voit alors apparaitre l’ensemble des objets qui peuvent être utilisés. On retrouve toutes les classes, tous les membres et les méthodes de celles-ci.

Se connecter à Exchange

A l’aide de l’outil de connectivité, on peut éventuellement s’assurer que l’accès est autorisé et que le service “autodiscover” est activé. Si ce n’est pas le cas, il est possible que vous rencontriez l’une ou l’autre erreur de connexion. Cela peut se traduire par des exceptions.

Voyons quelles sont les étapes à réaliser :

  • La première chose que l’on doit faire est d’instancier un objet de type “ExchangeService”. Le paramètre du constructeur devra être l’une des constantes qui indique la version utilisée (ex : 2010 SP1).
  • Ensuite, pour se connecter, on va instancier un objet de type “WebCredentials” et le lier au service. Cet objet est utilisé pour les authentifications basées sur les mots de passe, comme NTLM ou Kerberos. Les trois paramètres du constructeur sont donc le nom d’utilisateur, le mot de passe et le domaine.
  • Enfin, il faut récupérer et valider l’URL du service “autodiscover”. La validation sera réalisée par l’intermédiaire d’un délégué. En Windev, il faut donc alors créer la procédure correspondante (“validateRedirectionUrl” dans notre exemple) qui possède un seul paramètre de type “chaine”.

Une fois que toutes ces opérations sont réalisées, si aucune exception n’a été déclenchée, c’est que la connexion a bien été effectuée. Dans ce cas, on peut essayer de récupérer le contenu de la boite de réception.

Analyse des éléments de la boite de réception

Nous allons maintenant récupérer dix objets de la boite de réception et afficher le sujet de chacun d’eux dans une fenêtre de trace de Windev. Pour commencer, il faudra déclarer une instance d’un objet de type “Folder”.

Voyons quelles sont les étapes à réaliser :

  • Nous allons accéder au dossier en le liant au service par l’intermédiaire de la fonction “Bind” de la classe “Folder”. Pour éviter les erreurs liées aux types, nous créons les objets en deux temps.
  • Nous allons ensuite instancier un objet “FindResults” qui représente le résultat d’une recherche (fonction “FindItems”). Dans notre cas, nous souhaitons récupérer 10 éléments dans la boite de réception.

Notre instance de l’objet “FindResults” contient un membre qui se nomme “Items” et qui est du type “IEnumerable”, de ce fait il sera possible d’effectuer une boucle “POUR TOUT” (il s’agit en fait de l’équivalent du “foreach” en C#).

Lire le sujet du mail

Dans la fameuse boucle que nous créons, nous allons faire en sorte de récupérer le contenu du sujet afin de l’afficher. Il faut donc charger l’e-mail en spécifiant son identifiant. On va aussi prévoir de charger l’ensemble des pièces jointes en le spécifiant clairement par l’intermédiaire d’une propriété, comme l’explique la documentation Microsoft.

Voyons quelles sont les étapes à réaliser :

  • Avant la boucle, nous créons une variable de type “EmailMessage” en utilisant le constructeur qui prend en paramètre l’objet “ExchangeService”, que l’on a initialisé plus haut. Pour éviter les problèmes éventuels de conversion, nous avons réalisé les étapes en bouts de code séparés.
  • Dans la boucle, nous devons déclarer une collection de type “PropertyDefinitionBase” dans laquelle on stockera la constante “ItemSchema.Attachments”. Nous avons tenté d’écrire l’appel à la fonction “Bind” comme dans la documentation mais cela a généré une erreur de conversion.
  • Enfin, avec la fonction “Trace” ou “Info” (fonction Windev), nous pouvons afficher le sujet.

Dans la trace précédente, nous avons également ajouté un petit compteur, juste dans un but de debugging. Le contenu des sujets mentionnés ci-dessus a été masqué pour une question de confidentialité.

Récupérer une pièce jointe texte ou une archive

Dans notre e-mail, supposons qu’il existe une pièce jointe de type texte ou bien même une archive ZIP. Pour récupérer le fichier, nous avons dû adapter la proposition de la documentation MSDN pour que le code soit fonctionnel. Après plusieurs essais et quelques recherches sur la toile, nous avons enfin trouvé la méthode pour pouvoir récupérer le fichier et l’enregistrer dans un répertoire.

Dans la boucle où nous affichons le sujet de l’e-mail, nous avons effectué une seconde boucle pour analyser chaque pièce jointe. Certaines limitations nous empêchant d’effectuer des conversions, nous avons dû adapter l’une ou l’autre partie pour distinguer les fichiers des autres pièces jointes.

Voyons quelles sont les étapes à réaliser :

  • Tout d’abord, nous allons déclarer un objet dynamique de type “FileAttachment”, si et seulement si nous retrouvons l’extension “.txt” ou “.zip” dans le nom. Si ce n’est pas le cas, nous ne traitons pas la pièce jointe. Bien sûr il aurait été préférable de comparer les types !
  • Ensuite, nous stockons l’objet issu de la boucle dans cet objet dynamique. Il ne nous reste plus qu’à le charger dans un fichier du même nom et ce dans le répertoire de notre choix (ici il est renseigné en “dur”).
  • Il ne faut bien sûr pas oublier de détruire les objets dynamiques qui ont été alloués (ce qui permet de libérer la mémoire utilisée par ceux-ci). Cela se fait grâce au mot-clé “libérer” du WLangage.

Le fichier sera alors automatiquement enregistré dans le répertoire indiqué, avec le nom qui a été attribué à la pièce jointe. Après il ne nous restera plus qu’à utiliser l’une ou l’autre fonction Windev pour effectuer le traitement de notre choix.

Interagir avec les e-mails

Sachez qu’il est également possible d’effectuer d’autres opérations, comme déplacer un message dans un autre dossier, voire le supprimer définitivement. Pour cela nous vous invitons à regarder de plus près la documentation MSDN qui est plutôt bien fournie.

[WD17] Le délai d’attente de la requête a expiré

[WD17] Le délai d’attente de la requête a expiré

Lorsque vous tentez d’exécuter une requête sur un serveur distant SQL Server, vous pouvez obtenir l’erreur suivante : “Erreur 80040e31, le délai d’attente de la requête a expiré”. En fait, le serveur n’a pas répondu assez vite à la commande envoyée. En effet, il faut bien distinguer le temps d’exécution maximum d’une requête distante (qu’on définit dans les paramètres de SQL Server) et le temps accordé avant qu’une commande envoyée n’expire. C’est pour cela qu’on ne doit pas modifier directement de paramètre sur le serveur mais bien dans la chaine de connexion à la base de données.

Avec HDécritConnexion, vous pouvez définir des informations étendues. Vous pouvez également faire de même en éditant la propriété “InfosEtendues” de la variable de type “Connexion”. La documentation vous indique quelles sont les informations optionnelles utilisables dans la chaine de connexion. Celle qui attire notre attention ici est “WD Command Timeout”, qui permet de fixer la durée maximale de l’exécution d’une commande.

Les valeurs possibles sont :

  • -1 : valeur par défaut définie par la couche client (dans WinDev, il s’agit de 30 secondes – cela s’applique aussi à HyperFileSQL C/S).
  • 0 : illimité. Faites bien attention car des blocages peuvent survenir.
  • > 0 : temps en secondes (dans notre exemple, nous avons indiqué 600 secondes, soit 10 minutes).

En .NET (par exemple en VB ou en C#), il est également possible de définir ce timeout en le précisant dans une propriété (CommandTimeout) de la variable SqlCommand. L’exemple ci-dessous permet d’augmenter le temps jusqu’à 90 secondes avant l’expiration.

Enfin, en Java, il suffit d’appeler la fonction “setQueryTimeOut” de l’objet “Statement” qu’on aura au préalable initialisé. Dans l’exemple, on laisse un délai de 60 secondes pour envoyer la commande, avant qu’elle n’expire. Il faut impérativement utiliser cette fonction avant d’exécuter la requête.

Nous pouvons évidemment continuer à citer les différentes analogies entre les nombreux langages de programmation qui existent, mais cela risquerait de prendre beaucoup trop de temps. Ainsi, voici un billet qui pourra servir de mémo dans le futur, au cas où cela vous échapperait à nouveau.