Inno Setup - Defining a default path inside [code] - function

I'm trying to read the Windows Registry so my app updates can retrieve the previously saved instalation path as its DefaultDirName.
I've read somewhere that I should call a function, like:
DefaultDirName="{code:GetPath}"
The problem is that I need to define a default path, in case the function does not find a previous one. For instance, 'C:\MyPath'. So I did this:
[Code]
function GetPath(Value: String): String;
var
OrigPath: string;
begin
Result := '{sd}\MyPath';
if RegQueryStringValue(HKCU, 'Software\MyApp', 'PathExec', OrigPath) then
Result := OrigPath;
end;
That is not working. When I run the setup, at the destination dir dialog I'm getting literally "C:\PathOfMySetup\{sd}\MyPath", not "C:\MyPath".
What should I write at that first "Result := " line in order to "MyPath" be created at the System Drive?
Thanks.

The constants in Pascal Script are not magically expanded. You have to expand them explicitly using the ExpandConstant function:
Result := ExpandConstant('{sd}\MyPath');

Related

When connecting to Oracle DB I get External: SIGSEGV error

I'll say how I reproduce the problem on lazarus.
I have a form and a datamodule using zeos to enstablish a connection with a local oracle db.
The problem born when I put some code to interlocute with the db.
Here is an example:
OracleMng.ZQuery1.SQL.Clear;
That is exactly the line going in error.
Here is the full code of the form:
unit form1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, DBGrids, StdCtrls,
datamodule2;
type
{ TLogin }
TLogin = class(TForm)
Button1: TButton;
DBGrid1: TDBGrid;
procedure Button1Click(Sender: TObject);
private
public
end;
var
Login: TLogin;
implementation
{$R *.lfm}
{ TLogin }
procedure TLogin.Button1Click(Sender: TObject);
begin
OracleMng.ZQuery1.SQL.Clear;
end;
end.
Here is the code of the datamodule:
unit datamodule2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DB, ZConnection, ZDataset, ZSqlMonitor;
type
{ TOracleMng }
TOracleMng = class(TDataModule)
DataSource1: TDataSource;
ZConnection1: TZConnection;
ZQuery1: TZQuery;
private
public
end;
var
OracleMng: TOracleMng;
implementation
{$R *.lfm}
{ TOracleMng }
end.
I'm trying
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then OracleMng.ZQuery1.SQL.add('select * from help');
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then OracleMng.ZQuery1.ExecSQL;
dbgrid1.refresh;
I have no more errors but the DBGrid1 is not filled.
This is my project lpr file:
program project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, zcomponent, datamodule2, form1
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource:=True;
Application.Scaled:=True;
Application.Initialize;
Application.CreateForm(TLogin, Login);
Application.Run;
end.
The fact that the change I suggested in my comment, namely
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then
OracleMng.ZQuery1.SQL.Clear
evidently stopped you getting the SIGSEGV error suggests that your DataModule and
form are being created in the wrong order, i.e. form first. Check this out by going to
Project | View Source in the IDE. If you see something like
program MyProgram;
[...]
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TDataModule1, DataModule1);
Application.Run;
end.
they are in the wrong order, so swap the two CreateForm lines
Application.CreateForm(TDataModule1, DataModule1);
Application.CreateForm(TForm1, Form1);
With that change, you should no longer need the
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then`
Next thing: You seem to be confused about when to use
ZQuery1.ExecSQL
and
ZQuery1.Open
Open is intended for when the SQL statement you are using produces a result set, that is
a collection of records which can be viewed in a TDBGrid. The most usual way to do this
is to use a SELECT statement as in
ZQuery1.SQL.Text := 'select * from MyTable';
ZQuery1.Open;
ExecQuery is intended for use where your SQL statement performs some operation on the database
which does not involve SELECTing records. The most common SQL statements which need ExecSQL are
UPDATE
INSERT
DELETE
though there are others, for example statements which execute stored procedures on the SQL Server
(note that some stored procedures return result sets and so need Open, rather than ExecSQL).
Note that ExecSQL will clear out any records which are in the dataset (ZQuery1) so after
you need to do Open again using a suitable SQL statement
var
S : String;
begin
S := 'update MyTable set number = number +1 where id = 5';
ZQuery.SQL.Text := S;
ZQuery1.ExecSQL; // no records shown in DBGrid1 from here
S := 'select * from MyTable';
ZQuery.SQL.Text := S;
ZQuery1.Open; // records shown in DBGrid1 again
end;
Note that I do
S := 'select * from MyTable';
ZQuery.SQL.Text := S;
instead of
ZQuery1.SQL.Clear;
ZQuery1.SQL.Add('select * from myTable');
The reason for this is that it's much easier to see the whole SQL statement in the debugger by
inspecting the variable S than inspecting the ZQuery1.SQL.Text property and much easier to
see any syntax errors.
You should always Close a dataset that you've Opened once you have finished working with it as it ensures what the data on disk is up to date. if the last SQL operation was ExecSQL, you don't need to close the dataset.
If you set the query's Text property the way I do, with ZQuery1.SQL.Text, you don't need to uses Clear. In any case, it is only equivalent to doing ZQuery1.SQL.Text := '' and it does not affect the state of the dataset - it only does anything when you call ExecSQL or Open.

Delphi - DataSet creating too many connections in MySQL

I am having a problem using the TFDDataSet component in my application.
I have a function that fetch many times if a customer has new orders. If it returns empty the function ends.
...
fdm_XMLREsumo.Close;
fdm_XMLREsumo.Active := false;
_DataSetJSON := SM.GetXMLResumoChave( pEnt_ID, pChave); //See Edit 1
_DataSet.Close;
_DataSet := TFDJSONDataSetsReader.GetListValueByName( _DataSetJSON, sXMLResumo );
_DataSet.Open; <-- here's the problem
if not _DataSet.IsEmpty then begin
exit;
end;
fdm_XMLREsumo.AppendData( _DataSet );
...
The problem is every time it executes _DataSet.Open; it creates a new connection in my DB.Because of that I'm having a too many connections exception.I've checked in my Server Properties and it is like this:
I have tried Connection.Close,_DataSet.Close, _DataSet.Free and _DataSet.Destroy but nothing worked.I read this, and it explains that even if you do _DataSet.Close the connection still exists, because DataSets work in memory.There is also this guy having a similar issue, but using Query. Does anyone know how can I manage to solve this?I am using MySQL
EDIT 1
As #CraigYoung helped me saying my example needs MCVE
SM.GetXMLResumoChave method:
Here it uses a connection to the database that is closed at the end of the function. Already debugged, and here it does not leave an open connection in MySQL Proccess List
function TDAOXMLResumo.GetXMLResumoChave(xEnt_id: Integer; xChave: String): TFDJSONDataSets;
begin
if not oSM.FDConn.Connected then
oSM.FDConn.Connected := true;
QueryPesquisa.SQL.Clear;
QueryPesquisa.SQL.Text :=
' select * from table' +
' where ent_id = :ent_id ' +
' and xre_chNFe = :xre_chNFe ';
QueryPesquisa.ParamByName('ent_id').Asinteger := xEnt_id;
QueryPesquisa.ParamByName('xre_chNFe').Asstring := xChave;
Result := TFDJSONDataSets.Create;
//TFDJSONDataSetsWriter.ListAdd Opens the Query that is passed as parameter and store the data as JSON in the TFDJSONDataSets (Result) object
TFDJSONDataSetsWriter.ListAdd(Result, sXMLResumo, QueryPesquisa);
//Closing the Query
QueryPesquisa.Close;
//Closing the Connection
oSM.FDConn.Close;
end;`
Basically, the _DataSet is only receiving a JSON List here: _DataSet := TFDJSONDataSetsReader.GetListValueByName( _DataSetJSON, sXMLResumo );, and then open it to access the data in it.

Delphi XE5 TBlobField.SaveToStream outputs only 4 bytes for a mysql blob field

I'm inserting an image file into a blob field using...
procedure TfrmMain.FileToDB(filename: string; blobfield: TBlobField);
var
FS: TFileStream;
begin
FS := TFileStream.Create(filename,fmOpenRead);
try
BlobField.LoadFromStream(FS);
finally
FS.Free;
end;
end;
This works fine, and I can even open the file in mysql workbench and as it's an image file I can also view it.
When I try to save the image back to disk using...
procedure TfrmMain.DBToFile(filename: string; blobfield: TBlobField);
var
FS: TFileStream;
begin
FS := TFileStream.Create(filename,fmCreate);
try
BlobField.SaveToStream(FS);
finally
FS.Free;
end;
end;
I get a file of only 4 bytes. ??
I've tried this with BLOB / MEDIUMBLOB / LARGEBLOB type fields, and using the above BlobField.SaveToStream() method, as well as creating a blob stream and using FS.CopyFrom(). When using the later method, the blobstream.size property shows a value of 4 also.
I'm using the unicode driver, and a unicode schema in mysql. What could be going on here?
While the problem is not resolved using FireDAC, switching to ADO components got me past this issue.
procedure TfrmMain.DBToFile(filename: string; blobfield: TBlobField);
var
FS: TFileStream;
begin
FS := TFileStream.Create(filename,fmCreate);
try
BlobField.SaveToStream(FS);
**FS.position:=0; // It can help**
finally
FS.Free;
end;
end;

What is opposite of ExtractRelativePath in Pascal?

I often use ExtractRelativePath to get the relative path between two path. But i cannot see any function opposite to it. This is an example from freepascal.org:
Uses sysutils;
Procedure Testit (FromDir,ToDir : String);
begin
Write ('From "',FromDir,'" to "',ToDir,'" via "');
Writeln (ExtractRelativePath(FromDir,ToDir),'"');
end;
Begin
Testit ('/pp/src/compiler','/pp/bin/win32/ppc386');
Testit ('/pp/bin/win32/ppc386','/pp/src/compiler');
Testit ('e:/pp/bin/win32/ppc386','d:/pp/src/compiler');
Testit ('e:\pp\bin\win32\ppc386','d:\pp\src\compiler');
End.
Output of this program
From "/pp/src/compiler" to "/pp/bin/win32/ppc386" via "../bin/win32/ppc386"
From "/pp/bin/win32/ppc386" to "/pp/src/compiler" via "../../src/compiler"
From "e:/pp/bin/win32/ppc386" to "d:/pp/src/compiler" via "../../src/compiler"
From "e:\pp\bin\win32\ppc386" to "d:\pp\src\compiler" via "../../src/compiler"
I need a function F to perform reverse action of ExtractRelativePath, for example:
F('/pp/src/compiler', '../bin/win32/ppc386') return '/pp/bin/win32/ppc386'.
Do you know any function like this? Thank you in advance.
Yes, sure. http://docwiki.embarcadero.com/Libraries/XE5/en/System.IOUtils.TPath.Combine
System.IOUtils.TPath.Combine
class function Combine(const Path1, Path2: string): string; inline; static;
Description
Combines two paths strings.
Call Combine to obtain a new combined path from two distinct paths. If
the second path is absolute, Combine returns it directly; otherwise
Combine returns the first path concatenated with the second one.
Above was written when the question was tagged by delphi
Now, for FPC simple scan through SysUtils sources lands you onto
c:\codetyphon\fpcsrc\rtl\objpas\sysutils\finah.inc
Which has
function ConcatPaths(const Paths: array of String): String;
Which is documented at
http://www.freepascal.org/docs-html/rtl/sysutils/concatpaths.html
ConcatPaths
Concatenate an array of paths to form a single path
Declaration
Source position: finah.inc line 42
function ConcatPaths( const Paths: array of ):;
Description
ConcatPaths will concatenate the different path components in Paths to
a single path. It will insert directory separators between the various
components of the path as needed. No directory separators will be
added to the beginning or the end of the path, and none will be taken
away.
Example
program ex96;
{ This program demonstrates the Concatpaths function }
uses sysutils;
begin
// will write /this/path/more/levels/
Writeln(ConcatPaths(['/this/','path','more/levels/']));
// will write this/path/more/levels/
Writeln(ConcatPaths(['this/','path','more/levels/']));
// will write this/path/more/levels
Writeln(ConcatPaths(['this/','path','more/levels']));
end.

XQUery collections: deleting?

I am new to xquery, and I am trying to use a collection to reload my webpage and keep some information. My problem is after I create the collection and save my node using (sausalito) the collection stays alive even afterI close the program. Next time I use the collection it has nodes already in it. I only need the collection to save a node, then reload website and delete node. Problem is that I am not able to delete the collection or the nodes. I tried using delete-nodes() and other methods from http://www.zorba-xquery.com/doc/zorba-1.4.0/zorba/xqdoc/xhtml/www.zorba-xquery.com_modules_xqddf.html#delete-index-1
What I have
declare collection resultview:collection as node()*;
declare variable $resultview:collection as xs:QName := xs:QName("resultview:collection");
declare sequential function resultview:add($allMovies as element(movies))
{
for $movie in $allMovies
return xqddf:insert-nodes($resultview:collection, $allMovies);
fn:trace(xqddf:collection($resultview:collection), "Collection data: "),
exit returning resultview:list();
};
declare sequential function resultview:deleteList() {
let $a := ""
return xqddf:delete-index($resultview:collection);
exit returning resultview:list();
};
if I do understand you correctly, this should work:
declare collection resultview:collection as node()*;
declare variable $resultview:collection as xs:QName := xs:QName("resultview:collection");
declare sequential function resultview:add($allMovies as element(movies))
{
xqddf:insert-nodes($resultview:collection, $allMovies);
resultview:list();
};
declare sequential function resultview:deleteList() {
xqddf:delete-nodes(
$resultview:collection,
xqddf:collection($resultview:collection));
resultview:list();
};
use delete-nodes instead of delete-index (the latter deletes a complete index and not a node at a specific index position).
does that help?