I have created a resource file for a Delphi 2007 application. The resource files contains 10 Bitmap entries. I was wondering if there was a way to load all of the bitmaps into an Imagelist by recursively going through the resource file or do I have to pull them out one at a time.
Thanks in advance.
To add all RT_BITMAP resource type images from the current module to an image list I would use this:
uses
CommCtrl;
function EnumResNameProc(hModule: HMODULE; lpszType: LPCTSTR; lpszName: LPTSTR;
lParam: LONG_PTR): BOOL; stdcall;
var
BitmapHandle: HBITMAP;
begin
Result := True;
BitmapHandle := LoadBitmap(HInstance, lpszName);
if (BitmapHandle <> 0) then
begin
ImageList_Add(HIMAGELIST(lParam), BitmapHandle, 0);
DeleteObject(BitmapHandle);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumResourceNames(HInstance, RT_BITMAP, #EnumResNameProc,
LONG_PTR(ImageList1.Handle));
end;
I'm guessing that with "recursively going through the resource file" you want to ask is it possible to load the resources without knowing their name. For that there is class of API functions which allow you to enumerate resources in given module. See the "Resource Overviews, Enumerating Resources" topic for more info on that.
However, since you embedd the bitmaps into the exe yourself it is much easier to give them names which allow easy iteration, ie, in RC file:
img1 BITMAP foo.bmp
img2 BITMAP bar.bmp
Here name "pattern" is img + number. Now it is easy to load the images in a loop:
var x: Integer;
ResName: string;
begin
x := 1;
ResName := 'img1';
while(FindResource(hInstance, PChar(ResName), RT_BITMAP) <> 0)do begin
// load the resource and do something with it
...
// name for the next resource
Inc(x);
ResName := 'img' + IntToStr(x);
end;
Related
I have a DLL that reads input from a file and writes output to another file. Instead I want to read input from an XML "object" (request) and deliver a response.
Here is the call:
unit gen1;
interface
uses
SysUtils;
function genber(Zin: PChar): Integer; export; stdcall;
implementation
uses
gen2;
function genber(Zin: PChar): Integer;
begin
Result := -1;
try
IBIS(ZIn);
Result := 1;
finally
end;
end;
end.
And in gen2 I use this:
AssignFile(ZINXML, ZIN);
AssignFile(ZUTXML, 'UT_' + Time + '.XML');
What shall I use instead of AssignFile for reading a XML file?
TL;DR:
Use LoadXMLDocument instead of AssignFile for working with XML files.
Detailed answer:
Example unit:
unit gen2;
interface
uses
XMLDOC, XMLIntf;
procedure IBIS(const Filename: string);
implementation
procedure IBIS(const Filename: string);
var
Doc: IXMLDocument;
data,
Node: IXMLNode;
begin
if not FileExists(Filename) then
exit;
Doc := LoadXMLDocument(Filename);
data := Doc.DocumentElement;
// look for a specific node
Node := data.ChildNodes.FindNode('Nodename');
// or loop over all child nodes
for i := 0 to Data.ChildNodes.Count - 1 do
begin
Node := Data.ChildNodes[i];
// read and process child nodes here...
end;
// don't free Doc, data, Node... these are interfaces!
end;
end.
Sources:
Delphi load XML
How to read data from xml file and display it over the text box in delphi language
Delphi - TXMLDocument created at run-time generates AV, with component on the form is working
http://docwiki.embarcadero.com/Libraries/en/Xml.XMLDoc.LoadXMLDocument
I have embedded several resources into the executable, for instance language (text) files.
Below you can see the contents of Languages.rc file:
Language_English RCDATA Languages\English.ini
Language_German RCDATA Languages\German.ini
Language_Czech RCDATA Languages\Czech.ini
I found this answer, which definitely helps, however I have rather hard time implementing it.
Suppose you want to get the list of those resources as a EOL-delimited string, then the first step would be defining EnumRCDataProc function:
function EnumRCDataProc(hModule: HMODULE; lpszType, lpszName: PChar; lParam: NativeInt): BOOL; stdcall;
begin
TStrings(lParam).Add(lpszName);
Result := True;
end;
Once we have that done, we can get to work:
function EnumerateRCDataResourceNames: string;
var
ExecutableHandle: HMODULE;
ResourcesList: TStringList;
begin
ExecutableHandle := LoadLibraryEx(PChar(Application.ExeName), 0, LOAD_LIBRARY_AS_DATAFILE);
try
ResourcesList := TStringList.Create;
try
EnumResourceNames(ExecutableHandle, RT_RCDATA, #EnumRCDataProc, NativeInt(ResourcesList));
Result := ResourcesList.Text;
finally
ResourcesList.Free;
end;
finally
FreeLibrary(ExecutableHandle);
end;
end;
Remarks:
As is in the original answer (see question), it is not possible to use LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE or LOAD_LIBRARY_AS_IMAGE_RESOURCE as these types are no longer defined in Delphi XE6, at least AFAIK.
You can, however, define those constants, according to MSDN:
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = $00000040
LOAD_LIBRARY_AS_IMAGE_RESOURCE = $00000020
I used the Resource DLL Wizard in Delphi 2010 to generate resource only dll's for my program. When I look at them using Notepad++ it seems they are using ANSI encoding. Is there some setting I missed? It doesn't seem like a unicode program should store it's resources in ANSI especially for Asian languages.
I was looking specifically at the TABOUTBOX RT_RCDATA record. I tried to load it using the following code,
procedure LoadFromResFile(const FileName: string);
var
LibHandle: THandle;
ResourceLocation: HRSRC;
ResourceSize: dword;
ResourceHandle: THandle;
ResourcePointer: pointer;
ResStr: string;
begin
LibHandle := LoadLibraryEx(PWideChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE or LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if LibHandle > 0 then
begin
ResourceLocation := FindResource(LibHandle, 'TABOUTBOX', RT_RCDATA);
ResourceSize := SizeofResource(LibHandle, ResourceLocation);
ResourceHandle := LoadResource(LibHandle, ResourceLocation);
ResourcePointer := LockResource(ResourceHandle);
if ResourcePointer <> nil then
begin
SetLength(ResStr, ResourceSize);
CopyMemory(#ResStr[1], ResourcePointer, ResourceSize);
FreeResource(ResourceHandle);
end;
FreeLibrary(LibHandle);
end else
begin
ResStr := SysErrorMessage(GetLastError);
ShowMessage(ResStr);
end;
I got garbage, but when I changed the type of ResStr to AnsiString, it showed up correctly. Opening the file in Notepad++ I can see that the dialog resources appear to be ansi, including the label captions.
The Resource DLL wizard creates RCDATA resources for localized DFMs. The RCDATA resource named TABOUTBOX is a binary DFM resource. String values stored within a DFM (component names, captions, etc) are encoded using UTF8 in modern Delphi versions, including 2010. But the DFM data itself is binary in nature, it represents the complete structure of serialized components. It is not itself Unicode data, so you can't load it as-is into a UnicodeString (it "works" when you change ResStr to an AnsiString, but only because of its 8bit nature). DFM resources are meant for TForm/TDataModule/TFrame-derived classes (in this case, TAboutBox) to load and de-serialize at runtime.
If you want to view a DFM resource as human-readible text, you have to use the ObjectBinaryToText() or ObjectResourceToText() function to decode it. For example:
var
LibHandle: THandle;
ResStrm: TResourceStream;
StrStrm: TStringStream;
ResStr: string;
begin
LibHandle := LoadLibraryEx(PChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE or LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if LibHandle > 0 then
begin
try
ResStrm := TResourceStream.Create(LibHandle, 'TABOUTBOX', RT_RCDATA);
try
StrStrm := TStringStream.Create;
try
ObjectBinaryToText(ResStrm, StrStrm);
StrStrm.Position := 0;
ResStr := StrmStrm.DataString;
finally
StrStrm.Free;
end;
finally
ResStrm.Free;
end;
finally
FreeLibrary(LibHandle);
end;
end else
begin
ResStr := SysErrorMessage(GetLastError);
end;
ShowMessage(ResStr);
end;
I currently can only playback my background sound from having my wave file next to my compiled exe. But I actually want to have a single static executable with the wave file inside. Is this possible in Delphi XE2?
This is my code:
SndPlaySound('.\Raw.wav', SND_ASYNC or SND_LOOP);
#This will play the Raw.wav that is next to my program.
You can add the SND_MEMORY flag, and pass a TResourceStream.Memory pointer as the first parameter.
First, use XE2's Project->Resources and Images menu item to add a new resource. Give it the path and filename of your .wav file, a resource type of RC_DATA (it's not in the drop down list, but you can manually type it in), and a resource name you can use at runtime to refer to it. (In my example, I'm using C:\Microsoft Office\Office12\MEDIA\APPLAUSE.WAV, and giving it a resource name of APPLAUSE.)
procedure TForm2.Button1Click(Sender: TObject);
var
Res: TResourceStream;
begin
Res := TResourceStream.Create(HInstance, 'APPLAUSE', 'RC_DATA');
try
Res.Position := 0;
SndPlaySound(Res.Memory, SND_MEMORY or SND_ASYNC or SND_LOOP);
finally
Res.Free;
end;
end;
If you use PlaySound() instead of sndPlaySound(), you can utilize the SND_RESOURCE flag to play the wave sound directly from its resource without having to load it into memory first.
type "WAVE" as Resource type when importing the wav file in the Resource Editor (Delphi 10, Project, Resources and Images)
and simply use
PlaySound(resourceIndentifierName, 0, SND_RESOURCE or SND_ASYNC);
P.S. uppercase no longer required
Just tested and it works on mine:
var
hFind, hRes: THandle;
Song : PChar;
begin
hFind := FindResource(HInstance, 'BATTERY', 'WAV');
if (hFind <> 0) then
begin
hRes := LoadResource(HInstance, hFind);
if (hRes <> 0) then
begin
Song := LockResource(hRes);
if Assigned(Song) then
begin
SndPlaySound(Song, snd_ASync or snd_Memory);
end;
UnlockResource(hRes);
end;
FreeResource(hFind);
end;
I'm trying embed an Excel file into my Delphi 5 application, so I can avoid my users just deleting the file accidentally.
Using the embedded file, I create it on disk with a Save dialog, and then open it with the Excel := CreateOleObject('Excel.Application'); method. I've seen examples on how to load a resource, using THandles, but I don't seem to get it working with Excel.WorkBooks.Open(EmbeddedExcelFile);.
Have you had to do something like this before? How would you do it?
Thanks!
You have to include the file as a resource. Say you have a blah.xls
Create a blah.rc file with the following content
blah RCDATA blah.xls
compile it with the resource compiler into blah.res
embed the RES file within the executable
{$R 'blah.res'}
in your application code, extract the file and run it with this code
procedure ExtractAndRun(resourceID:string; resourceFn:string);
var
resourceStream: TResourceStream;
fileStream: TFileStream;
tempPath: string;
fullFileName: string;
begin
tempPath:=GetTempDir;
FullFilename:=TempPath+'\'+resourceFN;
if not FileExists(FullFilename) then
begin
resourceStream := TResourceStream.Create(hInstance, resourceID, RT_RCDATA);
try
fileStream := TFileStream.Create(FullFilename, fmCreate);
try
fileStream.CopyFrom(resourceStream, 0);
finally
fileStream.Free;
end;
finally
resourceStream.Free;
end;
end;
ShellExecute(0,'open', pchar(FullFilename), nil, nil, SW_SHOWNORMAL);
end;
you'll have to add ShellApi in your uses clause
maybe you'll need this GetTempDir function
function GetTempDir: string;
var
Buffer: array[0..MAX_PATH] of char;
begin
GetTempPath(SizeOf(Buffer) - 1, Buffer);
result := StrPas(Buffer);
end;
invoke the function like this
extractAndRun('blah','blah.xls');
I am pretty sure it will not work. You have to save the file in a temp folder, alter it and and then do whatever you want.