Report dans les DLL
Introduction
Le problème initial est la production dynamique de rapports
VFP au format PDF pour être téléchargeables sur un site internet.
Le site tourne sous Windows Server, avec IIS et PHP. PHP
permet d’instancier un objet COM+ à partir d’une DLL créée en VFP 9. Cet
article traite des problèmes rencontrés avec l’instruction « report »
dans les DLL VFP9
Dans tout ce qui suit, je considère des rapports qui fonctionnent
parfaitement bien quand ils sont lancés dans le contexte d’un EXE. Dans le contexte d’une DLL c’est l’objet de
cet article !
Dans une DLL Multi threads
Il n’est pas possible d’utiliser l’instruction
« report »
Report form MyReport noconsole to file MonFichier.ps nodialog
Pas d’erreur à la compilation, mais à l’exécution erreur 1001 : Feature is not available
C’est une erreur générée par le runtime car le code VFP est
exactement le même, que l’on compile un EXE, une DLL mono ou une DLL multi threads.
Dans une DLL Mono thread
En prenant des précautions dues au contexte mono thread, on peut, dans certaines conditions, utiliser
« report ».
Avec « reportbehavior 80 »
Cela n’est pas possible. Même en prenant les précautions
pour ne pas écrire dans une interface utilisateur, par exemple :
Set reportbehavior 80
Report form MyReport noconsole to file MonFichier.ps nodialog
Génère l’erreur
2031 : User-interface operation not allowed at
this time
Alors que dans un EXE, cela marche sans problème. Le fichier.ps
est un fichier PostScript que l’on peut visualiser avec gsView par exemple.
Avec « reportbehavior
90 »
Cela
dépend.
Set reportbehavior 90
Report form MyReport noconsole to file MonFichier.ps nodialog
Génère l’erreur 1733 : Class definition OUTPUT type 0 is not found
Il
semblerait qu’ils aient oublié le cas « to file », et que seuls
« to print » et « preview » aient été prévus.
Par
contre
Set reportbehavior 90
Report form MyReport noconsole to print nodialog
Fonctionne
bien avec une « imprimante pdf ». Personnellement j’utilise
« Bullzip PDF printer » qui me donne satisfaction. Extraordinaire !
Sauf que, dès que l’on met une image dans le report (aussi petite soit-elle),
on récupère une erreur 1108 : Picture too big,
corrupt, or in wrong format
C’est
un problème lié à l’utilisation de GDI+. Des problèmes analogues ont été décrits
par Calvin Hsia (voir http://blogs.msdn.com/b/calvin_hsia/archive/2005/07/24/442873.aspx )
Mais
je ne pense pas que ce soit tout à fait la même raison, car on a le même
problème que ce soit un JPG, un PNG ou un BMP.
Le
problème n’apparait pas de manière systématique, dans un tout petit report,
avec la même image, ça marche. Pour produire l’erreur il faut que le rapport
soit assez compliqué.
Par
contre si l’on met le BMP dans un champ général d’une table, cela marche, c’est
un contournement possible. C’est d’ailleurs la seule solution que j’ai trouvée.
Toutes ces raisons m’ont fait essayer xFrx.
Avec XFRX
Les difficultés rencontrées sont les suivantes :
Avec le mode classique (sans utiliser les nouvelles possibilités du report
90)
loSes = XFRX("XFRX#INIT")
loSes.SetParams("output.pdf",,.T.,,.T.,,"PDF")
loSes.ProcessReport("Rapport.frx")
loSes.finalize()
Ça
marche à 99.9%
Mon
report est assez compliqué, avec beaucoup d’impressions conditionnelles,
certains traits horizontaux ne sont pas à leur bonne place. Ce n’est pas très
grave, mais ce n’est pas joli. L’image JPG située dans l’entête s’affiche bien.
Un
autre avantage, dans ce cas, est la possibilité d’utiliser une DLL Multi
threads car aucune instruction report n’est utilisée. Tout est fait par xfrx.
En utilisant les nouvelles possibilités du report 90 (reportbehavior 90)
loLsn = XFRX("XFRX#LISTENER")
loLsn.SetParams( "output.pdf",,.T.,,.T.,, "PDF" )
report form rapport.frx object loLsn
On
l’a bien compris, cette façon de faire ne peut être utilisée que dans une DLL
mono thread.
Ça
marche bien, les traits horizontaux sont bien positionnés, mais on rencontre le
même problème avec les images. Cela n’est pas étonnant, car xFrx sous-traite
l’affichage à ReportOutPut.app. Je n’ai pas testé mais je pense que le même
contournement doit être possible avec un champ général.
Conclusion
La solution que j’avais développée dans le cadre de Windows
server 2008 était différente. Je n’avais pas trouvée de solution telle que celle
décrite ci-dessus et j’avais choisi de produire le rapport à partir d’un EXE
qui était lancé dans la DLL. Le lancement de l’EXE se faisant proprement par un
CreateProcess de win32api. En attendant la fin de l’exe et en sérialisant les
demandes, cette méthode marchait parfaitement bien.
Sous Windows server 2012, je n’arrive plus à lancer l’exe à
partir d’un CreateProcess. C’est un problème de droits. J’ai essayé de beaucoup
de manières, sans succès. Je pense que les droits et la sécurité sont plus
stricts que sur Windows server 2008. On trouve beaucoup d’articles sur ce
sujet.
En désespoir de cause, je pensais développer un spooler de
rapport (un EXE VFP), qui dans mon cas, serait relativement simple. Ayant
besoin d’un seul rapport défini par quelques paramètres. La DLL pourrait faire
une demande de rapport en créant une entrée dans une table. Le spooler
regardant périodiquement s’il y a des demandes, les satisfait au fur et à
mesure et indique que le rapport est prêt. Le spooler est une sorte de service,
mais malheureusement on ne sait pas créer de service en VFP. On contourne la
difficulté en faisant démarrer automatiquement une session au démarrage du
serveur qui elle-même lance automatiquement notre EXE. LogonExpert (produit
payant) permet de le faire.
Jusqu’au moment où j’ai trouvé une solution ...
Postscriptum
J’ai passé beaucoup plus de temps à trouver la solution
simple, qu’il n’aurait fallu de temps pour développer le Spooler. J’aime les
solutions simples, mais ce sont les plus difficiles à trouver.
Robert Plagnard le 2 août 2016 |
Bonjour Robert
et quelle est cette solution ?
Sur mes applications sous FoxInCloud, j'utilise XFRX et reportbehavoir à 90 em mode DLL mono-thread.
Les images passent bien, hormis quelques cas de police code à barre.
Dans le report le champ OLe est coché expression ou nom de variable et en source de control le nom de ma variable.
Cette variable étant initialisée avec le chemin du fichier image avant le lancement du report.
Cordialement