2
votes

I've written a Delphi package where two of components use the same PNG resource which I placed in GraphContour.res file using following script:

compile_res.bat:

@brcc32 GraphContour.rc

GraphContour.rc:

GRAPH_CONTOUR RCDATA GraphContour.png

If both units of components include {$R GraphContour.res} directive, components work, but I get a compiler warning:

[dcc32 Hint] H2161 Warning: Duplicate resource: Type 10 (RCDATA), ID GRAPH_CONTOUR; File C:\Borland\Components\MyControls\GraphContour.res resource kept; file C:\Borland\Components\MyControls\GraphContour.res resource discarded.

If I remove {$R GraphContour.res} directive from units placing it into DPK file:

{$R *.res}
{$R GraphContour.res}

warnings go away, I can compile the package, the resource is being shown at design time, but at run time I get an error:

Project ComponentTest.exe raised exception class EResNotFound with message 'Resource GRAPH_CONTOUR not found'.

A bit of code from one of these two controls:

procedure TMyDisplay.CreateWnd();
var png: TPngImage;
begin
  inherited;
  //......................
  png := TPngImage.Create;
  try
    png.LoadFromResourceName(HInstance, 'GRAPH_CONTOUR'); //Error is here
    _knob.Center.Picture.Graphic := png;
  finally
    png.Free();
  end;
  //......................
end;

I looked into BPL file with binary viewer and found that GRAPH_CONTOUR string name is there.

I tried to use FindClassHInstance instead of HInstance. It didn't help.

png.LoadFromResourceName(FindClassHInstance(Self.ClassType), 'GRAPH_CONTOUR');

How to include resource to BPL properly?

Update

Here is test application I used to check for resource availability:

program Loadres;

uses Winapi.Windows;

procedure PrintLastError();
var
  cchMsg, code: Cardinal;
  buf: array[0..512] of WideChar;
begin
  code := GetLastError();
  cchMsg := FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS,
    nil, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), @buf, SizeOf(buf), nil);
  MessageBoxW(0, @buf, 'Error', MB_ICONERROR);
end;

var
  hm, hResInfo, hResData, hPngFile: NativeUInt;
  rp: Pointer;
  wb, rs: Cardinal;

begin
  hm := LoadLibrary('C:\Documents and Settings\All Users\Documents\Embarcadero\Studio\17.0\Bpl\MyControls.bpl');
  if hm = 0 then begin PrintLastError(); Exit; end;
  try
    hResInfo := FindResource(hm, 'GRAPH_CONTOUR', RT_RCDATA);
    if hResInfo = 0 then begin PrintLastError(); Exit; end;
    hResData := LoadResource(hm, hResInfo);
    if hResData = 0 then begin PrintLastError(); Exit; end;
    rs := SizeofResource(hm, hResInfo);
    rp := LockResource(hResData);
    try
      hPngFile := CreateFile('TheContour.png', GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, 0);
      try
        WriteFile(hPngFile, rp^, rs, wb, nil);
      finally
        CloseHandle(hPngFile);
      end;
    finally
      UnlockResource(hResData);
    end;
  finally
    FreeLibrary(hm);
  end;
end.

It finds resource, reports the right resource size, and writes resource to file. The file matches the original one.

1
FindClassHInstance works fine. We don't see your code that uses it. We also don't know where TMyDisplay resides. In the package with the two controls? In which case HInstance would be correct.David Heffernan
So HInstance is correctDavid Heffernan
No, it doesn't contain garbage. I used a pointer wrongly in WriteFile. Now I have updated the question.Paul
So I guess that your next debugging task is to understand why your code produces EResNotFound. One possibility is that you aren't using runtime packages. Perhaps you are compiling the source code for your component directly into your executable.David Heffernan
You don't need any articles. Just create a unit which holds no code, but links the resource. Use that unit from your two component source files. Include that unit in the package and your executable. OK?David Heffernan

1 Answers

3
votes

Based on the debugging that you did, and described in comments, the problem is that your executable program is not using the runtime package. Instead it compiles the source files into the executable directly. Since they no longer link the resource, it is not present in the executable.

Some options:

  1. Use runtime packages. This might require you to split your current package into a designtime and runtime package.
  2. Put the resource into a separate unit (doesn't need to have any code, just the resources that you need to be linked). Include that unit in your designtime package and your executable. If you arrange that your component source files use that resource unit, then it will be forced to be included in any module that needs the resources.