访问资源的一个单元
// ***************************************************************
// madRes.pas version: 1.0h ? date: 2004-04-11
// -------------------------------------------------------------
// resource functions for both NT and 9x families
// -------------------------------------------------------------
// Copyright (C) 1999 - 2004 www.madshi.net, All Rights Reserved
// ***************************************************************
// 2004-04-11 1.0h (1) "CompareString" replaced by "madStrings.CompareStr"
// (2) GetResourceW checks for "update = 0" now
// 2004-03-08 1.0g (1) CompareString(LANG_ENGLISH) fixes sort order (czech
OS)
// (2) force file time touch, when resources are changed
// 2003-11-10 1.0f (1) checksum field in the PE header is now set up
correctly
// (2) CodePage field in the resource headers stays 0 now
// (3) ImageDebugDirectory handling improved (Microsoft
linker)
// 2003-06-09 1.0e (1) language was not treated correctly
// (2) cleaning up the internal trees contained a little
bug
// 2002-11-07 1.0d (1) UpdateResource raised AV (only inside IDE) when
update=0
// (2)
PImageSectionHeader.PointerToLinenumbers/Relocations
// is corrected now (if necessary)
// 2002-10-17 1.0c (1) some debug structures were not updated correctly
// (2) resources must be sorted alphabetically
// 2002-10-12 1.0b CreateFileW is not supported in 9x, of course (dumb me)
// 2002-10-11 1.0a (1) the resource data was not always aligned correctly
// (2) the virtual size of the res section was sometimes
wrong
// (3) data given into UpdateResourceW is buffered now
// (4) added some icon and bitmap specific functions
// 2002-10-10 1.0 initial release
unit madRes;
{$I mad.inc}
interface
uses Windows;
// ***************************************************************
// first of all clone the official win32 APIs
function BeginUpdateResourceW (fileName : PWideChar;
delExistingRes : bool ) : dword;
stdcall;
function EndUpdateResourceW (update : dword;
discard : bool ) : bool; stdcall;
function UpdateResourceW (update : dword;
type_ : PWideChar;
name : PWideChar;
language : word;
data : pointer;
size : dword ) : bool; stdcall;
// ***************************************************************
// get the raw data of the specified resource
function GetResourceW (update : dword;
type_ : PWideChar;
name : PWideChar;
language : word;
var data : pointer;
var size : dword ) : bool; stdcall;
// ***************************************************************
// icon specific types and functions
type
// structure of icon group resources
TPIconGroup = ^TIconGroup;
TIconGroup = packed record
reserved : word;
type_ : word; // 1 = icon
itemCount : word;
items : array [0..maxInt shr 4 - 1] of packed record
width : byte; // in pixels
height : byte;
colors : byte; // 0 for 256+ colors
reserved : byte;
planes : word;
bitCount : word;
imageSize : dword;
id : word; // id of linked RT_ICON resource
end;
end;
// structure of ico file header
TPIcoHeader = ^TIcoHeader;
TIcoHeader = packed record
reserved : word;
type_ : word; // 1 = icon
itemCount : word;
items : array [0..maxInt shr 4 - 1] of packed record
width : byte; // in pixels
height : byte;
colors : byte; // 0 for 256+ colors
reserved : byte;
planes : word;
bitCount : word;
imageSize : dword;
offset : dword; // data offset in ico file
end;
end;
// get the specified icon group resource header
function GetIconGroupResourceW (update : dword;
name : PWideChar;
language : word;
var iconGroup : TPIconGroup) : bool;
stdcall;
// save the specified icon group resource to an ico file
function SaveIconGroupResourceW (update : dword;
name : PWideChar;
language : word;
icoFile : PWideChar) : bool; stdcall;
// load the specified ico file into the resources
// if the icon group with the specified already exists, it gets fully
replaced
function LoadIconGroupResourceW (update : dword;
name : PWideChar;
language : word;
icoFile : PWideChar) : bool; stdcall;
// delete the whole icon group including all referenced icons
function DeleteIconGroupResourceW (update : dword;
name : PWideChar;
language : word ) : bool; stdcall;
// ***************************************************************
// bitmap specific functions
// save the specified bitmap resource to a bmp file
function SaveBitmapResourceW (update : dword;
name : PWideChar;
language : word;
bmpFile : PWideChar) : bool; stdcall;
// load the specified bmp file into the resources
function LoadBitmapResourceW (update : dword;
name : PWideChar;
language : word;
bmpFile : PWideChar) : bool; stdcall;
// ***************************************************************
var DontShrinkResourceSection : boolean = false;
implementation
uses madStrings, madTools;
// ***************************************************************
type
// Windows internal types
TAImageSectionHeader = array [0..maxInt shr 6 - 1] of
TImageSectionHeader;
TImageResourceDirectoryEntry = packed record
NameOrID : dword;
OffsetToData : dword;
end;
PImageResourceDirectoryEntry = ^TImageResourceDirectoryEntry;
TImageResourceDirectory = packed record
Characteristics : dword;
timeDateStamp : dword;
majorVersion : word;
minorVersion : word;
numberOfNamedEntries : word;
numberOfIdEntries : word;
entries : array [0..maxInt shr 4 - 1] of
TImageResourceDirectoryEntry;
end;
PImageResourceDirectory = ^TImageResourceDirectory;
TImageResourceDataEntry = packed record
OffsetToData : dword;
Size : dword;
CodePage : dword;
Reserved : dword;
end;
PImageResourceDataEntry = ^TImageResourceDataEntry;
// madRes internal types
TPPResItem = ^TPResItem;
TPResItem = ^TResItem;
TResItem = packed record
id : integer;
name : wideString;
child : TPResItem;
next : TPResItem;
strBuf : string; // temporare memory buffer for item data < 32kb
case isDir: boolean of
true : (attr : dword;
time : dword;
majorVer : word;
minorVer : word;
namedItems : dword;
idItems : dword);
false : (data : pointer;
size : dword;
fileBuf : dword; // temporare file buffer for item data
>= 32kb
codePage : dword;
reserved : dword);
end;
TDAPResItem = array of TPResItem;
TResourceHandle = record
fh : dword;
map : dword;
buf : pointer;
nh : PImageNtHeaders;
tree : TPResItem;
end;
TPResourceHandle = ^TResourceHandle;
// ***************************************************************
// round up the value to the next align boundary
function Align(value, align: dword) : dword;
begin
result := ((value + align - 1) div align) * align;
end;
// move file contents, can make smaller or bigger
// if moving is necessary, the file mapping must be temporarily undone
function MoveFileContents(fh, pos: dword; dif: integer; var map: dword;
var buf: pointer) : boolean;
var moveSize : dword;
procedure CloseHandles;
begin
UnmapViewOfFile(buf);
CloseHandle(map);
end;
function OpenHandles : boolean;
begin
map := CreateFileMapping(fh, nil, PAGE_READWRITE, 0, 0, nil);
buf := MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, 0);
result := buf <> nil;
end;
function CheckPos : boolean;
begin
result := true;
if pos > GetFileSize(fh, nil) then begin
if dif < 0 then
CloseHandles;
SetFilePointer(fh, pos, nil, FILE_BEGIN);
SetEndOfFile(fh);
if dif < 0 then
result := OpenHandles;
end;
moveSize := GetFileSize(fh, nil) - pos;
end;
procedure SetSize;
begin
SetFilePointer(fh, integer(GetFileSize(fh, nil)) + dif, nil,
FILE_BEGIN);
SetEndOfFile(fh);
end;
procedure MoveIt;
begin
Move(pointer(dword(buf) + pos)^, pointer(int64(dword(buf) + pos) +
dif)^, moveSize);
end;
begin
result := false;
if dif > 0 then begin
CloseHandles;
CheckPos;
SetSize;
if OpenHandles then begin
MoveIt;
result := true;
end;
end else
if CheckPos then begin
MoveIt;
CloseHandles;
SetSize;
result := OpenHandles;
end;
end;
// get a pointer tree of all available resources
function GetResTree(module, resOfs, virtResOfs: dword) : TPResItem;
function ParseResEntry(nameOrID, offsetToData: dword) : TPResItem;
function GetResourceNameFromId(name: dword) : wideString;
var wc : PWideChar;
i1 : integer;
begin
wc := pointer(module + resOfs + name);
SetLength(result, word(wc[0]));
for i1 := 1 to Length(result) do
result[i1] := wc[i1];
end;
var irs : PImageResourceDirectory;
i1 : integer;
irde : PImageResourceDataEntry;
ppri : ^TPResItem;
begin
New(result);
ZeroMemory(result, sizeOf(result^));
with result^ do begin
isDir := offsetToData and $80000000 <> 0;
if nameOrID and $80000000 <> 0 then
name := GetResourceNameFromId(nameOrID and (not $80000000))
else id := nameOrID;
if isDir then begin
dword(irs) := module + resOfs + offsetToData and (not $80000000);
attr := irs^.Characteristics;
time := irs^.timeDateStamp;
majorVer := irs^.majorVersion;
minorVer := irs^.minorVersion;
namedItems := irs^.numberOfNamedEntries;
idItems := irs^.numberOfIdEntries;
ppri := @child;
for i1 := 0 to irs^.numberOfNamedEntries +
irs^.numberOfIdEntries - 1 do begin
ppri^ := ParseResEntry(irs^.entries[i1].NameOrID,
irs^.entries[i1].OffsetToData);
ppri := @ppri^^.next;
end;
end else begin
dword(irde) := module + resOfs + offsetToData;
size := irde^.Size;
codePage := irde^.CodePage;
reserved := irde^.Reserved;
data := pointer(module + irde^.OffsetToData - (virtResOfs -
resOfs));
end;
end;
end;
begin
result := ParseResEntry(0, $80000000);
end;
// returns a unique temp file name with full path
function GetTempFile(res: TPResItem) : string;
var arrCh : array [0..MAX_PATH] of char;
begin
if GetTempPath(MAX_PATH, arrCh) > 0 then
result := string(arrCh) + '\'
else result := '';
result := result + '$mad$res' + IntToHexEx(GetCurrentProcessID, 8) +
IntToHexEx(dword(res)) + '$';
end;
// totally free the pointer tree
procedure DelResTree(var res: TPResItem);
var res2 : TPResItem;
begin
while res <> nil do begin
DelResTree(res^.child);
res2 := res;
res := res^.next;
if (not res2^.isDir) and (res2^.fileBuf <> 0) then begin
CloseHandle(res2^.fileBuf);
DeleteFile(pchar(GetTempFile(res2)));
end;
Dispose(res2);
end;
end;
// calculate how big the resource section has to be for the current tree
// returned is the value for the structure, name and data sections
procedure CalcResSectionSize(res: TPResItem; var ss, ns, ds: dword);
var res2 : TPResItem;
begin
with res^ do
if isDir then begin
inc(ss, 16 + (namedItems + idItems) *
sizeOf(TImageResourceDirectoryEntry));
res2 := res^.child;
while res2 <> nil do begin
if res2^.name <> '' then
inc(ns, Length(res2^.name) * 2 + 2);
CalcResSectionSize(res2, ss, ns, ds);
res2 := res2^.next;
end;
end else begin
inc(ss, sizeOf(TImageResourceDataEntry));
inc(ds, Align(res^.size, 4));
end;
end;
// creates the whole resource section in a temporare buffer
function CreateResSection(virtResOfs: dword; res: TPResItem; var buf:
pointer; ss, ns, ds: dword) : boolean;
var sp, np, dp : dword;
fh : dword;
map : dword;
s1 : string;
procedure Store(res: TPResItem);
var c1 : dword;
i1 : integer;
res2 : TPResItem;
wc : PWideChar;
begin
if res^.isDir then begin
with PImageResourceDirectory(dword(buf) + sp)^ do begin
inc(sp, 16 + (res^.namedItems + res.idItems) *
sizeOf(TImageResourceDirectoryEntry));
Characteristics := res^.attr;
timeDateStamp := res^.time;
majorVersion := res^.majorVer;
minorVersion := res^.minorVer;
numberOfNamedEntries := res^.namedItems;
numberOfIdEntries := res^.idItems;
c1 := 0;
res2 := res^.child;
while res2 <> nil do begin
if c1 < res^.namedItems then begin
entries[c1].NameOrID := np or $80000000;
wc := pointer(dword(buf) + np);
word(wc[0]) := Length(res2^.name);
for i1 := 1 to Length(res2^.name) do
wc[i1] := res2^.name[i1];
inc(np, Length(res2^.name) * 2 + 2);
end else
entries[c1].NameOrID := res2^.id;
if res2^.isDir then
entries[c1].OffsetToData := sp or $80000000
else entries[c1].OffsetToData := sp;
Store(res2);
inc(c1);
res2 := res2^.next;
end;
end;
end else
with PImageResourceDataEntry(dword(buf) + sp)^ do begin
inc(sp, sizeOf(TImageResourceDataEntry));
OffsetToData := dp + virtResOfs;
Size := res^.size;
CodePage := res^.codePage;
Reserved := res^.reserved;
if res^.data <> nil then
Move(res^.data^, pointer(dword(buf) + dp)^, Size)
else
if res^.strBuf <> '' then
Move(pointer(res^.strBuf)^, pointer(dword(buf) + dp)^, Size)
else
if res^.fileBuf <> 0 then begin
SetFilePointer(res^.fileBuf, 0, nil, FILE_BEGIN);
ReadFile(res^.fileBuf, pointer(dword(buf) + dp)^, Size, c1,
nil);
end;
inc(dp, Align(Size, 4));
end;
end;
begin
result := false;
sp := 0;
np := ss;
dp := Align(ss + ns, 4);
if ss + ns + ds > 0 then begin
fh := CreateFile(pchar(GetTempFile(res)), GENERIC_READ or
GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
if fh <> INVALID_HANDLE_VALUE then begin
SetFilePointer(fh, Align(ss + ns, 4) + ds, nil, FILE_BEGIN);
SetEndOfFile(fh);
map := CreateFileMapping(fh, nil, PAGE_READWRITE, 0, 0, nil);
if map <> 0 then begin
buf := MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if buf <> nil then begin
ZeroMemory(buf, Align(ss + ns, 4) + ds);
Store(res);
end;
CloseHandle(map);
end;
CloseHandle(fh);
end;
DeleteFile(pchar(s1));
end;
end;
// returns a specific child folder, if it can be found
function FindDir(tree: TPResItem; name: PWideChar) : TPPResItem;
var ws : wideString;
begin
result := @tree^.child;
if dword(name) and $FFFF0000 <> 0 then begin
ws := name;
while (result^ <> nil) and ((not result^^.isDir) or (result^^.name <>
ws)) do
result := @result^^.next;
end else
while (result^ <> nil) and ((not result^^.isDir) or (result^^.name <>
'') or (result^^.id <> integer(name))) do
result := @result^^.next;
end;
// returns a specific child data item, if it can be found
function FindItem(tree: TPResItem; language: word) : TPPResItem;
begin
result := @tree^.child;
while (result^ <> nil) and (result^^.isDir or (result^^.id <> language))
do
result := @result^^.next;
end;
// ***************************************************************
function CreateFileX(fileName: PWideChar; write, create: boolean) : dword;
var c1, c2, c3 : dword;
begin
if write then begin
c1 := GENERIC_READ or GENERIC_WRITE;
c2 := 0;
end else begin
c1 := GENERIC_READ;
c2 := FILE_SHARE_READ or FILE_SHARE_WRITE;
end;
if create then c3 := CREATE_ALWAYS
else c3 := OPEN_EXISTING;
if GetVersion and $80000000 = 0 then
result := CreateFileW(fileName, c1, c2,
nil, c3, 0, 0)
else result := CreateFileA(pchar(string(wideString(fileName))), c1, c2,
nil, c3, 0, 0);
end;
function BeginUpdateResourceW(fileName: PWideChar; delExistingRes: bool) :
dword; stdcall;
var rh : TPResourceHandle;
ash : ^TAImageSectionHeader;
c1 : dword;
i1 : integer;
begin
result := 0;
New(rh);
ZeroMemory(rh, sizeOf(rh^));
with rh^ do begin
fh := CreateFileX(fileName, true, false);
if fh <> dword(-1) then begin
map := CreateFileMapping(fh, nil, PAGE_READWRITE, 0, 0, nil);
if map <> 0 then begin
buf := MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if buf <> nil then begin
nh := GetImageNtHeaders(dword(buf));
if nh <> nil then begin
SetLastError(ERROR_FILE_NOT_FOUND);
dword(ash) := dword(nh) + sizeOf(nh^);
for i1 := 0 to nh^.FileHeader.NumberOfSections - 1 do
if ash[i1].VirtualAddress =
nh^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAd
dress then begin
if delExistingRes then begin
New(tree);
ZeroMemory(tree, sizeOf(tree^));
tree^.isDir := true;
end else
tree := GetResTree(dword(buf), ash[i1].PointerToRawData,
ash[i1].VirtualAddress);
result := dword(rh);
break;
end;
end;
end;
end;
end;
if result = 0 then begin
c1 := GetLastError;
EndUpdateResourceW(dword(rh), true);
SetLastError(c1);
end;
end;
end;
procedure CalcCheckSum(baseAddress: pointer; size: dword);
var nh : PImageNtHeaders;
i1 : dword;
c1 : dword;
begin
nh := GetImageNtHeaders(dword(baseAddress));
nh^.OptionalHeader.CheckSum := 0;
c1 := 0;
for i1 := 0 to (size - 1) div 2 do begin
c1 := c1 + word(baseAddress^);
if c1 and $ffff0000 <> 0 then
c1 := c1 and $ffff + c1 shr 16;
inc(dword(baseAddress), 2);
end;
c1 := word(c1 and $ffff + c1 shr 16);
nh^.OptionalHeader.CheckSum := c1 + size;
end;
function EndUpdateResourceW(update: dword; discard: bool) : bool; stdcall;
var rh : TPResourceHandle absolute update;
ash : ^TAImageSectionHeader;
ss, ns, ds : dword;
storeBuf : pointer;
i1, i2, i3, i4 : integer;
pidb : PImageDebugDirectory;
begin
result := true;
if update <> 0 then
try
with rh^ do begin
if not discard then begin
result := false;
dword(ash) := dword(nh) + sizeOf(nh^);
for i1 := 0 to nh^.FileHeader.NumberOfSections - 1 do
if ash[i1].VirtualAddress =
nh^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAd
dress then begin
ss := 0;
ns := 0;
ds := 0;
CalcResSectionSize(tree, ss, ns, ds);
CreateResSection(nh^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RE
SOURCE].VirtualAddress, tree, storeBuf, ss, ns, ds);
i2 := int64(Align(Align(ss + ns, 4) + ds,
nh^.OptionalHeader.FileAlignment)) - int64(ash[i1].SizeOfRawData);
if (i2 < 0) and DontShrinkResourceSection then
i2 := 0;
if (i2 <> 0) and (not MoveFileContents(fh,
ash[i1].PointerToRawData + ash[i1].SizeOfRawData, i2, map, buf)) then
break;
nh := GetImageNtHeaders(dword(buf));
dword(ash) := dword(nh) + sizeOf(nh^);
with nh^.OptionalHeader,
DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE] do begin
inc(nh^.OptionalHeader.SizeOfInitializedData, i2);
i3 := int64(Align(Align(ss + ns, 4) + ds,
SectionAlignment)) - Align(Size, SectionAlignment);
ash[i1].SizeOfRawData := Align(Align(ss + ns, 4) + ds,
FileAlignment);
ash[i1].Misc.VirtualSize := Align(ss + ns, 4) + ds;
Size := Align(ss + ns, 4) + ds;
inc(SizeOfImage, i3);
for i4 := 0 to nh^.FileHeader.NumberOfSections - 1 do
if ash[i4].VirtualAddress > VirtualAddress then begin
inc(ash[i4].VirtualAddress, i3);
inc(ash[i4].PointerToRawData, i2);
if ash[i4].PointerToLinenumbers >
ash[i1].PointerToRawData then
inc(ash[i4].PointerToLinenumbers, i2);
if ash[i4].PointerToRelocations >
ash[i1].PointerToRawData then
inc(ash[i4].PointerToRelocations, i2);
end;
for i4 := low(DataDirectory) to high(DataDirectory) do
if DataDirectory[i4].VirtualAddress > VirtualAddress
then
inc(DataDirectory[i4].VirtualAddress, i3);
pidb := nil;
if DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size > 0
then
for i4 := 0 to nh^.FileHeader.NumberOfSections - 1 do
if
(DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress >=
ash[i4].VirtualAddress) and
( (i4 = nh^.FileHeader.NumberOfSections - 1) or
(DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress < ash[i4 +
1].VirtualAddress) ) then begin
pidb := pointer(dword(buf) +
ash[i4].PointerToRawData +
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress -
ash[i4].VirtualAddress);
break;
end;
if pidb <> nil then begin
i4 := DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
if i4 mod sizeOf(TImageDebugDirectory) = 0 then
i4 := i4 div sizeOf(TImageDebugDirectory);
for i4 := 1 to i4 do begin
if pidb^.PointerToRawData > ash[i1].PointerToRawData
then begin
if pidb^.PointerToRawData <> 0 then
inc(pidb^.PointerToRawData, i2);
if pidb^.AddressOfRawData <> 0 then
inc(pidb^.AddressOfRawData, i3);
end;
inc(pidb);
end;
end;
end;
Move(storeBuf^, pointer(dword(buf) +
ash[i1].PointerToRawData)^, Align(ss + ns, 4) + ds);
UnmapViewOfFile(storeBuf);
DeleteFile(pchar(GetTempFile(tree)));
i2 := Align(Align(ss + ns, 4) + ds,
nh^.OptionalHeader.FileAlignment) - (Align(ss + ns, 4) + ds);
if i2 > 0 then
ZeroMemory(pointer(dword(buf) + ash[i1].PointerToRawData +
Align(ss + ns, 4) + ds), i2);
result := true;
break;
end;
CalcCheckSum(buf, GetFileSize(fh, nil));
end;
DelResTree(tree);
UnmapViewOfFile(buf);
CloseHandle(map);
if SetFilePointer(fh, 0, nil, FILE_END) <> $ffffffff then
SetEndOfFile(fh);
CloseHandle(fh);
end;
Dispose(rh);
except result := false end;
end;
function UpdateResourceW(update: dword; type_, name: PWideChar; language:
word; data: pointer; size: dword) : bool; stdcall;
procedure SetData(item: TPResItem);
var c1 : dword;
begin
item^.id := language;
item^.data := nil;
item^.size := size;
item^.codePage := 0;//language;
if size > 32 * 1024 then begin
item^.fileBuf := CreateFile(pchar(GetTempFile(item)), GENERIC_READ
or GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
if item^.fileBuf <> INVALID_HANDLE_VALUE then
WriteFile(item^.fileBuf, data^, size, c1, nil)
else item^.fileBuf := 0;
end else
SetString(item^.strBuf, pchar(data), size);
end;
function AddItem(tree: TPResItem) : TPResItem;
var ppr1 : TPPResItem;
begin
ppr1 := @tree^.child;
while (ppr1^ <> nil) and (ppr1^^.id < language) do
ppr1 := @ppr1^^.next;
New(result);
ZeroMemory(result, sizeOf(result^));
result^.next := ppr1^;
ppr1^ := result;
SetData(result);
inc(tree^.idItems);
end;
function AddDir(tree: TPResItem; name: PWideChar) : TPResItem;
var ppr1 : TPPResItem;
s1 : string;
begin
New(result);
ZeroMemory(result, sizeOf(result^));
result^.isDir := true;
ppr1 := @tree^.child;
if dword(name) and $FFFF0000 = 0 then begin
while (ppr1^ <> nil) and ((ppr1^^.name <> '') or (ppr1^^.id <
integer(name))) do
ppr1 := @ppr1^.next;
result^.id := integer(name);
inc(tree^.idItems);
end else begin
s1 := wideString(name);
while (ppr1^ <> nil) and (ppr1^^.name <> '') and
(CompareStr(ppr1^^.name, s1) < 0) do
ppr1 := @ppr1^.next;
result^.name := name;
inc(tree^.namedItems);
end;
result^.next := ppr1^;
ppr1^ := result;
end;
procedure DelItem(const items: array of TPPResItem);
var pr1 : TPResItem;
i1 : integer;
begin
for i1 := 0 to Length(items) - 2 do begin
if items[i1]^.name = '' then
dec(items[i1 + 1]^.idItems )
else dec(items[i1 + 1]^.namedItems);
pr1 := items[i1]^;
items[i1]^ := items[i1]^^.next;
if (not pr1^.isDir) and (pr1^.fileBuf <> 0) then begin
CloseHandle(pr1^.fileBuf);
DeleteFile(pchar(GetTempFile(pr1)));
end;
Dispose(pr1);
if items[i1 + 1]^.idItems + items[i1 + 1]^.namedItems > 0 then
break;
end;
end;
var ppr1, ppr2, ppr3 : TPPResItem;
begin
result := true;
if update <> 0 then
try
with TPResourceHandle(update)^ do begin
ppr1 := FindDir(tree, type_);
if ppr1^ <> nil then begin
ppr2 := FindDir(ppr1^, name);
if ppr2^ <> nil then begin
ppr3 := FindItem(ppr2^, language);
if ppr3^ <> nil then begin
if data <> nil then
SetData(ppr3^)
else
DelItem([ppr3, ppr2, ppr1, @tree]);
end else
if data <> nil then
AddItem(ppr2^)
else
if language = 0 then
DelItem([ppr2, ppr1, @tree]);
end else
if data <> nil then
AddItem(AddDir(ppr1^, name))
else
if (language = 0) and (name = nil) then
DelItem([ppr1, @tree]);
end else
if data <> nil then
AddItem(AddDir(AddDir(tree, type_), name));
end;
except result := false end;
end;
// ***************************************************************
function GetResourceW(update: dword; type_, name: PWideChar; language:
word; var data: pointer; var size: dword) : bool; stdcall;
var res1 : TPResItem;
begin
result := false;
data := nil;
size := 0;
try
if update <> 0 then
with TPResourceHandle(update)^ do begin
res1 := FindDir(tree, type_)^;
if res1 <> nil then begin
res1 := FindDir(res1, name)^;
if res1 <> nil then begin
res1 := FindItem(res1, language)^;
result := res1 <> nil;
if result then begin
data := res1^.data;
size := res1^.size;
end;
end;
end;
end;
except result := false end;
end;
// ***************************************************************
function GetIconGroupResourceW(update: dword; name: PWideChar; language:
word; var iconGroup: TPIconGroup) : bool; stdcall;
var c1 : dword;
begin
result := GetResourceW(update, PWideChar(RT_GROUP_ICON), name, language,
pointer(iconGroup), c1);
end;
function SaveIconGroupResourceW(update: dword; name: PWideChar; language:
word; icoFile: PWideChar) : bool; stdcall;
var ig : TPIconGroup;
fh : dword;
ih : TPIcoHeader;
id : pointer;
i1 : integer;
c1, c2 : dword;
p1 : pointer;
begin
result := false;
if GetIconGroupResourceW(update, name, language, ig) then begin
fh := CreateFileX(icoFile, true, true);
if fh <> INVALID_HANDLE_VALUE then
try
c2 := 0;
for i1 := 0 to ig^.itemCount - 1 do
inc(c2, ig^.items[i1].imageSize);
ih := nil;
id := nil;
try
GetMem(ih, 6 + 16 * ig^.itemCount);
GetMem(id, c2);
Move(ig^, ih^, 4);
ih^.itemCount := 0;
c1 := dword(id);
for i1 := 0 to ig^.itemCount - 1 do begin
Move(ig^.items[i1], ih^.items[ih^.itemCount], 14);
if GetResourceW(update, PWideChar(RT_ICON),
PWideChar(ig^.items[i1].id), language, p1, c2) then begin
ih^.items[ih^.itemCount].offset := c1 - dword(id);
Move(p1^, pointer(c1)^, ig^.items[i1].imageSize);
inc(c1, ig^.items[i1].imageSize);
inc(ih^.itemCount);
end;
end;
for i1 := 0 to ih^.itemCount - 1 do
inc(ih^.items[i1].offset, 6 + 16 * ih^.itemCount);
result := (ih^.itemCount > 0) and
WriteFile(fh, ih^, 6 + 16 * ih^.itemCount, c2, nil)
and
WriteFile(fh, id^, c1 - dword(id), c2, nil);
finally
FreeMem(ih);
FreeMem(id);
end;
finally CloseHandle(fh) end;
end;
end;
function LoadIconGroupResourceW(update: dword; name: PWideChar; language:
word; icoFile: PWideChar) : bool; stdcall;
function FindFreeID(var sid: integer) : integer;
var pr1 : TPResItem;
begin
with TPResourceHandle(update)^ do begin
pr1 := FindDir(tree, PWideChar(RT_ICON))^;
if pr1 <> nil then begin
pr1 := pr1^.child;
while true do begin
while (pr1 <> nil) and ((pr1^.name <> '') or (pr1^.id <> sid))
do
pr1 := pr1^.next;
if pr1 <> nil then
inc(sid)
else break;
end;
end;
result := sid;
end;
end;
var ico : TPIcoHeader;
fh : dword;
c1, c2 : dword;
ig : TPIconGroup;
ids : array of integer;
i1 : integer;
sid : integer; // smallest id
begin
result := false;
fh := CreateFileX(icoFile, false, false);
if fh <> INVALID_HANDLE_VALUE then
try
c1 := GetFileSize(fh, nil);
GetMem(ico, c1);
try
if ReadFile(fh, pointer(ico)^, c1, c2, nil) and (c1 = c2) then
begin
if GetIconGroupResourceW(update, name, language, ig) then begin
SetLength(ids, ig^.itemCount);
sid := maxInt;
for i1 := 0 to high(ids) do begin
ids[i1] := ig^.items[i1].id;
if ids[i1] < sid then
sid := ids[i1];
end;
end else
sid := 50;
DeleteIconGroupResourceW(update, name, language);
GetMem(ig, 6 + 14 * ico^.itemCount);
try
Move(ico^, ig^, 6);
for i1 := 0 to ico^.itemCount - 1 do begin
Move(ico^.items[i1], ig^.items[i1], 14);
if i1 < length(ids) then
ig^.items[i1].id := ids[i1]
else ig^.items[i1].id := FindFreeID(sid);
if not UpdateResourceW(update, PWideChar(RT_ICON),
PWideChar(ig^.items[i1].id), language,
pointer(dword(ico) +
ico^.items[i1].offset), ico^.items[i1].imageSize) then
exit;
end;
result := UpdateResourceW(update, PWideChar(RT_GROUP_ICON),
name, language, ig, 6 + 14 * ig^.itemCount);
finally FreeMem(ig) end;
end;
finally FreeMem(ico) end;
finally CloseHandle(fh) end;
end;
function DeleteIconGroupResourceW(update: dword; name: PWideChar;
language: word) : bool; stdcall;
var ig : TPIconGroup;
i1 : integer;
begin
if GetIconGroupResourceW(update, name, language, ig) then begin
result := UpdateResourceW(update, PWideChar(RT_GROUP_ICON), name,
language, nil, 0);
if result then
for i1 := 0 to ig^.itemCount - 1 do
result := UpdateResourceW(update, PWideChar(RT_ICON),
PWideChar(ig^.items[i1].id), language, nil, 0) and result;
end else
result := true;
end;
// ***************************************************************
function SaveBitmapResourceW(update: dword; name: PWideChar; language:
word; bmpFile: PWideChar) : bool; stdcall;
var bfh : TBitmapFileHeader;
p1 : pointer;
c1, c2 : dword;
fh : dword;
begin
result := false;
if GetResourceW(update, PWideChar(RT_BITMAP), name, language, p1, c1)
then begin
pchar(@bfh.bfType)[0] := 'B';
pchar(@bfh.bfType)[1] := 'M';
bfh.bfSize := sizeOf(bfh) + c1;
bfh.bfReserved1 := 0;
bfh.bfReserved2 := 0;
bfh.bfOffBits := sizeOf(TBitmapFileHeader) +
sizeOf(TBitmapInfoHeader);
if PBitmapInfo(p1)^.bmiHeader.biBitCount <= 8 then
inc(bfh.bfOffBits, 4 shl PBitmapInfo(p1)^.bmiHeader.biBitCount);
fh := CreateFileX(bmpFile, true, true);
if fh <> INVALID_HANDLE_VALUE then
try
WriteFile(fh, bfh, sizeOf(bfh), c2, nil);
WriteFile(fh, p1^, c1, c2, nil);
result := true;
finally CloseHandle(fh) end;
end;
end;
function LoadBitmapResourceW(update: dword; name: PWideChar; language:
word; bmpFile: PWideChar) : bool; stdcall;
var bmp : pointer;
c1, c2 : dword;
fh : dword;
begin
result := false;
fh := CreateFileX(bmpFile, false, false);
if fh <> INVALID_HANDLE_VALUE then
try
c1 := GetFileSize(fh, nil) - sizeOf(TBitmapFileHeader);
GetMem(bmp, c1);
try
SetFilePointer(fh, sizeOf(TBitmapFileHeader), nil, FILE_BEGIN);
result := ReadFile(fh, pointer(bmp)^, c1, c2, nil) and (c1 = c2)
and
UpdateResourceW(update, PWideChar(RT_BITMAP), name,
language, bmp, c1);
finally FreeMem(bmp) end;
finally CloseHandle(fh) end;
end;
end.