How to get local date time in Inno Setup? - inno-setup

Is there a way to get local date time stamp in Inno Setup ?

The answer depends on when you need it.
Needed at the time the setup is built
Needed at the time of install.
Needed at the time the setup is built.
You will need to use ISPP which is part of the Quick Start pack.
You can use the str GetDateTimeString(str, str, str) function.
Example: #define MyDateTimeString GetDateTimeString('dd/mm/yyyy hh:nn:ss', '-', ':');
The help menu in ISTool (Also a part of the Quick Start Pack) has a good help file for ISPP functions including this one where there is a page of information on this function.
Needed at the time of install.
Although a different source, the function is also called GetDateTimeString
Then it must be in a pascal coding block.
Example:
function DateTime : String;
begin
result := GetDateTimeString('dd/mm/yyyy hh:nn:ss', '-', ':');
end;
The details of how to use it are found in the Help File.
Although both functions have the same name, the context of when they are used is important to understanding why you would get one value over another.

Related

Inno setup: Replace string in XML file using StringChangeEx

Related to Inno setup: how to replace a string in XML file?
As suggested in the answer I'm using a template xml, let's say "app.xml". I would like to replace a string in that file: "Dsn=Serverxxx" with the result of a function "DsnName" {code:DsnName} I use elsewhere in the script.
[Code]
procedure WriteAppPath;
var
FileData: String;
begin
LoadStringFromFile(ExpandConstant('{app}\app.xml'), FileData);
StringChange(FileData, 'XXXXXMARKERXXXXX', ExpandConstant('{app}'));
SaveStringToFile(ExpandConstant('{app}\app.xml'), FileData, False);
end;
I cant seem to get this to work. How would the code above look acomplishing this? And, how would it look using StringChangeEx?
I know this will probably be marked as duplicate, but I just can't figure it out.
Thanks.
I think if you look at the Inno Setup help it explains it:
http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_stringchangeex
Description:
Changes all occurrences in S of FromStr to ToStr. If SupportDBCS is True (recommended unless you require binary safety), double-byte character sequences in S are recognized and handled properly. Otherwise, the function behaves in a binary-safe manner. Returns the number of times FromStr was matched and changed.
So what you are doing is:
Reading in your text file as one long text string.
Changing all instances of Xxx with Yyy in the string.
Saving the string back to the file.
But looking here it does states:
Loads the specified binary or non Unicode text file into the specified string. Returns True if successful, False otherwise.
So this function might not be ok for you because XML files are usually Unicode.
The answer here explains.

Cmd String to PAnsiChar in delphi

I am relatively new to Delphi and I want to make a quick application the uses the ShellExecute command.
I want to use string values in edit boxes to add to the command line for doing the processing job outside of the application.
Everything works fine, but I get the error :
"Incompatible types: String and PAnsiChar"
I have tried to convert it using:
Variable := PAnsiChar(AnsiString(editbox.Text), but to no avail.
Can anyone assist me with this problem please.
In Delphi 7, it's a simple typecast to PChar, which is already a PAnsiChar:
PChar(YourStringVariable);
or
PChar('Some text here'); // Cast not needed; demonstration only
PChar('C:\' + AFileName); // Cast needed because of variable use
Using it with ShellExecute:
AFile := 'C:\MyDir\Readme.txt';
Res := ShellExecute(0, 'open', PChar(AFile),
nil, nil, SW_NORMAL )
How could it work fine when you cannot compile it?
You have posted too little code to be sure what is wrong, but you definitively have one typecast too much. AnsiChar is type that can store only single character and it makes no sense here.
If Variable is PAnsiChar then you should be using:
Variable := PAnsiChar(editbox.Text)

Executing functions stored in a string

Lets say that there is a function in my Delphi app:
MsgBox
and there is a string which has MsgBox in it.
I know what most of you are going to say is that its possible, but I think it is possible because I opened the compiled exe(compiled using delphi XE2) using a Resource Editor, and that resource editor was built for Delphi. In that, I could see most of the code I wrote, as I wrote it. So since the variables names, function names etc aren't changed during compile, there should a way to execute the functions from a string, but how? Any help will be appreciated.
EDIT:
What I want to do is to create a simple interpreter/scripting engine. And this is how its supposed to work:
There are two files, scr.txt and arg.txt
scr.txt contains:
msg_show
0
arg.txt contains:
"Message"
And now let me explain what that 0 is:
First, scr.txt's first line is function name
second line tells that at which line its arguments are in the arg.txt, i.e 0 tells that "Message" is the argument for msg_show.
I hope my question is now clear.
I want to make a simple scripting engine.
In order to execute arbitrary code stored as text, you need a compiler or an interpreter. Either you need to write one yourself, or embed one that already exists. Realistically, the latter option is your best option. There are a number available but in my view it's hard to look past dwscript.
I think I've already solved my problem! The answer is in this question's first answer.
EDIT:
But with that, as for a workaround of the problem mentioned in first comment, I have a very easy solution.
You don't need to pass all the arguments/parameters to it. Just take my example:
You have two files, as mentioned in the question. Now you need to execute the files. It is as simple as that:
read the first line of scr.txt
check if it's a function. If not, skip the line
If yes, read the next line which tells the index where it's arguments are in arg.txt
pass on the index(an integer) to the "Call" function.
Now to the function which has to be executed, it should know how many arguments it needs. i.e 2
Lets say that the function is "Sum(a,b : integer)".It needs 2 arguments
Now let the function read the two arguments from arg.txt.
And its done!
I hope it will help you all.
And I can get some rep :)

Should I use String instead of TFilename?

When passing filename parameters to procedures/functions, should I use TFilename or String.
If there is a difference, what is it, and what are then potential ramifications if using a String?
e.g.
procedure TForm1.OpenFile(const AFilename : String);
begin
//Open the file if it exists
...
end;
I think TFilename should be used when developing components because that way IDE can show it's property editor (TOpenDialog will be shown when clicked on ellipsis in the property inspector).
Other than this there is basically no difference which one to use. Remember that if you use TFilename you must add SysUtils to your uses clause.
The only practical difference between string and TFileName types in a plain code is in passing an argument by reference; the following code
procedure GetFileName(var FileName: TFileName);
begin
FileName:= 'abcd.abc';
end;
procedure TForm1.Button2Click(Sender: TObject);
var
S: string;
begin
GetFileName(S);
end;
does not compile with error
[DCC Error] E2033 Types of actual and formal var parameters must be identical
Maybe this is a bit too obvious, but using the string type doesn't communicate anything about the intended usage of a variable. But when you encounter a variable declared as a TFileName, there's a lot more detail communicated right there.
The same principle applys to other basic types like Integer, Cardinal, Double etc. Instead you might want to consider using aliases for these like TCustomerID, THashValue, TInterestRate, etc. as these communicate much clearer what the intended usage of these variables is.
This improves readablility, and also allows for changing the base-type when needed, without having to touch any code using the type... just a recompile and you're done (but do be carefull with binary compatibility ofcourse).
Hmm, My strong preference is for const AFilename: String;
For the reason that especially for larger projects, if you ever need to add source code from another coder, if they have used lots of custom types like TCustomerID, THashValue, TInterestRate, instead of Integer, Cardinal, Double, then you have lots of the above mentioned E2033 to resolve.
Even lots of delphi built in source code doesn't use TFileName, like:
function MatchesMask(const Filename, Mask: string): Boolean;
Furthermore if I have a variable defined like AFileName: TFileName; then its obvious its a filename & the named type doesn't add any readability for me, if anything in some cases it makes code less readable, because you have to click through to check what actual variable its derived from.

Delphi Translating Strings

I'm using Delphi 2007 and I wonder how the following problem can be solved:
I have to translate AComp.Caption for example, but the string that I want to assign to the caption, often depends on some data (for example a date or a number, that gets Formatted). Therefore I have to save the data and the string in a new variable for every translation, which is really annoying.
What I want to do is something like that:
// will add the string and data to an internal list of Translator
// and will then return a DynamicString, which represents the translated value
AComp.Caption := T.NewTranslatedString("Hello %s, do you like cheese?", User)
(Note that AComp.Caption ("Hello %s..") can be changed in different methods)
When switching to another language, you would call T.TranslateAgain() and the value of all strings will be translated and, if data given, formatted again.
Is this possible or do you know another way for solving the given problem?
Thanks in advance
Additional question:
Are strings normal objects, that I can subclass and add dynamic behaviour that changes the string itself in special cases?
Delphi strings are not objects, you can't add behaviours to them. You would need to develop your own class.
The Windows way to localize applications is to get advantage of resources, that can be changed (and loading redirected) without changes to the code (no need to call special functions or add new components), and without run-time calls but to load the resource. The only disadvantage of resources is they cannot be changed easily by the end user. The Delphi 2007 standard localization tools use this approach.
Anyway there are some libraries like dxGetText (which is a port of the GNU gettext library) or TsiLang, for example that use a more "intrusive" approach, requiring changes to your code or adding components. In exchange they can simplify end-user localization.
Before developing your own localization library, I would check if one of the existing ones fits youe needs.
Note: Be aware that Delphi localization tool has significant issues that weren't fixed until XE (which I didn't test yet). See for example QC #79449. Unluckily the fix was never backported to earlier releases.
You can use Delphi's own translator tool. It is able to extract strings and resourcestrings from your source code and form DFM files, and gives you a graphical user interface to translate them to any language. It then creates a resource DLL for each language. The DLL containing the translated strings and DFM data. You should deploy this translation DLL with your project to the destination machine.
In your case, your strings are divided into two groups; fixed strings which do not need any further processing, and parametrized strings which need some additional data to be formatted properly. For the fixed strings, you can just type in the translation into translator tool. For parametrized strings, save each one as a resourcestring and use the resourcestring for formatting them. For example:
resourcestring
strDoYouLikeCheese = 'Hello %s, do you like cheese?';
...
AComp.Caption := Format(strDoYouLikeCheese,[User]);
Now you can use the translator tool or any resource editor to translate the resourcestring into your desired language without the need for changing your source code or recompiling it.
What you want to do is to localize your application. Delphi has support for this, based around the resourcestring keyword. However, I've never done any localization myself so I recommend that you do some websearch for this topic or perhaps wait for the other experts here to supply more detailed help!
You could use a dictionary to keep track of the string mappings, something like this
TTranslator = class
private
FMappings : TDictionary <String, String>;
public
function Translate (const SrcStr : String) : String;
procedure SetMapping (const SrcStr, DestStr : String);
end;
function TTranslator.Translate (const SrcStr : String) : String;
begin
if not FMappings.TryGetValue (SrcStr, Result) then
Result := SrcStr;
end;
procedure TTranslator.SetMapping (const SrcStr, DestStr : String);
begin
FMappings.AddOrSetValue (SrcStr, DestStr);
end;
Translating would then be simply several calls to SetMappings. This gives you a lot of flexiblity. Anyway, you might consider using the built-in localization support or even third-party solutions.
EDIT: Just saw that you are using Delphi 2007, so you don't have TDictionary available. The idea should remain valid, just use any dictionary implementation or a list-based approach.
And to answer the other part of your question: no, strings are not normal object (actually they are not objects at all). They are special in various ways (memory management, copy-on-write behaviour) and it is not possible to subclass them. But that's not what you want anyway if I understood the question correctly.

Resources