HomePage   Delphi Library   Shopping 

Resource Management Step by Step

This article shows how to create a resource file and use it from within your Delphi App.It creates a resource file containing an AVI,a string,a bitmap and a sound.

1.Create a new directory as C:\Res.

2.Copy RW32CORE.DLL and BRCC32.exe from Delphi Dir/Bin to C:\Res

3.Create a bitmap(Bitmap1.bmp) ,A Wave file (Engine.wav),An AVI File(MyAVI.avi) and

a Text File with Notepad (Test.txt and in this file write a string for example Hello.).

And Copy all these file to C:\Res.

4.Open a new project and Save it As C:\Res\Project1.

5.From File/New open Text.

6.Write exactly these lines :

Bitmap_1 Bitmap "Bitmap1.bmp"

MyWavFile Wave "engine.wav"

Hello String "TEST.TXT"

AVI_1 AVI "MyAVI.avi"

You can copy and paste these lines.

7.Save this txt file as MyRes.rc.Don't forget to set Save Dialog's filter to Any File.Don't set it to Text Files.

Now you should have Project1.exe,BRCC32.exe,RW32CORE.DLL,Bitmap1.bmp,Test.txt,engine.wav, MyAVI.avi,MyRes.rc all under C:\Res directory.

8.Now open MSDOS Prompt,change directory to C:\Res and type BRCC32 MyRes.rc and then press enter.

Now MyRes.res file will be created under C:\Res.

9.Return to your project's unit and add {$R MyRes.res} after {$R *.DFM} directive.

It will look like this:

{$R *.DFM}

{$R MyRes.res}

10.Now Run your project to recreate your exe so that it will link MyRes.res to itself..

11.Now project1.exe contains all the resource files.

If you want to check it open Resxplor.exe from Delphi Dir/Demos/Resxplor Directory.From File/Open open your project's exe project1.exe and you will see your resources listed.

12.Now you can use your resources by calling apropos API calls.

13.Place an Image,an Animate and four Buttons to your form.

OnClick events are as follows:

procedure TForm1.Button1Click(Sender: TObject);

begin

Animate1.ResHandle := 0;

Animate1.ResName := 'AVI_1';

Animate1.Active := True;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

Image1.Picture.Bitmap.Handle:=LoadBitmap(HInstance,'Bitmap_1');

end;

To use PlaySound add MMSystem to your uses clause;

procedure TForm1.Button3Click(Sender: TObject);

begin

PlaySound(('MyWavFile'),HInstance,snd_resource or snd_sync);

end;

procedure TForm1.Button4Click(Sender: TObject);

var

hRes : THandle; {handle to the resource}

pRes : pointer; {pointer to the resource}

ResSize : longint; {aligned size of the resource}

i : integer; {counting variable}

{$IFDEF WIN32}

s : string; {a string to play with}

{$ELSE}

// s : string; {a string to play with}

{$ENDIF}

begin

{find the resource}

hRes := FindResource(hInstance, 'Hello', 'String');

if hRes = 0 then begin

ShowMessage('Could not find the resource');

exit;

end;

{get the aligned size of the resource}

ResSize := SizeOfResource(hinstance, hRes);

if ResSize = 0 then begin

ShowMessage('Nothing to load - size = 0');

Exit;

end;

{load the resource}

hRes := LoadResource(hInstance, hRes);

if hRes = 0 then begin

ShowMessage('Resource Load Failure');

Exit;

end;

{Get a pointer to the resource}

pRes := LockResource(hRes);

if pRes = nil then begin

ShowMessage('Resource Lock Failure');

FreeResource(hRes);

Exit;

end;

{convert the resource pointer to a string}

s:='';

i := 0;

while pChar(pRes)[i] <> '' do begin

s := s + pChar(pRes)[i];

inc(i);

end;

{prove it works}

ShowMessage(s); // show the string in the test.txt

{unlock and free the resource}

UnLockResource(hRes);

FreeResource(hRes);

end;

Written by CEVAHÝR PARLAK

http://www.macrotech.bigstep.com

 

 A Resource Monitor for Delphi

Copyright (c) by Bob Swart


Programming in Delphi is fun. It can also be dangerous, especially when it comes to freeing memory and resources. Although Delphi has a powerful mechanism of safeguarding the allocation and deallocation of memory and resources using a try-finally block, it's still up to the programming to do so. And in case you forget to deallocate a component, you'll lose memory and especially resources fast. How can we be sure our applications don't leak? We need a resource monitor, to watch things behind our back, and report the percentage of free resources to us. In this article, we'll design and implement a simple resource monitor that will be able to assist us in watching resource and memory leaks.

Free Resources
Windows itself can tell us how much resources are available (in percentages). The Windows 3.1 API function GetFreeSystemResources returns the percentage of free space for system resources. The declaration of the function is as follows:

  function GetFreeSystemResources(SysResource: Word): Word;

Where the SysResource parameter specifies the type of resource to be checked: GFSR_SYSTEMRESOURCES for the percentage of free space for system resources; GFSR_GDIRESOURCES for the percentage of free space for GDI resources (device-context handles, brushes, pens, regions, fonts, and bitmaps) or GFSR_USERRESOURCES for the percentage of free space for USER resources (window and menu handles).

Free Memory
In contrast to Windows Resources, we can ask Delphi how much memory is has available for our application. Like Turbo and Borland Pascal, this can be done using the MemAvail function from the system unit. Note that MemAvail does return the total amount of available memory, which is not necessary in one continuous block. If you need to know the biggest block of available memory, you should call MaxAvail instead.
Finally, it is important to know the amount of free memory below the 1Mbytes. This amount of free memory is needed for every executed Windows application, which explains why you can get the message "Insufficient memory to run application" on a 16Mb Pentium that says you have over 12Mb of memory and 60% of resources left.

Resource Monitor: Design
Now that we know what resource and memory information we'd like to display, let's talk about the form in which we would like to display this information. I've separated the information in two groups, as can be seen below. The first group, Memory, contains two labels, while the second group, Resources, contains three labels. The five labels will have captions that contain the actual values of MemAvail, BaseMem, Free System, User and GDI resources.

The source code for the form declaration is as follows:

unit Monform;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls, StdCtrls;

Type
  TFormResourceMonitor = class(TForm)
    GroupBoxMemory: TGroupBox;
    GroupBoxResources: TGroupBox;
    LabelMemAvail: TLabel;
    LabelBaseMem: TLabel;
    LabelFreeSystemResources: TLabel;
    LabelFreeUserResources: TLabel;
    LabelFreeGDIResources: TLabel;
    Timer1: TTimer;
    X: TLabel;
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormResourceMonitor: TFormResourceMonitor;

Resource Monitor: Implementation
Of course, the resource and memory information needs to be updated frequently on the form. In order to ensure a constant and continues accurate display of this information, we also need a timer component (from the System page on the component palette). It's the timer's OnTimer event that will update the five labels for us (see procedure Timer1Timer in the following listing).

implementation
uses ToolHelp;
{$R *.DFM}

  procedure TFormResourceMonitor.Timer1Timer(Sender: TObject);
  var Tmp: String;
      BaseMem: LongInt;
  begin
    { free memory }
    FmtStr(Tmp,'MemAvail: %d KBytes',[MemAvail div 1024]);
    LabelMemAvail.Caption := Tmp;
    FmtStr(Tmp,'Base Mem: %d KBytes (%d)',[GetBaseMem div 1024,GetBaseMem]);
    LabelBaseMem.Caption := Tmp;
    { free resources }
    FmtStr(Tmp,'Free System Resources: %d%%',
           [GetFreeSystemResources(gfsr_SystemResources)]);
    LabelFreeSystemResources.Caption := Tmp;
    FmtStr(Tmp,'Free User Resources: %d%%',
           [GetFreeSystemResources(gfsr_UserResources)]);
    LabelFreeUserResources.Caption := Tmp;
    FmtStr(Tmp,'Free GDI Resources: %d%%',
           [GetFreeSystemResources(gfsr_GDIResources)]);
    LabelFreeGDIResources.Caption := Tmp;
    { show that we're still alive and running... }
    if (X.Caption = 'x') then X.Caption := '+'
                         else X.Caption := 'x';
  end;
end.

Note that the GetBaseMem function (not displayed here) is adapted from Matt Pietrek's "Below1mb.C" as it appeared in July 94 Microsoft Developer's Network CD.

Result
If we set the FormStyle property of the form to fsStayOnTop we ensure that this little application will be on top at all times; a nice feature when guarding and watching over resource allocations and deallocations.

If you look carefully to the screenshot of the Resource Monitor in action, you'll see a tiny '+' sign at the bottom right part of the form. If you run the application, this '+' sign will seem to change in a 'x' and back at every update of the form. I've included this to ensure (or proof) that the application is actually updating the resource and memory information. In the listing above you already saw the last two lines of procedure Timer1Timer that are responsible for this behaviour.

A Resource Monitor for Delphi -- more --
Actually, the title of this section should be "less" instead of more, as we'll see how we can modify and customise the ResMon application to something that uses a lot less screen estate...

Last time in DDJ...
The first part of this article was published in the August 1995 issue of Delphi Developer's Journal. As you've just read, we wrote a Resource Monitor for Delphi, a nice litttle application that shows the free system resources and available memory on a form that stays on top at all times. Right after writing the tool and that article, I gave the program to my friends and asked their comments after a while of real usage (my best friends are programmers, so I expected a lot of feedback). Well, I got what I asked for, and more...

Little Brother
Every one came back within a few minutes and asked for a smaller version: it just used up too much of their screen, even at 1024x768. So, the first thing I did was to try to get the form smaller, by removing the groupboxes and adding some static lines together. Compare the two figures below and you'll know what I mean.

Out of my way...
The new ResMon was small enough to be used by everyone without cluttering their screen, and everybody was happy. Almost, that is. One of the guys didn't like the initial position at the lower left bottom of the screen, and wanted ResMon to pop up at the lower tight bottom of the screen. So, I modified the FormCreate method to set the Left and Top properties to his new prefered values, based on the dimensions of the screen itself:

  procedure TFormResourceMonitor.FormCreate(Sender: TObject);
  begin
    Left := GetSystemMetrics(SM_CXFULLSCREEN) - Width;
    Top := GetSystemMetrics(SM_CYFULLSCREEN) + GetSystemMetrics(SM_CYCAPTION) - Height;
  end {FormCreate};

When a third one claimed he prefered the ResMon to start in the upper right corner of the screen, I decided to let them figure it out by themselves, and enable ResMon to save it's position at closedown in a RESMON.INI file, to be able re-read and use this position again at startup. Again, a tiny modification in FormCreate and FormDestroy:

  procedure TFormResourceMonitor.FormCreate(Sender: TObject);
  var INI: TIniFile;
  begin
    INI := TIniFile.Create(ChangeFileExt(ParamStr(0),'.INI'));
    Top := INI.ReadInteger('window','WindowOrg.Y',0);
    Left := INI.ReadInteger('window','WindowOrg.X',0);
    INI.Free
  end {FormCreate};

  procedure TFormResourceMonitor.FormDestroy(Sender: TObject);
  var INI: TIniFile;
  begin
    INI := TIniFile.Create(ChangeFileExt(ParamStr(0),'.INI'));
    INI.WriteInteger('window','WindowOrg.Y',Top);
    INI.WriteInteger('window','WindowOrg.X',Left);
    INI.Free
  end {FormDestroy};

You saw me using ParamStr(0) to get the base name of the application. This is a convinient and documented way to get the filename (there are other ways, but I prefer this one). Changing the extention of this file to .INI and we have an instant ini-filename next to our executable!
The third parameter to the INI.ReadInteger functions specify the default value if the inifile (or the specified section) does not exist yet. This yields a default position for ResMon at the upper left corner, but as soon as the user moves it to another position and closes the application, the inifile is updated, and this last position will be used for the next startup.

Don't lose your head!
While ResMon now was working out of everybody's way, it still seemed a bit in the way at some times. Can't you make it smaller again, like lose the title bar? they asked me. Well, I could try... One way to eliminate the title bar is to remove the SW_CAPTION style from the Style in the CreateParams procedure:

  procedure TFormResourceMonitor.CreateParams(var Params: TCreateParams);
  { if you remove the SW_CAPTION from the Style, you can elimiate the title bar }
  begin
    inherited CreateParams(Params);
    with Params do Style := Style AND NOT WS_CAPTION
  end {CreateParams};

However, this does not have the desired effect unless you have given the BorderStyle property the value bsNone. With a value of bsDialog or any other value, the caption just remains where it is. Furthermore, you will get the effct at run-time only, and not at design time (so at design time you still see the caption). Personally, I feel that if we use a BorderStyle of bsNone, the form looks a little lost, like the figure below, but you get used to it after a while:

I said move out of my way!
There's one little problem with a caption-less window... You can't seem to move it. In order to move a window, you have to click on the title bar or caption and drag it to another place. Optionally, you can click on the system menu entry Move and use the keyboard to move the window. The ResMon application from last figure does have neither title bar nor system menu, so there is no way to move it, or is there?

Actually, we can cheat by adding a message handler for the WM_NCHITEST message and return HTCAPTION instead of HTCLIENT. That way, Windows will think it's on the caption, when in fact it's on the client area. That way, we can move the window by just clicking anywhere in it and dragging it away. The declaration for the WM_NCHITEST message handler (inside the TFormResourceMonitor) is as follows:

  procedure WMNCHitText(var Msg: TWMNCHitTest); message WM_NCHITTEST;

The implementation is just as easy, just return HTCAPTION if Windows thinks it's HTCLIENT:

  procedure TFormResourceMonitor.WMNCHitText(var Msg: TWMNCHitTest);
  begin
    inherited;
    if Msg.Result = HTCLIENT then Msg.Result := HTCAPTION
  end {WMNCHitText};

The final touch
You may have noticed that the color of the ResMon window is yellow (or at least has changed compared to the previous two figures). The last wish I received was to let everyone give the ResMon window it's own personal favorite color. The value could be read and written in the ini-file again, but how to we change the color? The easiest way would be to respond to the OnClick event of a mous and popup a ColorDialog. Well, close but no cigar. This won't work. The OnClick event seems not to work at all anymore. What happened? We played with the WM_NCHITEST message, that's what happened! While we're letting Windows believe it's on the caption instead of the client area at all times, we cannot get an OnClick event (which is generated by a mouse click on, you guessed it, the client area). So, what can we do? The only solution I found was to respond to a Key event (the OnKeyPress event) and pop the ColorDialog up whenever a key was pressed. This actually involved more steps, as you must first activate the ResMon application by clicking somewhere on its client area, ehum, make that caption.

Source Code
The final source code for ResMon (version 2.0) is as follows:

unit Monform;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls, StdCtrls, INIFiles;

Type
  TFormResourceMonitor = class(TForm)
    Timer1: TTimer;
    Bevel1: TBevel;
    LabelMemAvail: TLabel;
    LabelFreeResources: TLabel;
    X: TLabel;
    ColorDialog1: TColorDialog;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
    procedure CreateParams(var Params: TCreateParams); override;
    procedure WMNCHitText(var Msg: TWMNCHitTest); message WM_NCHITTEST;
  public
    { Public declarations }
  end;

var
  FormResourceMonitor: TFormResourceMonitor;

implementation
uses ToolHelp;
{$R *.DFM}

function GetBaseMem: LongInt;
{ Code for this routine adapted from Matt Pietrek's "Below1mb.c" Oct 93 Microsoft
  Systems Journal, as it appeared in July 94 Microsoft Developer's Network CD
}
var EndOfBlock,EndOfMem,Total: LongInt;
    GE: TGlobalEntry;
    RC: Bool;
begin
  Result := 0;
  EndOfBlock := $7FFFFFFF;
  GE.dwSize := Sizeof(GE);
  RC := GlobalFirst(@GE, Global_All);
  with GE do
  begin
    while RC do
    begin
      dwAddress := dwAddress AND $7FFFFFFF;
      if dwAddress > $100000 then
      begin
        dwAddress := EndOfMem;
        hBlock := $FFFF;
        wType := GT_Sentinel
      end;
      if (wType <> gt_Free) AND (hBlock <> 0) AND
       (((hBlock AND 1) <> 0) OR (wcLock <> 0) OR (wcPageLock <> 0)) then
      begin
        if EndOfBlock < dwAddress then Inc(Result, dwAddress - EndOfBlock);
        if hBlock = $FFFF then Break;
        EndOfBlock := dwAddress + dwBlockSize
      end;
      EndOfMem := dwAddress + dwBlockSize;
      RC := GlobalNext(@GE, Global_All)
    end
  end
end {GetBaseMem};

procedure TFormResourceMonitor.Timer1Timer(Sender: TObject);
var BaseMem: LongInt;
begin
  { free memory }
  BaseMem := GetBaseMem;
  LabelMemAvail.Caption := Format('Mem: %d Kb (Base: %d)',[MemAvail div 1024,BaseMem]);
  { free resources }
  LabelFreeResources.Caption := Format('System: %d%%  User: %d%%  GDI: %d%%',
    [GetFreeSystemResources(gfsr_SystemResources),
     GetFreeSystemResources(gfsr_UserResources),
     GetFreeSystemResources(gfsr_GDIResources)]);
  { show that we're still alive and running... }
  if X.Caption = 'x' then X.Caption := '+'
                     else X.Caption := 'x'
end;

procedure TFormResourceMonitor.FormCreate(Sender: TObject);
var INI: TIniFile;
begin
  INI := TIniFile.Create(ChangeFileExt(ParamStr(0),'.INI'));
  Top := INI.ReadInteger('window','WindowOrg.Y',0);
  Left := INI.ReadInteger('window','WindowOrg.X',0);
  Color := INI.ReadInteger('window','Colour',clTeal);
  INI.Free
end;

procedure TFormResourceMonitor.FormDestroy(Sender: TObject);
var INI: TIniFile;
begin
  INI := TIniFile.Create(ChangeFileExt(ParamStr(0),'.INI'));
  INI.WriteInteger('window','WindowOrg.Y',Top);
  INI.WriteInteger('window','WindowOrg.X',Left);
  INI.WriteInteger('window','Colour',Color);
  INI.Free
end;

procedure TFormResourceMonitor.CreateParams(var Params: TCreateParams);
{ if you remove the SW_CAPTION from the Style, you can elimiate the title bar }
begin
  inherited CreateParams(Params);
  with Params do Style := Style AND NOT WS_CAPTION
end {CreateParams};

procedure TFormResourceMonitor.WMNCHitText(var Msg: TWMNCHitTest);
{ Note that you won't be able to *move* the resulting window. However, if you
  add another message handler for the "WM_NCHITEST" message, you can make the
  window movable by "grabbing" anywhere in its client area... }
begin
  inherited;
  if Msg.Result = HTCLIENT then Msg.Result := HTCAPTION
end {WMNCHitText};

procedure TFormResourceMonitor.FormKeyPress(Sender: TObject; var Key: Char);
begin
  ColorDialog1.Color := Color { start with old color };
  if ColorDialog1.Execute then Color := ColorDialog1.Color
end;

end.

Form Layout
The form layout is as follows (note that you'll still see the title bar at design time, although it will disappear at run time):

object FormResourceMonitor: TFormResourceMonitor
  Left = 221
  Top = 164
  Hint = 'Dr.Bob'#39's Resource Monitor  (Esc = colour)'
  BorderIcons = [biSystemMenu, biMinimize]
  BorderStyle = bsNone
  ClientHeight = 29
  ClientWidth = 206
  Color = clTeal
  Font.Color = clBlack
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = [fsBold]
  FormStyle = fsStayOnTop
  PixelsPerInch = 96
  ShowHint = True
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  OnKeyPress = FormKeyPress
  TextHeight = 13
  object Bevel1: TBevel
    Left = 0
    Top = 0
    Width = 206
    Height = 29
    Align = alClient
    ParentShowHint = False
    ShowHint = False
    Style = bsRaised
  end
  object LabelMemAvail: TLabel
    Left = 2
    Top = 1
    Width = 86
    Height = 13
    Caption = 'LabelMemAvail'
    ParentShowHint = False
    ShowHint = False
  end
  object LabelFreeResources: TLabel
    Left = 2
    Top = 14
    Width = 117
    Height = 13
    Caption = 'LabelFreeResources'
    ParentShowHint = False
    ShowHint = False
  end
  object X: TLabel
    Left = 192
    Top = 1
    Width = 9
    Height = 13
    Caption = 'X'
    ParentShowHint = False
    ShowHint = False
  end
  object Timer1: TTimer
    OnTimer = Timer1Timer
    Left = 144
  end
  object ColorDialog1: TColorDialog
    Left = 120
  end
end

If you have any more wishes for ResMon, don't hesitate to ask. As you've seen by now, with Delphi we're only limited by our imagination (and Windows itself).

You can download the executable version of RESMON (82,750 bytes) to see it work for yourself.


This webpage (c) 2000 by webmaster drs. Robert E. Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.

Creating a Resource only DLL

FAQ1595D.txt Creating a Resource only DLL

Category :Miscellaneous

Platform :All

Product :All 32 bit

Question:

How do I create a Resource only DLL?

Answer:

Create and build an empty DLL project, that contains a resource

link reference to the .res file that contains your resources.

Example:

library ResTest;

uses

SysUtils;

{$R MYRES.RES}

begin

end.

To use your resource only DLL, simply load the dll and the

resources

you wish to use:

Example:

{$IFDEF WIN32}

const BadDllLoad = 0;

{$ELSE}

const BadDllLoad = 32;

{$ENDIF}

procedure TForm1.Button1Click(Sender: TObject);

var

h : THandle;

Icon : THandle;

begin

h := LoadLibrary('RESTEST.DLL');

if h <= BadDllLoad then

ShowMessage('Bad Dll Load')

else begin

Icon := LoadIcon(h, 'ICON_1');

DrawIcon(Form1.Canvas.Handle, 10, 10, Icon);

FreeLibrary(h);

end;

end;

Creating Resource Files

This is helpful if you do not want to have a wave file in the directory of the program you distribute. One problem with inserting a wave file into your .exe is that it makes your programs .exe larger, so it is a good idea if your program uses small wave files or maybe one or two large wave files.

The Delphi example below should work on Delphi version above Delphi 2.

First open an editor like Notepad.exe, for this example we will use a wave file called Laser.wav

and this is the wave file we will insert into our programs .exe

Type these words into the text file:

TheLaser Wave "Laser.wav"

Then save the text file as:

mywave.rc

We will use BRCC32.exe that comes with Delphi, it should be in Delphi's Bin directory to compile the file.

In order to use BRCC32.exe you have to use a DOS window, using DOS is not hard at all.

Whenever you use DOS all you have to do, to get back to Windows (Your desktop) is type the word exit, then press the Enter key.

You should have your wave files and your .rc file in the same folder/directory.

So we will type this sentance in the DOS window:

(We are in the c:\Delphi 3\sounds directory)

c:\Delphi 3\sounds>BRCC32 MYWAVE.RC

If this does not work or you get an error, you may not have Delphi in your computers path.

A way around this is for you to copy the BRCC32.exe and RW32CORE.DLL into your folder/directory that you are using, then try again.

(The above files, BRCC32.exe and RW32CORE.DLL, may be different, for different versions of Delphi)

Use Explorer or another File Manager and have a look in your directory, you should find that you have an extra file called mywave.RES.

Start Delphi, then start a new project (Application), add MMSystem in the Uses clause like this:

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, MMSystem;

You cannot play sounds without the MMSystem unit, in other words PlaySound will not work without the MMSystem unit.

Look for {$R *.DFM} in Unit1 and add {$R MYWAVE.RES} next to it.

Next drop a Button onto your Form, double-click on the Button and add this code:

PlaySound(PChar('TheLaser'), hInstance, snd_Sync or snd_Resource);

Note we use 'TheLaser' to call the wave file.

Run the program and then click on the Button, you should now here the Laser.wav file.

Your complete unit should look like this:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, MMSystem;

type

TForm1 = class(TForm)

Button1: TButton;

procedure Button1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var Form1: TForm1;

implementation

{$R *.DFM} {$R MYWAVE.RES}

procedure TForm1.Button1Click(Sender: TObject);

begin

PlaySound(PChar('TheLaser'), hInstance, snd_Sync or snd_Resource);

Loading Bitmaps and Cursors from RES files

Bitmaps and cursors can be stored in a resource (RES) files and linked into your application's EXE file. RES files can be created

with Delphi's Image Editor or Borland's Resource Workshop that comes with the Delphi RAD Pack. Bitmaps and cursors stored in RES files

(after being bound into an EXE or DLL) can be retrieved by using the API functions LoadBitmap and LoadCursor, respectively.

Loading Bitmaps

The LoadBitmap API call is defined as follows:

function LoadBitmap(Instance: THandle;

BitmapName: PChar): HBitmap;

The first parameter is the instance handle of the module (EXE or DLL) that contains the RES file you wish to get a resource from. Delphi

provides the instance handle of the EXE running in the global variable called Hinstance. For this example it is assumed that the module that

you are trying to load the bitmap from is your application. However, the module could be another EXE or DLL file. The following example

loads a bitmap called BITMAP_1 from a RES file linked into the application's EXE:

procedure TForm1.Button1Click(Sender: TObject);

var

Bmp: TBitmap;

begin

Bmp := TBitmap.Create;

Bmp.Handle := LoadBitmap(HInstance,'BITMAP_1');

Canvas.Draw(0, 0, Bmp);

Bmp.Free;

end;

There is one drawback to using the LoadBitmap API call though LoadBitmap is a Windows 3.0 API call and loads in bitmaps only as

DDBs (Device Dependent Bitmaps). This can cause color palette problems when retrieving DIBs (Device Independent Bitmaps) from

RES files. The code listed below can be used to retrieve DIBs from RES files. This code loads the bitmap as a generic resource, puts

it into a stream, and then does a LoadFromStream call which causes Delphi to realize the color palette automatically.

 procedure TForm1.Button1Click(Sender: TObject);

const

BM = $4D42; {Bitmap type identifier}

var

Bmp: TBitmap;

BMF: TBitmapFileHeader;

HResInfo: THandle;

MemHandle: THandle;

Stream: TMemoryStream;

ResPtr: PByte;

ResSize: Longint;

begin

BMF.bfType := BM;

{Find, Load, and Lock the Resource containing BITMAP_1}

HResInfo := FindResource(HInstance, 'BITMAP_1', RT_Bitmap);

MemHandle := LoadResource(HInstance, HResInfo);

ResPtr := LockResource(MemHandle);

 {Create a Memory stream, set its size, write out the bitmap header, and finally write out the Bitmap }

Stream := TMemoryStream.Create;

ResSize := SizeofResource(HInstance, HResInfo);

Stream.SetSize(ResSize + SizeOf(BMF));

Stream.Write(BMF, SizeOf(BMF));

Stream.Write(ResPtr^, ResSize);

{Free the resource and reset the stream to offset 0}

FreeResource(MemHandle);

Stream.Seek(0, 0);

{Create the TBitmap and load the image from the MemoryStream}

Bmp := TBitmap.Create;

Bmp.LoadFromStream(Stream);

Canvas.Draw(0, 0, Bmp);

Bmp.Free;

Stream.Free;

end;

Loading Cursors

The LoadCursor API call is defined as follows:

function LoadCursor(Instance: THandle;

CursorName: PChar): HCursor;

The first parameter is the Instance variable of the module that contains the RES file. As above, this example assumes that the

module that you are trying to load the cursor from is your application. The second parameter is the name of the cursor.

Under the interface section declare:

const

crMyCursor = 5; {Other units can use this constant}

Next, add the following two lines of code to the form's OnCreate event as follows:

procedure TForm1.FormCreate(Sender: TObject);

begin

Screen.Cursors[crMyCursor] := LoadCursor(HInstance, 'CURSOR_1');

Cursor := crMyCursor;

end;

or you may want to change one of the standard Delphi cursors as follows (the Cursor constants can be found in the On-line Help

under Cursors Property):

procedure TForm1.FormCreate(Sender: TObject);

begin

{This example changes the SQL Hourglass cursor}

Screen.Cursors[crSQLWait] := LoadCursor(HInstance, 'CURSOR_1');

end;

Note: Normally it is necessary to delete any cursor resources with the DeleteCursor, however, in Delphi this is not necessary because

Delphi will delete the all cursors in the Cursors array.

Hosted by www.Geocities.ws

1