|
Accueil-->Composant ASP en C++
Ecrire en C++ un composant ASP
Richard est aussi un féru de la programmation.
je lui apprend le C et il est excellent en ASP, d'ailleurs il est webmaster de Web O Concept
weboconcept.com
Il veut pouvoir afficher un fichier PNG autogénéré et représentant un texte donné
d’une police donnée dans les couleurs de son choix. sur
une page ASP.
Impossible de parvenir à cette tache, il faut pour y parvenir écrire un composant ActiveX pour ASP,
c’est à dire une DLL COM.
Et bien allons y ! Ce sera une des bases du nouveau site
de Richard 2002fonts.com
Les BMP et les DIB sont documentées dans les API Win32 mais rien sur les PNG !
Pour écrire des PNG Il existe LIBPNG et ZLIB
http://www.libpng.org/pub/png/pngcode.html
FreeImage encapsule ces librairies et est disponible
en freeware sur http://www.6ixsoft.com
De plus FreeImage est téléchargeable sous la forme
d’une DLL Win32 accompagnée de ses fichiers .H et .LIB.
C’est vraiment facile à utiliser.
Précisons ce que nous voulons faire :
En ASP nous voulons utiliser via VB-Script
un objet créé
en C++ possédant une méthode nommée
CreateFile dont les arguments doivent être :
- Le nom et le chemin du fichier à créer
- Le texte à afficher dans le PNG
- Le nom de la police sélectionnée
- La largeur de l’image
- La hauteur de l’image
- La taille en point de la police
- La couleur du fond
- La couleur de la police
- Un indicateur précisant si le fichier doit être physiquement créé
Il est intéressant d’avoir une valeur de retour pour savoir si la méthode a fonctionné
et si le fichier a été créé.
Tout d’abord petit problème :
- VB-Script est un langage non typé dont
les variables sont du type non dénommé
VARIANT. En résumé il n’est pas nécessaire de
déclarer les variables et en plus leurs types
ne peuvent être défini. True vaut -1
- A l’inverse C++ est fortement typé. Pour WIN32 TRUE vaut 1 et les chaînes de caractè
res sont des ASCIIZ c’est à dire terminées par le caractère NULL.
Comment allons nous pouvoir passer
des paramètres corrects dans une DLL COM? En
y regardant de plus près le type variant se décompose en interne en sous-types dont les long signés,
les booléens, et les chaînes de caractères
BSTR (Plus d’autres types qui ne nous intéressent pas ici).
- Valeurs entières.
Passer en argument à VB-Script un type VARIANT de sous type long revient à passer un long.
Je ne ferais pas de contrôle de validité de l’argument passé,
en espérant que l’on ne passera pas une chaîne !
- Couleurs RGB.
Pour la couleur, OLE et VB-Script utilise le type OLE_COLOR qui n’est qu’un typedef de long.
Par contre WIN32 attend un type COLORREF qui n’est qu’un typedef de unsigned long.
Une API permet la conversion entre les 2 types.
- Booléens.
Pour les booléens OLE et VB-Script utilise le sous-type VARIANT_BOOL
(True vaut –1 et sera exprimé en C++ par VARIANT_TRUE)
alors que WIN32 le type BOOL (True vaut 1)
- Chaînes de caractères.
Pour VB-Script comme pour OLE, et bien, ce n’est pas le même format qu’en
WIN32. Elle sont du sous-type BSTR avec un indicateur du nombre de caractères suivi
des caractères proprement dits écrits en UNICODE c’est à dire des caractères
sur 16 bits.
En C les chaînes sont du type ASCIIZ sous la forme de suite de char
(8 bits) référencées par des pointeurs char* et terminées par le caractère
NULL !
Pour compliquer Windows a introduit aussi UNICODE sur les plates-formes
NT et les chaînes sont sous la forme de suite de short (16 bits) référencées
par des pointeurs short*.
Le type BSTR est utilisable en C, mais les API ne le comprennent pas ! Grâce à dieu, les conversions sont possibles entre les genres et
de nombreuses macros facilitent la tache.
La méthode devrait donc s’écrire sous la forme
VARIANT_BOOL NomClasse::CreateFile (BSTR bstrFileName,
BSTR bstrText,
BSTR bstrFontName,
LONG cx,
LONG cy,
LONG nFontSize,
OLE_COLOR ocBackColor,
OLE_COLOR ocFontColor,
VARIANT_BOOL
vbCreateFile) ;
Et bien pas de chance ! Ma fonction C ne marche pas car la valeur de retour en VB n’est pas la même qu’en C.
La valeur de retour du C doit être obligatoirement du type HRESULT et est utilisée comme code d’erreur
interne en VB pour les traitements d’erreurs.
Les valeur en entrée vont devoir être précédées
de [in] et la valeur de retour de la méthode doit être obligatoirement
un pointeur précédé de [out, retval] situé comme
dernier paramètre de le méthode C++. Ce dernier paramètre
ne sera pas visible en VB et sera utilisé comme valeur de retour.
Pas simple…
Heureusement nous allons utiliser ATL Wizard qui va se charger de tout cela !
Dans VC++6.0 faire File, New
Choisir ATL COM AppWizard
Indiquer comme nom du projet PNGASP qui sera aussi le nom du composant ASP
Cliquer OK
Valider les paramètres par défaut en cliquant Finish
Cliquer OK
Le wizard génère automatiquement le code source primaire
Choisir Insert, New ATL Object…
Choisir Objects, ActiveX Server Component, Cliquer Next.
Dans l’onglet Names taper uniquement le ShortName, le reste se rempli tout seul.
Regardez bien, le progID et vous allez avoir une idée assez
précise de la description du composant que nous sommes en train de créer :
- PNGASP est le nom du composant ActiveX
- Font2PnG est le nom de l’objet référencé dans le composant
Pour les onglets suivants je n’expliquerai pas les options possibles, appliquez simplement…
Dans l’onglet Attributes cocher Theading Model Both et Aggregation No
Dans l’onglet ASP laisser tout coché. Cliquer OK
Dans le ClassView de VC++6.0 pointer l’interface Ifont2Png et Add Method
Taper CreateFile comme nom de méthode et pour Parameters
(tout à la suite et sans saut de ligne)
[in] BSTR bstrFileName,
[in] BSTR bstrText,
[in] BSTR bstrFontName,
[in] LONG cx,
[in] LONG cy,
[in] LONG nFontSize,
[in] OLE_COLOR ocBackColor,
[in] OLE_COLOR ocFontColor,
[in] VARIANT_BOOL vbCreateFile,
[out, retval] VARIANT_BOOL *pvbRetVal);
Cliquer OK
Un Fichier IDL est automatiquement créé et inclus à la
compilation Les fichiers Font2Png.cpp et .h sont modifiés.
Ajoutons tout d’abord la librairie FreeImage.lib et le header FreeImage.h dans le répertoire du projet et insérons
les dans VC
Il faut maintenant implémenter la méthode CreateFile dans Font2Png.cpp (entièrement en WIN32 !)
STDMETHODIMP CFont2Png::CreateFile(BSTR bstrFileName,
BSTR bstrText,
BSTR bstrFontName,
LONG cx,
LONG cy,
LONG nFontSize,
OLE_COLOR ocBackColor,
OLE_COLOR ocFontColor,
VARIANT_BOOL vbCreateFile,
VARIANT_BOOL *pvbRetVal)
{
HBITMAP hBitmap;
HDC hdc, hdcMem;
HFONT hFont;
HBRUSH hBrush;
HPEN hPen;
int cPlanes, cBitsPixel;
int nLogFontSize;
SIZE sizeRequired;
RECT rc;
BITMAP bm;
CHAR *pszFileName = NULL;
TCHAR *pszFontName = NULL;
TCHAR *pszText = NULL;
int cchFileName;
int cchFontName;
int cchText;
COLORREF crBackColor;
COLORREF crFontColor;
HRESULT hres;
//Fixe à faux par défaut
la valeur de retour
*pvbRetVal = VARIANT_FALSE;
//Conversion couleurs OLE -> couleurs
Windows
hres = OleTranslateColor (ocBackColor, NULL, &crBackColor);
if (hres != S_OK) return hres;
hres = OleTranslateColor (ocFontColor, NULL, &crFontColor);
if (hres != S_OK) return hres;
//Calcule la taille des chaînes
UNICODE au format basic BSTR
cchFileName = wcslen (bstrFileName) + 1;
cchFontName = wcslen (bstrFontName) + 1;
cchText = wcslen (bstrText) + 1;
//Alloue les psz pur en CHAR* pour MBCS et WCHAR* pour UNICODE
//sauf pour le nom de fichier non UNICODE
pszFileName = new CHAR [cchFileName];
pszFontName = new TCHAR [cchFontName];
pszText = new TCHAR [cchText];
if ((pszFileName == NULL) ||(pszFontName == NULL) ||(pszText
== NULL)) return (S_FALSE);
#ifdef _UNICODE
//Change de BSTR Unicode vers WCHAR* sauf pour
//le nom de fichier non UNICODE
int rval1 = WideCharToMultiByte (CP_ACP, 0, bstrFileName, -1, pszFileName, cchFileName, NULL, NULL);
WCHAR *rval2 = wcscpy(pszFontName, bstrFontName);
WCHAR *rval3 = wcscpy(pszText, bstrText);
if ((rval1 == 0) || (rval2 == NULL) || (rval3 == NULL))
return (S_FALSE);
#else //_MCBS
// Change de BSTR Unicode vers CHAR*
int rval1 = WideCharToMultiByte (CP_ACP, 0, bstrFileName, -1, pszFileName, cchFileName , NULL, NULL);
int rval2 = WideCharToMultiByte (CP_ACP, 0, bstrFontName, -1, pszFontName, cchFontName , NULL, NULL);
int rval3 = WideCharToMultiByte (CP_ACP, 0, bstrText, -1, pszText, cchText, NULL, NULL);
if ((rval1 == 0) || (rval2 == 0)
|| (rval3 == 0))
return (S_FALSE);
#endif
//Créer temporairement un contexte de l’écran
et trouver
//ses capacités, calculer la taille logique de la police
hdc = CreateIC (_T("DISPLAY"), NULL, NULL, NULL) ;
hdcMem = CreateCompatibleDC (hdc) ;
cPlanes = GetDeviceCaps(hdc, PLANES);
cBitsPixel = GetDeviceCaps(hdc, BITSPIXEL);
nLogFontSize = - MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
DeleteDC (hdc);
//Créer une fonte et trouver la taille
du texte correspondant
hFont = CreateFont (nLogFontSize, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pszFontName) ;
SelectObject (hdcMem, hFont) ;
GetTextExtentPoint32 (hdcMem, pszText,
lstrlen (pszText), &sizeRequired);
//Vérifier si la taille demandée permet d'écrire
le texte,
// sinon l'agrandir
if (cx < sizeRequired.cx) cx
= sizeRequired.cx;
if (cy < sizeRequired.cy) cy
= sizeRequired.cy;
//Créer un DDB de la taille recalculée et y écrire
dedans
hBitmap = CreateBitmap (cx, cy, cPlanes, cBitsPixel, NULL);
SelectObject (hdcMem, hBitmap);
hBrush = CreateSolidBrush(crBackColor);
SelectObject (hdcMem, hBrush);
hPen = CreatePen(PS_SOLID, 1, crBackColor);
SelectObject (hdcMem, hPen);
Rectangle(hdcMem,0,0, cx, cx);
SetBkColor(hdcMem, crBackColor);
SetTextColor(hdcMem, crFontColor);
rc.left = 0;
rc.top = 0;
rc.right = rc.left + cx;
rc.bottom = rc.top + cy;
DrawText(hdcMem, pszText, -1, &rc, DT_SINGLELINE | DT_CENTER
| DT_VCENTER);
//Créer le FreeImageBitmap et le PNG
if (vbCreateFile == VARIANT_TRUE)
{
FIBITMAP *pFIBitmap;
GetObject(hBitmap, sizeof (BITMAP),
(LPSTR)&bm);
pFIBitmap = FreeImage_Allocate(bm.bmWidth, bm.bmHeight, (WORD)(bm.bmPlanes * bm.bmBitsPixel));
GetDIBits(hdcMem, hBitmap, 0, bm.bmHeight, FreeImage_GetBits(pFIBitmap), FreeImage_GetInfo(pFIBitmap), DIB_RGB_COLORS);
if (FreeImage_SavePNG(pFIBitmap, pszFileName , PNG_DEFAULT))
*pvbRetVal = VARIANT_TRUE;
FreeImage_Free(pFIBitmap);
}
//Destruction des objets restants
DeleteObject(hBitmap);
DeleteObject(hFont);
DeleteObject(hBrush);
DeleteObject(hPen);
DeleteDC (hdcMem);
delete pszFileName;
delete pszFileName;
delete pszFileName;
return S_OK;
}
Il n’y a plus qu’a compiler et builder la DLL…
Faites donc même un Batch Build vous aurez toutes les versions Release possibles !
Il s’agit de DLL COM enregistrée c’est à dire d’un ActiveX et non
pas de DLL standard Windows contrairement à FreeImage.dll qui est une DLL standard. Le compilateur a fait l’enregistrement
pour vous.
Si vous voulez utiliser cette DLL sur un poste autre il est nécessaire
de l’enregistrer grâce à regSvr32
Si vous tapez cette commande voici le dialogue qui vous explique la syntaxe
Je conseille de placer FreeImage.dll et PngAsp.dll dans le répertoire système.
Comment utiliser ou plutôt vérifier notre composant ?
Je n’aborderai pas ASP car j’en suis incapable. Alors nous allons écrire un petit script VB…
Vous pouvez écrire ce fichier en tant que fichier texte avec l’éditeur
de votre choix mais VC va très bien, il suffit juste de donner l’extension .vbs !
Ecrivons Font2Png.vbs (attention ne pas faire de saut de ligne dans l’appel de la méthode)
Dim oFont2Png
Set oFont2Png=CreateObject("PNGASP.Font2Png.1")
oFont2Png.CreateFile "c:\test.png","Ceci est écrit avec un composant ASP","Arial",500,50,43,RGB(255,0,0),RGB(0,0,255),1
Set oFont2Png = Nothing
Double cliquez sur Font2Png.vbs
Et voici le résultat !
Copyright © Jean-Pierre Fayeulle
|