Accueil
Qui suis-je ?
 
Mes livres
Les pompes rotodynamiques
Commander un livre
 
Mes programmes
Nopopup
Aide française
Télécharger
English help
Download
 
Optipump
English help
Download
 
SaveRes
Aide française
Télécharger
English help
Download
 
TTFName
Aide française
Télécharger
 
Mes articles
Composant ASP en C/C++
VxD W9x en ASM
 
Mes photos
Afrique australe
Autres photos
 
 visiteurs.
 pages vues.
 connecté(s).
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