LeTay
asked on
Execute an external program from inside a Delphi application
I am looking for a working Delphi code that when it will be executed, will execute an external program, says "myprogram.exe", passing argument "myargument".
But the Delphi application should "wait" until "myprogram.exe" terminates, before to continue execution.
I had some old stuff already, but it does not work anymore.
Delphi is 10.1 Berlin running on Windows 10
Thanks
But the Delphi application should "wait" until "myprogram.exe" terminates, before to continue execution.
I had some old stuff already, but it does not work anymore.
Delphi is 10.1 Berlin running on Windows 10
Thanks
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Will try some of these codes and come back to experts once completed...
ASKER
How can I pass a parameter in the two first example of the notepad ?
I need to pass a file name...
I need to pass a file name...
ASKER
I tried RunAsAdminAndWait passing the notepad exe full file name
I got the Windows warning that I was running as admin, I accepted, then nothing else, no notepad running and back to the caller
I got the Windows warning that I was running as admin, I accepted, then nothing else, no notepad running and back to the caller
To pass a filename to a notepad, you can try this:
begin
ShellExecute(Handle,'open',
'c:\windows\notepad.exe',
'c:\SomeText.txt', nil, SW_SHOWNORMAL) ;
end;
Remember to include shellapi in your uses clause.
begin
ShellExecute(Handle,'open',
'c:\windows\notepad.exe',
'c:\SomeText.txt', nil, SW_SHOWNORMAL) ;
end;
Remember to include shellapi in your uses clause.
I tried RunAsAdminAndWait passing the notepad exe full file nameOk, here's a simple sample using my function tested on Delphi 10.4
I got the Windows warning that I was running as admin, I accepted, then nothing else, no notepad running and back to the caller
unit unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Winapi.Shellapi, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function RunAsAdminAndWait(hWnd: hWnd; FileName: string; Parameters: string;
ShowCommand: Integer = SW_SHOWNORMAL): Boolean;
{
Redesign for UAC Compatibility (UAC)
http://msdn.microsoft.com/en-us/library/bb756922.aspx
}
var
sei: TShellExecuteInfo;
ExitCode: dword;
begin
ZeroMemory(@sei, SizeOf(sei));
sei.cbSize := SizeOf(TShellExecuteInfo);
sei.Wnd := hWnd;
sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI or
SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb := pchar('runas');
sei.lpFile := pchar(FileName); // PAnsiChar;
if Parameters <> '' then
sei.lpParameters := pchar(Parameters); // PAnsiChar;
sei.nShow := ShowCommand; // Integer;
if ShellExecuteEx(@sei) then
begin
repeat
Application.ProcessMessages;
GetExitCodeProcess(sei.hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE) or Application.Terminated;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
RunAsAdminAndWait(Application.handle, 'c:\windows\notepad.exe', 'c:\Test.txt',
SW_SHOWNORMAL);
showMessage('Notepad closed');
end;
end.
Depending on the idle time your want to give other applications and the responsiveness of your UI, the actual wait should be:
btw, for optimal responsiveness, you would use a thread to run the execute in and then you can do in the UI what you want.
bOK := Boolean(ShellExecuteEx(@Info));
if bOK then
begin
if bWait then
begin
while
WaitForSingleObject(Info.hProcess, 100) = WAIT_TIMEOUT
do Application.ProcessMessages;
bOK := GetExitCodeProcess(Info.hProcess, DWORD(Result));
end
else
Result := 0;
end;
where WAIT_TIMEOUT is a value between 500ms, especially for animating or updating the wait message and the average external application runtime.btw, for optimal responsiveness, you would use a thread to run the execute in and then you can do in the UI what you want.
1. Check if a process is active (there must be an API for this)
2. Maybe your external application can do something you can notice from your delphi program. For instance, if a file open was closed. So you can try to open that file. If you get an error, you probably are not done with the external app running. I dont know how good is this specific approach.
3. I was thinking in measure how much time the external app runs. So you can put a timer to expecting that app finished and you resume your delphi program.
I found this code, maybe it can work for you:
// Execute the Windows Calculator and pop up
// a message when the Calc is terminated.
uses ShellApi;
...
var
SEInfo: TShellExecuteInfo;
ExitCode: DWORD;
ExecuteFile, ParamString, StartInString: string;
begin
ExecuteFile:='c:\Windows\Calc.exe';
FillChar(SEInfo, SizeOf(SEInfo), 0);
SEInfo.cbSize := SizeOf(TShellExecuteInfo);
with SEInfo do begin
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := Application.Handle;
lpFile := PChar(ExecuteFile);
{
ParamString can contain the
application parameters.
}
// lpParameters := PChar(ParamString);
{
StartInString specifies the
name of the working directory.
If ommited, the current directory is used.
}
// lpDirectory := PChar(StartInString);
nShow := SW_SHOWNORMAL;
end;
if ShellExecuteEx(@SEInfo) then begin
repeat
Application.ProcessMessages;
GetExitCodeProcess(SEInfo.hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE) or
Application.Terminated;
ShowMessage('Calculator terminated');
end
else ShowMessage('Error starting Calc!');
end;
Also, I found this
An example uses "CreateProcress" (which is more appropriate on a 32bit-platform) to start notepad and waits until you close it:
var
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
NotepadExe: String;
begin
SetLength(NotepadExe, MAX_PATH);
GetWindowsDirectory(Pchar(NotepadExe), MAX_PATH);
StrCat(Pchar(NotepadExe),'\notepad.exe');
ZeroMemory(@StartupInfo,SizeOf(TStartupInfo));
StartupInfo.cb := SizeOf(TStartupInfo);
if(CreateProcess(Pchar(NotepadExe), Nil, Nil, Nil,
False, NORMAL_PRIORITY_CLASS,
Nil, Nil, StartupInfo,ProcessInfo))
then
begin
WaitForSingleObject(pi.hProcess,INFINITE);
ShowMessage('Hi there');
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end
end;
Note: none of these ideas are mine. I found on the net: https://www.tek-tips.com/viewthread.cfm?qid=186953