home *** CD-ROM | disk | FTP | other *** search
- unit ExeImage;
-
- interface
-
- uses
- TypInfo, Classes, SysUtils, Windows, Graphics, RXTypes;
-
- type
-
- { Exceptions }
-
- EExeError = class(Exception);
-
- { Forward Declarations }
-
- TResourceItem = class;
- TResourceClass = class of TResourceItem;
- TResourceList = class;
-
- { TExeImage }
-
- TExeImage = class(TComponent)
- private
- FFileName: string;
- FFileHandle: THandle;
- FFileMapping: THandle;
- FFileBase: Pointer;
- FDosHeader: PIMAGE_DOS_HEADER;
- FNTHeader: PIMAGE_NT_HEADERS;
- FResourceList: TResourceList;
- FIconResources: TResourceItem;
- FCursorResources: TResourceItem;
- FResourceBase: Longint;
- FResourceRVA: Longint;
- function GetResourceList: TResourceList;
- function GetSectionHdr(const SectionName: string;
- var Header: PIMAGE_SECTION_HEADER): Boolean;
- public
- constructor CreateImage(AOwner: TComponent; const AFileName: string);
- destructor Destroy; override;
- property FileName: string read FFileName;
- property Resources: TResourceList read GetResourceList;
- end;
-
- { TResourceItem }
-
- TResourceItem = class(TComponent)
- private
- FList: TResourceList;
- FDirEntry: PIMAGE_RESOURCE_DIRECTORY_ENTRY;
- function DataEntry: PIMAGE_RESOURCE_DATA_ENTRY;
- function FExeImage: TExeImage;
- function FirstChildDirEntry: PIMAGE_RESOURCE_DIRECTORY_ENTRY;
- function GetResourceItem(Index: Integer): TResourceItem;
- function GetResourceType: TResourceType;
- protected
- function GetName: string; virtual;
- function GetResourceList: TResourceList; virtual;
- public
- constructor CreateItem(AOwner: TComponent; ADirEntry: Pointer);
- function IsList: Boolean; virtual;
- function Offset: Integer;
- function Size: Integer;
- function RawData: Pointer;
- function ResTypeStr: string;
- procedure SaveToFile(const FileName: string);
- procedure SaveToStream(Stream: TStream); virtual;
- property Items[Index: Integer]: TResourceItem read GetResourceItem; default;
- property List: TResourceList read GetResourceList;
- property Name: string read GetName;
- property ResType: TResourceType read GetResourceType;
- end;
-
- { TIconResource }
-
- TIconResource = class(TResourceItem)
- protected
- function GetResourceList: TResourceList; override;
- public
- function IsList: Boolean; override;
- end;
-
- { TIconResEntry }
-
- TIconResEntry = class(TResourceItem)
- protected
- FResInfo: PIconResInfo;
- function GetName: string; override;
- procedure AssignTo(Dest: TPersistent); override;
- public
- procedure SaveToStream(Stream: TStream); override;
- end;
-
- { TCursorResource }
-
- TCursorResource = class(TIconResource)
- protected
- function GetResourceList: TResourceList; override;
- end;
-
- { TCursorResEntry }
-
- TCursorResEntry = class(TIconResEntry)
- protected
- FResInfo: PCursorResInfo;
- function GetName: string; override;
- end;
-
- { TBitmapResource }
-
- TBitMapResource = class(TResourceItem)
- protected
- procedure AssignTo(Dest: TPersistent); override;
- public
- procedure SaveToStream(Stream: TStream); override;
- end;
-
- { TStringResource }
-
- TStringResource = class(TResourceItem)
- protected
- procedure AssignTo(Dest: TPersistent); override;
- end;
-
- { TMenuResource }
-
- TMenuResource = class(TResourceItem)
- private
- FNestStr: string;
- FNestLevel: Integer;
- procedure SetNestLevel(Value: Integer);
- protected
- procedure AssignTo(Dest: TPersistent); override;
- property NestLevel: Integer read FNestLevel write SetNestLevel;
- property NestStr: string read FNestStr;
- end;
-
- { TResourceList }
-
- TResourceList = class(TComponent)
- protected
- FList: TList;
- FResDir: PIMAGE_RESOURCE_DIRECTORY;
- FExeImage: TExeImage;
- FResType: Integer;
- function List: TList; virtual;
- function GetResourceItem(Index: Integer): TResourceItem;
- public
- constructor CreateList(AOwner: TComponent; ResDirOfs: Longint;
- AExeImage: TExeImage);
- destructor Destroy; override;
- function Count: Integer;
- property Items[Index: Integer]: TResourceItem read GetResourceItem; default;
- end;
-
- { TIconResourceList }
-
- TIconResourceList = class(TResourceList)
- protected
- function List: TList; override;
- end;
-
- { TCursorResourceList }
-
- TCursorResourceList = class(TResourceList)
- protected
- function List: TList; override;
- end;
-
- implementation
-
- { This function maps a resource type to the associated resource class }
-
- function GetResourceClass(ResType: Integer): TResourceClass;
- const
- TResourceClasses: array[TResourceType] of TResourceClass = (
- TResourceItem, { rtUnknown0 }
- TCursorResEntry, { rtCursorEntry }
- TBitmapResource, { rtBitmap }
- TIconResEntry, { rtIconEntry }
- TMenuResource, { rtMenu }
- TResourceItem, { rtDialog }
- TStringResource, { rtString }
- TResourceItem, { rtFontDir }
- TResourceItem, { rtFont }
- TResourceItem, { rtAccelerators }
- TResourceItem, { rtRCData }
- TResourceItem, { rtMessageTable }
- TCursorResource, { rtGroupCursor }
- TResourceItem, { rtUnknown13 }
- TIconResource, { rtIcon }
- TResourceItem, { rtUnknown15 }
- TResourceItem); { rtVersion }
- begin
- if (ResType >= Integer(Low(TResourceType))) and
- (ResType <= Integer(High(TResourceType))) then
- Result := TResourceClasses[TResourceType(ResType)] else
- Result := TResourceItem;
- end;
-
- { Utility Functions }
-
- function Min(A, B: Integer): Integer;
- begin
- if A < B then Result := A
- else Result := B;
- end;
-
- { This function checks if an offset is a string name, or a directory }
- {Assumes: IMAGE_RESOURCE_NAME_IS_STRING = IMAGE_RESOURCE_DATA_IS_DIRECTORY}
-
- function HighBitSet(L: Longint): Boolean;
- begin
- Result := (L and IMAGE_RESOURCE_DATA_IS_DIRECTORY) <> 0;
- end;
-
- function StripHighBit(L: Longint): Longint;
- begin
- Result := L and IMAGE_OFFSET_STRIP_HIGH;
- end;
-
- function StripHighPtr(L: Longint): Pointer;
- begin
- Result := Pointer(L and IMAGE_OFFSET_STRIP_HIGH);
- end;
-
- { This function converts a pointer to a wide char string into a pascal string }
-
- function WideCharToStr(WStr: PWChar; Len: Integer): string;
- begin
- if Len = 0 then Len := -1;
- Len := WideCharToMultiByte(CP_ACP, 0, WStr, Len, nil, 0, nil, nil);
- SetLength(Result, Len);
- WideCharToMultiByte(CP_ACP, 0, WStr, Len, PChar(Result), Len, nil, nil);
- end;
-
- { Exceptions }
-
- procedure ExeError(const ErrMsg: string);
- begin
- raise EExeError.Create(ErrMsg);
- end;
-
- { TExeImage }
-
- constructor TExeImage.CreateImage(AOwner: TComponent; const AFileName: string);
- begin
- inherited Create(AOwner);
- FFileName := AFileName;
- FFileHandle := CreateFile(PChar(FFileName), GENERIC_READ, FILE_SHARE_READ,
- nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if FFileHandle = INVALID_HANDLE_VALUE then ExeError('Couldn''t open: '+FFileName);
- FFileMapping := CreateFileMapping(FFileHandle, nil, PAGE_READONLY, 0, 0, nil);
- if FFileMapping = 0 then ExeError('CreateFileMapping failed');
- FFileBase := MapViewOfFile(FFileMapping, FILE_MAP_READ, 0, 0, 0);
- if FFileBase = nil then ExeError('MapViewOfFile failed');
- FDosHeader := PIMAGE_DOS_HEADER(FFileBase);
- if not FDosHeader.e_magic = IMAGE_DOS_SIGNATURE then
- ExeError('unrecognized file format');
- FNTHeader := PIMAGE_NT_HEADERS(Longint(FDosHeader) + FDosHeader.e_lfanew);
- if IsBadReadPtr(FNTHeader, sizeof(IMAGE_NT_HEADERS)) or
- (FNTHeader.Signature <> IMAGE_NT_SIGNATURE) then
- ExeError('Not a PE (WIN32 Executable) file');
- end;
-
- destructor TExeImage.Destroy;
- begin
- if FFileHandle <> INVALID_HANDLE_VALUE then
- begin
- UnmapViewOfFile(FFileBase);
- CloseHandle(FFileMapping);
- CloseHandle(FFileHandle);
- end;
- inherited Destroy;
- end;
-
- function TExeImage.GetSectionHdr(const SectionName: string;
- var Header: PIMAGE_SECTION_HEADER): Boolean;
- var
- I: Integer;
- begin
- Header := PIMAGE_SECTION_HEADER(FNTHeader);
- Inc(PIMAGE_NT_HEADERS(Header));
- Result := True;
- for I := 0 to FNTHeader.FileHeader.NumberOfSections - 1 do
- begin
- if Strlicomp(Header.Name, PChar(SectionName), IMAGE_SIZEOF_SHORT_NAME) = 0 then Exit;
- Inc(Header);
- end;
- Result := False;
- end;
-
- function TExeImage.GetResourceList: TResourceList;
- var
- ResSectHdr: PIMAGE_SECTION_HEADER;
- begin
- if not Assigned(FResourceList) then
- begin
- if GetSectionHdr('.rsrc', ResSectHdr) then
- begin
- FResourceBase := ResSectHdr.PointerToRawData + LongWord(FDosHeader);
- FResourceRVA := ResSectHdr.VirtualAddress;
- FResourceList := TResourceList.CreateList(Self, FResourceBase, Self);
- end
- else
- ExeError('No resources in this file.');
- end;
- Result := FResourceList;
- end;
-
- { TResourceItem }
-
- constructor TResourceItem.CreateItem(AOwner: TComponent; ADirEntry: Pointer);
- begin
- inherited Create(AOwner);
- FDirEntry := ADirEntry;
- end;
-
- function TResourceItem.DataEntry: PIMAGE_RESOURCE_DATA_ENTRY;
- begin
- Result := PIMAGE_RESOURCE_DATA_ENTRY(FirstChildDirEntry.OffsetToData
- + Cardinal(FExeImage.FResourceBase));
- end;
-
- function TResourceItem.FirstChildDirEntry: PIMAGE_RESOURCE_DIRECTORY_ENTRY;
- begin
- Result := PIMAGE_RESOURCE_DIRECTORY_ENTRY(StripHighBit(FDirEntry.OffsetToData) +
- FExeImage.FResourceBase + SizeOf(IMAGE_RESOURCE_DIRECTORY));
- end;
-
- function TResourceItem.FExeImage: TExeImage;
- begin
- Result := (Owner as TResourceList).FExeImage;
- end;
-
- function TResourceItem.GetResourceItem(Index: Integer): TResourceItem;
- begin
- Result := List[Index];
- end;
-
- function TResourceItem.GetResourceType: TResourceType;
- begin
- Result := TResourceType((Owner as TResourceList).FResType);
- end;
-
- function TResourceItem.IsList: Boolean;
- begin
- Result := HighBitSet(FirstChildDirEntry.OffsetToData);
- end;
-
- function TResourceItem.GetResourceList: TResourceList;
- begin
- if not IsList then ExeError('ResourceItem is not a list');
- if not Assigned(FList) then
- FList := TResourceList.CreateList(Self, StripHighBit(FDirEntry.OffsetToData) +
- FExeImage.FResourceBase, FExeImage);
- Result := FList;
- end;
-
- function TResourceItem.GetName: string;
- var
- PDirStr: PIMAGE_RESOURCE_DIR_STRING_U;
- begin
- { Check for Level1 entries, these are resource types. }
- if (Owner.Owner = FExeImage) and not HighBitSet(FDirEntry.Name) and
- (FDirEntry.Name <= 16) then
- begin
- Result := Copy(GetEnumName(TypeInfo(TResourceType), FDirEntry.Name), 3, 20);
- Exit;
- end;
-
- if HighBitSet(FDirEntry.Name) then
- begin
- PDirStr := PIMAGE_RESOURCE_DIR_STRING_U(StripHighBit(FDirEntry.Name) +
- FExeImage.FResourceBase);
- Result := WideCharToStr(@PDirStr.NameString, PDirStr.Length);
- Exit;
- end;
- Result := Format('%d', [FDirEntry.Name]);
- end;
-
- function TResourceItem.Offset: Integer;
- begin
- if IsList then
- Result := StripHighBit(FDirEntry.OffsetToData)
- else
- Result := DataEntry.OffsetToData;
- end;
-
- function TResourceItem.RawData: Pointer;
- begin
- with FExeImage do
- Result := pointer(FResourceBase - FResourceRVA + LongInt(DataEntry.OffsetToData));
- end;
-
- function TResourceItem.ResTypeStr: string;
- begin
- Result := Copy(GetEnumName(TypeInfo(TResourceType), Ord(ResType)), 3, 20);
- end;
-
- procedure TResourceItem.SaveToFile(const FileName: string);
- var
- FS: TFileStream;
- begin
- FS := TFileStream.Create(FileName, fmCreate);
- try
- Self.SaveToStream(FS);
- finally
- FS.Free;
- end;
- end;
-
- procedure TResourceItem.SaveToStream(Stream: TStream);
- begin
- Stream.Write(RawData^, Size);
- end;
-
- function TResourceItem.Size: Integer;
- begin
- if IsList then
- Result := 0
- else
- Result := DataEntry.Size;
- end;
-
- { TBitmapResource }
-
- procedure TBitmapResource.AssignTo(Dest: TPersistent);
- var
- MemStr: TMemoryStream;
- BitMap: TBitMap;
- begin
- if (Dest is TPicture) then
- begin
- BitMap := TPicture(Dest).Bitmap;
- MemStr := TMemoryStream.Create;
- try
- SaveToStream(MemStr);
- MemStr.Seek(0,0);
- BitMap.LoadFromStream(MemStr);
- finally
- MemStr.Free;
- end
- end
- else
- inherited AssignTo(Dest);
- end;
-
- procedure TBitmapResource.SaveToStream(Stream: TStream);
-
- function GetDInColors(BitCount: Word): Integer;
- begin
- case BitCount of
- 1, 4, 8: Result := 1 shl BitCount;
- else
- Result := 0;
- end;
- end;
-
- var
- BH: TBitmapFileHeader;
- BI: PBitmapInfoHeader;
- BC: PBitmapCoreHeader;
- ClrUsed: Integer;
- begin
- FillChar(BH, sizeof(BH), #0);
- BH.bfType := $4D42;
- BH.bfSize := Self.Size + sizeof(BH);
- BI := PBitmapInfoHeader(RawData);
- if BI.biSize = sizeof(TBitmapInfoHeader) then
- begin
- ClrUsed := BI.biClrUsed;
- if ClrUsed = 0 then
- ClrUsed := GetDInColors(BI.biBitCount);
- BH.bfOffBits := ClrUsed * SizeOf(TRgbQuad) +
- sizeof(TBitmapInfoHeader) + sizeof(BH);
- end
- else
- begin
- BC := PBitmapCoreHeader(RawData);
- ClrUsed := GetDInColors(BC.bcBitCount);
- BH.bfOffBits := ClrUsed * SizeOf(TRGBTriple) +
- sizeof(TBitmapCoreHeader) + sizeof(BH);
- end;
- Stream.Write(BH, SizeOf(BH));
- Stream.Write(RawData^, Self.Size);
- end;
-
-
- { TIconResource }
-
- function TIconResource.GetResourceList: TResourceList;
- begin
- if not Assigned(FList) then
- FList := TIconResourceList.CreateList(Owner, LongInt(RawData), FExeImage);
- Result := FList;
- end;
-
- function TIconResource.IsList: Boolean;
- begin
- Result := True;
- end;
-
- { TIconResEntry }
-
- procedure TIconResEntry.AssignTo(Dest: TPersistent);
- var
- hIco: HIcon;
- begin
- if Dest is TPicture then
- begin
- hIco := CreateIconFromResource(RawData, Size, (ResType = rtIconEntry), $30000);
- TPicture(Dest).Icon.Handle := hIco;
- end
- else
- inherited AssignTo(Dest);
- end;
-
- function TIconResEntry.GetName: string;
- begin
- if Assigned(FResInfo) then
- with FResInfo^ do
- Result := Format('%d X %d %d Colors', [bWidth, bHeight, bColorCount])
- else
- Result := inherited GetName;
- end;
-
- procedure TIconResEntry.SaveToStream(Stream: TStream);
- begin
- with TIcon.Create do
- try
- Handle := CreateIconFromResource(RawData, Self.Size, (ResType <> rtIcon), $30000);
- SaveToStream(Stream);
- finally
- Free;
- end;
- end;
-
- { TCursorResource }
-
- function TCursorResource.GetResourceList: TResourceList;
- begin
- if not Assigned(FList) then
- FList := TCursorResourceList.CreateList(Owner, LongInt(RawData), FExeImage);
- Result := FList;
- end;
-
- { TCursorResEntry }
-
- function TCursorResEntry.GetName: string;
- begin
- if Assigned(FResInfo) then
- with FResInfo^ do
- Result := Format('%d X %d %d Bit(s)', [wWidth, wWidth, wBitCount])
- else
- Result := inherited GetName;
- end;
-
- { TStringResource }
-
- procedure TStringResource.AssignTo(Dest: TPersistent);
- var
- P: PWChar;
- ID: Integer;
- Cnt: Cardinal;
- Len: Word;
- begin
- if (Dest is TStrings) then
- with TStrings(Dest) do
- begin
- BeginUpdate;
- try
- Clear;
- P := RawData;
- Cnt := 0;
- while Cnt < StringsPerBlock do
- begin
- Len := Word(P^);
- if Len > 0 then
- begin
- Inc(P);
- ID := ((FDirEntry.Name - 1) shl 4) + Cnt;
- Add(Format('%d, "%s"', [ID, WideCharToStr(P, Len)]));
- Inc(P, Len);
- end;
- Inc(Cnt);
- end;
- finally
- EndUpdate;
- end;
- end
- else
- inherited AssignTo(Dest);
- end;
-
- { TMenuResource }
-
- procedure TMenuResource.SetNestLevel(Value: Integer);
- begin
- FNestLevel := Value;
- SetLength(FNestStr, Value * 2);
- FillChar(FNestStr[1], Value * 2, ' ');
- end;
-
- procedure TMenuResource.AssignTo(Dest: TPersistent);
- var
- IsPopup: Boolean;
- Len: Word;
- MenuData: PWord;
- MenuEnd: PChar;
- MenuText: PWChar;
- MenuID: Word;
- MenuFlags: Word;
- S: string;
- begin
- if (Dest is TStrings) then
- with TStrings(Dest) do
- begin
- BeginUpdate;
- try
- Clear;
- MenuData := RawData;
- MenuEnd := PChar(RawData) + Size;
- Inc(MenuData, 2);
- NestLevel := 0;
- while PChar(MenuData) < MenuEnd do
- begin
- MenuFlags := MenuData^;
- Inc(MenuData);
- IsPopup := (MenuFlags and MF_POPUP) = MF_POPUP;
- MenuID := 0;
- if not IsPopup then
- begin
- MenuID := MenuData^;
- Inc(MenuData);
- end;
- MenuText := PWChar(MenuData);
- Len := lstrlenw(MenuText);
- if Len = 0 then
- S := 'MENUITEM SEPARATOR'
- else
- begin
- S := WideCharToStr(MenuText, Len);
- if IsPopup then
- S := Format('POPUP "%s"', [S]) else
- S := Format('MENUITEM "%s", %d', [S, MenuID]);
- end;
- Inc(MenuData, Len + 1);
- Add(NestStr + S);
- if (MenuFlags and MF_END) = MF_END then
- begin
- NestLevel := NestLevel - 1;
- Add(NestStr + 'ENDPOPUP');
- end;
- if IsPopup then
- NestLevel := NestLevel + 1;
- end;
- finally
- EndUpdate;
- end;
- end
- else
- inherited AssignTo(Dest);
- end;
-
- { TResourceList }
-
- constructor TResourceList.CreateList(AOwner: TComponent; ResDirOfs: Longint;
- AExeImage: TExeImage);
- var
- DirEntry: PIMAGE_RESOURCE_DIRECTORY_ENTRY;
- begin
- inherited Create(AOwner);
- FExeImage := AExeImage;
- FResDir := Pointer(ResDirOfs);
- if AOwner <> AExeImage then
- if AOwner.Owner.Owner = AExeImage then
- begin
- DirEntry := PIMAGE_RESOURCE_DIRECTORY_ENTRY(FResDir);
- inc(PIMAGE_RESOURCE_DIRECTORY(DirEntry));
- FResType := TResourceItem(Owner).FDirEntry.Name;
- end
- else
- FResType := (AOwner.Owner.Owner as TResourceList).FResType;
- end;
-
- destructor TResourceList.Destroy;
- begin
- inherited Destroy;
- FList.Free;
- end;
-
- function TResourceList.List: TList;
- var
- I: Integer;
- DirEntry: PIMAGE_RESOURCE_DIRECTORY_ENTRY;
- DirCnt: Integer;
- ResItem: TResourceItem;
- begin
- if not Assigned(FList) then
- begin
- FList := TList.Create;
- DirEntry := PIMAGE_RESOURCE_DIRECTORY_ENTRY(FResDir);
- inc(PIMAGE_RESOURCE_DIRECTORY(DirEntry));
- DirCnt := FResDir.NumberOfNamedEntries + FResDir.NumberOfIdEntries - 1;
- for I := 0 to DirCnt do
- begin
- { Handle Cursors and Icons specially }
- ResItem := GetResourceClass(FResType).CreateItem(Self, DirEntry);
- if Owner = FExeImage then
- if (TResourceType(DirEntry.Name) in [rtCursorEntry, rtIconEntry]) then
- begin
- if TResourceType(DirEntry.Name) = rtCursorEntry then
- FExeImage.FCursorResources := ResItem else
- FExeImage.FIconResources := ResItem;
- Inc(DirEntry);
- Continue;
- end;
- FList.Add(ResItem);
- Inc(DirEntry);
- end;
- end;
- Result := FList;
- end;
-
- function TResourceList.Count: Integer;
- begin
- Result := List.Count;
- end;
-
- function TResourceList.GetResourceItem(Index: Integer): TResourceItem;
- begin
- Result := List[Index];
- end;
-
- { TIconResourceList }
-
- function TIconResourceList.List: TList;
- var
- I, J, Cnt: Integer;
- ResData: PIconResInfo;
- ResList: TResourceList;
- ResOrd: Cardinal;
- IconResource: TIconResEntry;
- begin
- if not Assigned(FList) then
- begin
- FList := TList.Create;
- Cnt := PIconHeader(FResDir).wCount;
- PChar(ResData) := PChar(FResDir) + SizeOf(TIconHeader);
- ResList := FExeImage.FIconResources.List;
- for I := 0 to Cnt - 1 do
- begin
- ResOrd := ResData.wNameOrdinal;
- for J := 0 to ResList.Count - 1 do
- begin
- if ResOrd = ResList[J].FDirEntry.Name then
- begin
- IconResource := ResList[J] as TIconResEntry;
- IconResource.FResInfo := ResData;
- FList.Add(IconResource);
- end;
- end;
- Inc(ResData);
- end;
- end;
- Result := FList;
- end;
-
- { TCursorResourceList }
-
- function TCursorResourceList.List: TList;
- var
- I, J, Cnt: Integer;
- ResData: PCursorResInfo;
- ResList: TResourceList;
- ResOrd: Cardinal;
- CursorResource: TCursorResEntry;
- begin
- if not Assigned(FList) then
- begin
- FList := TList.Create;
- Cnt := PIconHeader(FResDir).wCount;
- PChar(ResData) := PChar(FResDir) + SizeOf(TIconHeader);
- ResList := FExeImage.FCursorResources.List;
- for I := 0 to Cnt - 1 do
- begin
- ResOrd := ResData.wNameOrdinal;
- for J := 0 to ResList.Count - 1 do
- begin
- if ResOrd = ResList[J].FDirEntry.Name then
- begin
- CursorResource := ResList[J] as TCursorResEntry;
- CursorResource.FResInfo := ResData;
- FList.Add(CursorResource);
- end;
- end;
- Inc(ResData);
- end;
- end;
- Result := FList;
- end;
-
- end.
-