unit ExeMod; { This is the first beta of the full version 1.00 That allows an ExeMod exe to work properly even from a CD..for info on how an exe on CD is handled please see the notes in the Interface section.Written by G.A. Carpenter (works on D2 thru D7) This is FreeWare for any purpose...use as you will. Consider it public domain... feel free to make any changes you want... if you improve it I'd love to get a copy of the improvements :-)[email protected] <gwen carpenter (Gwena)>You can use this in any program, commercial or freeware, and you DO NOT need to mention where it came from. (no attribution needed)If you use NT/2000/XP please read the NT-NOTE below!This unit enables you to make a delphi application that can alter its own exe file on disk... this appears to happen at runtime but actually the switch quickly takes place after a shutdown of the running app during which a copy of the exe deletes the old version and replaces it with a new version.While it is also possible to alter a running exe directly by altering the hd sectors that hold the exe's data. I abandoned that scheme because although it worked quickly and transparently it proved to be risky :-(This unit lets you easily create... -Self altering exe files -Program Generators -Apps that update live over the internet -Apps that keep an internal record of data (no ini file or registry usage needed) -File handling utils -Copy protection schemes -Self extracting archives -Programs that alter and store data in other programs and files. ...and many other strange things ;-) NOTE! ExeMod will not delete a demarc delimited string from an external file if the deletion would take the file down to 0 bytes in length! This is because some disk cleanup utils can be set to automatically remove 0 length files. If you intend to use an external file to hold data that will be handled by ExeMod you should begin by placing at least a single byte of data at the beginning of the file.------ NT-NOTE!If you use ExeMod.pas on an NT machine then you may need to run the exe outside the delphi ide... for some reason NT causes the debugger to faint at the sight of my code :-) the exe files run just fine on NT but just not from inside the ide....------- Here are some simple demos for you to look over.(Remember to put ExeMod.pas into your compiler's path or into your project's directory... then add ExeMod to your program's uses clause) ------------------------------------------------------------------------ -Code to alter a byte in an exe and then alter the exe at runtime and put the change onto the exe's file on diskprocedure TForm1.Button1Click(Sender: TObject); begin ReadExe; //Read exe file from disk and put into global string named Exe Exe[3] := 'A'; //Change byte 3 of Global string Exe AlterExe; //Alter the exe file on disk then quickly restart program end; ------------------------------------------------------------------------ -Code to add a file from disk to the end of the exe at runtime. Assume a small txt file named C:\myfile.txt exists and contains the text 'test'procedure TForm1.Button1Click(Sender: TObject); begin AddFile2Exe('gac1','C:\myfile.txt'); AlterExe; end;The string 'gac1' serves to locate the data at the end of the exe. I call gac1 a 'Demarc name' because when combined with a few other characters it serves as a demarcation line in the exe file... this gives you an easy way to search through the file and find the location of the start of your data.Here is what is added to the exe at runtime when you click the buttonSO!#GAC1秚estEO!#GAC1The data from the file is the 4 bytes 'test' all the rest is just delimiter data so that ExeMod can locate the data in the exe file. The Character '? in the string is to tell the program where the name given to the data string ('GAC1') ends. it is chr(182) please do NOT use chr(182) as a character in any demarc name or you will cause an error!AddFile2Exe will load the exe from disk if it has not already been loaded... it checks to see if Exe = '' and if so does a ReadExe; ----------------------------------------------------------------------- -Code to extract an ExeMod data block from the exe at runtime and save it to a file on disk... then remove the block from the exe and alter the exe on disk so that it no longer contains any trace of the data block.procedure TForm1.Button1Click(Sender: TObject); begin ExtractAndStrip('gac1','myfile2.txt'); AlterExe; end;This extracts the ExeMod data block gac1 to a file named 'myfile2.txt' the file is placed into the directory where the program's exe file is located.This sort of code is good for making a self-extracting installation program in delphi. All of your help files,dlls..etc can be extracted during the first run of your program... exe size will be small because the data will be removed from the exe after extraction. All files will be extracted to the program's dir unless a full path name is given. ----------------------------------------------------------------------- Code to save a copy of the running exeprocedure TForm1.Button1Click(Sender: TObject); begin Exe2File('MyNewExe.exe'); Exe := ''; //set Exe to empty string (save space if data no longer needed) end;Saves a copy of the running exe as myNewExe.exe in the same directory where the running exe is. If Exe has been altered the altered version will be saved... if Exe has not been loaded yet then ReadExe is called. ----------------------------------------------------------------------- Code to save a modified copy of the running exe... this simple code gives you a program generator.procedure TForm1.Button1Click(Sender: TObject); begin ReadExe; //add code here to modify the string named 'Exe' Exe2File('MyNewExe.exe'); Exe := ''; //set Exe to empty string (save space if data no longer needed) end;This simple code is actually VERY powerful and VERY useful... --------------------------------------------------------------------------> Much more Demo code and many more procedures to come..soon as I get some more free time :-) }
interfaceuses Windows, SysUtils, Classes, Forms, ShellAPI, Dialogs;var Exe: string;procedure Delay(ms: longint); procedure ReadExe; procedure String2File(String2BeSaved, FileName: string); function WinDrv: char; function GetDemarcCount: integer; procedure GetDemarcName(DNumber: Integer; var DName: String); function File2String(FileName: string): string; function PeekExeByte(Byte2Get: Integer): byte; function PeekExeWord(Word2Get: Integer): word; procedure PeekExeString(StartByte,Count: Integer; var ReturnedStr: String); procedure PokeExeByte(Byte2set: Integer; ByteVal: Byte); procedure PokeExeByteI(Byte2set: Integer; ByteVal: Byte); procedure PokeExeString(StartByte: Integer; String2Insert: String); procedure PokeExeStringI(StartByte: Integer; String2Insert: String); procedure ExtractFromExe(DemarcStr: string; var ExtractedStr: string); procedure ExtractFromFile(DemarcStr: string; DataFile: string; var ExtractedStr: string); procedure DelFromString(DemarcStr: string; var String2Change: string); procedure DelFromExe(DemarcStr: string); procedure DelFromFile(DemarcStr, FileName: string); procedure Add2File(DemarcStr, FileName, String2Add: string); procedure ReplaceInFile(DemarcStr, FileName, ReplacementString: string); procedure TackOnFile(DemarcStr, FileName, File2Add: string); procedure Add2String(DemarcStr, String2Add: string; var String2Alter: string); procedure ReplaceInString(DemarcStr, ReplacementString: string; var String2Alter: string); procedure ReplaceInExe(DemarcStr, ReplacementString: string); procedure InsOrReplaceInString(DemarcStr, ReplacementString: string; var String2Alter: string); procedure InsOrReplaceInExe(DemarcStr, ReplacementString: string); procedure AlterExe; procedure ExtractAndStrip(DemarcStr, FileName: string); procedure Exe2File(FileName: string); procedure Extract2File(DemarcStr, FileName: string); procedure AddFile2Exe(DemarcStr, FileName: string); procedure Add2Exe(DemarcStr, String2Add: string); procedure Stream2Exe(TempStream: TMemoryStream); procedure String2Stream(a: String;var b: TMemoryStream); procedure Stream2String(b: TMemoryStream;var a: String); implementation//================================================== //This is just a simple procedure to implement a delayprocedure Delay(ms: longint); var TheTime: LongInt; begin TheTime := GetTickCount + ms; while GetTickCount < TheTime do Application.ProcessMessages; end; //===================================================//=================================================== //This code returns the char of the drive where //windows is installedfunction WinDrv: char; var WinDir : String; n : Integer; begin SetLength(WinDir,256); n := GetWindowsDirectory(PChar(WinDir),256); SetLength(WinDir,n); Result := WinDir[1]; end; //===================================================//=================================================== //This function returns the number of data strings that //are stored in the exe under demarc names... if demarc //delimited data strings have been added to the global //string Exe but not yet flushed to disk by an AlterExe //command, they will still be included in this count!function GetDemarcCount: integer; var Count,X: Integer; begin Count := 0; If Exe = '' then ReadExe; For X := 1 to Length(Exe)-10 do begin If (Exe[X] = 'S') and (Exe[X+1] = 'O') and (Exe[X+2] = '!') and (Exe[X+3] = '#') then begin Inc(Count); end; end; Result := Count; end; //===================================================//=================================================== //You pass a number (DNumber) to this procedure and it //returns a string (DName) that contains the DNumber'th //demarc name from the exe file. If the DNumber'th //demarc string does not exist then an empty string //is returned. This procedure lets you easily look through //the running exe or any other file to discover the demarc //names under which data has been stored.procedure GetDemarcName(DNumber: Integer; var DName: String); var Count,X,Y: Integer; begin Count := 0; If Exe = '' then ReadExe; For X := 1 to Length(Exe)-10 do begin If (Exe[X] = 'S') and (Exe[X+1] = 'O') and (Exe[X+2] = '!') and (Exe[X+3] = '#') then begin Inc(Count); If Count = DNumber then begin Y := X+4; While Exe[Y] <> chr(182) do begin DName := DName+Exe[Y]; Inc(Y); end; end; end; end; end; //=================================================== //=================================================== //This code loads the Exe file from disk and into //the global string var 'Exe'procedure ReadExe; var ExeStream: TFileStream; begin ExeStream := TFileStream.Create(Application.ExeName, fmOpenRead or fmShareDenyNone); try SetLength(Exe, ExeStream.Size); ExeStream.ReadBuffer(Pointer(Exe)^, ExeStream.Size); finally ExeStream.Free; end; end; //===================================================//=================================================== //This code can write any string to disk as a fileprocedure String2File(String2BeSaved, FileName: string); var MyStream: TMemoryStream; begin if String2BeSaved = '' then exit; SetCurrentDir(ExtractFilePath(Application.ExeName)); MyStream := TMemoryStream.Create; try MyStream.WriteBuffer(Pointer(String2BeSaved)^, Length(String2BeSaved)); MyStream.SaveToFile(FileName); finally MyStream.Free; end; end; //===================================================//=================================================== //This code can read any file from disk and into a stringfunction File2String(FileName: string): string; var MyStream: TMemoryStream; MyString: string; begin MyStream := TMemoryStream.Create; try MyStream.LoadFromFile(FileName); MyStream.Position := 0; SetLength(MyString, MyStream.Size); MyStream.ReadBuffer(Pointer(MyString)^, MyStream.Size); finally MyStream.Free; end; Result := MyString; end; //===================================================//=================================================== //This code lets you read a byte from the running exe //directly from the ram. Byte 1 = first byte of exe //DO NOT attempt to read values lower than 1 or function //will fail and produce random data. Be advised that //bytes that are appended to the exe file cannot be read //using this function as that data is not in ram at runtime! //Do not attempt to read within or beyond a few hundred bytes //of the end of your exe (minus any appended data) as data //beyond that range cannot be retrieved with this simple //function... there is no range checking for the upper //bound so use caution.function PeekExeByte(Byte2Get: Integer): byte; Begin If Byte2Get < 1 then Exit; Result := byte(pointer(Hinstance+Byte2Get-1)^); End; //===================================================//=================================================== //This function is nearly identical to the one above //so the comments for PeekExeByte apply here as well.function PeekExeWord(Word2Get: Integer): word; Begin If Word2Get < 1 then Exit; Result := word(pointer(Hinstance+Word2Get-1)^); End; //===================================================//=================================================== //This code returns a string from the exe image in RAM //cautions are similar to PeekExeByteprocedure PeekExeString(StartByte,Count: Integer; var ReturnedStr: String); var X: Integer; Begin If StartByte < 1 then Exit; For X := StartByte to StartByte+Count-1 do begin ReturnedStr := ReturnedStr+(char(pointer(Hinstance+X-1)^)); end; End; //===================================================
其实正如CDSoftwareWj(95927)所说的,不会麻烦,上面的代码应该也不难读懂啊,我先把这个贴完吧//=================================================== //This code can stuff a string into the exe starting at //a specified point... changes are NOT immediately saved //to disk.procedure PokeExeString(StartByte: Integer; String2Insert: String); var X: Integer; Begin If Exe = '' then ReadExe; If StartByte + Length(String2Insert) > Length(Exe) then Exit; If StartByte < 1 then Exit; For X := 1 to Length(String2Insert) do begin Exe[X+StartByte-1] := String2Insert[X]; end; end; //===================================================//=================================================== //This code can stuff a string into the exe starting at //a specified point... changes ARE immediately saved //to disk... Take care!procedure PokeExeStringI(StartByte: Integer; String2Insert: String); var X: Integer; Begin If Exe = '' then ReadExe; If StartByte + Length(String2Insert) > Length(Exe) then Exit; If StartByte < 1 then Exit; For X := 1 to Length(String2Insert) do begin Exe[X+StartByte-1] := String2Insert[X]; end; AlterExe; end; //=================================================== //=================================================== //Pokes a byte value into a specified byte in the string //named Exe. This string holds the image of the running //executable. The altered version of exe is NOT immediately //written to disk!procedure PokeExeByte(Byte2set: Integer; ByteVal: Byte); Begin If Exe = '' then ReadExe; If Byte2Set > Length(Exe) then Exit; Exe[Byte2Set] := chr(ByteVal); end; //===================================================//=================================================== //Identical to PokeExeByte procedure except the changed //byte IS immediately written to disk! USE CARE as the //state of the exe will be lost on restart unless you //store all desired data inside the string named Exe.procedure PokeExeByteI(Byte2set: Integer; ByteVal: Byte); Begin If Exe = '' then ReadExe; If Byte2Set > Length(Exe) then Exit; Exe[Byte2Set] := chr(ByteVal); AlterExe; end; //===================================================//=================================================== //This code gets data from the exe that was stored under a //specified demarc name and places it into a stringprocedure ExtractFromExe(DemarcStr: string; var ExtractedStr: string); var d, e: integer; begin if Length(Exe) = 0 then ReadExe; if Pos(uppercase('so!#' + DemarcStr + chr(182)), Exe) > 0 then begin d := Pos(uppercase('so!#' + DemarcStr + chr(182)), Exe) + length(uppercase('so!#' + DemarcStr + chr(182))); e := Pos(uppercase('eo!#' + DemarcStr), Exe); ExtractedStr := Copy(Exe, d, e - d); end; end; //=================================================== //=================================================== //This code gets a demarc delimited data string from //an external file and loads it into a stringprocedure ExtractFromFile(DemarcStr: string; DataFile: string; var ExtractedStr: string); var d, e: integer; Temp: String; begin Temp := File2String(DataFile); if Pos(uppercase('so!#' + DemarcStr + chr(182)), Temp) > 0 then begin d := Pos(uppercase('so!#' + DemarcStr + chr(182)), Temp) + length(uppercase('so!#' + DemarcStr + chr(182))); e := Pos(uppercase('eo!#' + DemarcStr), Temp); ExtractedStr := Copy(Temp, d, e - d); end; end; //=================================================== //=================================================== //This code deletes a demarc delimited string from any other stringprocedure DelFromString(DemarcStr: string; var String2Change: string); var a, b: string; begin a := UpperCase('so!#' + DemarcStr + chr(182)); b := UpperCase('eo!#' + DemarcStr); delete(String2Change, pos(a, String2Change), (pos(b, String2Change) + length(b) - pos(a, String2Change))); end; //===================================================//=================================================== //This code deletes a demarc delimited string from global //string named Exeprocedure DelFromExe(DemarcStr: string); begin If Exe = '' then ReadExe; DelFromString(DemarcStr,Exe); end; //=================================================== //=================================================== //This code deletes a demarc delimited string from a //file on diskprocedure DelFromFile(DemarcStr, FileName: string); var MyString: string; begin MyString := File2String(FileName); DelFromString(DemarcStr, MyString); String2File(MyString, FileName); end; //===================================================//=================================================== //This code adds a demarc delimited string to a fileprocedure Add2File(DemarcStr, FileName, String2Add: string); var MyString: string; begin If DemarcStr = '' then Exit; MyString := File2String(FileName); MyString := MyString + uppercase('so!#' + DemarcStr + chr(182)) + String2Add + uppercase ('eo!#' + DemarcStr); String2File(MyString, FileName); end; //===================================================//=================================================== //This code adds a demarc delimited string to a file //if the string already exists it is replacedprocedure ReplaceInFile(DemarcStr, FileName, ReplacementString: string); begin If DemarcStr = '' then Exit; DelFromFile(DemarcStr, FileName); Add2File(DemarcStr, FileName, ReplacementString); end; //===================================================//=================================================== //This code adds a demarc delimited file to another fileprocedure TackOnFile(DemarcStr, FileName, File2Add: string); var Mystring: string; begin If DemarcStr = '' then Exit; MyString := File2String(File2add); Add2File(DemarcStr, FileName, MyString); end; //===================================================//=================================================== //This code adds a demarc delimited string to any other //string.procedure Add2String(DemarcStr, String2Add: string; var String2Alter: string); begin If DemarcStr = '' then Exit; String2Alter := String2Alter + uppercase('so!#' + DemarcStr + chr(182)) + String2Add + uppercase('eo!#' + DemarcStr); end; //===================================================
//=================================================== //This code replaces the data held within a demarc delimited //string with new data... the demarc string must exist.procedure ReplaceInString(DemarcStr, ReplacementString: string; var String2Alter: string); begin If DemarcStr = '' then Exit; if pos(uppercase('so!#' + DemarcStr + chr(182)), String2Alter) = 0 then exit; DelFromString(DemarcStr, String2Alter); Add2String(DemarcStr, ReplacementString, String2Alter); end; //===================================================//=================================================== //This code replaces the data held within a demarc delimited //string with new data... the demarc string must exist. this //is identical to ReplaceInString except it defaults to //the global string named Exe.procedure ReplaceInExe(DemarcStr, ReplacementString: string); begin If DemarcStr = '' then Exit; if pos(uppercase('so!#' + DemarcStr + chr(182)), Exe) = 0 then exit; DelFromString(DemarcStr, Exe); Add2String(DemarcStr, ReplacementString, Exe); end; //===================================================//=================================================== //This code will replace the data held within a demarc delimited //string with new data... if the demarc delimited string does not //exist it will be created.procedure InsOrReplaceInString(DemarcStr, ReplacementString: string; var String2Alter: string); begin If DemarcStr = '' then Exit; DelFromString(DemarcStr, String2Alter); Add2String(DemarcStr, ReplacementString, String2Alter); end; //===================================================//=================================================== //This code will replace the data held within a demarc delimited //string with new data... if the demarc delimited string does not //exist it will be created. This is identical with InsOrReplaceInString //except it defaults to the global string Exe.procedure InsOrReplaceInExe(DemarcStr, ReplacementString: string); begin If DemarcStr = '' then Exit; If Exe = '' Then ReadExe; DelFromString(DemarcStr, Exe); Add2String(DemarcStr, ReplacementString, Exe); end; //===================================================//=================================================== //This code swaps the current version of the running exe //on disk for the version that is held in the global //string named 'Exe' Your application quickly shuts down and //re-starts as the new version. ANY DATA THAT YOU WANT THE NEW //VERSION TO BE AWARE OF MUST BE PASSED AS DATA INCLUDED //IN 'Exe' before calling this procedure!!!procedure AlterExe; begin if (Exe) <> '' then begin String2File(Exe, 'temp0a0.exe'); ShellExecute(0, 'open', PChar('temp0a0.exe'), PChar('"'+ExtractFilename(Application.ExeName)+'"'), nil, SW_SHOW); Application.Terminate; end; end; //===================================================//=================================================== //This code extracts a demarc delimited string from the //global string 'Exe' to a file. The data is then stripped //out of 'Exe' .. this procedure is very useful for //making self extracting archives that remove all the //archived files from the exe after extraction... reducing //hd space required... just store all your dll's, images //etc in the exe and extract and strip them the first //time the user runs the program :-)procedure ExtractAndStrip(DemarcStr, FileName: string); var Temp: string; begin ExtractFromExe(DemarcStr, Temp); if Length(Temp) <> 0 then begin DelFromString(DemarcStr, Exe); String2File(Temp, FileName); end; end; //===================================================//=================================================== //This code saves the global string exe to disk as a //new exe.procedure Exe2File(FileName: string); begin if Exe = '' then ReadExe; String2File(Exe, FileName); end; //===================================================//=================================================== //This code extracts a demarc delimited string from the //exe and saves it as a file.procedure Extract2File(DemarcStr, FileName: string); var MyString: string; begin ExtractFromExe(DemarcStr, MyString); if MyString <> '' then String2File(MyString, FileName); end; //===================================================//=================================================== //This code adds a file to the global string exe as //a demarc delimited string.procedure AddFile2Exe(DemarcStr, FileName: string); var MyString: string; begin If DemarcStr = '' then Exit; MyString := File2String(FileName); if Exe = '' then ReadExe; Add2String(DemarcStr, MyString, Exe); end; //===================================================//=================================================== //This code adds a demarc delimited string to the //global string Exe.procedure Add2Exe(DemarcStr, String2Add: string); begin If DemarcStr = '' then Exit; if Exe = '' then readExe; Add2String(DemarcStr, String2Add, Exe); end; //===================================================//=================================================== //This code replaces the current exe file on disk with //one that is held in a TMemoryStream. This makes it //easy to update an exe by downloading a new version //over the internet and into a stream... use any of //the free internet packs... like Fpiettes or Synapse libprocedure Stream2Exe(TempStream: TMemoryStream); begin SetCurrentDir(ExtractFilePath(Application.ExeName)); TempStream.SaveToFile('temp0a0.exe'); ShellExecute(0, 'open', PChar('temp0a0.exe'), PChar(ExtractFilename(Application.ExeName)), nil, SW_SHOW); Application.Terminate; end; //===================================================//============================================================================== //This code copies a string to a streamprocedure String2Stream(a: String;var b: TMemoryStream); begin b.Position := 0; b.WriteBuffer(Pointer(a)^,Length(a)); b.Position := 0; end;//==============================================================================//============================================================================== //This code copies a stream to a stringprocedure Stream2String(b: TMemoryStream;var a: String); begin b.Position := 0; SetLength(a,b.Size); b.ReadBuffer(Pointer(a)^,b.Size); b.Position := 0; end;
//============================================================================== //This is the ExeMod.pas Initialization code...this code runs each time your //exe is started... this code handles the actual swapping of one version of //the exe for another. This is done by an exe file swap and re-start carried //out by a temporary copy of the exe named temp0a0.exe. The initialization //code can look for and delete a file named temp0a0.exe if it is found //in the current dir. This is how temp0a0.exe is removed after an exe swap. initialization begin SetCurrentDir(ExtractFilePath(Application.ExeName));//****************************************************************************** //NOTE--> WinDrv = The drive where Windows is installed! // //Beginning of code that lets an ExeMod exe work properly even //if it is on a read-only CD ... this code will cause the exe //on the CD to create a dir on WinDrv with the same name as the running //exe. Then it moves a copy of the exe to that dir and runs that //copy before shutting down. Thereafter any time the exe on CD is //run it will act only as a shortcut to the version on WinDrv. //If you do not want this functionality then just comment out this //code or delete it from this unit. You can also disable this code //by changing byte 3 of your exe so that it holds a zero value. //'MZP' becomes 'MZ' followed by a zero byte. //Programs that do not attempt to alter themselves at runtime and //which do not attempt to save files in the current dir should //disable this code... otherwise the app will always needlessly copy //itself to WinDrv and run from there!if (FileGetAttr(Application.ExeName) and faReadOnly) > 0 then begin if PeekExeByte(3) = 0 then Exit; FileSetAttr(Application.ExeName, FileGetAttr(Application.ExeName) xor faReadOnly); if (FileGetAttr(Application.ExeName) and faReadOnly) > 0 then begin If FileExists(Windrv+':\'+ExtractFileName(Application.ExeName)+ '\'+ExtractFileName(Application.ExeName)) then begin ShellExecute(0, 'open', PChar((WinDrv+':\'+ExtractFileName(Application.ExeName)+ '\'+ExtractFileName(Application.ExeName))),nil, nil, SW_SHOW); Application.Terminate; end else begin CreateDir(WinDrv+':\'+ExtractFileName(Application.ExeName)); Exe2File(WinDrv+':\'+ExtractFileName(Application.ExeName)+ '\'+ExtractFileName(Application.ExeName)); ShellExecute(0, 'open', PChar((WinDrv+':\'+ExtractFileName(Application.ExeName)+ '\'+ExtractFileName(Application.ExeName))),nil, nil, SW_SHOW); Application.terminate; end; end; end; //This is the end of the code that allows proper operation from a CD. //****************************************************************************** if ParamStr(1) = 'deltemp' then begin while not (deletefile('temp0a0.exe')) do begin if (FileGetAttr('temp0a0.exe') and faReadOnly) > 0 then FileSetAttr('temp0a0.exe', FileGetAttr('temp0a0.exe') xor faReadOnly); delay(10); end; deletefile('temp0a0.exe'); end; if UpperCase(ExtractFileName(Application.ExeName)) = ('TEMP0A0.EXE') then begin ReadExe; while not (deletefile(ParamStr(1))) do begin if (FileGetAttr(ParamStr(1)) and faReadOnly) > 0 then FileSetAttr(ParamStr(1), FileGetAttr(ParamStr(1)) xor faReadOnly); delay(10); end; String2File(Exe, ParamStr(1)); ShellExecute(0, 'open', PChar(ParamStr(1)), PChar('deltemp'), nil, SW_SHOW); Application.Terminate; end; end;end.
我贴出来吧
{
This is the first beta of the full version 1.00
That allows an ExeMod exe to work properly even
from a CD..for info on how an exe on CD is handled
please see the notes in the Interface section.Written by G.A. Carpenter (works on D2 thru D7)
This is FreeWare for any purpose...use as you will.
Consider it public domain... feel free to make
any changes you want... if you improve it I'd love
to get a copy of the improvements :-)[email protected] <gwen carpenter (Gwena)>You can use this in any program, commercial or freeware,
and you DO NOT need to mention where it came from.
(no attribution needed)If you use NT/2000/XP please read the NT-NOTE below!This unit enables you to make a delphi application that can
alter its own exe file on disk... this appears to happen
at runtime but actually the switch quickly takes place after
a shutdown of the running app during which a copy of the exe
deletes the old version and replaces it with a new version.While it is also possible to alter a running exe directly by altering
the hd sectors that hold the exe's data. I abandoned that scheme
because although it worked quickly and transparently it proved to
be risky :-(This unit lets you easily create...
-Self altering exe files
-Program Generators
-Apps that update live over the internet
-Apps that keep an internal record of data
(no ini file or registry usage needed)
-File handling utils
-Copy protection schemes
-Self extracting archives
-Programs that alter and store data in other programs and files.
...and many other strange things ;-)
NOTE! ExeMod will not delete a demarc delimited string from an
external file if the deletion would take the file down to 0 bytes
in length! This is because some disk cleanup utils can be set to
automatically remove 0 length files. If you intend to use an external
file to hold data that will be handled by ExeMod you should begin
by placing at least a single byte of data at the beginning of the file.------
NT-NOTE!If you use ExeMod.pas on an NT machine then you may need to run the exe
outside the delphi ide... for some reason NT causes the debugger to
faint at the sight of my code :-) the exe files run just fine on NT
but just not from inside the ide....-------
Here are some simple demos for you to look over.(Remember to put ExeMod.pas into your compiler's path or into your
project's directory... then add ExeMod to your program's uses clause)
------------------------------------------------------------------------
-Code to alter a byte in an exe and then alter the exe at runtime and
put the change onto the exe's file on diskprocedure TForm1.Button1Click(Sender: TObject);
begin
ReadExe; //Read exe file from disk and put into global string named Exe
Exe[3] := 'A'; //Change byte 3 of Global string Exe
AlterExe; //Alter the exe file on disk then quickly restart program
end;
------------------------------------------------------------------------
-Code to add a file from disk to the end of the exe at runtime.
Assume a small txt file named C:\myfile.txt exists and contains
the text 'test'procedure TForm1.Button1Click(Sender: TObject);
begin
AddFile2Exe('gac1','C:\myfile.txt');
AlterExe;
end;The string 'gac1' serves to locate the data at the end of the exe.
I call gac1 a 'Demarc name' because when combined with a few other
characters it serves as a demarcation line in the exe file... this
gives you an easy way to search through the file and find the location
of the start of your data.Here is what is added to the exe at runtime when you click the buttonSO!#GAC1秚estEO!#GAC1The data from the file is the 4 bytes 'test' all the rest is just delimiter
data so that ExeMod can locate the data in the exe file.
The Character '? in the string is to tell the program where the name
given to the data string ('GAC1') ends. it is chr(182) please do NOT
use chr(182) as a character in any demarc name or you will cause an error!AddFile2Exe will load the exe from disk if it has not already been
loaded... it checks to see if Exe = '' and if so does a ReadExe;
-----------------------------------------------------------------------
-Code to extract an ExeMod data block from the exe at runtime and save
it to a file on disk... then remove the block from the exe and alter the
exe on disk so that it no longer contains any trace of the data block.procedure TForm1.Button1Click(Sender: TObject);
begin
ExtractAndStrip('gac1','myfile2.txt');
AlterExe;
end;This extracts the ExeMod data block gac1 to a file named 'myfile2.txt'
the file is placed into the directory where the program's exe file is
located.This sort of code is good for making a self-extracting installation
program in delphi. All of your help files,dlls..etc can be extracted
during the first run of your program... exe size will be small because
the data will be removed from the exe after extraction. All files will
be extracted to the program's dir unless a full path name is given.
-----------------------------------------------------------------------
Code to save a copy of the running exeprocedure TForm1.Button1Click(Sender: TObject);
begin
Exe2File('MyNewExe.exe');
Exe := ''; //set Exe to empty string (save space if data no longer needed)
end;Saves a copy of the running exe as myNewExe.exe in the same directory
where the running exe is. If Exe has been altered the altered version
will be saved... if Exe has not been loaded yet then ReadExe is called.
-----------------------------------------------------------------------
Code to save a modified copy of the running exe... this simple code
gives you a program generator.procedure TForm1.Button1Click(Sender: TObject);
begin
ReadExe;
//add code here to modify the string named 'Exe'
Exe2File('MyNewExe.exe');
Exe := ''; //set Exe to empty string (save space if data no longer needed)
end;This simple code is actually VERY powerful and VERY useful...
--------------------------------------------------------------------------> Much more Demo code and many more procedures to come..soon as I
get some more free time :-)
}
Windows, SysUtils, Classes, Forms, ShellAPI, Dialogs;var Exe: string;procedure Delay(ms: longint);
procedure ReadExe;
procedure String2File(String2BeSaved, FileName: string);
function WinDrv: char;
function GetDemarcCount: integer;
procedure GetDemarcName(DNumber: Integer; var DName: String);
function File2String(FileName: string): string;
function PeekExeByte(Byte2Get: Integer): byte;
function PeekExeWord(Word2Get: Integer): word;
procedure PeekExeString(StartByte,Count: Integer; var ReturnedStr: String);
procedure PokeExeByte(Byte2set: Integer; ByteVal: Byte);
procedure PokeExeByteI(Byte2set: Integer; ByteVal: Byte);
procedure PokeExeString(StartByte: Integer; String2Insert: String);
procedure PokeExeStringI(StartByte: Integer; String2Insert: String);
procedure ExtractFromExe(DemarcStr: string; var ExtractedStr: string);
procedure ExtractFromFile(DemarcStr: string; DataFile: string; var ExtractedStr: string);
procedure DelFromString(DemarcStr: string; var String2Change: string);
procedure DelFromExe(DemarcStr: string);
procedure DelFromFile(DemarcStr, FileName: string);
procedure Add2File(DemarcStr, FileName, String2Add: string);
procedure ReplaceInFile(DemarcStr, FileName, ReplacementString: string);
procedure TackOnFile(DemarcStr, FileName, File2Add: string);
procedure Add2String(DemarcStr, String2Add: string; var String2Alter: string);
procedure ReplaceInString(DemarcStr, ReplacementString:
string; var String2Alter: string);
procedure ReplaceInExe(DemarcStr, ReplacementString: string);
procedure InsOrReplaceInString(DemarcStr, ReplacementString:
string; var String2Alter: string);
procedure InsOrReplaceInExe(DemarcStr, ReplacementString: string);
procedure AlterExe;
procedure ExtractAndStrip(DemarcStr, FileName: string);
procedure Exe2File(FileName: string);
procedure Extract2File(DemarcStr, FileName: string);
procedure AddFile2Exe(DemarcStr, FileName: string);
procedure Add2Exe(DemarcStr, String2Add: string);
procedure Stream2Exe(TempStream: TMemoryStream);
procedure String2Stream(a: String;var b: TMemoryStream);
procedure Stream2String(b: TMemoryStream;var a: String);
implementation//==================================================
//This is just a simple procedure to implement a delayprocedure Delay(ms: longint);
var
TheTime: LongInt;
begin
TheTime := GetTickCount + ms;
while GetTickCount < TheTime do
Application.ProcessMessages;
end;
//===================================================//===================================================
//This code returns the char of the drive where
//windows is installedfunction WinDrv: char;
var
WinDir : String;
n : Integer;
begin
SetLength(WinDir,256);
n := GetWindowsDirectory(PChar(WinDir),256);
SetLength(WinDir,n);
Result := WinDir[1];
end;
//===================================================//===================================================
//This function returns the number of data strings that
//are stored in the exe under demarc names... if demarc
//delimited data strings have been added to the global
//string Exe but not yet flushed to disk by an AlterExe
//command, they will still be included in this count!function GetDemarcCount: integer;
var Count,X: Integer;
begin
Count := 0;
If Exe = '' then ReadExe;
For X := 1 to Length(Exe)-10 do
begin
If (Exe[X] = 'S') and (Exe[X+1] = 'O')
and (Exe[X+2] = '!') and (Exe[X+3] = '#')
then
begin
Inc(Count);
end;
end;
Result := Count;
end;
//===================================================//===================================================
//You pass a number (DNumber) to this procedure and it
//returns a string (DName) that contains the DNumber'th
//demarc name from the exe file. If the DNumber'th
//demarc string does not exist then an empty string
//is returned. This procedure lets you easily look through
//the running exe or any other file to discover the demarc
//names under which data has been stored.procedure GetDemarcName(DNumber: Integer; var DName: String);
var Count,X,Y: Integer;
begin
Count := 0;
If Exe = '' then ReadExe;
For X := 1 to Length(Exe)-10 do
begin
If (Exe[X] = 'S') and (Exe[X+1] = 'O')
and (Exe[X+2] = '!') and (Exe[X+3] = '#')
then
begin
Inc(Count);
If Count = DNumber then
begin
Y := X+4;
While Exe[Y] <> chr(182) do
begin
DName := DName+Exe[Y];
Inc(Y);
end;
end;
end;
end;
end;
//===================================================
//===================================================
//This code loads the Exe file from disk and into
//the global string var 'Exe'procedure ReadExe;
var
ExeStream: TFileStream;
begin
ExeStream := TFileStream.Create(Application.ExeName, fmOpenRead
or fmShareDenyNone);
try
SetLength(Exe, ExeStream.Size);
ExeStream.ReadBuffer(Pointer(Exe)^, ExeStream.Size);
finally
ExeStream.Free;
end;
end;
//===================================================//===================================================
//This code can write any string to disk as a fileprocedure String2File(String2BeSaved, FileName: string);
var
MyStream: TMemoryStream;
begin
if String2BeSaved = '' then exit;
SetCurrentDir(ExtractFilePath(Application.ExeName));
MyStream := TMemoryStream.Create;
try
MyStream.WriteBuffer(Pointer(String2BeSaved)^, Length(String2BeSaved));
MyStream.SaveToFile(FileName);
finally
MyStream.Free;
end;
end;
//===================================================//===================================================
//This code can read any file from disk and into a stringfunction File2String(FileName: string): string;
var
MyStream: TMemoryStream;
MyString: string;
begin
MyStream := TMemoryStream.Create;
try
MyStream.LoadFromFile(FileName);
MyStream.Position := 0;
SetLength(MyString, MyStream.Size);
MyStream.ReadBuffer(Pointer(MyString)^, MyStream.Size);
finally
MyStream.Free;
end;
Result := MyString;
end;
//===================================================//===================================================
//This code lets you read a byte from the running exe
//directly from the ram. Byte 1 = first byte of exe
//DO NOT attempt to read values lower than 1 or function
//will fail and produce random data. Be advised that
//bytes that are appended to the exe file cannot be read
//using this function as that data is not in ram at runtime!
//Do not attempt to read within or beyond a few hundred bytes
//of the end of your exe (minus any appended data) as data
//beyond that range cannot be retrieved with this simple
//function... there is no range checking for the upper
//bound so use caution.function PeekExeByte(Byte2Get: Integer): byte;
Begin
If Byte2Get < 1 then Exit;
Result := byte(pointer(Hinstance+Byte2Get-1)^);
End;
//===================================================//===================================================
//This function is nearly identical to the one above
//so the comments for PeekExeByte apply here as well.function PeekExeWord(Word2Get: Integer): word;
Begin
If Word2Get < 1 then Exit;
Result := word(pointer(Hinstance+Word2Get-1)^);
End;
//===================================================//===================================================
//This code returns a string from the exe image in RAM
//cautions are similar to PeekExeByteprocedure PeekExeString(StartByte,Count: Integer; var ReturnedStr: String);
var X: Integer;
Begin
If StartByte < 1 then Exit;
For X := StartByte to StartByte+Count-1 do
begin
ReturnedStr := ReturnedStr+(char(pointer(Hinstance+X-1)^));
end;
End;
//===================================================
//This code can stuff a string into the exe starting at
//a specified point... changes are NOT immediately saved
//to disk.procedure PokeExeString(StartByte: Integer; String2Insert: String);
var X: Integer;
Begin
If Exe = '' then ReadExe;
If StartByte + Length(String2Insert) > Length(Exe) then Exit;
If StartByte < 1 then Exit;
For X := 1 to Length(String2Insert) do
begin
Exe[X+StartByte-1] := String2Insert[X];
end;
end;
//===================================================//===================================================
//This code can stuff a string into the exe starting at
//a specified point... changes ARE immediately saved
//to disk... Take care!procedure PokeExeStringI(StartByte: Integer; String2Insert: String);
var X: Integer;
Begin
If Exe = '' then ReadExe;
If StartByte + Length(String2Insert) > Length(Exe) then Exit;
If StartByte < 1 then Exit;
For X := 1 to Length(String2Insert) do
begin
Exe[X+StartByte-1] := String2Insert[X];
end;
AlterExe;
end;
//===================================================
//===================================================
//Pokes a byte value into a specified byte in the string
//named Exe. This string holds the image of the running
//executable. The altered version of exe is NOT immediately
//written to disk!procedure PokeExeByte(Byte2set: Integer; ByteVal: Byte);
Begin
If Exe = '' then ReadExe;
If Byte2Set > Length(Exe) then Exit;
Exe[Byte2Set] := chr(ByteVal);
end;
//===================================================//===================================================
//Identical to PokeExeByte procedure except the changed
//byte IS immediately written to disk! USE CARE as the
//state of the exe will be lost on restart unless you
//store all desired data inside the string named Exe.procedure PokeExeByteI(Byte2set: Integer; ByteVal: Byte);
Begin
If Exe = '' then ReadExe;
If Byte2Set > Length(Exe) then Exit;
Exe[Byte2Set] := chr(ByteVal);
AlterExe;
end;
//===================================================//===================================================
//This code gets data from the exe that was stored under a
//specified demarc name and places it into a stringprocedure ExtractFromExe(DemarcStr: string; var ExtractedStr: string);
var
d, e: integer;
begin
if Length(Exe) = 0 then ReadExe;
if Pos(uppercase('so!#' + DemarcStr + chr(182)), Exe) > 0 then
begin
d := Pos(uppercase('so!#' + DemarcStr + chr(182)), Exe)
+ length(uppercase('so!#' + DemarcStr + chr(182)));
e := Pos(uppercase('eo!#' + DemarcStr), Exe);
ExtractedStr := Copy(Exe, d, e - d);
end;
end;
//===================================================
//===================================================
//This code gets a demarc delimited data string from
//an external file and loads it into a stringprocedure ExtractFromFile(DemarcStr: string; DataFile: string; var ExtractedStr: string);
var
d, e: integer;
Temp: String;
begin
Temp := File2String(DataFile);
if Pos(uppercase('so!#' + DemarcStr + chr(182)), Temp) > 0 then
begin
d := Pos(uppercase('so!#' + DemarcStr + chr(182)), Temp)
+ length(uppercase('so!#' + DemarcStr + chr(182)));
e := Pos(uppercase('eo!#' + DemarcStr), Temp);
ExtractedStr := Copy(Temp, d, e - d);
end;
end;
//===================================================
//===================================================
//This code deletes a demarc delimited string from any other stringprocedure DelFromString(DemarcStr: string; var String2Change: string);
var
a, b: string;
begin
a := UpperCase('so!#' + DemarcStr + chr(182));
b := UpperCase('eo!#' + DemarcStr);
delete(String2Change, pos(a, String2Change), (pos(b, String2Change)
+ length(b) - pos(a, String2Change)));
end;
//===================================================//===================================================
//This code deletes a demarc delimited string from global
//string named Exeprocedure DelFromExe(DemarcStr: string);
begin
If Exe = '' then ReadExe;
DelFromString(DemarcStr,Exe);
end;
//===================================================
//===================================================
//This code deletes a demarc delimited string from a
//file on diskprocedure DelFromFile(DemarcStr, FileName: string);
var
MyString: string;
begin
MyString := File2String(FileName);
DelFromString(DemarcStr, MyString);
String2File(MyString, FileName);
end;
//===================================================//===================================================
//This code adds a demarc delimited string to a fileprocedure Add2File(DemarcStr, FileName, String2Add: string);
var
MyString: string;
begin
If DemarcStr = '' then Exit;
MyString := File2String(FileName);
MyString := MyString + uppercase('so!#' + DemarcStr + chr(182)) + String2Add + uppercase
('eo!#' + DemarcStr);
String2File(MyString, FileName);
end;
//===================================================//===================================================
//This code adds a demarc delimited string to a file
//if the string already exists it is replacedprocedure ReplaceInFile(DemarcStr, FileName, ReplacementString: string);
begin
If DemarcStr = '' then Exit;
DelFromFile(DemarcStr, FileName);
Add2File(DemarcStr, FileName, ReplacementString);
end;
//===================================================//===================================================
//This code adds a demarc delimited file to another fileprocedure TackOnFile(DemarcStr, FileName, File2Add: string);
var
Mystring: string;
begin
If DemarcStr = '' then Exit;
MyString := File2String(File2add);
Add2File(DemarcStr, FileName, MyString);
end;
//===================================================//===================================================
//This code adds a demarc delimited string to any other
//string.procedure Add2String(DemarcStr, String2Add: string; var String2Alter: string);
begin
If DemarcStr = '' then Exit;
String2Alter := String2Alter + uppercase('so!#' + DemarcStr + chr(182))
+ String2Add + uppercase('eo!#' + DemarcStr);
end;
//===================================================
//This code replaces the data held within a demarc delimited
//string with new data... the demarc string must exist.procedure ReplaceInString(DemarcStr, ReplacementString: string;
var String2Alter: string);
begin
If DemarcStr = '' then Exit;
if pos(uppercase('so!#' + DemarcStr + chr(182)), String2Alter) = 0 then exit;
DelFromString(DemarcStr, String2Alter);
Add2String(DemarcStr, ReplacementString, String2Alter);
end;
//===================================================//===================================================
//This code replaces the data held within a demarc delimited
//string with new data... the demarc string must exist. this
//is identical to ReplaceInString except it defaults to
//the global string named Exe.procedure ReplaceInExe(DemarcStr, ReplacementString: string);
begin
If DemarcStr = '' then Exit;
if pos(uppercase('so!#' + DemarcStr + chr(182)), Exe) = 0 then exit;
DelFromString(DemarcStr, Exe);
Add2String(DemarcStr, ReplacementString, Exe);
end;
//===================================================//===================================================
//This code will replace the data held within a demarc delimited
//string with new data... if the demarc delimited string does not
//exist it will be created.procedure InsOrReplaceInString(DemarcStr, ReplacementString: string;
var String2Alter: string);
begin
If DemarcStr = '' then Exit;
DelFromString(DemarcStr, String2Alter);
Add2String(DemarcStr, ReplacementString, String2Alter);
end;
//===================================================//===================================================
//This code will replace the data held within a demarc delimited
//string with new data... if the demarc delimited string does not
//exist it will be created. This is identical with InsOrReplaceInString
//except it defaults to the global string Exe.procedure InsOrReplaceInExe(DemarcStr, ReplacementString: string);
begin
If DemarcStr = '' then Exit;
If Exe = '' Then ReadExe;
DelFromString(DemarcStr, Exe);
Add2String(DemarcStr, ReplacementString, Exe);
end;
//===================================================//===================================================
//This code swaps the current version of the running exe
//on disk for the version that is held in the global
//string named 'Exe' Your application quickly shuts down and
//re-starts as the new version. ANY DATA THAT YOU WANT THE NEW
//VERSION TO BE AWARE OF MUST BE PASSED AS DATA INCLUDED
//IN 'Exe' before calling this procedure!!!procedure AlterExe;
begin
if (Exe) <> '' then
begin
String2File(Exe, 'temp0a0.exe');
ShellExecute(0, 'open', PChar('temp0a0.exe'),
PChar('"'+ExtractFilename(Application.ExeName)+'"'), nil, SW_SHOW);
Application.Terminate;
end;
end;
//===================================================//===================================================
//This code extracts a demarc delimited string from the
//global string 'Exe' to a file. The data is then stripped
//out of 'Exe' .. this procedure is very useful for
//making self extracting archives that remove all the
//archived files from the exe after extraction... reducing
//hd space required... just store all your dll's, images
//etc in the exe and extract and strip them the first
//time the user runs the program :-)procedure ExtractAndStrip(DemarcStr, FileName: string);
var
Temp: string;
begin
ExtractFromExe(DemarcStr, Temp);
if Length(Temp) <> 0 then
begin
DelFromString(DemarcStr, Exe);
String2File(Temp, FileName);
end;
end;
//===================================================//===================================================
//This code saves the global string exe to disk as a
//new exe.procedure Exe2File(FileName: string);
begin
if Exe = '' then ReadExe;
String2File(Exe, FileName);
end;
//===================================================//===================================================
//This code extracts a demarc delimited string from the
//exe and saves it as a file.procedure Extract2File(DemarcStr, FileName: string);
var
MyString: string;
begin
ExtractFromExe(DemarcStr, MyString);
if MyString <> '' then String2File(MyString, FileName);
end;
//===================================================//===================================================
//This code adds a file to the global string exe as
//a demarc delimited string.procedure AddFile2Exe(DemarcStr, FileName: string);
var
MyString: string;
begin
If DemarcStr = '' then Exit;
MyString := File2String(FileName);
if Exe = '' then ReadExe;
Add2String(DemarcStr, MyString, Exe);
end;
//===================================================//===================================================
//This code adds a demarc delimited string to the
//global string Exe.procedure Add2Exe(DemarcStr, String2Add: string);
begin
If DemarcStr = '' then Exit;
if Exe = '' then readExe;
Add2String(DemarcStr, String2Add, Exe);
end;
//===================================================//===================================================
//This code replaces the current exe file on disk with
//one that is held in a TMemoryStream. This makes it
//easy to update an exe by downloading a new version
//over the internet and into a stream... use any of
//the free internet packs... like Fpiettes or Synapse libprocedure Stream2Exe(TempStream: TMemoryStream);
begin
SetCurrentDir(ExtractFilePath(Application.ExeName));
TempStream.SaveToFile('temp0a0.exe');
ShellExecute(0, 'open', PChar('temp0a0.exe'),
PChar(ExtractFilename(Application.ExeName)), nil, SW_SHOW);
Application.Terminate;
end;
//===================================================//==============================================================================
//This code copies a string to a streamprocedure String2Stream(a: String;var b: TMemoryStream);
begin
b.Position := 0;
b.WriteBuffer(Pointer(a)^,Length(a));
b.Position := 0;
end;//==============================================================================//==============================================================================
//This code copies a stream to a stringprocedure Stream2String(b: TMemoryStream;var a: String);
begin
b.Position := 0;
SetLength(a,b.Size);
b.ReadBuffer(Pointer(a)^,b.Size);
b.Position := 0;
end;
//This is the ExeMod.pas Initialization code...this code runs each time your
//exe is started... this code handles the actual swapping of one version of
//the exe for another. This is done by an exe file swap and re-start carried
//out by a temporary copy of the exe named temp0a0.exe. The initialization
//code can look for and delete a file named temp0a0.exe if it is found
//in the current dir. This is how temp0a0.exe is removed after an exe swap. initialization
begin
SetCurrentDir(ExtractFilePath(Application.ExeName));//******************************************************************************
//NOTE--> WinDrv = The drive where Windows is installed!
//
//Beginning of code that lets an ExeMod exe work properly even
//if it is on a read-only CD ... this code will cause the exe
//on the CD to create a dir on WinDrv with the same name as the running
//exe. Then it moves a copy of the exe to that dir and runs that
//copy before shutting down. Thereafter any time the exe on CD is
//run it will act only as a shortcut to the version on WinDrv.
//If you do not want this functionality then just comment out this
//code or delete it from this unit. You can also disable this code
//by changing byte 3 of your exe so that it holds a zero value.
//'MZP' becomes 'MZ' followed by a zero byte.
//Programs that do not attempt to alter themselves at runtime and
//which do not attempt to save files in the current dir should
//disable this code... otherwise the app will always needlessly copy
//itself to WinDrv and run from there!if (FileGetAttr(Application.ExeName) and faReadOnly) > 0 then
begin
if PeekExeByte(3) = 0 then Exit;
FileSetAttr(Application.ExeName, FileGetAttr(Application.ExeName)
xor faReadOnly);
if (FileGetAttr(Application.ExeName) and faReadOnly) > 0 then
begin
If FileExists(Windrv+':\'+ExtractFileName(Application.ExeName)+
'\'+ExtractFileName(Application.ExeName)) then
begin
ShellExecute(0, 'open', PChar((WinDrv+':\'+ExtractFileName(Application.ExeName)+
'\'+ExtractFileName(Application.ExeName))),nil, nil, SW_SHOW);
Application.Terminate;
end
else
begin
CreateDir(WinDrv+':\'+ExtractFileName(Application.ExeName));
Exe2File(WinDrv+':\'+ExtractFileName(Application.ExeName)+
'\'+ExtractFileName(Application.ExeName));
ShellExecute(0, 'open', PChar((WinDrv+':\'+ExtractFileName(Application.ExeName)+
'\'+ExtractFileName(Application.ExeName))),nil, nil, SW_SHOW);
Application.terminate;
end;
end;
end;
//This is the end of the code that allows proper operation from a CD.
//****************************************************************************** if ParamStr(1) = 'deltemp' then
begin
while not (deletefile('temp0a0.exe')) do
begin
if (FileGetAttr('temp0a0.exe') and faReadOnly) > 0
then FileSetAttr('temp0a0.exe', FileGetAttr('temp0a0.exe')
xor faReadOnly);
delay(10);
end;
deletefile('temp0a0.exe');
end;
if UpperCase(ExtractFileName(Application.ExeName)) = ('TEMP0A0.EXE') then
begin
ReadExe;
while not (deletefile(ParamStr(1))) do
begin
if (FileGetAttr(ParamStr(1)) and faReadOnly) > 0
then FileSetAttr(ParamStr(1), FileGetAttr(ParamStr(1))
xor faReadOnly);
delay(10);
end;
String2File(Exe, ParamStr(1));
ShellExecute(0, 'open', PChar(ParamStr(1)),
PChar('deltemp'), nil, SW_SHOW);
Application.Terminate;
end;
end;end.
动态的生成EXE文件 Exe2File ExtractFromExe
将图片放入文件中 Add2File甚至加入另一个exe文件都可以
这是一段写的非常漂亮的程序,
详细的注释,并且给出了调用的例子(最前面那段),值得花一点时间去看一下的
我当初就是为了写一个生成屏幕保护程序的软件,后来找到了这段代码
你真是一个不称职的程序员。
要不要别人给你把工程写好了,再全部给你啊
我寫了一個追加自己資源到exe的類.它可以讀寫你自己的資源.
unit UserExeResU;interface
uses classes,Dialogs ;
Type
TUserResHead = packed record
User : string[4];// 'User'
ResCount : integer ;
ResSetoff:int64 ;
MaxID : integer ;
end;
TResHead = packed record
ResID : integer ;
Name : string[64] ;
Length: integer ;
end;
TUserExeRes = class
private
FUserResHead : TUserResHead ;
FExeStream : TFileStream ;
function GetResCount: integer;
function GetMaxID: integer;
protected
Function ReadToStream(Length:integer;Stream:TStream):boolean;
public
Function GetResName(ResID:integer):string;
property ResCount : integer Read GetResCount ;
property MaxID : integer Read GetMaxID ;
Function ReadToStreamForID(ResID:integer;stream:TStream):boolean;
Function ReadToStreamForName(ResName:shortstring;Stream:TStream):boolean;
Function WriteToRes(ResName:shortstring;Stream:TStream):integer;
constructor Create(ExeName:string);
destructor Destroy; override;
published end;implementationuses SysUtils;{ TUserExeRes }constructor TUserExeRes.Create(ExeName: string);
var
Mode : word ;
begin
fillchar(FUserResHead,sizeof(TUserResHead),0);
if not FileExists(ExeName) then
begin
Mode :=fmCreate or fmShareDenyNone ;
FUserResHead.User :='User';
FUserResHead.MaxID := 0 ;
FUserResHead.ResCount :=0;
FUserResHead.ResSetoff :=0 ;
end
else
mode :=fmOpenReadWrite or fmShareDenyNone ;
FExeStream := TFileStream.Create(ExeName,mode) ;
FExeStream.Seek(-1,soFromEnd);
if FUserResHead.User ='User' then
FExeStream.Write(FUserResHead,sizeof(TUserResHead)) ;
if FExeStream.Size< sizeof(TUserResHead) then
begin
FExeStream.Free ;
raise Exception.Create('Error!!');
end;
FillChar(FUserResHead,sizeof(TUserResHead),0) ;
FExeStream.Position:=FExeStream.Size-sizeof(TUserResHead);
FExeStream.Read(FUserResHead,sizeof(TUserResHead));
if FUserResHead.User<>'User' then
begin
FUserResHead.User := 'User' ;
FUserResHead.ResCount := 0 ;
FUserResHead.ResSetOff := FExeStream.Size ;
FUserResHead.MaxID := 0 ;
FExeStream.Position := FExeStream.Size ;
FExeStream.Write(FUserResHead,sizeof(TUserResHead)) ;
end;
end;destructor TUserExeRes.Destroy;
begin
FExeStream.Free ;
inherited;
end;function TUserExeRes.GetMaxID: integer;
begin
Result := FUserResHead.MaxID ;
end;function TUserExeRes.GetResCount: integer;
begin
Result :=FUserResHead.ResCount ;
end;function TUserExeRes.GetResName(ResID: integer): string;
var
ResHead :TResHead;
begin
Result :='';
FExeStream.Position := FUserResHead.ResSetOff ;
while FExeStream.Position< (FExeStream.Size-sizeof(TUserResHead)) do
begin
fillchar(ResHead,sizeof(TResHead),0);
FExeStream.Read(ResHead,sizeof(TResHead));
if ResHead.ResID=ResID then
begin
Result:=ResHead.Name ;
Break ;
end else
FExeStream.Position := FExeStream.Position+ ResHead.Length ;
end;
end;function TUserExeRes.ReadToStream(Length: integer;
Stream: TStream): boolean;
var
buf : String ;
begin
Result := False ;
if (FExeStream.Position+Length)>FExeStream.Size then
Exit ;
Setlength(Buf,Length);
FExeStream.Read(Pointer(buf)^,Length);
stream.Write(Pointer(buf)^,Length);
Result := true ;
end;function TUserExeRes.ReadToStreamForID(ResID: integer;
stream: TStream): boolean;
var
ResHead :TResHead;
begin
Result := False ;
FExeStream.Position := FUserResHead.ResSetOff ;
while FExeStream.Position< (FExeStream.Size-sizeof(TUserResHead)) do
begin
fillchar(ResHead,sizeof(TResHead),0);
FExeStream.Read(ResHead,sizeof(TResHead));
if ResHead.ResID=ResID then
begin
Result:=ReadToStream(ResHead.Length,Stream) ;
Break ;
end else
FExeStream.Position := FExeStream.Position + ResHead.Length ;
end;
end;function TUserExeRes.ReadToStreamForName(ResName: shortstring;
Stream: TStream): boolean;
var
ResHead :TResHead;
begin
Result := False ;
FExeStream.Position := FUserResHead.ResSetOff;
while FExeStream.Position< (FExeStream.Size-sizeof(TUserResHead)) do
begin
fillchar(ResHead,sizeof(TResHead),0);
FExeStream.Read(ResHead,sizeof(TResHead));
if ResHead.Name=ResName then
begin
Result:=ReadToStream(ResHead.Length,Stream) ;
Break ;
end else
FExeStream.Position := FExeStream.Position + ResHead.Length ;
end;
end;Function TUserExeRes.WriteToRes(ResName: shortstring; Stream: TStream):integer;
var
ResHead :TResHead;
Buf : array of byte ;
begin
Result := -1 ;
if Stream.Size<=0 then Exit ;
FUserResHead.ResCount:=FUserResHead.ResCount+1 ;
FUserResHead.MaxID :=FUserResHead.MaxID+1;
ResHead.ResID := FUserResHead.MaxID ;
ResHead.Name := resName;
ResHead.Length := Stream.Size ;
FExeStream.Position := FExeStream.Size-sizeof(TUserResHead);
FExeStream.Write(ResHead,sizeof(TResHead)) ;
SetLength(Buf,Stream.Size);
stream.Position := 0 ;
Stream.Read(Pointer(Buf)^,Stream.Size) ;
FExeStream.Write(Pointer(Buf)^,Stream.Size) ;
FExeStream.Write(FUserResHead,sizeof(TUserResHead)) ;
Result := ResHead.ResID
end;end.