Mega Code Archive

 
Categories / Delphi / System
 

Easy EXE attached data! (UPDATED October 1st, 2004)

Title: Easy EXE attached data! (UPDATED October 1st, 2004) Question: Using resources to include files into your EXE is a great thing, but I prefer using another way... Answer: Basis: how do I attach data to my executable file? When you compiled your code, and you want to attach 1 file to your file just use this command line: COPY /B PROJECT1.EXE + DATA.TXT PROJECT2.EXE A file, called Project2.Exe will be created and it will contain first file with 2nd file attached. That doesn't compromise EXE and it will be still working. Second step: how to easly extract that file from the executable? First of all, you should use this function: function GetAttachedData(Stream: TStream): boolean; var pMySelf : PChar; IdX : Integer; SectionsCount : Integer; EXESize : Cardinal; EXEOriginalSize: Cardinal; SR : TSearchRec; FS : TFileStream; EXEName : array[0..MAX_PATH] of Char; const IMAGE_PE_SIGNATURE = $00004550; begin result:=False; if not Assigned(Stream) then Exit; try Stream.Position:=0; Stream.Size:=0; // Gets EXE/DLL filename. FillChar(EXEName, SizeOf(EXEName), #0); GetModuleFileName(HInstance, EXEName, MAX_PATH); // Gets file size. EXESize:=0; if FindFirst(EXEName, faAnyFile, SR)=0 then begin EXESize:=SR.Size; SysUtils.FindClose(SR); end; // Gets originalsize. EXEOriginalSize:=0; try pMySelf:=Pointer(HInstance); if PImageDosHeader(pMySelf).E_MagicIMAGE_DOS_SIGNATURE then Exit; Inc(pMySelf, PImageDosHeader(pMySelf)._lfanew); if PDWORD(pMySelf)^IMAGE_PE_SIGNATURE then Exit; Inc(pMySelf, SizeOf(DWORD)); SectionsCount:=PImageFileHeader(pMySelf).NumberOfSections; Inc(pMySelf, SizeOf(TImageFileHeader) + SizeOf(TImageOptionalHeader)); for IdX:=1 to SectionsCount do begin with PImageSectionHeader(pMySelf)^ do if (PointerToRawData + SizeOfRawData)EXEOriginalSize then EXEOriginalSize:=PointerToRawData + SizeOfRawData; Inc(pMySelf, SizeOf(TImageSectionHeader)); end; except on E: Exception do EXEOriginalSize:=0; end; // If there's something attached... if EXESizeEXEOriginalSize then try FS:=TFileStream.Create(EXEName, fmOpenRead or fmShareDenyNone); try try FS.Position:=EXEOriginalSize; Stream.CopyFrom(FS, EXESize - EXEOriginalSize); result:=True; except on E: Exception do result:=False; end; finally FS.Free(); end; except on E: Exception do result:=False; end; except on E: Exception do result:=False; end; end; Then, you can use it like this: procedure TForm1.Button1Click(Sender: TObject); var s: TMemoryStream; begin s:=TMemoryStream.Create(); try if GetAttachedData(s) then s.SaveToFile('output.txt'); finally s.Free(); end; end; or, if you want to copy it directly from disk to disk without using your memory, you can now do this (October 1st, 2004's update!): procedure TForm1.Button1Click(Sender: TObject); var f: TFileStream; begin f:=TFileStream.Create('C:\DATA.TXT', fmCreate or fmReadWrite or fmShareDenyNone); try GetAttachedData(f); finally f.Free(); end; end; About using executable compressors That's all. You don't need to use constants, resources or anything else. Note that the code will work also if the EXE file is compressed with tools like UPX or similar. Thanks goes to... ...the site U.N.D.U. for some code snippets. Article and code updates: - October 1st, 2004: * Generalized that function to work with every type of stream, that means you can output to memory or to file directly by using TMemoryStream or TFileStream classes. * Bettered the reading of the data. It was using a TMemoryStream to load all the data to memory and then passing it to the output, that was meaning using a memory amount that was up to 2 times the size of the attached data (imagine it with 50Mb of data...). It now reads it directly from disk (where your program already is!)