Get My Documents folder path in delphi - delphi

i use the following code to get special directories
uses
ActiveX, ShlObj;
{...}
procedure TForm1.Button1Click(Sender: TObject);
// Replace CSIDL_HISTORY with the constants below
var
Allocator: IMalloc;
SpecialDir: PItemIdList;
FBuf: array[0..MAX_PATH] of Char;
PerDir: string;
begin
if SHGetMalloc(Allocator) = NOERROR then
begin
SHGetSpecialFolderLocation(Form1.Handle, CSIDL_PERSONAL, SpecialDir);
SHGetPathFromIDList(SpecialDir, #FBuf[0]);
Allocator.Free(SpecialDir);
ShowMessage(string(FBuf));
end;
end;
And now i want to get the my documents path
so i use
mydocfolderpath := string(FBuf) + '\Documents' and i think it works well
but my doubt is this the mydocuments path on all windows PCs (personalfolder/documents) can the user change this stucture and make my documents folder anywhare else (eg: c:\documents)
if the user an change the path give me a proper way and i like to know what is the name of mydocuments folder (My Documents or Documents)

If you are using a recent version of Delphi (XE5 or greater) then you can use the new platform agnostic classes. Basically include System.IOUtils in your uses then use TPath.GetDocumentsPath to get the documents folder.
Check out the Doc Wiki

CSIDL_PERSONAL is the My Documents folder:
CSIDL_PERSONAL FOLDERID_Documents
Version 6.0. The virtual folder that
represents the My Documents desktop
item. This is equivalent to
CSIDL_MYDOCUMENTS.
Previous to Version 6.0. The file
system directory used to physically
store a user's common repository of
documents. A typical path is
C:\Documents and Settings\username\My
Documents. This should be
distinguished from the virtual My
Documents folder in the namespace. To
access that virtual folder, use
SHGetFolderLocation, which returns the
ITEMIDLIST for the virtual location,
or refer to the technique described in
Managing the File System.Managing the File System.
See: http://msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx for a list and description of all CSIDL constants available

Related

File Not Found in Delphi (FMX)

I am trying to load a bunch of lines from a text file located in same directory as the .dproj and Delphi can't find the file I'm trying to read from.
the code is as follows:
procedure TFoPrincipale.Button2Click(Sender: TObject);
var monFich : TextFile;
Sligne : string;
begin
try
AssignFile(monFich, 'docText.txt');
Reset(monFich);
except
showmessage('Le fichier est introuvable');
exit;
end;
while not Eof (monFich) do
begin
Readln(monFich, Sligne);
Memo1.Lines.Add(Sligne);
end;
CloseFile(monFich);
end;
If you are using default project options then your application executable isn't compiled into the project directory but instead into the Win32\Debug or Win32\Release subfolder of your project folder.
So you should take into account the relative path to your file. In your case, the desired file is in second parent folder of the folder in which your executable resides.
I recommend you first get the path location of your executable file using ExtractFilePath(Application.ExeName).
Then you can make use of TDirectory.GetParent() in order to move up the directory chain until you reach the desired directory.

How to share an application and associated datafile

I have written a program (firemonkey) using delphi community edition. I would like to share the program, but the .exe file that my friends will be downloading has to access a text file from time to time to retrieve strings. When writing the program I used an event handler, to load the text file at startup:
tform3.formCreate (Sender:Tobject);
...
assignfile(myfile,('C:**********.txt));
...
Worked just fine during the
design stage.
As a hobbyist, I now find myself stuck. If I use INNO setup compiler to create an installation program, which I plan to do, I can't have this same hardwired reference ('C:*****) to the data file's location. What I need is to change the above code such that the .exe file can locate the supporting datafile irrespective of where that .exe file (and datafile) ends up on someone else's PC.
How can I do this? i.e. What code do I need (in place of the above) to ensure that the installation program I hand out will install an .exe file that can locate the data file it references?
Any help, much appreciated. Still learning.
Read-only access
If the data file should always be opened in read-only mode, the simplest solution is to place it next to the *.exe file. Then, at runtime, you dynamically find the path to the *.exe file and modify it to find the path to the data file. For instance,
uses
IOUtils;
procedure TForm1.FormCreate(Sender: TObject);
var
FileName: string;
begin
FileName := TPath.Combine(ExtractFilePath(ParamStr(0)), 'data.txt');
ShowMessage(TFile.ReadAllText(FileName, TEncoding.UTF8));
end;
ParamStr(0) contains the path to the *.exe file, such as
'C:\Users\Andreas Rejbrand\Documents\Embarcadero\Studio\Projects\test\Win32\Debug\Project1.exe'
Then ExtractFilePath(ParamStr(0)) is
'C:\Users\Andreas Rejbrand\Documents\Embarcadero\Studio\Projects\test\Win32\Debug\'
and, finally, TPath.Combine(ExtractFilePath(ParamStr(0)), 'data.txt') is
'C:\Users\Andreas Rejbrand\Documents\Embarcadero\Studio\Projects\test\Win32\Debug\data.txt'
Make sure the installer puts the data file next to the *.exe file.
Read and write access
If we are talking about a settings file or some other file that each user needs to change (via the software), you cannot place it next to the *.exe file, because the *.exe file typically resides in the Program Files folder, which is read only. Also, there is only one Program Files folder, but possibly many users on the PC, and each user should have his or her own copy.
The solution is to save the file in the user's own folders, specifically, the AppData folder:
FileName := TPath.GetHomePath + '\Mariner\My Word Processor App\Settings\settings.ini';
(using a different approach to path building).
On my system, this becomes
'C:\Users\Andreas Rejbrand\AppData\Roaming\Mariner\My Word Processor App\Settings\settings.ini'
Your installer (Inno Setup) has built-in support for placing files in this location.
If it is only accessed read only, you could also consider adding it as a resource to the executable. Which would then allow you to simply distribute this executable without the need for an installer.
Delphi Dabbler has an example, but I found it a bit confusing. I'll link to it (PDF) anyway.
You can let the user select the place where the file has to be saved. Proposing AppData folder if the file is for each individual user or CommonAppData if the file has to be shared between different users.
When the use selected the data file destination, you can save it to an INI file. The INI file can be stored, without asking the user, either to the registry or to an INI file saved in the AppData folder or ProgramData folder.
Here is a snipped of source code to get hand on those special folders:
const
SectionWindow = 'Window';
SectionData = 'Data';
CompanyFolder = 'YourCompanyName';
constructor TForm1.Create(AOwner: TComponent);
var
CommonPath : array [0..MAX_PATH] of Char;
LocalPath : array [0..MAX_PATH] of Char;
LangFileName : String;
begin
SHGetFolderPath(0, CSIDL_COMMON_APPDATA, 0, SHGFP_TYPE_CURRENT, #CommonPath[0]);
SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, SHGFP_TYPE_CURRENT, #LocalPath[0]);
FIniSection := SectionWindow;
FIniSectionData := SectionData;
FAppName := ChangeFileExt(ExtractFileName(Application.ExeName), '');
FCommonAppData := IncludeTrailingPathDelimiter(CommonPath) +
CompanyFolder + '\' + FAppName + '\';
FLocalAppData := IncludeTrailingPathDelimiter(LocalPath) +
CompanyFolder + '\' + FAppName + '\';
FIniFileName := FLocalAppData + FAppName + '.ini';
ForceDirectories(FCommonAppData);
ForceDirectories(FLocalAppData);
inherited Create(AOwner);
end;

Delphi OpenTools API - Editing the project requires clause

I have written an OpenTools wizard for creating a skeleton for a custom project type. It does work, and the project and units are properly created. But, how do I edit the .dpk or .dpk file's requires clause?
A call to ModuleServices.CreateModule(MyIOTAProjectCreatorInterface) gives me the .dproj file only.
In my VCL Component Installer (since XE, this is part of the Delphi IDE), I do it this way:
procedure TCompInstallWizard.AddReferenceFiles(InstallProject: IOTAProject;
const FileNames: array of string);
var
ReferenceFile: string;
begin
WriteDebugMessage('AddReferenceFiles');
for ReferenceFile in FileNames do
if not ContainsFile(InstallProject, ReferenceFile) then
InstallProject.AddFile(ReferenceFile, False);
end;
with the help of function IOTAProject.AddFile(FileName, IsUnitOrForm). Note that I call it like this:
if FPersonality = ppCppBuilder then
AddReferenceFiles(InstallProject,
['rtl.bpi', 'designide.bpi', 'vcl.bpi', 'vclactnband.bpi',
'vclx.bpi', 'xmlrtl.bpi'])
else
AddReferenceFiles(InstallProject,
['rtl.dcp', 'designide.dcp', 'vcl.dcp', 'vclactnband.dcp',
'vclx.dcp', 'xmlrtl.dcp']);
Note that the docs say:
{ Call this function to add an arbitrary file to the project. NOTE: some
files have special meaning to different projects. For example: adding
VCL60.DCP will cause a new entry in a package project's "requires" list
while it will be a raw file to any other project type. Set IsUnitOrForm
to true for files that are considered items that the project would
process directly or indirectly (ie. .pas, .cpp, .rc, etc..) or can be
opened in the code editor. For all others, including binary files
(.res, .bpi, .dcp, etc..) set this to False. }
procedure AddFile(const AFileName: string; IsUnitOrForm: Boolean);
This means that if you add a 'bla.dcp' it will automatically land in the requires section, and if you add a 'bla.pas' file, it will land in the contains section. It took me a while to find out.

With FireMonkey and its cross-platforms, where should I store my application data?

Usually, with Windows, I save my application's data in the user folder (%appdata%).
For that, I use the function ExpandEnvironmentStrings which is linked to Windows to get the folder I need, and I store inside a subfolder my inifile.
Is there any best practice to manage that and be compliant with all the supported platforms (Windows 32b, 64b & Mac)?
I successfully tested like that:
procedure TfrmMain.SaveSettings;
var
fnINI: TFileName;
ini : TIniFile;
begin
fnINI := IncludeTrailingPathDelimiter(GetHomePath) + IncludeTrailingPathDelimiter(APP_NAME) + ChangeFileExt(APP_NAME, '.ini');
if ForceDirectories(ExtractFilePath(fnINI)) then
begin
ini := TIniFile.Create(fnINI);
try
ini.WriteString(INI_CONNECTION, INI_IP, edtIP.Text);
finally
ini.Free;
end;
end;
end;
Haven't tried XE2 but probably you could use SysUtils.GetHomePath. Also check IOUtils where you can find useful records (TFile, TPath, TDirectory) for managing files, paths and directories. They should support different platforms.

embedded file into delphi exe application (not as a separate file from the appliaction)

i want to embedded a file (any kind of type) to my exe application and be able to extract in the remote to use it, i know how do it by embedded into resource,but i don't want to place the files in the app directory, i want to store all files (like .rec) into my exe, in c# it is possible to store as text file and then read it by FileStream but in Delphi the resource files is separate from the exe file.
is there any solution to do this ? Thanks a lot!
You should make an .rc file and add that to your project. The content of the RC file is like:
FIXED48 IMAGE ..\Resources\Fixed48x48.png
MENU16 IMAGE ..\Resources\Menu16x16.png
TICK SOUND ..\Resources\Tick.wav
PING SOUND ..\Resources\Ping.wav
Now after you do a build you can load one of these fikles using a TResourceStream:
procedure TdmReportGeneral.InsertLogo(Memo: TStringList; View: TfrView);
var
S: TResourceStream;
begin
if (View is TfrPictureView) and (View.Name = 'Logo') then begin
S := TResourceStream.Create( 0, 'FIXED48', 'IMAGE' );
try
// do something useful... TfrPictureView(View).Picture.MetaFile.LoadFromStream( S );
finally
S.Free();
end;
end;
end;
You should be able to get the Delphi compiler to link your resource into your EXE by adding it as a {$R myresource.res} pragma in a unit in your project. You can then get a handle to it via a call to FindResource when you need to read it.
This article takes you through the appropriate steps.
DelphiDabbler has a great article on this very topic. They even include 2 example projects for download that show how to embed a file as a resource, and how to read it back.
You can download a worked example that
demonstrates what we've described here
– it uses the above code. The zip file
contains a pair of projects. The first
is a program that embeds a supplied
rich text file in a resource file. The
second program includes the resource
file and displays the rich text in a
rich edit component as above.

Resources