I'm trying to use some windows command line tools from within a short Pascal program. To make it easier, I'm writing a function called DoShell which takes a command line string as an argument and returns a record type called ShellResult, with one field for the process's exitcode and one field for the process's output text.
I'm having major problems with some standard library functions not working as expected. The DOS Exec() function is not actually carrying out the command i pass to it. The Reset() procedure gives me a runtime error RunError(2) unless i set the compiler mode {I-}. In that case i get no runtime error, but the Readln() functions that i use on that file afterwards don't actually read anything, and furthermore the Writeln() functions used after that point in the code execution do nothing as well.
Here's the source code of my program so far. I'm using Lazarus 0.9.28.2 beta, with Free Pascal Compiler 2.24
program project1;
{$mode objfpc}{$H+}
uses
Classes, SysUtils, StrUtils, Dos
{ you can add units after this };
{$IFDEF WINDOWS}{$R project1.rc}{$ENDIF}
type
ShellResult = record
output : AnsiString;
exitcode : Integer;
end;
function DoShell(command: AnsiString): ShellResult;
var
exitcode: Integer;
output: AnsiString;
exepath: AnsiString;
exeargs: AnsiString;
splitat: Integer;
F: Text;
readbuffer: AnsiString;
begin
//Initialize variables
exitcode := 0;
output := '';
exepath := '';
exeargs := '';
splitat := 0;
readbuffer := '';
Result.exitcode := 0;
Result.output := '';
//Split command for processing
splitat := NPos(' ', command, 1);
exepath := Copy(command, 1, Pred(splitat));
exeargs := Copy(command, Succ(splitat), Length(command));
//Run command and put output in temporary file
Exec(FExpand(exepath), exeargs + ' >__output');
exitcode := DosExitCode();
//Get output from file
Assign(F, '__output');
Reset(F);
Repeat
Readln(F, readbuffer);
output := output + readbuffer;
readbuffer := '';
Until Eof(F);
//Set Result
Result.exitcode := exitcode;
Result.output := output;
end;
var
I : AnsiString;
R : ShellResult;
begin
Writeln('Enter a command line to run.');
Readln(I);
R := DoShell(I);
Writeln('Command Exit Code:');
Writeln(R.exitcode);
Writeln('Command Output:');
Writeln(R.output);
end.
At a quick look I see that you try to split command based on space. What if:
I try execute something without parameters, like fpc? (answer: exepath will be empty)
I try execute something with full path and with space in it like C:\Program Files\Edit Plus 3\editplus.exe?
I tried Exec() and it seems to work when you give it full path to executable you want to run, but output redirection does not work. Look at: Command line redirection is performed by the command line interpreter. However you can execute .bat file that does redirection (create temporary .bat file with command user gives + redirection, and run that batch).
You can use that:
uses sysutils;
begin
ExecuteProcess('cmd','/c dir C:\foo');
ExecuteProcess('C:\foo\bar.exe','param1 param2');
end.
If you want to get output of command, you may want to see this post. http://wiki.freepascal.org/Executing_External_Programs#TProcess
Do not use dos.exec, it is limited to a short (255 char) command line. Use sysutils.executeprocess.
However Michal's comments probably touch the main issue. When executing via kernel (not shell) functions, one should always provide a complete path. Also, using kernel functions one can't use shell commands like redirection.
In general, I suggest you try to use the TProcess class in the process unit. It abstracts all of this and more, and is also used by Lazarus to call external tools.
Related
I use Delphi 7 IDE. I want to run .exe from the command line with parameters (to be defined).
How can I know if my app is running with the CMD command?
How can I read the parameter with source code?
How can I know if my app is running with the CMD command?
You can't, nor do you ever need to. If your project is a console app, then the .exe is always run inside of a console window process. If the project is not a console app, then there is no console, but you can create one if needed using AllocConsole(). Any process, whether it is a console app or not, can receive command-line parameter, though.
How can I read the parameter with source code?
Use the ParamCount() and ParamStr() functions in the System unit, or the FindCmdLineSwitch() function in the SysUtils unit.
Or, use GetCommandLine() in the Windows unit and parse the raw command-line data yourself.
var
bHideForm = boolean ;
begin
bHideForm := False;
bHideForm := ParamCount > 0;
if bHideForm then
bMetadataExport := GetParamValue(C_MENU, sMetaDataMenu);
{--------------------- function GetParamValue ----------------------------- }
function GetParamValue(aParamName: string; out oParamValue: string): Boolean;
var
i: Integer;
s: string;
begin
Result := False;
for i := 1 to ParamCount do
begin
s := ParamStr(i);
Result := pos(aParamName, UpperCase(s)) = 1;
if Result then
begin
oParamValue := copy(s, length(aParamName)+2, MaxInt);
Exit;
end;
end;
end;
end;
Using Delphi (windows app) i want to get list of other applications running currently. Here How to check if a process is running using Delphi? i've found great tutorial about geting filenames/names of running application, however it gives names only process name (for example NOTEPAD.EXE). I've used naturally part with
UpperCase(ExtractFileName(FProcessEntry32.szExeFile))
and
UpperCase(ExtractFilePath(FProcessEntry32.szExeFile))
and just
UpperCase(FProcessEntry32.szExeFile)
but obviously FProcessEntry32.szExeFile does not have a path to file/process
Is there a simply way of getting list with paths? Here's How to get the list of running processes including full file path? solution with JclSysInfo library, but i cant use it in place of work in project.
I looked at what I could in Google and what I found usually concerned just the application that is running or the application that is active, but I can't just find a list of all running applications. Maybe i'm missing something obvious?
I'm not looking for any complex procedures, I'm not much interested in process parrent, or if there is no access to the process path, I don't have it and don't bother.
Any simple hint?
OK, due to helpfull comment from #TLama i've combined topics above to take name and path of process:
function processExists(exeFileName: string): Boolean;
var
ContinueLoopP, ContinueLoopM: BOOL;
FSnapshotHandle1, FSnapshotHandle2: THandle;
FProcessEntry32: TProcessEntry32;
FMODULEENTRY32: TMODULEENTRY32;
begin
FSnapshotHandle1 := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
FMODULEENTRY32.dwSize := SizeOf(FMODULEENTRY32);
ContinueLoopP := Process32First(FSnapshotHandle1, FProcessEntry32);
ContinueLoopM := Module32First(FSnapshotHandle2, FMODULEENTRY32);
Result := False;
while Integer(ContinueLoopP) <> 0 do
begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeFileName))) then
Result := True;
ShowMessage(FMODULEENTRY32.szExePath + FProcessEntry32.szExeFile);
ContinueLoopP := Process32Next(FSnapshotHandle1, FProcessEntry32);
ContinueLoopM := Module32Next(FSnapshotHandle2, FMODULEENTRY32);
end;
CloseHandle(FSnapshotHandle1);
CloseHandle(FSnapshotHandle2);
end;
But still FProcessEntry32.szExeFile returns empty string. What i'm doing wrong? Thank You in advance.
I cannot write comment (low score), so I need to write as "answer". Try this code,
using FProcessEntry32.th32ProcessID as parameter:
Function QueryFullProcessImageNameW(hProcess:THandle; dwFlags:Cardinal; lpExeName:PWideChar; Var lpdwSize:Cardinal) : Boolean; StdCall; External 'Kernel32.dll' Name 'QueryFullProcessImageNameW';
Function GetFullPath(Pid:Cardinal) : UnicodeString;
Var rLength:Cardinal;
Handle:THandle;
Begin Result:='';
Handle:=OpenProcess(PROCESS_QUERY_INFORMATION, False, Pid);
If Handle = INVALID_HANDLE_VALUE Then Exit;
rLength:=256; // allocation buffer
SetLength(Result, rLength+1); // for trailing space
If Not QueryFullProcessImageNameW(Handle, 0, #Result[1],rLength) Then Result:='' Else SetLength(Result, rLength);
End;
This is a simple way I think. If you want to get the loaded DLL's full name, use
FMODULEENTRY32.hModule with GetModuleFileNameW function.
I'm trying to do a program that reads numbers from a file, outputs them into a vector and then writes them. The code compiles nicely, but when run, it gets stuck with just a prompt without delivering any output.
Program LectorDeEnteros;
type
Arreglo = array [1..30] of integer;
var
//Arch:text;
Prom:byte;
i:integer;
ArregloA:Arreglo;
Procedure CargadorVectorialdeArchivo (var ArregloA:Arreglo);
var
Arch:text;
i:integer;
Begin
assign (Arch,'Numeros.txt');
reset (Arch);
i := 1;
while not eof(Arch) do
Write(Arch);Read(ArregloA[i]);
i := i + 1;
End;
Begin
CargadorVectorialdeArchivo(ArregloA);
for i := 1 to 14 do
WriteLn(ArregloA[i]:3);
End.
As i said, there are no error messages, just the prompt and no output. I have to CTRL-Z to get it out of this "loop". The expected output would be the numbers of the array, one on each line.
Rewrite the procedure as this:
Procedure CargadorVectorialdeArchivo (var ArregloA:Arreglo);
var
Arch:text;
i:integer;
Begin
assign (Arch,'Numeros.txt');
reset (Arch);
i := 1;
while not eof(Arch) do
begin
Read(Arch,ArregloA[i]);
i := i + 1;
end;
End;
Putting Arch in front of the file tells the compiler that you want to read the contents from that file, not from the keyboard.
I want to capture cmd.exe output and show it in a diffrent gui I am making. I want to make a command interpreter with extended functionality. The dir command works flawless, the problem ocurrs when I try to execute another process like ipconfig.
I don't see ipconfig output. Is there a workarround on that ?!
I use TProcess component from Lazarus (FreePascal)
proc := TProcess.Create(nil);
proc.Options:= [poUsePipes, poNoConsole];
proc.ShowWindow:= swoHIDE;
proc.Executable:= 'cmd';
Reading output thread:
if (Length(cmd) > 0) then
begin
cmd := cmd + #13#10;
proc.Input.Write(cmd[1], Length(cmd)); // here I write command from user
strikes := 0;
end
else
if proc.Output.NumBytesAvailable > 0 then
begin
while proc.Output.NumBytesAvailable > 0 do
begin
FillChar(buf, sizeof(buf), #0);
proc.Output.Read(buf, sizeof(buf) - 1);
data := data + buf;
end;
// data gets echoed to user
It works fine for me (I use FPC 3.1.1 & Lazarus 1.5 but I hope it does not matter):
proc.Options:= [poUsePipes];
proc.ShowWindow:= swoHIDE;
proc.Executable:= 'cmd';
...
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
var
cmd: String;
begin
if Key = #13 then
begin
Key := #0;
if not proc.Active then
proc.Active := True;
cmd := Edit1.Text + LineEnding;
proc.Input.Write(cmd[1], Length(cmd));
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
buf: array[0..65535] of Char;
begin
if proc.Output.NumBytesAvailable > 0 then
begin
while proc.Output.NumBytesAvailable > 0 do
begin
FillChar(buf, sizeof(buf), #0);
proc.Output.Read(buf, sizeof(buf) - 1);
Memo1.Lines.Add(buf);
end;
end;
end;
I guess that you just does not catch process output properly.
Good luck.
PS: If you need to create some windows console-like application, the best way, I think, is using Windows console API instead of cross-platform Lazarus components.
PPS: To emulate console look and behavior with Lazarus use CmdLine component.
In general it is smart to first check if the short examples don't solve the problem:
e.g.
uses process;
var s : ansistring;
begin
runcommand('ipconfig',['/all'],s);
writeln(s);
end.
works fine, and saves a whole lot of trouble. (FPC 2.6.2+ though)
I'm working with Inno Setup Compiler (Pascal Scripting).
My form has an image object (TBitmapImage) and I'd like to supply a dynamic image obtained from the web URL. Is it possible to silently download an image (or other type of file) in Inno Setup scripting?
I would write a small Win32 program that downloads a file from the Internet, such as
program dwnld;
uses
SysUtils, Windows, WinInet;
const
PARAM_USER_AGENT = 1;
PARAM_URL = 2;
PARAM_FILE_NAME = 3;
function DownloadFile(const UserAgent, URL, FileName: string): boolean;
const
BUF_SIZE = 4096;
var
hInet, hURL: HINTERNET;
f: file;
buf: PByte;
amtc: cardinal;
amti: integer;
begin
result := false;
hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
try
hURL := InternetOpenUrl(hInet, PChar(URL), nil, 0, 0, 0);
try
GetMem(buf, BUF_SIZE);
try
FileMode := fmOpenWrite;
AssignFile(f, FileName);
try
Rewrite(f, 1);
repeat
InternetReadFile(hURL, buf, BUF_SIZE, amtc);
BlockWrite(f, buf^, amtc, amti);
until amtc = 0;
result := true;
finally
CloseFile(f);
end;
finally
FreeMem(buf);
end;
finally
InternetCloseHandle(hURL);
end;
finally
InternetCloseHandle(hInet);
end;
end;
begin
ExitCode := 0;
if ParamCount < 3 then
begin
MessageBox(0,
PChar(Format('%s: This program requires three command-line arguments.',
[ExtractFileName(ParamStr(0))])),
PChar(ExtractFileName(ParamStr(0))),
MB_ICONERROR);
Exit;
end;
if FileExists(ParamStr(PARAM_FILE_NAME)) then
DeleteFile(PChar(ParamStr(PARAM_FILE_NAME)));
if DownloadFile(ParamStr(PARAM_USER_AGENT), ParamStr(PARAM_URL),
ParamStr(PARAM_FILE_NAME)) then
ExitCode := 1;
end.
This program takes three command-line arguments: the UserAgent to be sent to the web server (can be anything, such as "MyApp Setup Utility"), the URL of the file on the Internet, and the file name of the file that is being created. Don't forget to enclose the arguments inside quotation marks ("). The exit code of the program is 0 if the download failed, and 1 if the download succeeded.
Then, in your Inno Setup script, you can do
[Files]
Source: "dwnld.exe"; DestDir: "{app}"; Flags: dontcopy
[Code]
function InitializeSetup: boolean;
var
ResultCode: integer;
begin
ExtractTemporaryFile('dwnld.exe');
if Exec(ExpandConstant('{tmp}\dwnld.exe'),
ExpandConstant('"{AppName} Setup Utility" "http://privat.rejbrand.se/sample.bmp" "{tmp}\bg.bmp"'),
'', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode) then
if ResultCode = 1 then
(* Now you can do something with the file ExpandConstant('{tmp}\bg.bmp') *);
end;
Unfortunately, however, I know of no means by which you can change the WizardImageFile during runtime...
It's actually possible to download almost anything from the web using InnoTools Downloader.
Inno setup doesnt have any inbuilt functions for this, however, you can perform this action using batch files that do the job for you.
1) download a command line URL resource downloader like -
http://www.chami.com/free/url2file_wincon.html
Some tips on how to use it -
http://www.chami.com/tips/windows/062598W.html
2) Package it in your installer
3) create a batch file that calls url2file.exe and fetches your image into the app directory
4) Call this batch file in the initialize setup command of Inno Setup installer script.
5) Use that image wherever you want!
ps - If you are using the image in the setup, check if differed image loading is allowed or not.. i am not sure about that.
Let me know if you have any other questions