1

I'm using a RC file with images and strings , like this :

splash RCDATA splash.png
ilogo RCDATA ilogo.png
site RCDATA { "https://www.example.com" }

I then compile this file with BRCC32 and generate a RES file.

I know how to extract the images, using a code like this :

Var
  img: TWICImage;
Begin
  Try
    img := TWICImage.create;
    img.LoadFromResourceName(HInstance, resName);
    im.Picture.Graphic := img;
    Result := true;
  Finally
    img.free;
  End;

But i don't know how to extract the string RCDATA, for instance the 'site' RCDATA ; how can i do this ?

1 Answer 1

5

This depends on the encoding of your RCDATA. The low-level approach is to read the bytes and then interpret them explicitly in the encoding you know is right:

var RS := TResourceStream.Create(HInstance, 'greeting', RT_RCDATA);
try
  var LData := TBytes(nil);
  SetLength(LData, RS.Size);
  RS.ReadData(LData, RS.Size);
  var LStr := TEncoding.ASCII.GetString(LData);
  ShowMessage(LStr);
finally
  RS.Free;
end;

A marginally simpler approach is to use a TStringList, for example:

var List := TStringList.Create;
try
  var RS := TResourceStream.Create(HInstance, 'greeting', RT_RCDATA);
  try
    List.LoadFromStream(RS, TEncoding.ASCII);
    ShowMessage(List.Text);
  finally
    RS.Free;
  end;
finally
  List.Free;
end;

And a very important off-topic remark:

Your code

Var
  img: TWICImage;
Begin
  Try
    img := TWICImage.create;
    img.LoadFromResourceName(HInstance, resName);
    im.Picture.Graphic := img;
    Result := true;
  Finally
    img.free;
  End;

has a serious bug that can cause memory corruption, at least in some contexts. Specifically, if this is the code of a procedure or function, then img is a local variable of a non-managed type, and so it is not initialized. Hence, initially, img is a "random" pointer. Its value is the native-sized integer that happens to be there in your computer's RAM.

So if the TWICImage.Create constructor raises an exception (which is very much a possibility), then you'll do img.Free on a random pointer img. Then anything can happen. If you are lucky, you get an access violation almost immediately.

Always write

var LFrog := TFrog.Create;
try
  { use LFrog }
finally
  LFrog.Free;
end

and never write

try
  LFrog := TFrog.Create;
  { use LFrog }
finally
  LFrog.Free; // wrong!
end
Sign up to request clarification or add additional context in comments.

4 Comments

In your 1st example, the TBytes is redundant since the bytes are already in memory. You can use the TEncoding's GetCharCount() + GetChars() methods to decode the original bytes directly into the target string, eg: var LData := PByte(RS.Memory); var LStr: string; var LEnc := TEncoding.ASCII; SetLength(LStr, LEnc.GetCharCount(LData, RS.Size)); LEnc.GetChars(LData, RS.Size, PChar(LStr), Length(LStr));
@RemyLebeau: I agree the dynamic array is redundant. What I'd really want is something like TEncoding.ASCII.GetString(RS.Memory, RS.Size);. I have a faint recollection of there being a feature request about such a GetString overload.
Indeed. I just realized my suggestion won't work because the PByte overloads are strict protected! The public overloads only accept TBytes or array of Byte, and you can't pass a PByte to either one (actually, you can, if you use a dirty type-casting hack, but that's not any better). Oh well...
var LFrog := TFrog.Create; ignores the begin ... end logic of the original code, declaring variables non-inline. You should add another "always do" example that doesn't make use of inline variables to make it more clear what needs to be done (and what inline variable declarations do automatically (in case of exceptions)).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.