I saw this question here: How to get an output of an Exec'ed program in Inno Setup?
But I can't get it to work myself, the commented out code are my attempts to make this work, but I resorted to a bat file because I couldn't make my redirection work. CacheInstanceName and CacheInstanceDir are global variable defined elsewhere:
function CheckCacheExists(): Integer;
var
args: String;
buffer: String;
ResultCode: Integer;
begin
// args := 'qlist ' + CacheInstanceName + ExpandConstant(' nodisplay > {tmp}\appcheck.txt');
// MsgBox(args, mbInformation, MB_OK);
// Exec(CacheInstanceDir + '\bin\ccontrol.exe', 'qlist ' + CacheInstanceName + ExpandConstant(' nodisplay > "{tmp}\appcheck.txt"'), '', SW_SHOW,
ExtractTemporaryFile('checkup.BAT');
Exec(ExpandConstant('{tmp}\checkup.BAT'), CacheInstanceDir + ' ' +
CacheInstanceName + ' ' + ExpandConstant('{tmp}'), '', SW_SHOW,
ewWaitUntilTerminated, ResultCode);
LoadStringFromFile(ExpandConstant('{tmp}\appcheck.txt'),buffer);
if Pos('^', buffer) = 0 then
begin
Result := 0
end
else
begin
Result := 1
end
end;
What am I doing wrong?
The output redirection syntax is a feature of the command prompt, not the core Windows APIs. Therefore if you want to redirect output then you need to invoke the command via {cmd} /c actual-command-line > output-file. Don't forget to include quotes where appropriate, as {tmp} (and other constants) may contain spaces.
However, you should strongly consider rewriting whatever is in that batch file into actual code. Anything you can do in a batch file you can do either directly in the Inno script or in a DLL that you call from the script. And this permits you greater control over error checking and the format of whatever data you want to retrieve.
Try running the command directly on your command line with the arguments in your args string to see what the result is which may give an indication of the problem.
Also, check that the file you are trying to redirect your output to is not in use by another process. I have found that when this occurs the actual command may execute successfully with the Exec command returning True but the ResultCode indicates an error and no output is written to the file used in the redirect. In this particular instance of the file being used by another instance the SysErrorMessage(ResultCode) command returns simply Incorrect function. However, testing directly on the command line as I mentioned first returns that the file is in use by another process.
Related
I have this TEST console app created in delphi 10.3.2.
I can update a memcached server running on "ubuntu linux" with it from win32 app,
If I compile this console app and run it on linux it will not work.
I think it's an encoding issue.
program ProjecttestLinuxMemCach;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, system.json,system.netencoding,system.DateUtils,
idtcpclient,idGlobal;
var
tcp : TIdTCPClient;
s : String;
begin
try
tcp := TIdTCPClient.Create;
try
tcp.ConnectTimeout := 1000;
tcp.ReadTimeout := 3000;
tcp.Host := '192.168.30.141'; // my local memcached server running on linux.
tcp.Port := 11211;
tcp.ReuseSocket := rsTrue;
s:='set q 0 0 1'+sLineBreak+'b'+sLineBreak;
tcp.Connect;
tcp.Socket.Write(s);
repeat
s:= tcp.Socket.ReadLn(sLineBreak);
until (s = 'END')or ( s='' ) or ( s='STORED' );
writeln(s)
finally
tcp.Free;
end;
readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
I tried to change sLineBreak to #10 for Linux same issue.
When I run on linux I get blank response and value is not set.
Any idea howto fix ?
Thanks
The RTL's sLineBreak constant is CRLF (#13#10) on Windows but only LF (#10) on Linux.
According to the Memcached protocol spec:
There are two kinds of data sent in the memcache protocol: text lines
and unstructured data. Text lines are used for commands from clients
and responses from servers. Unstructured data is sent when a client
wants to store or retrieve data. The server will transmit back
unstructured data in exactly the same way it received it, as a byte
stream. The server doesn't care about byte order issues in
unstructured data and isn't aware of them. There are no limitations on
characters that may appear in unstructured data; however, the reader
of such data (either a client or a server) will always know, from a
preceding text line, the exact length of the data block being
transmitted.
Text lines are always terminated by \r\n. Unstructured data is also
terminated by \r\n, even though \r, \n or any other 8-bit characters
may also appear inside the data. Therefore, when a client retrieves
data from a server, it must use the length of the data block (which it
will be provided with) to determine where the data block ends, and not
the fact that \r\n follows the end of the data block, even though it
does.
Indy's IOHandler has a WriteLn() method that sends the IdGlobal.EOL constant (aka CRLF) on all platforms. And the IOHandler.ReadLn() handles both CRLF and LF terminators by default on all platforms if you don't explicitly specify your own terminator.
For a consistent experience on all platforms, I suggest you remove all use of sLineBreak from your TCP code and let Indy handle the line breaks for you, eg:
program ProjecttestLinuxMemCach;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.JSON, System.NetEncoding, System.DateUtils,
IdTCPClient, IdGlobal;
var
tcp : TIdTCPClient;
s : String;
begin
try
tcp := TIdTCPClient.Create;
try
tcp.ConnectTimeout := 1000;
tcp.ReadTimeout := 3000;
tcp.Host := '192.168.30.141'; // my local memcached server running on linux.
tcp.Port := 11211;
tcp.ReuseSocket := rsTrue;
tcp.Connect;
try
tcp.Socket.WriteLn('set q 0 0 1');
tcp.Socket.WriteLn('b');
repeat
s := tcp.Socket.ReadLn;
until (s = 'END') or (s = '' {and not tcp.Socket.ReadLnTimedOut}) or (s = 'STORED');
Writeln(s);
finally
tcp.Disconnect;
end;
finally
tcp.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
I'm trying to install a piece of software during an installation.I decided to create a folder within the temp folder if it wasn't present to first store the executable file as from what I can tell {tmp} will create a random name, and I'm thinking that if i try and execute the file, using {tmp} again will only create a new random name
// Check if utilites folder is in the temp folder
// if not we will create it here so there are no
// problems adding the files in the file section
if not DirExists(ExpandConstant('{%tmp}\Utilities\SDK')) then
ForceDirectories(ExpandConstant('{%tmp}\Utilities\SDK'));
From there he is the File Section, using the * I'm hoping that that will get every file within that folder.
Source: "{#ExternalInstallers}\Foo\SDK\*"; DestDir: "{%tmp}\Utilities\SDK";Flags: recursesubdirs createallsubdirs ; check:InstallCheck('Foo')
and from there if the required boolean is set to true, the execution of the file should begin from InitializeSetup()
if({#GNTCChecked})then
begin
Log('Start installing SDK for Foo Plugin...');
if not ShellExec('', ExpandConstant('{%tmp}\Utilities\SDK\setup.exe'), ExpandConstant('/S /v/qn'), '', SW_HIDE, ewWaitUntilTerminated, ErrorCode) then//added 18-05-2018 GK
begin
MsgBox('Cannot install Foo SDK! Foo may not work properly! Error Code: ' +IntToStr(ErrorCode) ,mbInformation, MB_OK);
Log('Cannot install Foo SDK! Foo may not work properly!');
end;
Log('Start installing CU7 for Foo Plugin...');
if not ShellExec('', ExpandConstant('{%tmp}\Utilities\SDK\FooBar.exe'), ExpandConstant('/S /v/qn'), '', SW_HIDE, ewWaitUntilTerminated, ErrorCode) then
begin
MsgBox('Cannot install CU7 for Foo! Foo may not work properly! Error Code: ' +IntToStr(ErrorCode) ,mbInformation, MB_OK);
Log('Cannot install CU7 for Foo! Foo may not work properly!');
end;
end;
end;
end;
None of the message boxes that I have told it to show if there is a problem show, However the added on program that I'm wanting to install does install.
I am passing a set of parameters from main setup through the command line which also includes file paths and reading the values in Subsetup. The command line parameters output looks like below
/dir="D:\XXX.Web" /dbname="XXX" /dbinstancename="XXX" /dbsapwd="123!"#$%" /dataFileDirectory="D:\MSSQL10_50.XXX\MSSQL\DATA" /logFileDirectory="D:\MSSQL10_50.XXX\MSSQL\Log" /backupDirectory="E:\MSSQL10_50.XXX\MSSQL\Backup" /sqlCmdLocation="C:\Program Files\Microsoft SQL Server\100\Tools\Binn\SQLCMD.EXE" /webappname="yyyy" /Nameservice="VVVV" /UserUsername="user"
I am getting the parameter values in the Subsetup using a custom GetParameter function and storing in the String like mentioned below...
// This is how I get the values and store in String
DBName := GetParameter('/dbname=', DBName);
DataFileDirectory := GetParameter('/dataFileDirectory=', DataFileDirectory);
// function to get the parameters from the commandline parameters
function GetParameter(ParamName, Default: String): String;
var
P, I: Integer;
begin
Result := Default;
if Length(ParamName) < 1 then Exit;
ParamName := Lowercase(ParamName);
for I := 2 to ParamCount() do begin
P := pos(ParamName, Lowercase(ParamStr(I)));
if P > 0 then begin
P := P + Length(ParamName);
Result := Copy(ParamStr(I), P, Length(ParamStr(I))+1-P);
Result := Trim(RemoveQuotes(Trim(Result)));
Exit;
end;
end;
end;
When I print all the values in the log file, I am getting the output like mentioned below.
DBName = XXX
DBSaPwd = ********
DBInstanceName = XXX
DataFileDirectory = D:\MSSQL10_50.XXX\MSSQL\DATA /logFileDirectory=D:\MSSQL10_50.XXX\MSSQL\Log /backupDirectory=E:\MSSQL10_50.XXX\MSSQL\Backup /sqlCmdLocation=C:\Program
LogFileDirectory = D:\MSSQL10_50.XXX\MSSQL\Log /backupDirectory=E:\MSSQL10_50.XXX\MSSQL\Backup /sqlCmdLocation=C:\Program
BackupDirectory = E:\MSSQL10_50.XXX\MSSQL\Backup /sqlCmdLocation=C:\Program
SqlCmdLocation = C:\Program
WebAppName = yyyy /Nameservice=VVVV /UserUsername=user /UserPassword=user /MSSQLServerSelection= /silent /restart=false
The file path value of DataFileDirectory for example is taking the next parameter values and so on for the subsequent values which is throwing "File name, directory name or volume label syntax is incorrect" error at the end of the setup execution.
I believe an escape sequence must be used to solve this problem. Please provide your suggestions to solve this error and to implement it correctly.
Thanks in advance
DeeJay
sorry for my english.
Hi
my installer will download files , by the addition of " IDP : Download plugin for Inno Setup," and I have a problem because they are downloaded in .zip format so need to be extracted , whether it is possible to be unpacked where the application ? It is an add-on or something?
Please help.
You can ship the unzip tool of your choice and use it for extraction.
I tend to ship "7za" (the command-line version of 7zip), because i found out that it's extraction speed is really good (and better than unzip).
Firstly, you integrate the extraction tool into your installer.
[Files]
Source: ..\utils\unzip\7za.exe; DestDir: {tmp}; Flags: dontcopy
Source: and some zip files ...
Note the dontcopy. This will not copy the file to the user system during the normal file copying stage, but statically compile the file into the installation.
Secondly, you might add a little DoUnzip helper method to your [Code] section.
It will use the tool from the temp folder.
procedure DoUnzip(source: String; targetdir: String);
var
unzipTool: String;
ReturnCode: Integer;
begin
// source contains tmp constant, so resolve it to path name
source := ExpandConstant(source);
unzipTool := ExpandConstant('{tmp}\7za.exe');
if not FileExists(unzipTool)
then MsgBox('UnzipTool not found: ' + unzipTool, mbError, MB_OK)
else if not FileExists(source)
then MsgBox('File was not found while trying to unzip: ' + source, mbError, MB_OK)
else begin
if Exec(unzipTool, ' x "' + source + '" -o"' + targetdir + '" -y',
'', SW_HIDE, ewWaitUntilTerminated, ReturnCode) = false
then begin
MsgBox('Unzip failed:' + source, mbError, MB_OK)
end;
end;
end;
Thirdly, you extract the unzip tool, then the zips and then you simply use the DoUnzip() method on the zips. These commands are for the [Code] section.
// extract 7za to temp folder
ExtractTemporaryFile('7za.exe');
// extract the zip to the temp folder (when included in the installer)
// skip this, when the file is downloaded with IDP to the temp folder
//ExtractTempraryFile('app.zip);
targetPath := ExpandConstant('{tmp}\');
// unzip the zip in the tempfolder to your application target path
DoUnzip(targetPath + 'app.zip', ExpandConstant('{app}'));
I tested code posted here:
Why ini file is not being saved?
After call "SaveSettings" procedure settings.ini is saved in UTF-8 format, all is OK. After second call (settings.ini already exists), ini file is updated but UTF8 encoded characters are lost, ??????????? is displayed instead. File encoding is lost ini contain plain TEXT.
Uses IniFiles;
const
SZ_APP_NAME;
Procedure TForm1.SaveSettings;
var
_MemIniU: TMemIniFile;
SettingsPath: string;
begin
SettingsPath := GetHomePath + PathDelim + SZ_APP_NAME + PathDelim;
_MemIniU := TMemIniFile.Create(ChangeFileExt(SettingsPath, 'Settings.ini'),
TEncoding.UTF8);
try
if Form1.WindowState <> TWindowState.wsMaximized then
begin
_MemIniU.WriteInteger(SZ_APP_NAME, 'WindowLeft', Form1.Left);
_MemIniU.WriteInteger(SZ_APP_NAME, 'WindowTop', Form1.Top);
_MemIniU.WriteInteger(SZ_APP_NAME, 'WindowWidth', Form1.Width);
_MemIniU.WriteInteger(SZ_APP_NAME, 'WindowHeight', Form1.Height);
_MemIniU.WriteString(SZ_APP_NAME, 'UnicodeText', Edit1.Text);
_MemIniU.UpdateFile;
end;
finally
_MemIniU.Free;
end;
end;