Error when changing TImage picture on click event - delphi

I have the following basic code :
procedure TForm4.shrek1Click(Sender: TObject);
begin
shrek1.Picture.LoadFromFile('donkey.jpeg');
end;
Where shrek1 is a TImage, and donkey.jpeg is the image I want shrek1 to load when clicked.
donkey.jpeg is located in the same directory of literally every other related project file, yet when I attempt to run the code I get an error:
Exception class EFOpenError with message 'Cannot open file "\(removed directory for privacy)\donkey.jpeg". The system cannot find the file specified
What am I doing wrong?

Always use absolute paths. Relative paths are relative to the calling processe's Current Working Directory, which can (and usually does) change value during the process's lifetime, and is not always what you expect.
If the JPG file is in the same folder as the your EXE, you can do this instead:
var
AppPath: string;
procedure TForm4.shrek1Click(Sender: TObject);
var
FileName: string;
begin
FileName := AppPath+'donkey.jpeg'; // <-- make sure this path is accurate!
shrek1.Picture.LoadFromFile(FileName);
end;
initialization
AppPath := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName));

Related

How to create a folder and a txt file in Delphi

How do I create a new folder in the internal storage (?) of Android (I want say, the main folder that has all the subfolders ... Whatsapp, DCIM, pictures, Ringtones, Alarms ..) and create a new .txt file inside in this folder.
I want to create a .txt file and I need the user to plug the USB cable into their computer, access the device, enter the folder my application creates, and copy this file to their desktop.
I tried this code to create the file:
procedure TF_start.Button2Click(Sender: TObject);
var
output_text: string;
arquivo: TextFile;
begin
output_text := 'test';
TFile.WriteAllText(TPath.Combine(TPath.GetDocumentsPath, 'test.txt'), 'content');
ReWrite(arquivo);
WriteLn(arquivo, output_text);
CloseFile(arquivo);
end;
But it does not work.
To get the internal storage(?) path, I found this code:
P := '/storage/';
if (FindFirst(P + '*', faAnyFile, Sr) = 0) then
repeat
Memo1.Lines.Add(Sr.Name);
until (FindNext(Sr) <> 0);
FindClose(Sr);
But I can't understand how it works, so I can't even use it.
I also found this link, but I didn't find any function that returns me the "general" directory path I want to create a folder.
The functions System.IOUtils.TPath.GetHomePath(), and System.IOUtils.TPath.GetDocumentsPath() do not return me the correct path.
System.SysUtils.GetHomePath() return -> /data/user/0/com.embarcadero.app/cache
System.IOUtils.TPath.GetDocumentsPath() return -> /data/com.embarcadero.app-1/lib/arm
#edit
Using the #Remy Lebeau code and this code I managed to get to this point.
The problem is that the code to update the directory with the files does nothing
Use System.IOUtils, Androidapi.Helpers, Androidapi.Jni.Media, Androidapi.JNI.JavaTypes, Androidapi.JNI.GraphicsContentViewText;
//Button
procedure TF_start.Button2Click(Sender: TObject);
var
path_file output_text: string;
begin
path_file := TPath.Combine(System.IOUtils.TPath.GetSharedDownloadsPath, 'Folder_app');
output_text := 'test';
if not TDirectory.Exists(path_file) then
TDirectory.CreateDirectory(path_file);
try
TFile.WriteAllText(TPath.Combine(path_file, Nome_Arquivo), Arquivo_saida);
except
ShowMessage('An error occurred while saving the file.');
end;
end;
Another button:
procedure TF_corrida.BTNfinalize_appClick(Sender: TObject);
var
c: Integer;
JMediaScannerCon: Androidapi.Jni.Media.JMediaScannerConnection;
JMediaScannerCon_Client: Androidapi.Jni.Media.JMediaScannerConnection_MediaScannerConnectionClient;
begin
JMediaScannerCon:=TJMediaScannerConnection.JavaClass.init(TAndroidHelper.Context, JMediaScannerCon_Client);
JMediaScannerCon.connect;
c:=0;
while not JMediaScannerCon.isConnected do begin
Sleep(100);
inc(c);
if (c>20) then break;
end;
if (JMediaScannerCon.isConnected) then begin
JMediaScannerCon.scanFile(StringToJString(path_file), nil);
JMediaScannerCon.disconnect;
end;
end;
PS This warning had appeared:
[DCC Warning] u_corrida.pas(682): W1000 Symbol 'SharedActivityContext'
is deprecated: 'Use TAndroidHelper.Context'
So I changed the code
Note: I also tried replacing "path_file" with "System.IOUtils.TPath.GetSharedDownloadsPath", but to no avail too
This question has already been answered, the other question (index files and folders) has been moved to: How to index a created file in Android sdcard Delphi
You don't actually want "internal storage", that is private to your app and not even the user can access it (without root access to the device). You want "external storage" instead, so the user (and other apps) can access it.
Per Save files on device storage in Android's documentation:
Internal storage is best when you want to be sure that neither the user nor other apps can access your files.
External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.
Use one of the TPath.GetShared...() methods to get an "external storage" path, such as TPath.GetSharedDocumentsPath(). And make sure your app has the WRITE_EXTERNAL_STORAGE permission enabled.
Also, note that TFile.WriteAllText() will not create a missing folder (in fact, it will raise an EDirectoryNotFoundException). You have to create the folder yourself first, such as with TDirectory.CreateDirectory() or SysUtils.ForceDirectories(). TPath.Combine() simply concatenates the input strings together, it does not create the actual folder.
Try this:
procedure TF_start.Button2Click(Sender: TObject);
var
path, output_text: string;
begin
output_text := 'test';
path := TPath.Combine(TPath.GetSharedDocumentsPath, 'myfolder');
if not TDirectory.Exists(path) then
TDirectory.CreateDirectory(path);
TFile.WriteAllText(TPath.Combine(path, 'test.txt'), output_text);
end;

How to solve [FireDAC][Phys][SQLite] ERROR: unable to open database file, When app is installed in programFiles?

I developed a tool using Firedac with the database as SQLite.
after finishing the project and making an installer (InnoSetup) I get an error
[FireDAC][Phys][SQLite] ERROR: unable to open database file
when I launch the app (double click).
This is the connection parameters I use
constructor TDbInteract.Create(const aDatabasePath: string; const aOnNeedCredentials: TOnNeedCredentials);
var
aParams: array of string;
begin
if not TFile.Exists(aDatabasePath) then
raise Exception.Create('Database file not found');
aParams := ['DriverID=SQLite',
'Database=' + aDatabasePath,
'OpenMode=CreateUTF16',
'LockingMode=Normal',
'JournalMode=WAL',
'StringFormat=Unicode',
'Synchronous=Full',
'UpdateOptions.LockWait=True',
'BusyTimeout=30000',
'SQLiteAdvanced=temp_store=MEMORY;page_size=4096;auto_vacuum=FULL'];
InitiateResource(aParams, aOnNeedCredentials);
end;
procedure TDbInteract.InitiateResource(const aParams: array of string; const aOnNeedCredentials: TOnNeedCredentials);
var
I: Integer;
Credentials: TStringDynArray;
begin
FRowsAffected := 0;
FIsForeignKeyHonored := True;
FOwnsResultDataSets := True;
FDataSetContainer := TDataSetContainer.Create(nil);
FConnection := TFDConnection.Create(nil);
try
for I := Low(aParams) to High(aParams) do
begin
FConnection.Params.Add(aParams[I]);
end;
if Assigned(aOnNeedCredentials) then
begin
aOnNeedCredentials(Self, Credentials);
for I := Low(Credentials) to High(Credentials) do
begin
FConnection.Params.Add(Credentials[I]);
end;
end;
FConnection.Open;
except
raise;
end;
end;
**Identified problems:
I read somewhere (do not remember the page I was in) that SQLite engine requires full lock on the directory that it wants to write to. and this is the problem. How ever I run the tool as invoker and my account is an admin so that is not a problem. Also I have the same tool written in c# and this problem never occurs.
Solutions I found:
Run the tool as administrator
Do not Install the tool in ProgramFiles directory
I really don't like these solutions. and would like to run my tool from program Files directory as it is part of a bigger project.
Note: The database file is in programdata directory. It is created by the tool (this works).
Edit: I just tried putting the DB file in C:\Users\Nacereddine\AppData\Roaming\MyTool And I still have the same problem when the tool is installed in C:\Program Files (x86)\MyTool
This how I create the DB file
class procedure TDbInteract.CreateSQLiteDb(const aDatabasePath: string; const aTables: TStringDynArray);
var
I: Integer;
aParams: array of string;
aConnection: TFDConnection;
begin
aParams := ['DriverID=SQLite',
'Database=' + aDatabasePath,
'OpenMode=CreateUTF16',
'LockingMode=Normal',
'JournalMode=WAL',
'StringFormat=Unicode',
'Synchronous=Full',
'UpdateOptions.LockWait=True',
'BusyTimeout=30000',
'SQLiteAdvanced=temp_store=MEMORY;page_size=4096;auto_vacuum=FULL'];
aConnection := TFDConnection.Create(nil);
try
for I := Low(aParams) to High(aParams) do
begin
aConnection.Params.Add(aParams[I]);
end;
aConnection.Open();
for I := Low(aTables) to High(aTables) do
begin
aConnection.ExecSQL(aTables[I]);
end;
finally
aConnection.Close;
aConnection.Free;
end;
end;
Note: I do not know if this makes any difference but the Db file is encrypted.
Sorry for the trouble folks.
The problem was that we had a Localization db file installed with the tool in ProgramFiles.
What made me exclude that from my investigation is that, when opening this file I set the OpenMode to ReadOnly
FConnection.Params.Add('OpenMode=ReadOnly');
but as I said before in my question SQLite engine requires full access to the folder containing the db file so it preforms a lock on it (still did not find the page I read this on).
I checked this by playing around with open modes and debugging the tool each time.
once I changed the permissions of both the file and the directory the error was gone.
at the end I decided to move the localization file to the programData directory with the main db file and all is well.
I realized (Thank you for this #Ken and #David) that the programData directory also requires admin permissions to write to, and therefore I will move the db files to a more appropriate dir (i.e Users).
What is useful from this problem is that even if you connect to the Sqlite db file with OpenMode=ReadOnly, you still need write access for the path to that file.

How use fxaIgnore in TZFProcessFileFailureEvent

How ignore error 00033? this error occurs because another process is using the file.
Image 1
Image 2
It is possible to change the event handler so that when the error 00033 appears he ignore the file and jump to the next?
type TZFProcessFileFailureEvent = procedure (
Sender: TObject;
FileName: String;
Operation: TZFProcessOperation;
NativeError: Integer;
ErrorCode: Integer;
ErrorMessage: String;
var Action: TZFAction
) of object;
type TZFAction = (fxaRetry, fxaIgnore, fxaAbort);
property OnProcessFileFailure: TZFProcessFileFailureEvent;
my code for zip files...
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
FileName := 'C:\test.zip';
// Because we create a new archive,
// we set Mode to fmCreate
OpenArchive(fmCreate);
// Set base (default) directory for all archive operations
BaseDir := 'C:\';
// Add files to the archive by mask
AddFiles('*.exe');
CloseArchive();
end;
except
on E: Exception do
begin
Writeln('Exception: ', E.Message);
// Wait for the key to be pressed
Readln;
end;
end;
end.
Have you tried adding code like this to your OnProcessFileFailure handler
if NativeError = 1033 then
Action := fxaIgnore;
?
Even if you don't have documentation available for the zipping library you're using, the clue is in the fact that the Action parameter of the TZFProcessFileFailureEvent event is declared as a var parameter. That means that any change to its value that you make inside the handler is passed back to the code that called the event handler, so that you can signal to it how you want it to react to the event occurring.
Btw, I'm not sure why you included your image1 in your q, because you have not asked about that. If you want to know how to deal with a specific type of exception, like EFOpenError in an exception handler, look up how to do it in the Delphi online help.

Delphi : How to set sound/image locations?

How do I effectively set sound/image locations for example :
procedure TForm1.Button1Click(Sender: TObject); begin
begin
PlaySound('C:\Users\username\Desktop\project\sfx\Sounds\ding.wav', 0, SND_ASYNC);
end;
To something like :
procedure TForm1.Button1Click(Sender: TObject); begin
begin
PlaySound('ding.wav', 0, SND_ASYNC);
end;
Everytime I move The folder that contains the the project the sounds seem to not work anymore because the directory changes.
You should set / state the locations relative to the project file. For example:
var lApplicationExecutablePath: String;
lApplicationExecutablePath := ExtractFilePath(ParamStr(0));
...
var lSoundFile: String;
lSoundFile := TPath.Combine(FolderName, 'sfx\ding.wav');
This way you will have all needed files side by side no matter where the application is installed.
If you supply a relative path it is interpreted as relative to the process working directory. As a general rule, in a GUI application you should always supply absolute paths since the process working directory may be ill-defined.
You don't need to hard code the file name. If you know the name of the file, and the folder that it is in, then you can simply combine the name of file folder with the name of the file, and obtain the full path to the file. Pass that to PlaySound.
uses
IOUtils;
....
PlaySound(PChar(TPath.Combine(FolderName, FileName)), 0, SND_ASYNC);

Memo and create file and folder?

I'm new to Delphi and i'm a french user so sorry for my bad english...
So it is possible to create a file written in TMemo?
test.txt
dir1/dir2/text.txt
dir3/
My TMemo there are 3 lines, so I would like to take the first line and create the file test.txt in the current directory ..
2nd line: create a folder
3rd line: create a folder again+files.txt
etc ...
I think to use mkdir or ForceDirectories to create Directory and files? etc...
So my conclusion was to automate it.
You can help me please?
a small image so you can see:
With Program and on ButtonClick event
If I have understood the question correctly, this will
Create an empty text file in the application directory with the filename as per Memo Line 1
Create Folders as per Memo Line 2 and in the "base directory" per the edit
Create a Folder and empty TextFile as per Memo Line 3 again in the "base directory"
procedure TForm1.Button1Click(Sender: TObject);
var
Path: String;
F: TextFile;
begin
// Create File in current directory
Path := ExtractFilePath(ParamStr(0)) + Memo1.Lines.Strings[0];
if not FileExists(Path) then
begin
AssignFile(F, Path);
Rewrite(F);
//Writeln(F, 'text to write to file');
CloseFile(F);
end;
// Create Directories
Path := IncludeTrailingPathDelimiter(edPath.Text) + Memo1.Lines.Strings[1];
if not DirectoryExists(Path) then
ForceDirectories(Path);
// Create Directory and File
Path := IncludeTrailingPathDelimiter(edPath.Text) + Memo1.Lines.Strings[2];
if not DirectoryExists(ExtractFilePath(Path)) then
ForceDirectories(ExtractFilePath(Path));
if not FileExists(Path) then
begin
AssignFile(F, Path);
Rewrite(F);
//Writeln(F, 'text to write to file');
CloseFile(F);
end;
end;
Obviously needs significantly more error checking determining if paths valid and files / directories created etc...
Edit: code in browser so have no idea if it works but really is a simple thing to do.
You should only use a TMemo if you want to display the data before you save it, because the task of a visual control is to display something. But if you only want to use the Items propery of the TMemo for collecting strings and then save them to file you should use a TStringList instead:
var
i: Integer;
sl: TStringList;
begin
sl := TStringList.Create;
try
for i := 0 to Memo1.Lines.Count-1 do
sl.Add(Memo1.Lines[i]);
sl.SaveToFile(sl[1]);
finally
sl.free;
end;
end;
You may also like this thread: http://www.tek-tips.com/viewthread.cfm?qid=678231
EDIT2:
Memo1.Lines.SaveToFile(edit1.text + Memo1.Lines[0]);
Provided that Edit Control is named Edit1 and has your base path and first line of the TMemo has the file name. The other bit you need is an Event and by that I mean if you doubleclick your TMemo instance it will be the event that will start the cascade for saving the file.
As you see it is very easy and there are other ways such as SaveDialog that can make it much easier still. but hope this answers your question.

Resources