Resources in Delphi

Inleiding

Dit hoofdstuk gaat over het gebruik van (niet standaard/delphi) resources.

Een resources kan van alles zijn: bitmaps, icons, cursors, avi’s, wav’s etc. etc..

Een programma kan gebruik maken van externe resources, maar ook van interne resources, ofwel resources die in de exe-cutable zijn gelinkt.

Hier gaan we het over die laatste categorie hebben, en noemen ze kortweg resources.

In dit hoofdstuk leer je:

Voor- en nadelen van resources

Het grote voordeel van het gebruik van (interne) resources is dat je je project kunt beperken tot 1 exe-cutable.
Het is dus niet noodzakelijk om alle resources apart te distribueren.
Een ander groot voordeel is, dat je met behulp van resources je applicatie taal-afhankelijk kunt maken.

Nadelen zijn dat je exe-cutable een stuk groter wordt, het moet uiteindelijk allemaal in 1 exe-file zitten.
En een ander groot nadeel is dat het gebruik van resources verre van gestandaardiseerd is.
En met name over dat laatste aspect is dit hoofdstuk zeer nuttig, omdat ik de meest voorkomende resources in een voorbeeld zal behandelen.

Kijk alvast eens naar het eindresultaat van het voorbeeld, dan krijg je hopelijk al een beeld van wat er zoal mogelijk is.

Stappenplan

Om met resources te kunnen werken kun je het volgende stappenplan volgen:

– Verzamel alle resources die je nodig hebt op 1 plek, ofwel in 1 map
– Maak een resource-script
– Compileer het resource-script
– Link het resource-bestand
– Gebruik het resource-bestand

Om alle problemen te omzeilen kan het handig zijn om het programma brcc32.exe ook in de map te plaatsen.
Brcc32.exe is de resource-compiler die je in de Bin-map van Delphi kunt vinden.

Verzamelen van resources

Maak een project-map waar je alles in verzameld wat met het project te maken heeft.
Maak dus copieen van bestanden die elders op je schijf staan.
Dit voorkomt straks dat je (waar dan ook) een pad mee op moet geven, wat alleen maar tot verwarring kan leiden, of erger tot het niet goed functioneren van bijvoorbeeld de compiler.

– Maak een map waarin je het voorbeeld kunt gaan uitwerken
– Download de resources en pak ze in de zojuist gemaakte map uit

Resource-script maken

Een resource-script is een tekstbestandje die je maakt voor de resource-compiler.
De compiler maakt daar dan de binaire resource-file van.
Leer je meteen aan om alles, bahlve strings tussen “-tjes, in HOOFDLETTERS te tikken, ook dit voorkomt weer problemen met de compiler.

De standaard extensie voor een resource-script is .rs, maar dit is niet strikt noodzakelijk.

De inhoud van een resource-bestand is meestal als volgt opgebouwd:

<IDENTIFIER> <TYPE> <BESTAND>|”<bestand>”

waarbij <IDENTIFIER> de naam van de resource is zoals je die later in je Delphi-project aanroept, <TYPE> het type van de resource aangeeft, en <BESTAND> of “<bestand”> de naam van het resource-bestand is.

Soorten resources

De soorten of types van resources zijn:

– ICON, voor iconen
– BITMAP, voor bitmaps
– WAVE, voor wav’s
– AVI, voor avi’s
– RCDATA, voor bv. mp3’s
– JPEG, voor jpg’s
– CURSOR, voor cursors
– STRINGTABLE BEGIN [nummer],[“string”] END, voor string’s

De laatst genoemde is vooral handig als je je project meertalig wilt maken.

– Open het kladblok of een andere tekst-editor
– Tik de volgende tekst:

STRINGTABLE
BEGIN
1000,”&Bestand”
1010,”&Laden”
1020,”-“
1030,”&Afsluiten”
1100,”&Hulp”
1110,”&CopyRight”
2000,”&File”
2010,”&Open”
2020,”-“
2030,”E&xit”
2100,”&Help”
2110,”&CopyRight”
END

– Bewaar dit bestand onder de naam menu.rc in de map

Dit is natuurlijk het script voor het menu van ons voorbeeld. Ons voorbeeld wordt zowel in het Nederlands als in het Engels uitgevoerd.

Resource-script compileren

Om van een resource-script een binair resource-bestand te maken moet je gebruik maken van een resource-compiler.

In dit voorbeeld maken we gebruik van de Borland-resource-compiler brcc32.exe, die 32-bits resource-files maakt. Een resource-file heeft standaard .RES als extensie.

– Copieer de brcc32.exe compiler uit de bin-directory van Delphi naar je eigen map
– Open een DOS-box (Start–>Uitvoeren…: cmd) en activeer daarin je map(cd …)
– Tik in: brcc32 menu.rc

Als het goed is staat er nu ook het bestand menu.RES in je map.

(Om je nu een hoop tikwerk te besparen kun je hier alle resource-scripts downloaden.)

– Pak ze uit en compileer ze
– Open de rc-files ook even in je tekst-editor “ter leering ende vermaek”

Wat je hopelijk is opgevallen is dat je bij het ene type de bestandnaam tussen “-tjes moet zetten, en de andere keer niet. Dit is helaas niet consequent en zal vermoedelijk te maken hebben met de ontwikkeling van resource-files.
Het internet is natuurlijk een dankbare bron voor als je er niet uitkomt.

Voor alle duidelijkheid zal ik hieronder bij elk type aangeven of het bestand wel of niet tussen “-tjes moet staan.

TYPE WEL/NIET tussen “-tjes
AVI NIET
BITMAP NIET
CURSOR WEL
ICON NIET
JPEG NIET
RCDATA NIET
RT_HTML DEISCARDABLE WEL
WAVE WEL

Resource-file linken

Het linken van een resource-file in Delphi is werkelijk gemakkelijk.

In het implementation-gedeelte van je Unit staat reeds {$R *.DFM}.

Met de compiler-directive $R link je een resource in je project.

Dus om nu het resource-bestand menu.RES te linken gebruik je {$R menu.RES}, en deze regel kan direct onder de regel met {$R *.DFM}.

Was alles maar zo gemakkelijk, niet waar?

Resources gebruiken

Tot nog toe was het allemaal nog niet zo ingewikkeld.
Maar dat gaat gelukkig nu veranderen.

In Delphi kun je alle tot nog toe behandelde type resources in je project gebruiken, maar dat gaat helaas niet op een eenduidige manier.

Soms heeft Delphi zelf routines in huis om gebruik te maken van resources, en in andere gevallen zullen we onze toevlucht moeten nemen tot Windows-API’s.

Laten we maar eens beginnen.

– Open Delphi met een nieuw project
– Bewaar het project in je resource-map
– Zet een MainMenu-component op je form
– Zorg ervoor dat het menu er als volgt uitziet:

Hoofmenu-items: &Bestand (mnuBestand), &Hulp (menuHulp)
Submenu-items van &Bestand: &Laden (mnuBestandLaden), – (mnuBestandSep1), &Afsluiten (mnuBestandAfsluiten)
Submenu-items van &Hulp: &Copyright (mnuHulpCopyright)

Tussen haakjes staat steeds de naam van het item.

Overigens zijn de Captions niet echt interessant, want die worden straks toch overschreven door de waarde’s die in menu.RES staan.

– Voeg in de implementation-sectie onder de regel met {$R *.DFM} de volgende regel toe:
{$R menu.RES}
– Definieer 2 constanten: c_NL en c_UK met respectievelijk de waarden 1000 en 2000
– Declareer een variabele vTaal van het type integer
– Zet een RadioGroup-component op je form met als Caption ‘Taal’, Name ‘rgrTaal’, en met de volgende items:
NL en UK
– Implementeer het Click-event van rgrTaal als volgt:

procedure TForm1.rgrTaalClick(Sender: TObject);
begin
  if rgrTaal.ItemIndex = 0 then
    vTaal := c_NL
  else
    vTaal := c_UK;
  UpdateTaal;
end;

De procedure UpdateTaal moeten we nog maken. Het is de bedoeling om alles waar maar tekst aan te pas komt, dit wordt aangepast aan de gekozen taal: dus alles in het Nederlands of in het Engels.

Op dit moment hebben we allen menu.RES tot onze beschikking, dus dat kunnen we dan ook als enige hier aanpassen.

– Zet in de definitie van TForm1, in de Private-clausule de volgende declaratie:

procedure UpdateTaal;

– Implementeer de procedure UpdateTaal als volgt:

procedure TForm1.UpdateTaal;
begin
  mnuBestand.Caption := LoadStr(vTaal);
  mnuBestandLaden.Caption := LoadStr(vTaal + 10);
  mnuBestandAfsluiten.Caption := LoadStr(vTaal+30);
  mnuHulp.Caption := LoadStr(vTaal+100);
  mnuHulpCopyright.Caption := LoadStr(vTaal+110);
end;

Met de functie LoadStr() haal je een string op uit de stringtabel. Ik gebruik hier expres het woordje DE voor stringtabel, want Delphi kan maar met 1 stringtabel werken. Je mag best meer resource-files met stringtabel’s linken, maar Delphi maakt daar 1 groete stringtabel van.
Het is dus van groot belang dat ieder item uit iedere stringtabel uit ieder resource-bestand een uniek nummer heeft!

Merk op dat door het handig kiezen van de nummers in een stringtabel je aan een numerieke expressie genoeg hebt om het juiste item uit de stingtabel te destileren.
Zo liggen alle Nederlandse menu-items tussen de 1000 en 1999, en alle Engelse menu-items tussen de 2000 en 2999.

– Implementeer het FormCreate-event als volgt:

procedure TForm1.FormCreate(Sender: TObject);
begin
  vTaal := c_NL;
  rgrTaal.ItemIndex := 0;
  UpdateTaal;
end;

Nu zijn we klaar voor een eerste test.

– Bewaar het project en run het
– Kies een taal en klik in het menu
– Kies de andere taal en klik weer in het menu

Hopelijk werkt alles naar wens, dat wil zeggen dat de Captions van de menu-items worden aangepast aan de gekozen taal.

De rest van het voorbeeld ga ik niet zo uitkouwen als hierboven. Aan het einde van het hoofdstuk geef ik de volledige code weer, zodat je zelf het voorbeeld kunt afmaken.
Wel geef ik alvast hier alle componenten die op de Form staan:

Component Name Poperty=
Animate Animate1 AutoSize=False, (Container=Panel1)
Button btnAanUit Caption=’Aan’, Width=33, (Container=Panel1)
Image Image1 AutoSize=False, Stretch=True, Width=121, Height=153
Image Image2 AutoSize=False, Stretch=True, Width=15, Height=15, in linker-onderhoek van Image1
Label Label1 Staat links-boven Panel1
Panel Panel1 Width=137, Height=129
Panel Panel2 Width=505, Heigth=193
Timer Timer1 Interval=500
WebBrowser WebBrowser1 Align=alClient, (Container=Panel2)

Wat we hier moeten leren is hoe de verschillende resources in Delphi aan te roepen zijn.

Het afspelen van een AVI is simpel met een Animate-control:

In de property ResName komt de resource-naam (IDENTIFIER) van de AVI te staan,
in de property ResHandle zet je de waarde HInstance, en tot slot
set je de property Active op True.

Om een Bitmap in een Image-control te zetten kun je de volgende constructie gebruiken:

Image2.Picture.Bitmap.LoadFromResourceName(hInstance,’BULBOFF’);

Hier zie je dus een voorbeeld van een Delphi-oplossing. Simpel en doeltreffend.

Iets minder voor de hand liggend is het laden van een Icon:

Form1.Icon.Handle := LoadIcon(hInstance,’HJGSOFT’);
Application.Icon.Handle := LoadIcon(hInstance,’HJGSOFT’);

De functie LoadIcon geeft een waarde van het type HICON, en dat is een Handle naar de Icon.

Het laden en toekennen van cursors gaat als volgt:

Alle cursors in Delphi worden in een Array van het object Screen gestopt. Om aan een control een nieuwe cursor toe te kennen moet je eerst deze Array uitbreiden:

Screen.Cursors[100] := LoadCursor(hInstance,’FLAT’);
Screen.Cursors[101] := LoadCursor(hInstance,’GRAB’);

LoadCursor retourneert weer een handle naar de cursor. De index van de Array maakt niets uit, want op de achtergrond blijkt het hier niet om een gewone Array te gaan maar om een Pointer-type.
Wel is het handig te weten dat de standaard-cursors in Delphi een negatieve index hebben.
Om nu aan een control een nieuwe cursor toe te kennen is het voldoende om de index aan de property Cursor toe te kennen:

btnAanUit.Cursor := 100;

De rest van onze resources laat zich minder makkelijk gebruiken.

Daarvoor gaan we een aantal, min of meer, generieke procedures maken.

Voor het gebruik van jpg’s maken we:

procedure LoadJPEGFromRes(TheJPEG : string; ThePicture : TPicture);

die een string meekrijgt van de naam van de jpg in de resource-file, en het object waar de jpg moet komen te staan.

In ons voorbeeld gaan we het als volgt gebruiken:

LoadJPEGFromRes(‘ZOEF’,Image1.Picture);

En hier komt de implementatie:

procedure LoadJPEGFromRes(TheJPEG : string; ThePicture : TPicture);
  var
    ResHandle : THandle;
    MemHandle : THandle;
    MemStream : TMemoryStream;
    ResPtr    : PByte;
    ResSize   : Longint;
    JPEGImage : TJPEGImage;
begin
  ResHandle := FindResource(hInstance, PChar(TheJPEG), ‘JPEG’);//retourneert handle naar resource-blok
  MemHandle := LoadResource(hInstance, ResHandle);//laadt resource-blok in geheugen
  ResPtr    := LockResource(MemHandle);//locked het resource-blok en retourneert een pointer naar eerste byte van resource-blok
  MemStream := TMemoryStream.Create;//geheugen-stroom
  JPEGImage := TJPEGImage.Create;
  ResSize := SizeOfResource(hInstance, ResHandle);//grootte in bytes van resource-blok
  MemStream.SetSize(ResSize);//zet de grootte van geheugen-stroom
  MemStream.Write(ResPtr^, ResSize);//vult de geheugen-stroom met het resource-blok
  FreeResource(MemHandle);//voor 16-bits toepassingen, obsolete voor 32-bits toepassingen
  MemStream.Seek(0, 0);//zet de wijzer aan het begin van het geheugen-blok
  JPEGImage.LoadFromStream(MemStream);
  ThePicture.Assign(JPEGImage);
  JPEGImage.Free;
  MemStream.Free;
end;

Het commentaar spreekt hopelijk voor zich. Waar het op neerkomt is dat je de resource in een geheugenstroom zet en vandaaruit het jpeg-image vult. Een geheugenstroom kun je vergelijken met een bestand op schijf.
Gebruik de Unit JPEG in je Uses-clausule.

Voor het afspelen van een WAV volgen we min of meer hetzelfde als hierboven.
De procedure heet:

procedure PlayWaveFromRes(TheWave: string);

en in ons voorbeeld wordt het als volgt gebruikt:

PlayWaveFromRes(‘ZOEFZOEF’);

Hier is de implementatie:

procedure PlayWaveFromRes(TheWave: string);
  var
    hFind, hRes: THandle;
    Song: PChar;
begin
  hFind:=FindResource(HInstance, PChar(TheWave), ‘WAVE’);
  if hFind<>0 then
    begin
      hRes:=LoadResource(HInstance, hFind);
      if hRes<>0 then
        begin
          Song:=LockResource(hRes);
          if Assigned(Song) then//test voor nil, als Song is nil, dan false anders true
            SndPlaySound(Song, snd_Sync or snd_Memory);
          UnlockResource(hRes);
        end;
      FreeResource(hFind);
    end;
end;

In deze routine zitten iets meer tests om te kijken of alles in orde is, eigenlijk wel zo netjes.
Hier hebben we geen memory-stream nodig. Dat heeft te maken met de routine SndPlaySound. Voor details over deze routine verwijs ik graag naar de Delphi-help.

Een iets andere aanpak gaan we gebruiken voor het afspelen van een MP3.
De procedure zal heten:

procedure PlayMP3FromRes(TheMP3: string);

En we gebruiken hem als volgt:

PlayMP3FromRes(‘JOURNAAL’);

De implementatie:

procedure PlayMP3FromRes(TheMP3: string);
  var
    rStream: TResourceStream;
    fStream: TFileStream;
    fname: string;
    mp: TMediaPlayer;
begin
  fname:=ExtractFileDir(Paramstr(0))+’\’+TheMP3+’.MP3′;
  rStream := TResourceStream.Create(hInstance, TheMP3, RT_RCDATA);
  try
    fStream := TFileStream.Create(fname, fmCreate);
    try
      fStream.CopyFrom(rStream, 0);
    finally
      fStream.Free;
    end;
  finally
    rStream.Free;
  end;
  mp := TMediaPlayer.Create(Form1);
  mp.ParentWindow := Form1.Handle;
  mp.Visible := False;
  mp.Close;
  mp.FileName := fname;
  mp.Open;
  mp.Play;
end;

In plaats van een Memory-stream gebruiken we hier een echte file-stream. Nadeel is wel dat nu het bestand JOURNAAL.MP3 wordt aangemaakt.
Verder maken we gebruik van een MediaPlayer omdat deze in staat is MP3’s af te spelen.
De MediaPlayer wordt met het afsluiten van de applicatie vernietigd.
Vergeet hier niet de Unit mplayer aan te roepen.

Als laatste gaan we de html-resource gebruiken.

De procedure heet:

procedure LoadHTMLFromRes(TheHTML: string);

En je gebruikt hem als volgt:

LoadHTMLFromRes(‘INFONL’);

De implementatie is redelijk simpel:

procedure LoadHTMLFromRes(TheHTML: string);
  var
    ResURLStr: string;
    ModuleName: array[0..255] of Char;
begin
  GetModuleFileName(hInstance, ModuleName, 255);//retourneert het pad+bestandsnaam van de applicatie
  ResURLStr:=’res://’ + StrPas(ModuleName) + ‘/RT_HTML/’+TheHTML;//StrPas zet een 0-terminated-string om naar een Pascal-string
  Form1.WebBrowser1.Navigate(ResURLStr);
end;

Er zijn natuurlijk ook andere routines in Delphi om pad+bestandsnaam te achterhalen, maar deze werkt ook.
Kijk voor het gebruik van de WebBrowser naar de help van Delphi.

Zoals beloofd komt hier de complete source-code van het project:

Complete code

unit Unit1;interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, ExtCtrls, StdCtrls, JPEG, mmsystem, MPlayer, Menus, OleCtrls,
SHDocVw;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Animate1: TAnimate;
    btnAanUit: TButton;
    rgrTaal: TRadioGroup;
    Image1: TImage;
    Timer1: TTimer;
    Image2: TImage;
    MainMenu1: TMainMenu;
    mnuBestand: TMenuItem;
    mnuBestandLaden: TMenuItem;
    mnuBestandSep1: TMenuItem;
    mnuBestandAfsluiten: TMenuItem;
    mnuHulp: TMenuItem;
    mnuHulpCopyright: TMenuItem;
    Panel2: TPanel;
    WebBrowser1: TWebBrowser;
    Label1: TLabel;
    procedure btnAanUitClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure rgrTaalClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure Image1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure mnuBestandLadenClick(Sender: TObject);
    procedure mnuHulpCopyrightClick(Sender: TObject);
    procedure mnuBestandAfsluitenClick(Sender: TObject);
  private
    { Private declarations }
    procedure UpdateTaal;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}
{$R menu.RES}
{$R smp.RES}
{$R captions.RES}
{$R htmls.RES}

Const
  c_NL = 1000;
  c_UK = 2000;

var
  vTaal: Integer;

procedure LoadJPEGFromRes(TheJPEG : string; ThePicture : TPicture);
  var
    ResHandle : THandle;
    MemHandle : THandle;
    MemStream : TMemoryStream;
    ResPtr    : PByte;
    ResSize   : Longint;
    JPEGImage : TJPEGImage;
begin
  ResHandle := FindResource(hInstance, PChar(TheJPEG), ‘JPEG’);//retourneert handle naar resource-blok
  MemHandle := LoadResource(hInstance, ResHandle);//laadt resource-blok in geheugen
  ResPtr    := LockResource(MemHandle);//locked het resource-blok en retourneert een pointer naar eerste byte van resource-blok
  MemStream := TMemoryStream.Create;//geheugen-stroom
  JPEGImage := TJPEGImage.Create;
  ResSize := SizeOfResource(hInstance, ResHandle);//grootte in bytes van resource-blok
  MemStream.SetSize(ResSize);//zet de grootte van geheugen-stroom
  MemStream.Write(ResPtr^, ResSize);//vult de geheugen-stroom met het resource-blok
  FreeResource(MemHandle);//voor 16-bits toepassingen, obsolete voor 32-bits toepassingen
  MemStream.Seek(0, 0);//zet de wijzer aan het begin van het geheugen-blok
  JPEGImage.LoadFromStream(MemStream);
  ThePicture.Assign(JPEGImage);
  JPEGImage.Free;
  MemStream.Free;
end;

procedure PlayWaveFromRes(TheWave: string);
  var
    hFind, hRes: THandle;
    Song: PChar;
begin
  hFind:=FindResource(HInstance, PChar(TheWave), ‘WAVE’);
  if hFind<>0 then
    begin
      hRes:=LoadResource(HInstance, hFind);
      if hRes<>0 then
        begin
          Song:=LockResource(hRes);
          if Assigned(Song) then
            SndPlaySound(Song, snd_Sync or snd_Memory);
          UnlockResource(hRes);
        end;
      FreeResource(hFind);
    end;
end;

procedure PlayMP3FromRes(TheMP3: string);
  var
    rStream: TResourceStream;
    fStream: TFileStream;
    fname: string;
    mp: TMediaPlayer;
begin
    fname:=ExtractFileDir(Paramstr(0))+’\’+TheMP3+’.MP3′;
    rStream := TResourceStream.Create(hInstance, TheMP3, RT_RCDATA);
    try
      fStream := TFileStream.Create(fname, fmCreate);
      try
        fStream.CopyFrom(rStream, 0);
      finally
        fStream.Free;
      end;
    finally
      rStream.Free;
    end;
  mp := TMediaPlayer.Create(Form1);
  mp.ParentWindow := Form1.Handle;
 mp.Visible := False;
  mp.Close;
  mp.FileName := fname;
  mp.Open;
  mp.Play;
end;

procedure LoadHTMLFromRes(TheHTML: string);
  var
    ResURLStr: string;
    ModuleName: array[0..255] of Char;
begin
  GetModuleFileName(hInstance, ModuleName, 255);//retourneert het pad+bestandsnaam van de applicatie
  ResURLStr:=’res://’ + StrPas(ModuleName) + ‘/RT_HTML/’+TheHTML;//StrPas zet een 0-terminated-string om naar een Pascal-string
  Form1.WebBrowser1.Navigate(ResURLStr);
end;

procedure TForm1.btnAanUitClick(Sender: TObject);
begin
  if btnAanUit.Tag = 0 then
    begin
      Animate1.ResName := ‘COUNT8R’;
      Animate1.ResHandle := hInstance;
      Animate1.Active := True;
      btnAanUit.Caption := LoadStr(10*vTaal+10);
      btnAanUit.Tag := 1;
    end
  else
    begin
      Animate1.Active := False;
      Animate1.ResHandle := 0;
      btnAanUit.Caption := LoadStr(10*vTaal);
      btnAanUit.Tag := 0;
    end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  vTaal := c_NL;
  rgrTaal.ItemIndex := 0;
  LoadJPEGFromRes(‘ZOEF’,Image1.Picture);
  Screen.Cursors[100] := LoadCursor(hInstance,’FLAT’);
  Screen.Cursors[101] := LoadCursor(hInstance,’GRAB’);
  btnAanUit.Cursor := 100;
  Form1.Icon.Handle := LoadIcon(hInstance,’HJGSOFT’);
  Application.Icon.Handle := LoadIcon(hInstance,’HJGSOFT’);
  Image2.Picture.Bitmap.LoadFromResourceName(hInstance,’BULBOFF’);
  UpdateTaal;
end;

procedure TForm1.rgrTaalClick(Sender: TObject);
begin
  if rgrTaal.ItemIndex = 0 then
    vTaal := c_NL
  else
    vTaal := c_UK;
  UpdateTaal;
end;

procedure TForm1.UpdateTaal;
begin
  mnuBestand.Caption := LoadStr(vTaal);
  mnuBestandLaden.Caption := LoadStr(vTaal + 10);
  mnuBestandAfsluiten.Caption := LoadStr(vTaal+30);
  mnuHulp.Caption := LoadStr(vTaal+100);
  mnuHulpCopyright.Caption := LoadStr(vTaal+110);
  btnAanUit.Caption := LoadStr(10*vTaal+10*btnAanUit.Tag);
  rgrTaal.Caption := LoadStr(10*vTaal+20);
  rgrTaal.Items[0] := LoadStr(10*vTaal+30);
  rgrTaal.Items[1] := LoadStr(10*vTaal+40);
  Form1.Caption := LoadStr(10*vTaal+50);
  Image1.Hint := LoadStr(10*vTaal+80);
  Image2.Hint := Image1.Hint;
  Label1.Caption := LoadStr(10*vTaal+90);
  if vTaal = c_NL then
    LoadHTMLFromRes(‘INFONL’)
  else
    LoadHTMLFromRes(‘INFOUK’);
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
  PlayMP3FromRes(‘JOURNAAL’);
end;

procedure TForm1.Image1Click(Sender: TObject);
begin
  PlayWaveFromRes(‘ZOEFZOEF’);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if Image2.Tag = 0 then
    begin
      Image2.Picture.Bitmap.LoadFromResourceName(hInstance,’BULBOFF’);
      Image2.Tag := 1;
    end
  else
    begin
      Image2.Picture.Bitmap.LoadFromResourceName(hInstance,’BULBON’);
      Image2.Tag := 0;
    end;
  if btnAanUit.Cursor = 100 then
    btnAanUit.Cursor := 101
  else
    btnAanUit.Cursor := 100;
end;

procedure TForm1.mnuBestandLadenClick(Sender: TObject);
  var s: string;
begin
  s := LoadStr(vTaal*10+60);
  ShowMessage(s);
end;

procedure TForm1.mnuHulpCopyrightClick(Sender: TObject);
  var s: string;
begin
  s := LoadStr(vTaal*10+70);
  ShowMessage(s);
end;

procedure TForm1.mnuBestandAfsluitenClick(Sender: TObject);
begin
  close;
end;

end.

Ten slotte

Zoals je hebt gezien moet er hier en daar wel wat worden geprogrammeerd om van alle soorten resources gebruik te kunnen maken.
Ik heb geprobeerd wat licht in de duisternis te scheppen en een aantal verschillende oplossingen voor de problemen te geven.

Ik hoop dat je je voordeel hiermee kunt doen.

[Oplossing raadsel 1]