Potrai far riferimento anche alle Appendici alla fine di questo tutorial per ulteriori informazioni sulle risorse con VC++ e BC++

Prima di fare altre cose tratteremo delle risorse in modo da non ripetere gli stessi concetti per ogni sezione. Non sarà necessario compilare nulla in questa sezione, saranno proposti solo esempi.

Le risorse sono bit di dati predefiniti, conservati in formato binario all’interno del file eseguibile. Le risorse si creano all’interno dello script delle risorse, un file con estensione “.rc”. I compilatori commerciali generalmente dispongono di un editor ad interfaccia visuale che consente di creare risorse senza editare il file manualmente ma alcune volte si rende necessario editarlo ugualmente perchè l’editor visuale non fa sempre esattamente quello che si desidera.

Sfortunatamente compilatori differenti trattano le risorse differentemente. Farò del mio meglio per spiegare le caratteristiche comuni necessarie per lavorare con le risorse in generale.

L’editor di risorse incluso con MSVC++ rende molto difficoltosa la modifica manuale delle risorse perchè assegna loro un formato proprietario, e modificherà completamente a suo piacimento nel momento del salvataggio il file file creato manualmente. In linea generale non bisogna disturbarsi a creare manualmente il file .rc, sarà molto più utile essere in grado di modificarne uno già esistente. Un’altra seccatura di MSVC++ è che esso chiama automaticamente il file header delle risorse con “resource.h” anche se avreste voluto chiamarlo in un altro modo. Per semplicità quindi in questa sezione di esempi lo chiameremo anche noi in questo modo, ma vi illustrerò come cambiarne il nome nell’appendice dedicata ai compilatori.

Prendiamo un esempio di script di risorse molto semplice, con una singola icona.

#include "resource.h"

IDI_MYICON ICON "my_icon.ico"

Questo è tutto il contenuto del file. IDI_MYICON è l’identificatore della risorsa, ICON è il tipo della risorsa e “my_icon.ico” è il nome del file esterno che la contiene. Questo esempio dovrebbe funzionare con qualsiasi compilatore.

Cos’è #include "resource.h" ? Beh, il nostro programma ha bisogno di un modo per identificare l’icona all’interno del codice, e il modo migliore per farlo è assegnarle un ID unico. (IDI_MYICON). Possiamo farlo semplicemente creando il file “resource.h” e includendolo contemporaneamente all’interno dello script delle risorse, e all’interno del nostro file sorgente.

#define IDI_MYICON  101

Come potete notare, abbiamo assegnato a IDI_MYICON il valore 101. Ora possiamo tranquillamente dimenticarci dell’identificatore e usare 101 ovunque abbiamo bisogno di far riferimento all’icona all’interno del nostro codice. Tenete conto però che IDI_MYICON è molto più chiaro come riferimento ed è molto più semplice da ricordare quando ci sono tante risorse quindi tanti numeri di riferimento.

Ora aggiungiamo una risorsa MENU:

#include "resource.h"

IDI_MYICON ICON "my_icon.ico"

IDR_MYMENU MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "E&xit", ID_FILE_EXIT
    END
END

Dinuovo IDR_MYMENU è il nome della risorsa, MENU ne è il tipo. Troviamo due parole chiave nuove: BEGIN e END, servono appunto a specificare, come avrete intuito, l’inizio e la fine del blocco di istruzioni della risorsa corrente. Tenete conto che alcuni compilatori usano { al posto di BEGIN e } al posto di END. Se il vostro compilatore supporta entrambi, sentitevi liberi di usare quello che più vi aggrada. Se invece supporta solamente uno dei due, ovviamente per far funzionare uno script creato con le altre parole chiavi, dovrete effettuare le modifiche opportune.

Abbiamo aggiunto anche un nuovo identificativo ID_FILE_EXIT, quindi dovremo anggiungere anch’esso al nostro file header, resource.h, per usarlo all’interno del programma.

#define IDI_MYICON  101

#define ID_FILE_EXIT    4001

Generare e tenere traccia di tutti questi ID potrebbe diventare qualcosa di realmente fastidiosa all’interno di progetti molto grandi. Questo è il motivo per cui generalmente si usa un editor visuale per le risorse che provvede a fare il tutto al posto nostro. Però, ripeto, tenete sempre conto, che l’editor visuale non è uno strumento preciso al 100% e alcune volte potrebbe non fare esattamente quello che si desidera, l’errore più comune che commette è quello di assegnare lo stesso ID a più risorse. Per questo motivo è utile essere in grado di comprendere e modificare uno script di risorse da esso precedentemente creato.

Ora facciamo un esempio di come utilizzare le risorse all’interno del programma.

    HICON hMyIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON));

Il primo parametro di LoadIcon() è l’handle dell’istanza corrente (quella che viene passata in WinMain() e può essere ricavata anche usando GetModuleHandle() come dimostrate nelle sezioni precedenti). Il secondo parametro è l’identificativo della risorsa.

Vi starete sicuramente domandando a cosa serve MAKEINTRESOURCE() e forse anche perché LoadIcon() prende come parametro un tipo LPCTSTR invece di un UINT per esempio quando gli passiamo un ID. Tutto quello che fa MAKEINTRESOURCE() è un cast da un valore intero (il nostro ID) ad un LPCTSTR, che è quello che si aspetta LoadIcon(). Questo ci porta a parlare del secondo modo per identificare le risorse, cioè con le stringhe. Ad oggi quasi nessuno lo fa più; senza andare nei dettagli, se non si usa #define per assegnare un valore intero alla risorsa, il nome è interpretato come stringa e può essere fatto riferimento ad esso all’interno del programma in questo modo:

    HICON hMyIcon = LoadIcon(hInstance, "MYICON");

LoadIcon() e le altre API di caricamento delle risorse possono accorgersi della differenza tra un intero ed un puntatore passati controllando l’high word del valore. Se è 0 (siamo nel caso che l’identificativo della risorsa e’ un intero compreso tra 0 e 65535) assume che sia un ID. Questo in effetti limita le risorse a usare ID inferiori a 65535, ma generalmente non dovrebbe essere un problema a meno che non state lavorando ad un progetto grandissimo con un sacco di risorse. Se invece essa non è 0 allora assume che il valore è un puntatore ed esegue il lookup della risorsa in base al nome.