How do I add file data to my EXE file at RunTime in delphi - delphi

I have created an application that manage an embedded database
My Customer want this application to be in one file
My Task is to modify my application so it can extract database file from exe , edit it ,and include if again at run time not in compile time

An executable file cannot be modified while the executable is running. Which means that in order to achieve your goal you would need another process. You could do the following:
Start your process.
Extract the DB from the process image.
Make changes to the DB.
Extract another executable file from the original image.
Start a second process based on this extracted images.
Terminate the first process.
Have the second process update the disk image with the modified DB.
Frankly this is a quite terrible idea. Don't even attempt this. The complexity serves no useful purpose, and the whole concept feels brittle.
Keep the data in a file separate from the program, as nature intended.

This is of course a bad idea, just think of what virusscanners are going to think of this approach. Also what happens if the exe crashes, will your db now lose all of its updates?
You can have the exe create a self extracting archive containing all the files needed.
This works as follows (the steps are the same as #David above, except that the components listed do most of the work for you).
Extract self extracting zip.
this contains: the real exe to be started upon extract
the database
some files needed to recreate a new self extracting exe
Upon closing the program it will create a new zip file, including:
Itself (in readonly form)
The database
some files needed to recreate a new self extracting exe
It will then transform the zip-file into a new self-extracting exe
the new self-extracting archive will start the exe included in it's embedded zip file as per #1.
Here's some sample code from sfx-zip-delphi.
program SelfExtractingZip;
{$APPTYPE CONSOLE}
uses
// Add a ZipForge unit to the program
SysUtils, ZipForge, Classes;
var
archiver : TZipForge;
begin
// Create an instance of the TZipForge class
archiver := TZipForge.Create(nil);
try
with archiver do
begin
// Set the name of the archive file we want to create.
// We set extension to exe because we create a SFX archive
FileName := 'C:\test.exe';
// See SFXStub demo in ZipForge\Demos\Delphi folder
// to learn how to create a SFX stub
SFXStub := 'C:\SFXStub.exe';
// Because we create a new archive,
// we set Mode to fmCreate
OpenArchive(fmCreate);
// Set base (default) directory for all archive operations
BaseDir := 'C:\';
// Add the C:\test.txt file to the archive
AddFiles('c:\test.txt');
CloseArchive();
end;
except
on E: Exception do
begin
Writeln('Exception: ', E.Message);
// Wait for the key to be pressed
Readln;
end;
end;
end.
Solutions for self extracting exe's
Paid
If you want a paid solution: http://www.componentace.com/sfx-zip-delphi.htm
Free
Or free: http://tpabbrevia.sourceforge.net/Self-Extracting_Archives
The abbrevia docs for self-extracting files are here: https://sourceforge.net/projects/tpabbrevia/postdownload?source=dlp
See page 293.

Related

Changing paths in Delphi

I am working on a project where I need to play a video from a file in Delphi. I often work from home and school, and I have the problem that at home, my USB is drive 'J' and at school my USB is drive 'D'.
I manually go and change it every time. Is there a way for Delphi to automatically get the video from where ever it is?
Each sector has an image component laid over it for selecting the sector.
*Note, I know I can search for a specific file's location in Delphi, but I have over 24 different places where I need to play different videos, so searching would probably be my last resort, unless I use a procedure and set constants for each sector to differenciate between them.
The code currently looks as follows:
procedure TtForm.imgSector1Click(Sender: TObject);
begin
//Variables,this is for initializing them when I create them later.
//Procedures
SectorDeselect; //Procedure to turn all sector borders white
// Video
WindowsMediaPlayer1.Controls.stop;
WindowsMediaPlayer1.URL := 'J:\IT\PAT\phase 2\Videos\Footage1.mp4'; //Where my problem lies
WindowsMediaPlayer1.Controls.Play;
// Sector Info. The memos and Rich edits
redSectorInfo.Lines.Clear;
redSectorInfo.Lines.Add('');
// Sector. Highlighting the sector borders surrounding the sector
SectorBordr1.Brush.Color := clGreen;
SectorBorder10.Brush.Color := clGreen;
end;
I would suggest adding a TEdit control in your app's UI to let you specify the base drive/path for the files on the machine the app is currently running on. Your code can then construct individual file paths at runtime that are relative to that base path. Don't use hard-code paths in your code.
You can then save that base path into the Windows Registry in a new key you create, ie HKEY_CURRENT_USER\Software\MyApp. Or, you can save the path in a configuration file (INI, XML, JSON, etc) created in a subfolder in your Windows user profile, like %APPDATA%\MyApp. Your code can then read in that base path each time the app is run.
If the files are stored on a USB drive, an alternative solution would be to simply enumerate the available drives at runtime, such as with GetLogicalDriveStrings(). For each drive, append a relative path for a given file onto the end of it, and then check if that file exists, such as with FileExists(). If so, you now know which drive to use for all of the files until the next time your app is run (you can save the drive path between runs, as described above). If the file is not found, move on to the next drive.
What about adding a parameter on the CommandLine?
Start
D:\myfolder\myfile D
OR
Start
J:\myfolder\myfile J
GUI files can accept a parameter. Capture it with code like:
DriveLetter := ParamStr(1);

Read/Write file from a .zip archive

Should I work with files within the archive file. (Read - write). By the following code, I get a list of files on my needs.
Zip := TZipFile.Create;
try
Zip.Open(FilePath, TZipMode.zmRead);
For File_Name in Zip.FileNames do
begin
//some code
end;
finally
Zip.Close;
FreeAndNil(Zip);
end;
I used the TZipFile.Read method to reads a file from a .zip archive .
This method returns the complete content of file into a buffer of type TByte. But just need to read a 1MB file from the beginning, not the complete file.
After reading and analyzing a 1MB file, if needed, should be read complete file and make changes to the file and re-save the file to archive.
Memory and speed of the program is very important. I used to set the buffer size of the function SetLength, unfortunately complete content of file files are stored in the buffer.
What do you think?
Use the overloaded version of TZipFile.Read() that returns a TStream instead of a TBytes. That way, you do not have to read the entire file into memory, and can read just its beginning bytes as needed.
Unfortunately, there is no way to modify data inside of a zip archive using TZipFile. Although you can Extract() a particular file, modify it externally as needed, and then Add() it back into TZipFile, there is no way to remove/replace a given file in TZipFile. TZipFile is a simple framework, it can only read a zip archive and add new files to it, nothing else. If you need more control over a zip archive, you are better off using a more complete third-party solution, such as ZipForge.

How to properly configure a cross-platform Delphi XE2 project?

Right now I have 2 platforms (Mac and Win32) and 2 configs (Debug ans Release). Whole thing is under SVN.
Here is layout for build output:
.\App\$(Platform)\$(Config)
The code is split into few folders and located here:
.\Code\MyProject.dpr
.\Code\Common\
.\Code\Forms\
.\Code\Source\
Common data files are here:
.\Data\ custom data files (dlls, textures, models, etc.)
This scheme has certain flaws though, which I need to resolve, but I don't know how to make it better. I want to have only one set of data files in SVN under Data folder, but I need to have it copied to .\App\$(Platform)\$(Config) paths automatically on build (note, certain data files are common for both platforms, but some are not). Is there a way to setup build process to copy the files, like it does with Deployment and PAServer? Alternatively I could setup paths to Data files as ..\..\Data, but that looks weird to me.
Maybe there are some other options I'm not aware of, or project layout could be changed completely? How would you setup the project structure and build for cross-platform compiling?
Use the Post Build actions.
From Project Options | Build Events | Post Build Events | Commands
Further reading at
Creating Build Events
Pre and Post-Build Automation in Delphi
Ok, this is an old post, but this is what I do in Delphi (and similarly in Visual Studio) to get around this problem of having the different platform/config output executables in different folders, but a common set of data. I have a function to strip off the "sub" folder parts, then my app can always access the common data in the root of these folders (in your case .\App). Of course you can add further platforms and configs to the function ProjectPath as required:
uses System.StrUtils;
...
function ProjectPath : string;
// Removes sub-folders created by the Delphi IDE, so the executable can refer to the source folder,
// rather than to where the executable is.
// Excludes trailiong path delimiter
begin
Result := ExtractFilePath (Application.ExeName);
Result := System.StrUtils.ReplaceText (Result, '\Win32' , ''); // ReplaceText is case insensitive
Result := System.StrUtils.ReplaceText (Result, '\Win64' , '');
Result := System.StrUtils.ReplaceText (Result, '\Debug' , '');
Result := System.StrUtils.ReplaceText (Result, '\Release', '');
Result := ExcludeTrailingPathDelimiter (Result);
end;
...
ConnectionString := 'Database=' + ProjectPath +'\DATAFILE.DAT;etc.';

How I Compile Resources into my Application and Access them?

How can I make a single executable package that contains DLL and Image Resource Files?
Then how do I extract them from my Executable at Runtime?
Option 1 using the IDE (Delphi 2007 or Higher):
You can click the Project menu, then select Resources..., which you can load any file into. For your purpose this would be RC_DATA.
Option 2 without the IDE
If you do not have the above option, you will need to use the BRCC32 (Borland Resource Compiler) to create a .RES file from RC file, which you then link to your Application. To link Resource files without using the IDE, try the following:
Lets say for example we want to add a a couple of DLL files, and the name of the DLL files are MyLib1.dll and MyLib2.dll, to add this open Notepad, and type the following:
MYLIB1 RCDATA "..\MyLib1.dll"
MYLIB2 RCDATA "..\MyLib2.dll"
Make sure the ..\xxx.dll paths are correct, so obviously you need to edit that.
Now you need to save this as a .rc file, so File>Save As..(make sure the dropdown filter is All Files .) and name it MyResources.rc. Now you need to use the Resource Compiler to generate the Res file, using this console command:
BRCC32 MyResources.RC
You can write that command by using the Command Prompt, Start Menu > Run > cmd.exe, alternatively you can find the BRCC32.exe inside the bin folder of your Delphi setup and drag the MyResource.RC file onto.
This will create a Res file named MyResources.RES which you can include inside the Main Delphi form of your Application, like so:
{$R *.dfm}
{$R MyResources.res}
you can extract the resources by using something like this:
procedure ExtractResource(ResName: String; Filename: String);
var
ResStream: TResourceStream;
begin
ResStream:= TResourceStream.Create(HInstance, ResName, RT_RCDATA);
try
ResStream.Position:= 0;
ResStream.SaveToFile(Filename);
finally
ResStream.Free;
end;
end;
What I've found out to be convenient, is to use a .zip container.
Then you'll have two implementations:
Append some .zip content to an existing .exe, and the .exe code will retrieve the .zip content on request;
Embed the .zip content as a resource, then extract on request each content.
Solution 1 will add the .zip content after compilation. Whereas 2 will add the .zip content at compilation. For a setup program, I think solution 1 makes sense to me. For a way of retrieving some needed files (libraries, and even bitmaps or text) which are linked to a particular exe release, solution 2 could be envisaged.
Using .zip as format make it easy to parse the content, and allow compression. Using a tool like TotalCommander, you can even read the .zip file content with Ctrl+PgDown over the .exe. Very convenient.
You'll find in this link how you implement solution 1, and in this link (same page, but another post) how to use the TZipRead.Create() constructor to directly access to a .zip bundled as resource. You'll find in our repository how it works with working applications: e.g. how we embedded icons, textual content and graphviz + spell-checker libraries in the SynProject executable.
About performance, there is no difference between the two solutions, at least with our code. Both use memory mapped files to access the content, so it will be more or less identical: very fast.

Delphi Binary/Text File Necessity

I am an engineer and not a software programmer, so please excuse my ignorance.
I have written a Delphi(7SE) program to read “real” datatype from a USB port connected to two digital thermometers.
I have completed this much of the program.
What I have not completed as yet is explained by the following:
I wish to save this “real” data to a Binary File(s). A text file would be fine as well, but i'm concerned about having a big data file.
I also wish to read this data back from the Binary/Text File to display the data using my Delphi application.
I don’t think this would be too difficult. I currently save my data in .CSV format.
The twist here is that the binary file should contain data from different sessions initiated by the user of my application.
So when I click on say, a button called “ historical” data, a new window/form would pop up that would show different session times that I had started & stopped from earlier times. Then a session would be selected and data then retrieved for displaying.
Can this be done in one binary files or would you have to use 2 files: one for the “real” data and another which indexes the different session times?
My requirement for this way of saving binary data is that I would not have to keep typing in filenames and therefore keeping track of many data files.
For example a thermo.hst(historical data) and a thermo.idx (index file) file would contain all the information such as actual temp data, time of read data, session start & end times etc.
Any useful pointers and hopefully code with as much detail would be greatly appreciated.
I hope this sample code isn't too late to be helpful.
(I've added this as another answer from me so that I can cleanly list the code. If this or my previous post answers your question, please click the answer icon so I get reputation points!)
Below is some rough code that shows how to read the sections in an ini file and find the largest filename. I confirmed it compiles and seems to return valid values, but you'll need confirm it does what you need. It's more to show you the idea...
Note that if your data filenames have an extension, you'll have add code to remove the extension in my sample code using something like: FileName := ChangeFileExt(Filename, '').
// Call with an open inifile. Returns the name of the next filename, or '' if trouble
Function GetNextFileName( const IniFile: TInifile):String;
const
BASE_FILENAME = 'File.'; // sections in the ini file will be [File.1], [File.2], ... [File.100], etc.
var
Sections: TStringList;
NumericPartAsString: String;
NumericPartAsInteger: Integer;
ListIndex: Integer;
LargestFileNumberSeenSoFar: Integer;
begin
Result := '';
Sections := TStringList.Create;
IniFile.ReadSections(Sections); // fills StringList with the names of all sections in the ini file
if( Sections.Count = 0) then
Result := BASE_FILENAME + '1'
else
begin // find largest extension
LargestFileNumberSeenSoFar := -1;
ListIndex := 0;
while ListIndex <= (Sections.Count - 1) do // for every string (which is also a filename) in the string list:
begin
NumericPartAsString := StringReplace(Sections.Strings[ListIndex], BASE_FILENAME, '', []); // remove base filename
if (NumericPartAsString <> '') then
begin
NumericPartAsInteger := StrToIntDef(NumericPartAsString, -1);
if (NumericPartAsInteger > LargestFileNumberSeenSoFar) then
LargestFileNumberSeenSoFar := NumericPartAsInteger;
end;
inc(ListIndex);
end;
if (LargestFileNumberSeenSoFar > -1) then
Result := BASE_FILENAME + IntToStr(LargestFileNumberSeenSoFar + 1);
end;
Sections.Free;
end; { GetNextFileName }
procedure TForm1.Button1Click(Sender: TObject);
var
IniFile: TInifile;
NewFileName: String;
begin
IniFile := TInifile.Create('c:\junk\ini.ini');
NewFileName := GetNextFileName(Inifile);
if (NewFileName = '') then
ShowMessage('Error finding new filename')
else
ShowMessage('New filename is ' + NewFileName);
IniFile.Free;
end;
By using a database, you've in part just renamed part of the problem from "typing in file names and keeping track of many data files" to "typing in data set name and keeping track of many data sets."
In both cases, as an example, either the user or the program has to create a new file/data set name, and choose from a list of files/data sets to open later. And in both cases you have to make a call to a function named something like "DeleteDataSet".
You might re-consider whether you really need a database (with associated learning curve for API, how to define the data structure, update it in the field when something changes, browse the data in a viewer, access the data programatically, proprietary format, database maintenance, repair tools, installation, etc.) I'm sure that you could learn all these, and they might be valuable in future projects. But, maybe a simpler approach would be more appropriate and adequate for this one-time project by a non-software engineer.
If you're willing to have a proliferation of many unique, standalone data files on one folder, I'd encourage you to stick with what's working: use one CSV file per data set. (Have you run into speed or size issues with CSV files containing a single data set thus far? That would be an enormous amount of data!) One nice thing about CSV files is that you can just pop them into an editor to view or edit...
And, then, add a second file that contains filenames, and other descriptive information. This file would be a simple TIniFile:
[My Name one]
Date=06 June 2010
StartTime=12:30pm
StopTime=3:15pm
FileName=Data1.csv
[My Name two]
...
The tools available in Delphi for TIniFile will let you easily manage this list, including ReadSections into a string list that you can just assign to a combo box for the user to select a data set. (See example below) And, like the CSV files, you can just edit the .ini file in any text editor.
You'll need to build a UI to allow a user to delete a dataset (section in the ini file and associated .csv file). To give you an idea how the ini file would be used, here's the pseudo-code for deleting a data set:
(In IDE Object Inspector, set ComboBox.Style := csDropDownList to prevent user from typing in a name that doesn't exist.)
Load a combo-box that shows available data sets.
1. ComboBox.Items := IniFile.ReadSections;
In the combo-box's OnSelect event handler:
2. DeleteFile(IniFile.ReadString(CombBox.Text, 'FileName', ''));
3. IniFile.EraseSection(ComboBox.Text); // remove the section from the inifile
Heck, that's not a lot of code, even after you add a bit of protection and error checking!
Maybe the above solution will be voted down by others here as trying to put a round peg in a square hole or re-inventing the wheel. And I might agree with them. There are good arguments against this approach, including proliferation of many files. But, if it was me, I'd at least consider this approach as keeping-it-simple and not requiring anything new but that you learn the TIniFile object, which is quite powerful.
The data can be interleaved. Just start every block (a set of history) with a header that identifies the block and contains its length. When reading you can then easily separate that.
You can hold an additional index file next to this for fast access if you require, but if this is the case, I would start studying some embedded database. (TDBF, sqlite or embedded firebird).
I would also head in the database direction if I expected that my querying would get more complicated in the future.
If it is all about logging, the data doesn't get gigantic and the performance of the view is fine, I would keep the binary file and avoid the hassle of having users to install and maintain a databsae solution. (TDBF is maybe an exception to that, since it is completely statically linked)

Resources