Utilisation de DLL Windows (Dynamix Link Libraries)
Déploiement de code exécutable non généré par un SDK divalto
Tout code exécutable (dll, exe, scripts) non développé avec nos SDK sera désormais strictement interdit dans le Cloud Divalto. Cela était déjà le cas depuis un certain temps, mais certaines exceptions ont été autorisées dans le passé. Cela ne sera plus possible et ces fichiers seront supprimés de notre plateforme à compter du 01/05/2024.
À partir de maintenant aucun nouveau fichier exécutable et aucune mise à jour de fichiers existants ne sera accepté.
1) Introduction … la gestion des DLL ( Dynamic Link Libraries)
Divalto peut appeler les fonctions d'une DLL 32 bits et vous pouvez appeler ces fonctions à partir d'un programme DIVA.
Pour cela, il faut intégrer le code C sous forme d'une librairie Windows qui va contenir l'ensemble de vos fonctions ainsi que l'interface d'appel entre ces fonctions et Divalto.
Ces librairies s'appellent des DLL sous Windows et doivent être écrites avec un atelier de développement C pour Windows.
Puis, dans un programme DIVA, vous pouvez charger cette librairie et appeler vos fonctions en C.
2) Description de la gestion des DLL
Il faut avant l'écriture d'une DLL :
connaître la programmation en C
savoir utiliser un compilateur C pour Windows
connaître la programmation d'une DLL.
L'exemple fourni a été écrit avec le compilateur C de Microsoft.
Deux cas peuvent nécessiter l'écriture d'une DLL.On veut appeler dans un programme DIVA des fonctions écrites en C. On devra donc mettre ces fonctions dans une librairie Windows.
On veut appeler dans un programme DIVA des fonctions d'une autre DLL. On devra écrire une DLL qui servira d'interface entre Divalto et l'autre DLL.
2.1) Exemple d'appel de fonctions C.
Pour cela, nous avons écrit la DLL hardemo.dll et le programme de test demodll.dhsp.
Nous allons d'abord décrire l'appel de hardemo.dll par le programme demodll.dhsp, nous regarderons la création de hardemo.dll ensuite.
Listing du programme demodll.dhsp;>xdiva 1 iddll x 1 i x 1 param 88 ;infos a transmettre 2 fnc x 2 v1 x 2 v2 x 2 v3 x 2 affiche 80 procedure AfficherErreur(texte,nomdll) 1 texte a 1 nomdll a 1 i x beginp i = DllGetLastError display "Erreur " & binhexa(ldx(i)) & texte & " de la DLL " & nomdll endp main display system.clear display "Test de chargement de la dll hardemo.dll" 2 iddll = DllOpen("hardemo.dll") if iddll = 0 AfficherErreur(" au chargement","hardemo") ProgramExit endif fnc = 1 v1 = 20 v2 = 8 i = DllExecute(iddll,param) if i = 0 display v1 0 display " + " 0 display v2 0 display " = " 0 display v3 else AfficherErreur(" a l'appel de la fc 1","hardemo") endif fnc = 2 v1 = 20 v2 = 8 i = DllExecute(iddll,param) if i = 0 display v1 0 display " - " 0 display v2 0 display " = " 0 display v3 else AfficherErreur(" a l'appel de la fc 2","hardemo") endif fnc = 3 v1 = 20 v2 = 8 i = DllExecute(iddll,param) if i = 0 display v1 0 display " * " 0 display v2 0 display " = " 0 display v3 else AfficherErreur(" a l'appel de la fc 3","hardemo") endif fnc = 4 v1 = 20 v2 = 8 i = DllExecute(iddll,param) if i = 0 display v1 0 display " / " 0 display v2 0 display " = " 0 display v3 else AfficherErreur(" a l'appel de la fc 4","hardemo") endif fnc = 5 i = DllExecute(iddll,param) ;execute dll if i <> 0 AfficherErreur(" a l'appel de la fc 5","hardemo") endif i = DllClose(iddll) ProgramExit
On commence par charger la dll par la fonction :
iddll = DllOpen("hardemo.dll")
Cette fonction renvoie un numéro qui référencera cette DLL pour la suite du programme.
Ce numéro est compris entre 1 et 16, on peut donc charger jusqu'à 16 DLL en même temps.
Si la valeur de retour vaut 0, une erreur s'est produite. La fonction AfficheErreur affiche le code de l'erreur en appelant la fonction DllGetLastError.
Si le chargement s'est bien passé, on peut appeler les fonctions contenues dans la DLL par :
i = DllExecute(iddll,param)
La variable "iddll" indique la dll à appeler.
Bien sûr, il va falloir indiquer sur quels paramètres vont travailler les fonctions de la DLL.
La variable "param" indique donc une "zone mémoire" qu'on va passer à la DLL. Cette "zone mémoire" peut contenir différentes données que l'on aura renseignées au préalable. La structure de la zone param est totalement libre.
Lorsqu'Harmony appelle une DLL, il recherche dans la DLL une fonction
qui s'appelle "NewAppelDLL" et lui passe l'adresse de cette zone.
Harmony appelle toujours cette même fonction.
Nous avons mis plusieurs fonctions dans HARDEMO, la fonction NewAppelDll va donc servir d'aiguillage vers les fonctions internes de la DLL.
Aussi, il faut prévoir un mécanisme indiquant quelle est la fonction à exécuter.
Pour cela, la première donnée de notre zone "param",contient le numéro de fonction à exécuter, c'est la variable "fnc" de type x, le reste de la structure de param est découpé en plusieurs variables utilisées selon la fonction appelée.
Le résultat renvoyé‚ par hardemo se trouve aussi dans la zone param.
Lorsqu'on n'a plus besoin de la DLL, on la ferme par la fonction :
i = DllClose(iddll)
Maintenant, regardons le contenu du source hardemo.cpp.
Listing du programme hardemo.cpp
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include "patacnew.h"
typedef struct PARAMDLL {
short Fonction;
short v1;
short v2;
short v3;
unsigned char affiche[80];
} ParamDLL;
typedef ParamDLL __huge * PTPARAMDLL;
// -----------------------------------------------------------
// fonction de la DLL
// ------------------------------------------------------------
extern "C" void FAR PASCAL NewAppelDLL32(PTPARAMDLL pt ,
HPUSHORT AppelCM,
unsigned int * patac32,
FARPROC lpFonctionCM,
HINSTANCE hInst,
HWND hWnd0,
HWND hWndEnCours,
FARPROC lpFonctionCM32)
{
short lg;
HPUSHORT patac = (HPUSHORT)patac32;
switch( pt->Fonction )
{
case 1:
pt->v3 = pt->v1 + pt->v2;
break;
case 2:
pt->v3 = pt->v1 - pt->v2;
break;
case 3:
pt->v3 = pt->v1 * pt->v2;
break;
case 4:
pt->v3 = pt->v1 / pt->v2;
break;
case 5:
lg=wsprintf((LPSTR)pt->affiche,"salut je suis la tache %d",TACHE);
Cm32Display(pt->affiche,lg,1);
memcpy(pt->affiche,"coucou",6);
Cm32Display(pt->affiche,6,1);
break;
default:
break;
}
APPELCM = 0;
}
On y trouve l'appel au fichier patacnew.h, il contient diverses définitions et les fonctions obligatoires pour une DLL. ( voir le contenu du fichier patacnew.h ).
Puis la fonction : NewAppelDll32 et la structure ParamDLL.
Lorsque Divalto veut appeler une DLL,il recherche la fonction NewAppelDLL32 et lui passe l'adresse de la zone param qui se trouve dans Divalto. La structure ParamDLL, correspond à "l'image" de la zome "param". Le pointeur "PTPARAMDLL pt" va donc pointer sur cette mémoire.
Il suffit d'écrire :
pt->Fonction pour lire la variable "fnc" ,
pt->v1 pour lire ou écrire dans la variable "v1",
pt->affiche pour lire ou écrire dans la variable "affiche".
Les variables de type X en DIVA correspondent à des short (16 bits) en C, les variables alpha, à des char (8 bits) en C, et les variables de type L à des int. ( 32 bits) .
ATTENTION :
Le code des caractères dans Divalto est toujours du type ANSI. Il faut les convertir si on veut utiliser le code OEM avec la fonction Windows CharToOemBuff et OemToCharBuff.
En plus du pointeur sur la zone DIVA, Divalto passe à la fonction NewAppelDll32 :
AppelCM
Pointeur sur un short qui indiquera s'il faut appeler un CM32 , ceci n'est plus utilisé, il faut toujours mettre cette variable à 0 lorsqu'on sort de la DLL par la fc APPELCM = 0
Patac32
Un pointeur sur la zone PATAC de la tâche en cours.
lpFonctionsCM32
Pointeur utilisé pour appeler des cm32.
hInst
C'est le HINSTANCE de la tâche Divalto. Cette valeur est utilisée par quelques fonctions de windows.
hWnd0
C'est le HWND de la fenêtre principale. La fenêtre principale existe toujours, elle ne change pas entre deux appels de la DLL. Par exemple ,on peut l'utiliser si on veut mettre la tâche en cours en plein écran grâce à la fonction :
ShowWindow(hWnd0,SW_SHOWMAXIMIZED).
hWndEnCours
C'est le HWND de la fenêtre en cours, c'est celle qui se situe au sommet de la "pile des fenêtres" de la tâche en cours. Cette valeur peut être la même que hWnd0, mais peut changer entre deux appels de la DLL si le programme DIVA a ouvert une fenêtre.
Cette valeur est utile par exemple dans la fonction
MessageBox ( hWndEnCours , "texte" , "titre" , MB_OK );
car on veut que le message s'affiche au dessus de toutes les autres fenêtres de la tâche en cours.
Regardons le source de la fonction NewAppelDLL32.
Elle commence par un "switch" qui va aiguiller l'exécution de la DLL vers la bonne fonction, puis on trouve le corps de la fonction elle-même dans le "case". On aurait pu aussi appeler une sous fonction de la DLL à condition de lui passer les mêmes paramètres que la fonction NewAppelDLL32.
On peut aussi appeler des cm32 , par exemple :
lg=wsprintf((LPSTR)pt->affiche,"salut je suis la tache %d",TACHE);
Cm32Display(pt->affiche,lg,1);
La fonction Cm32Display est dans patacnew.h. Ce fichier contient la définition des fonctions qui permettent d'appeler directement un CM32.
Pour compiler notre DLL, nous avons aussi besoin du source hardemo.def.
Listing du source hardemo.def
LIBRARY HARDEMO
EXPORTS
NewAppelDLL32 @2
Il est utilisé par le linker. Il indique le type d'exécutable.
Le chapitre EXPORTS donne la liste des fonctions qui peuvent être appelées par d'autres programmes.
On y trouve le nom de la fonction : NewAppelDLL32 et son numéro d'ordre : @2
Il indique à l'éditeur de liens qu'il doit conserver aussi le nom de la fonction dans la DLL sinon l'appel de votre DLL ne marchera pas avec Divalto.
En final nous avons HARDEMO.DLL, c'est le code de la DLL. Il suffit de le recopier dans c:\divalto\sys et notre DLL est prête à travailler.