Board index » delphi » Resource File Headaches

Resource File Headaches

From: "Brent Boswell" <br...@noneofyourbusiness.net>
Subject: Resource File headache
Date: Thursday, March 07, 2002 3:05 PM

Hi guys,

As kind of a Delphi newbie, I have stumbled across a rather poorly
documented feature in some code that I am trying to maintain.  This centers
around a resource file, whose actual contents are a binary for a flash
loader for an embedded device.  The code originally worked...however, when I
try it, it blows up every time (at run time, not compile time).

I am using Delphi 5.  (This project was originally done in Delphi 4, if that
makes a difference...)

Here, the resource file is loaded:

unit MC68332;

interface

uses Windows, SysUtils, Classes, Forms, Async32, SerialDeviceImpl,
TerminalGroupXControl_TLB, Dialogs;

function ProgramMC68332(ImageStream: TStream; Device: TSerialDevice;
UserMode: Boolean): erErrorCode;

implementation

uses ProgressF;

// Link in the compilied FORTH assembly code module as a 'BINARY'
resource...
{$R 'E:\Firm\Term\T5\Boot\fs512K.res'}

<SNIP>

And here is where it is used in the code:

function ProgramMC68332(ImageStream: TStream; Device: TSerialDevice;
UserMode: Boolean): erErrorCode;
var
  LastTick: Cardinal;
  FinishedKey: Boolean;
begin
  Result := erInvalidPointer;
  { Load the FLASH upgrader from the control's resources }
  ControlModule := GetModuleHandle('TerminalGroupXControl.ocx');
  UpgraderResource := FindResource(ControlModule, '#201', 'BINARY');
  if UpgraderResource = 0 then
    MessageDlg('An error occured while programming the new firmware.'+#13+
     'The ActiveX control was unable to locate the terminals binary loader
file!',
     mtError, [mbOK], 0);
    Exit;
<SNIP>

I added the dialog box before the exit myself...and when the code blows up,
my dialog box gets thrown, so we know that the FindResource function is the
complaining party.

A hex editor reveals the following contents of the resource file header (for
those souls brave enough to debug a resource file: documentation on .res
file format available at multiple locations online):

 0000:0000  00 00 00 00 20 00 00 00 FF FF 00 00 FF FF 00 00   ....
...??..??..
 0000:0010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
................
 0000:0020  62 02 00 00 2C 00 00 00 42 00 49 00 4E 00 41 00
b...,...B.I.N.A.
 0000:0030  52 00 59 00 00 00 FF FF C9 00 00 00 00 00 00
R.Y...??......

Any ideas?

Thanks, I'm at whit's end here...

Brent Boswell
Leppo Instruments, Inc.

To e-mail: replace nowhere with leppo and net with com

 

Re:Resource File Headaches


In article <u8fuu04vagm...@corp.supernews.com>, "Brent Boswell"

Quote
<br...@noneofyourbusiness.net> writes:
>As kind of a Delphi newbie, I have stumbled across a rather poorly
>documented feature in some code that I am trying to maintain.  This centers
>around a resource file, whose actual contents are a binary for a flash
>loader for an embedded device.  The code originally worked...however, when I
>try it, it blows up every time (at run time, not compile time).

I think your problem is the naming of the resource in the .res file an the
resource type in FindResource().

AFAIUI the FindResource type parameter is a constant made with MakeIntResource
from a value defining your type (not the resource type name). This is placed as
a word (usual Intel byte swap) at the 2B/2C byte positions. Your value is $49.

I would suggest that you have two options ...

1 Recompile the .res file using a standard string in the .rc file of 'RCDATA'.
Then in FindResource use the standard corresponding resource type of RT_RCDATA.

2 Create your own resource type identifier with ...

  RT_MYBINARY := MakeIntResource($49);

... and use that in FindResource.

With either of these routes a _far_ easier way is to use a TResourceStream. As
an example I have done for someone else of putting data in a memo ...

// Contents of Test.rc = = = = = = =
// TESTRES  RCDATA  C:\AutoExec.bat
// = = = = = = = = = = = = = = = = =
// Make Test.res with DOS command in \Borland\Delphi n\Bin ... = = =
// BRCC32 Test /v
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
{$R Test.res}   // this puts the resource in the .exe at compile-time

procedure TForm1.LoadBtnClick(Sender: TObject);
var
  RS : TResourceStream;
begin
  RS := TResourceStream.Create(hInstance, 'TESTRES', RT_RCDATA);
                             { Note that RT_RCDATA is a constant which
                               recognises a string of 'RCDATA' in the resource}
  Memo1.Clear;
  Memo1.Font.Name := 'MS Sans Serif';
  Memo1.Lines.LoadFromStream(RS);
  RS.Free;
end;

procedure TForm1.LoadHexBtnClick(Sender: TObject);
var
  RS : TResourceStream;
  HexStr : string;
  i : integer;
  B : byte;
const
  BytesPerLine = 16;
begin
  RS := TResourceStream.Create(hInstance, 'TESTRES', RT_RCDATA);
  Memo1.Clear;
  Memo1.Font.Name := 'Courier New';
  HexStr := '';
  for i := 0 to RS.Size do begin
    RS.Read(B, SizeOf(byte));
    {format byte string}
    if Length(HexStr) = 0 then   // first byte, no leading space
      HexStr := Format('%2.2x', [B])
    else
      HexStr := Format('%s %2.2x', [HexStr, B]);
    if (((i + 1) mod BytesPerLine ) = 0) then begin
      Memo1.Lines.Add(HexStr);
      HexStr := '';
    end;
  end;
  if Length(HexStr) > 0 then
    Memo1.Lines.Add(HexStr);  // final part-line
  RS.Free;
end;

Alan Lloyd
alangll...@aol.com

Re:Resource File Headaches


Quote
"AlanGLLoyd" <alangll...@aol.com> wrote in message

news:20020308031619.10797.00000036@mb-cp.aol.com...

Quote
> I think your problem is the naming of the resource in the .res file an the
> resource type in FindResource().

Perhaps...good lead!  I'm checking into that.

Quote
> AFAIUI the FindResource type parameter is a constant made with
MakeIntResource
> from a value defining your type (not the resource type name). This is
placed as
> a word (usual Intel byte swap) at the 2B/2C byte positions. Your value is
$49.

Not necessarily.  According to two different articles that I downloaded (on
.res file formats, do a google search on ".res file format") , if and only
if the bytes at positions 29/2A contain the word "$FFFF", then the word at
2B/2C contains a code for a numbered resource type.  Otherwise, the data
starting at 29/2A is considered to be unicode text, with a unicode null ("00
00") at the end.

Quote
> I would suggest that you have two options ...

> 1 Recompile the .res file using a standard string in the .rc file of
'RCDATA'.
> Then in FindResource use the standard corresponding resource type of
RT_RCDATA.

> 2 Create your own resource type identifier with ...

>   RT_MYBINARY := MakeIntResource($49);

> ... and use that in FindResource.

> With either of these routes a _far_ easier way is to use a
TResourceStream. As
> an example I have done for someone else of putting data in a memo ...

> // Contents of Test.rc = = = = = = =
> // TESTRES  RCDATA  C:\AutoExec.bat
> // = = = = = = = = = = = = = = = = =
> // Make Test.res with DOS command in \Borland\Delphi n\Bin ... = = =
> // BRCC32 Test /v
> // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
> {$R Test.res}   // this puts the resource in the .exe at compile-time

> procedure TForm1.LoadBtnClick(Sender: TObject);
> var
>   RS : TResourceStream;
> begin
>   RS := TResourceStream.Create(hInstance, 'TESTRES', RT_RCDATA);
>                              { Note that RT_RCDATA is a constant which
>                                recognises a string of 'RCDATA' in the
resource}
>   Memo1.Clear;
>   Memo1.Font.Name := 'MS Sans Serif';
>   Memo1.Lines.LoadFromStream(RS);
>   RS.Free;
> end;

> procedure TForm1.LoadHexBtnClick(Sender: TObject);
> var
>   RS : TResourceStream;
>   HexStr : string;
>   i : integer;
>   B : byte;
> const
>   BytesPerLine = 16;
> begin
>   RS := TResourceStream.Create(hInstance, 'TESTRES', RT_RCDATA);
>   Memo1.Clear;
>   Memo1.Font.Name := 'Courier New';
>   HexStr := '';
>   for i := 0 to RS.Size do begin
>     RS.Read(B, SizeOf(byte));
>     {format byte string}
>     if Length(HexStr) = 0 then   // first byte, no leading space
>       HexStr := Format('%2.2x', [B])
>     else
>       HexStr := Format('%s %2.2x', [HexStr, B]);
>     if (((i + 1) mod BytesPerLine ) = 0) then begin
>       Memo1.Lines.Add(HexStr);
>       HexStr := '';
>     end;
>   end;
>   if Length(HexStr) > 0 then
>     Memo1.Lines.Add(HexStr);  // final part-line
>   RS.Free;
> end;

I will investigate using your method, too.  Sounds promising!  I'll let you
know what I did to fix it when I get something working here...many thanks
for the response.

By the way, I found two very good resource file compilers/editors at
http://www.torry.net/tools_resource.htm, along with a bunch of other Delphi
tools that looked very useful.

- Show quoted text -

Quote
> Alan Lloyd
> alangll...@aol.com

Re:Resource File Headaches


Okay,

I've got a working solution here.

The problem had nothing to do with the naming of the resource file...that
much works correctly (i.e. I can leave it as a "BINARY" resource).

I converted to accessing the resource using the TResourceStream class and
methods (as per your suggestion).  As a matter of fact, I was able to very
easily gain access to the raw byte stream of the resouce and know it's size
using the .Size and .Memory properties :-)

You are right, it is much easier :-)  Here is the new line of code which
loads the resource...

function ProgramMC68332(ImageStream: TStream; Device: TSerialDevice;
UserMode: Boolean): erErrorCode;
var
  LastTick: Cardinal;
  FinishedKey: Boolean;
  RS: TResourceStream;
  MemSize: LongInt;

begin
  Result := erInvalidPointer;
  { Load the FLASH upgrader from the control's resources }
  RS := TResourceStream.Create(hInstance,'#201','BINARY' );

And the code where the resource now gets downloaded (much simpler now!):

  { Load the FLASH field Upgrader program in the terminal }
  MemSize:=RS.Size;
  Device.FreeAllMemory;    // clean out all RAM resources for firmware
upgrade.
  Addr := Device.BlockCmd(MemSize, 'BLKRAM', RS.Memory^);

Thanks for the help!

Brent Boswell

Leppo Instruments, Inc.

Quote
"AlanGLLoyd" <alangll...@aol.com> wrote in message

news:20020308031619.10797.00000036@mb-cp.aol.com...
Quote
> In article <u8fuu04vagm...@corp.supernews.com>, "Brent Boswell"
> <br...@noneofyourbusiness.net> writes:

> >As kind of a Delphi newbie, I have stumbled across a rather poorly
> >documented feature in some code that I am trying to maintain.  This
centers
> >around a resource file, whose actual contents are a binary for a flash
> >loader for an embedded device.  The code originally worked...however,
when I
> >try it, it blows up every time (at run time, not compile time).

> I think your problem is the naming of the resource in the .res file an the
> resource type in FindResource().

> AFAIUI the FindResource type parameter is a constant made with
MakeIntResource
> from a value defining your type (not the resource type name). This is
placed as
> a word (usual Intel byte swap) at the 2B/2C byte positions. Your value is
$49.

> I would suggest that you have two options ...

> 1 Recompile the .res file using a standard string in the .rc file of
'RCDATA'.
> Then in FindResource use the standard corresponding resource type of
RT_RCDATA.

> 2 Create your own resource type identifier with ...

>   RT_MYBINARY := MakeIntResource($49);

> ... and use that in FindResource.

> With either of these routes a _far_ easier way is to use a
TResourceStream. As
> an example I have done for someone else of putting data in a memo ...

> // Contents of Test.rc = = = = = = =
> // TESTRES  RCDATA  C:\AutoExec.bat
> // = = = = = = = = = = = = = = = = =
> // Make Test.res with DOS command in \Borland\Delphi n\Bin ... = = =
> // BRCC32 Test /v
> // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
> {$R Test.res}   // this puts the resource in the .exe at compile-time

> procedure TForm1.LoadBtnClick(Sender: TObject);
> var
>   RS : TResourceStream;
> begin
>   RS := TResourceStream.Create(hInstance, 'TESTRES', RT_RCDATA);
>                              { Note that RT_RCDATA is a constant which
>                                recognises a string of 'RCDATA' in the
resource}
>   Memo1.Clear;
>   Memo1.Font.Name := 'MS Sans Serif';
>   Memo1.Lines.LoadFromStream(RS);
>   RS.Free;
> end;

> procedure TForm1.LoadHexBtnClick(Sender: TObject);
> var
>   RS : TResourceStream;
>   HexStr : string;
>   i : integer;
>   B : byte;
> const
>   BytesPerLine = 16;
> begin
>   RS := TResourceStream.Create(hInstance, 'TESTRES', RT_RCDATA);
>   Memo1.Clear;
>   Memo1.Font.Name := 'Courier New';
>   HexStr := '';
>   for i := 0 to RS.Size do begin
>     RS.Read(B, SizeOf(byte));
>     {format byte string}
>     if Length(HexStr) = 0 then   // first byte, no leading space
>       HexStr := Format('%2.2x', [B])
>     else
>       HexStr := Format('%s %2.2x', [HexStr, B]);
>     if (((i + 1) mod BytesPerLine ) = 0) then begin
>       Memo1.Lines.Add(HexStr);
>       HexStr := '';
>     end;
>   end;
>   if Length(HexStr) > 0 then
>     Memo1.Lines.Add(HexStr);  // final part-line
>   RS.Free;
> end;

> Alan Lloyd
> alangll...@aol.com

Other Threads