Construction of a class with task property in Ada 2005 - class

I have a class Test_Class in Ada 2005 which has a parent-linked task property called Primary, from type Primary_Task, defined as:
type Test_Class is tagged limited
record
Info : Integer;
Value : Float;
Primary : Primary_Task (Test_Class'Access);
end record;
I need build a one-step constructor for my class in the form
function Construct (T : access Test_Class) return Test_Class_Ptr is
begin
return new Test_Class'(Info => T.Info + 1,
Value => 0.0,
Primary => [WHAT I WANNA KNOW]);
end Construct;
Currently my code is:
-- test_pkg.ads
package Test_Pkg is
type Test_Class;
type Test_Class_Ptr is access all Test_Class;
task type Primary_Task (This_Test : access Test_Class) is
pragma Storage_Size (1000);
end Primary_Task;
type Test_Class is tagged limited
record
Info : Integer;
Value : Float;
Primary : Primary_Task (Test_Class'Access);
end record;
function Construct (T : access Test_Class) return Test_Class_Ptr;
end Test_Pkg;
-- test_pkg.adb
with Text_IO; use Text_IO;
package body Test_Pkg is
[...]
function Construct (T : access Test_Class) return Test_Class_Ptr is
T_Ptr : constant Test_Class_Ptr := new Test_Class;
begin
T_Ptr.Info := T.Info + 1;
T_Ptr.Value := 0.0;
return T_Ptr;
end Construct;
end Test_Pkg;
So, how can I code it? What should I put in Primary => [...] code? Should I change the definition of Primary : Primary_Task (Test_Class'Access); in Test_Class definition?

I got an answer from Randy Brukardt (thank you) on comp.lang.ada:
In Ada 2005 or later, use "<>" to default initialize a component in an
aggregate (which is the only thing you can do with a task).
(...)
function Construct (T : access Test_Class) return Test_Class_Ptr is
begin
return new Test_Class'(Info => T.Info + 1,
Value => 0.0,
Primary => <>);
end Construct;
However, I tried to compile it using GNAT GPL 2011 and got the GNATBUG below
c:\tst>gnatmake -gnat12 test_pkg.adb
gcc -c -gnat12 test_pkg.adb
+===========================GNAT BUG DETECTED==============================+
| GPL 2011 (20110428) (i686-pc-mingw32) GCC error: |
| in create_tmp_var, at gimplify.c:505 |
| Error detected around test_pkg.adb:20:29 |
| Please submit a bug report by email to report#adacore.com. |
| GAP members can alternatively use GNAT Tracker: |
| http://www.adacore.com/ section 'send a report'. |
| See gnatinfo.txt for full info on procedure for submitting bugs. |
| Use a subject line meaningful to you and us to track the bug. |
| Include the entire contents of this bug box in the report. |
| Include the exact gcc or gnatmake command that you entered. |
| Also include sources listed below in gnatchop format |
| (concatenated together with no headers between files). |
| Use plain ASCII or MIME attachment. |
+==========================================================================+
Please include these source files with error report
Note that list may not be accurate in some cases,
so please double check that the problem can still
be reproduced with the set of files listed.
Consider also -gnatd.n switch (see debug.adb).
test_pkg.adb
test_pkg.ads
raised TYPES.UNRECOVERABLE_ERROR : comperr.adb:423
gnatmake: "test_pkg.adb" compilation error
So GNAT GPL users may have to wait for the next release to use this solution.

Related

Sqlx only accept string literal in query macro, how to use variable?

The codes:
// very complex where clause combined from several runtime variables.
let query: String = String.from("where ..........");
let rows_num: i64 = sqlx::query!( &*query).fetch_one(connection)
The error from compiler:
error: expected string literal
--> src/abc.rs:80:25
|
80 | let rows_num: i64 = sqlx::query!(
| ____________^
81 | | &*query,
82 | | ).fetch_one(connection)
| |^
|
= note: this error originates in the macro sqlx::query (in Nightly builds, run with -Z macro-backtrace for more info)
And the doc states:
The query must be a string literal or else it cannot be introspected (and thus cannot be dynamic or the result of another macro).
I know the sqlx computes at compile time, my where clause computation is at run time.
I really want to use variable, because the where clause depends other several conditions.
Are there any ways to use variable in sqlx?
Using sqlx::query() function instead of the sqlx::query!() macro. The document doesn't mention it in their page.

What is the most efficient way to extract the last part of a split string in PostgreSQL?

I want to extract the subdomain of a fully qualified domain up to the second level in a PostgreSQL function.
At the moment I have the following snippet which works, but I'm not sure if this is the most efficient way of doing it:
subdomains := left(query, length(query) - length(tld));
RETURN reverse(split_part(reverse(subdomains), '.', 1)) || tld;
It is guaranteed that the query ends with the tld substring.
Examples:
+---------------------+---------+---------------+
| query | tld | output |
+---------------------+---------+---------------+
| abc.example.com | .com | example.com |
| x.y.z.example.co.uk | .co.uk | example.co.uk |
| zzz.123.yyy.com.br | .com.br | yyy.com.br |
+---------------------+---------+---------------+
This one is not horribly efficient too but at least does not reverse twice and I guess that array_length is cheap and string_to_array is roughly as expensive as split_part. This may be wrong but is worth trying.
sd_arr := string_to_array(subdomains, '.');
RETURN sd_arr[array_length(sd_arr , 1)] || tld;
Somewhat better w/o variable assignment:
RETURN (select arr[array_length(arr,1)] from (select string_to_array(subdomains, '.') as arr) t) || tld;
Not sure if this is more efficient, but you can compare it to your implementation:
create or replace function get_domain(p_input text, p_tld text)
returns text
as
$$
declare
l_tld text[];
l_items text[];
begin
l_tld := string_to_array(trim('.' from p_tld), '.');
l_items := string_to_array(trim('.' from p_input), '.');
return array_to_string(l_items[cardinality(l_items) - cardinality(l_tld):], '.');
end
$$
language plpgsql
immutable;
It essentially converts the input and the top level domain into arrays (stripping of any leading . to avoid empty array elements.
It then calculates the starting element to be returned by subtracting the length (=number of elements) of the tld from the length of the input. So for the input x.y.z.example.co.uk this is 6 - 2, which means it returns everything starting with the 4th element, which is then converted back to a "dotted" notation.
Online example

Getting an error of 'Token Unknown' while Execute Stored Procedures

I'm new in learning stored procedures in SQL.
I want to create a stored procedure for inserting values from automatic data by calculation.
Table Attendance:
EMPL_KODE |EMPL_NAME |DATE_IN |TIME_IN |TIME_OUT|TIME_IN |TIME_OUT
001 | Michel |25.04.2016 |06:50 |15:40 | |
002 | Clara |25.04.2016 |06:15 |15:43 | |
003 | Rafael |25.04.2016 |06:25 |15:45 | |
001 | Michel |26.04.2016 |06:23 |15:42 | |
002 | Clara |26.04.2016 |06:10 |15:41 | |
003 | Rafael |26.04.2016 |06:30 |15:42 | |
001 | Michel |27.04.2016 |06:33 |15:42 | |
002 | Clara |27.04.2016 |06:54 |15:44 | |
003 | Rafael |27.04.2016 |07:00 |15:45 | |
I want to fill TIME_IN and TIME_OUT values automatically by creating a stored procedure. Here is the code :
CREATE PROCEDURE InsertTotalEmployee
#TOTAL_MINUTES int,
#TOTAL_HOURS float
AS
BEGIN
INSERT INTO ATTENDANCE (TOTAL_MINUTES, TOTAL_HOURS)
VALUES (
SELECT
DATEDIFF(MINUTE, ATTENDANCE.TIME_IN, ATTENDANCE.TIME_OUT),
DATEDIFF(MINUTE, ATTENDANCE.TIME_IN, ATTENDANCE.TIME_OUT) / 60.0
)
END
After I write and execute my statement, a message error occurs:
Token unknown - line 2, column 5 #
I run the code using Flamerobin.
It looks like you are trying to use Microsoft SQL Server syntax in Firebird, that is not going to work.
For one, the # is not allowed like that in identifiers (unless you use double quotes around them), and the list of parameters must be enclosed in parentheses.
See the syntax of CREATE PROCEDURE. You need to change it to:
CREATE PROCEDURE InsertTotalEmployee(TOTAL_MINUTES int, TOTAL_HOURS float)
You also might want to change the datatype float to double precision, and the body of your stored procedure seems to be incomplete because you are selecting from nothing (a select requires a table to select from), and are missing a semicolon at the end of the statement.
All in all I suggest you study the Firebird language reference, then try to create a functioning insert and only then create a stored procedure around it.
Also note that when creating a stored procedure in Flamerobin, that you must switch statement terminators using set term otherwise Flamerobin can't send the stored procedure correctly, see also the first section in Procedural SQL (PSQL) Statements.

Error highlights in every line when grammar breaks somewhere in Xtext editor

public
class FirstExample { // Here in this example we define some properties in our class
WRITE // This WRITE command follows a rule type defined in my grammar
Hello World
private
String firstTitle ;
private
String secondTitle ;
private
Integer firstAmount ;
private
Integer secondAmount ;
SET
firstTitle = "Ramesh"
SET
secondTitle = "Suresh"
SET
firstAmount = 100
SET
secondAmount = 200
When I change the command 'WRITE' to 'WRITEE' then error highlights in every line.
In eclipse java editor when we write a wrong syntax then error shows on that line only.
I am searching solution for this issue for past 2 days but not able to find the solution.
Kindly help me.I am not able to upload the picture due to some 10 reputation message comes while posting the question..
I am sending the grammar.Kindly check it once.
grammar org.xtext.example.mydsl.MyPoc with org.eclipse.xtext.common.Terminals hidden (GUESS_COMMENT,WS)
generate myPoc "http://www.xtext.org/example/mydsl/MyPoc"
Domainmodel:
(elements+=Type)*;
Type:Class;
// This is the starting point of the grammar
Class:
(packageList+=packageList)*
(packageList+=Import)*
(directives+=Directives) 'class' name=ID ('extends' superType=[Class])? '{'
(greetings+=Greeting)*
(features+=Feature)*
setValues+=SetValues*
operations+=Operation*
functionCall+=ArithmeticOperation*
directives+=Directive*
allMethod+=AllMethod*
allMethodInClass+=AllMethodInClass*
samples+=Samples*
'}';
//Package list consist of all the packages we want to include in our class
packageList:
('package' name=ID) ('.'?) (classes+=[Class])?;
//Import list consist of all the packages we want to include in our class
Import:
'import' importedNamespace=QualifiedNameWithWildcard;
QualifiedNameWithWildcard:
'QualifiedName' '.*'?;
Greeting:
(directives+=Directives)'Hello World'';'
(directives+=Directives)?'bye'?';'
(directives+=Directives)?';'
;
// Features defines the properties of that class
// In our grammar by default we make the property private
// our grammar takes two types of DataType String or Integer
// Name of the property given as per rule ID defined in Terminal file
Feature:
(directives+=Directives)* type=DATA_TYPE name=ID ';';
// In SetValues we initialize the properties by using SET directive
SetValues:
(directives+=Directives) name=ID ('='?) (INT | STRING)';';
//In Operation we define the function,arguments with there Data Type
Operation:
'Do' name=ID ('(' types+=DATA_TYPE args+=ID (',' types+=DATA_TYPE
args+=ID)* ')')?';';
//In Arithmetic Operation we call the function using $ passing the parameters
ArithmeticOperation:
(directives+=Directives)? name=ID '=' (directives+=Directives)? ('$')? types+=[Operation] ('(' (args+=ID) ','
(args+=ID)* ')')?';';
//In Samples we use Do While loop and inside that loop we use if else block
Samples:
name=ID (directives+=Directives) ('!,' STRING)? loopConstruct+=LoopConstruct* operations+=Operation*;
// In AllMethod we can call any method by using class name with '.' operator
AllMethod:
(classes+=[Class]) "." types+=[Operation] ('(' (args+=INT | ID) ',' (args+=INT | ID)* ');')?;
// In AllMethodInClass we can see all methods present in that class
AllMethodInClass:
types+=[Operation] ('(' (args+=INT | ID) ',' (args+=INT | ID)* ');')?;
//In LoopConstruct we define how we can use Do while loop with directive types given below
LoopConstruct:
(directives+=Directives) ("{"
(directives+=Directives ('!,' STRING))*
(directives+=Directive)*
"}")
(directives+=Directives) ("{"
(directives+=Directives ('!,' STRING))*
(directives+=Directive)*
"}")?
(directives+=Directives)?;
Directive:
(directives+=Directives) ('(' name=ID LOOP_CODITION_CHECK name=ID ')')?
'{' (directives+=Directives ('!,' STRING)?)* '}';
Directives:
name=DirectiveType;
DirectiveType:
{DirectiveType} value=DIRECTIVES_TYPE;
// From here all are rules which shows that what we can use in our grammar
terminal DATA_TYPE:
('Integer' | 'String');
// These are some directives
// WRITE is following this rule
terminal DIRECTIVES_TYPE:
('SET' | 'WRITE' | 'READ' | 'QUIT' | 'If' | 'ElseIf' | 'Else' | 'EndIf' | 'Do' | 'while' | 'private' | 'public' | 'do');
terminal LOOP_CODITION_CHECK:
('=' | '>' | '<' | '!')+;
terminal GUESS_COMMENT : '//' !('\n'|'\r')* ('\r'? '\n');
I want to make it clear that WRITE is not only the issue.The line from where grammar breaks it shows error on the remaining lines comes after that line.
After changing my class rule with your code it gives lots of warning.I am sending the warning messages.
warning(200): ../org.xtext.example.myPoc/src-gen/org/xtext/example/mydsl/parser/antlr/internal/InternalMyPoc.g:416:2: Decision can match input such as "RULE_ID {RULE_ID..RULE_DATA_TYPE, RULE_DIRECTIVES_TYPE, 'Do'}" using multiple alternatives: 8, 9
As a result, alternative(s) 9 were disabled for that input
error(201): ../org.xtext.example.myPoc/src-gen/org/xtext/example/mydsl/parser/antlr/internal/InternalMyPoc.g:416:2: The following alternatives can never be matched: 9
warning(200): ../org.xtext.example.myPoc/src-gen/org/xtext/example/mydsl/parser/antlr/internal/InternalMyPoc.g:1166:1: Decision can match input such as "RULE_DIRECTIVES_TYPE '{' RULE_DIRECTIVES_TYPE '!,' RULE_STRING '}' RULE_DIRECTIVES_TYPE" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
warning(200): ../org.xtext.example.myPoc/src-gen/org/xtext/example/mydsl/parser/antlr/internal/InternalMyPoc.g:1184:1: Decision can match input such as "'Do' RULE_ID '(' RULE_DATA_TYPE RULE_ID ',' RULE_DATA_TYPE RULE_ID ')' ';'" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
warning(200): ../org.xtext.example.myPoc/src-gen/org/xtext/example/mydsl/parser/antlr/internal/InternalMyPoc.g:1546:3: Decision can match input such as "RULE_DIRECTIVES_TYPE" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
warning(200): ../org.xtext.example.myPoc.ui/src-gen/org/xtext/example/mydsl/ui/contentassist/antlr/internal/InternalMyPoc.g:568:1: Decision can match input such as "RULE_ID {RULE_ID, RULE_DATA_TYPE..RULE_DIRECTIVES_TYPE, '}', 'Do'..'('}" using multiple alternatives: 8, 9
As a result, alternative(s) 9 were disabled for that input
error(201): ../org.xtext.example.myPoc.ui/src-gen/org/xtext/example/mydsl/ui/contentassist/antlr/internal/InternalMyPoc.g:568:1: The following alternatives can never be matched: 9
warning(200): ../org.xtext.example.myPoc.ui/src-gen/org/xtext/example/mydsl/ui/contentassist/antlr/internal/InternalMyPoc.g:2810:42: Decision can match input such as "RULE_DIRECTIVES_TYPE '{' RULE_DIRECTIVES_TYPE '!,' RULE_STRING '}' RULE_DIRECTIVES_TYPE" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
warning(200): ../org.xtext.example.myPoc.ui/src-gen/org/xtext/example/mydsl/ui/contentassist/antlr/internal/InternalMyPoc.g:2838:39: Decision can match input such as "'Do' RULE_ID '(' RULE_DATA_TYPE RULE_ID ',' RULE_DATA_TYPE RULE_ID ')' ';'" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
warning(200): ../org.xtext.example.myPoc.ui/src-gen/org/xtext/example/mydsl/ui/contentassist/antlr/internal/InternalMyPoc.g:3561:1: Decision can match input such as "RULE_DIRECTIVES_TYPE" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
After the warning code gives an exception
GeneratorException: (Element: -UNKNOWN-; Reported by: Generator)
org.eclipse.emf.common.util.WrappedException: java.io.FileNotFoundException: ..\org.xtext.example.myPoc\src-gen\org\xtext\example\mydsl\parser\antlr\internal\InternalMyPocLexer.java (The system cannot find the file specified)
I have reduced my grammar and remove all the error and warnings from my code but the error highlighting in every line is still there.
Now my grammar is
grammar org.xtext.example.mydsl.MyPoc with org.eclipse.xtext.common.Terminals hidden (GUESS_COMMENT,WS)
generate myPoc "http://www.xtext.org/example/mydsl/MyPoc"
Domainmodel:
(elements+=Type)*;
Type:Class;
// This is the starting point of the grammar
Class:
(packageList+=packageList)*
(packageList+=Import)*
(directives+=Directives) 'class' name=ID ('extends' superType=[Class])? '{'
(greetings+=Greeting
|features+=Feature
|setValues+=SetValues)*
'}';
//Package list consist of all the packages we want to include in our class
packageList:
('package' name=ID) ('.'?) (classes+=[Class])?;
//Import list consist of all the packages we want to include in our class
Import:
'import' importedNamespace=QualifiedNameWithWildcard;
QualifiedNameWithWildcard:
'QualifiedName' '.*'?;
Greeting:
(directives+=Directives)'Hello World'';'
(directives+=Directives)?'bye'?';'
(directives+=Directives)?';'
;
// Features defines the properties of that class
// In our grammar by default we make the property private
// our grammar takes two types of DataType String or Integer
// Name of the property given as per rule ID defined in Terminal file
Feature:
(directives+=Directives)* type=DATA_TYPE name=ID ';';
// In SetValues we initialize the properties by using SET directive
SetValues:
(directives+=Directives) name=ID ('='?) (INT | STRING)';';
Directives:
name=DirectiveType;
DirectiveType:
{DirectiveType} value=DIRECTIVES_TYPE;
// From here all are rules which shows that what we can use in our grammar
terminal DATA_TYPE:
('Integer' | 'String');
// These are some directives used by us taken from there website
terminal DIRECTIVES_TYPE:
('SET' | 'WRITE' | 'READ' | 'QUIT' | 'If' | 'ElseIf' | 'Else' | 'EndIf' | 'Do' | 'while' | 'private' | 'public' | 'do');
terminal LOOP_CODITION_CHECK:
('=' | '>' | '<' | '!')+;
terminal GUESS_COMMENT : '//' !('\n'|'\r')* ('\r'? '\n');
And in eclipse xtext editor when I write private as privatee it shows error in every line.
My code is
public
class MyPoc {
private
String firstTitle ;
**privatee**
String title ;
private
String secondTitle ;
private
Integer firstAmount ;
private
Integer secondAmount ;
SET
firstTitle = "Ramesh";
SET
secondTitle = "Suresh";
SET
firstAmount = 100;
SET
secondAmount = 200;
}
Now I add some more grammar in my class attribute.The console shows no warning and no exception.But in eclipse editor again when we break grammar syntax it shows error in every line.
Class:
(packageList+=packageList)*
(packageList+=Import)*
(directives+=Directives) 'class' name=ID ('extends' superType=[Class])? LBRACKET
(directives+=Directives* (features+=Feature | setValues+=SetValues | operations+=Operation | functionCall+=ArithmeticOperation | allMethod+=AllMethod
| allMethodInClass+=AllMethodInClass | samples+=Samples) SEMICOLON)*
RBRACKET;
Kindly check it once and correct me if I am doing something wrong.
I have found that problem is not in the class part.The problem is in the parts where we define the rule in the grammar.
Like for ArithmeticOperation
ArithmeticOperation:
(directives+=Directives)? name=ID '=' (directives+=Directives)? ('$')? types+=[Operation] ('(' (args+=ID) ','
(args+=ID)* ')')?';';
Actually I have to write this code in eclipse.Here Concat is the opeartion name means method name.
SET Result = WRITE $ Concat (firstTitle , secondTitle)
Kindly correct me if I ma doing something wrong to achieve my target code.
Regards
Xtext error recovery system is not the same than Java Editor (JDT). Its behavior depends on how you write your rules.
Maybe your grammar is too much ambiguous and you should add a line termination symbol.
Also, you should read this article about error recovery with Xtext: Parser error recovery
Edit
Currently the class rule is very ambiguous with chaining rules with the same first token. The first thing you should do is to rewrite your class rule and remove all ambiguities.(warnings & erros when compiling grammar).
See the following example of class rule:
Class:
(packageList+=packageList)*
(packageList+=Import)*
(directives+=Directives) 'class' name=ID ('extends' superType=[Class])? '{'
(greetings+=Greeting | features+=Feature | setValues+=SetValues
| operations+=Operation | functionCall+=ArithmeticOperation
| directives+=Directive | allMethod+=AllMethod
| allMethodInClass+=AllMethodInClass | samples+=Samples)*
'}';
Edit 2
I made some refactorings to your grammar. And now it has the behavior you asked for.
Domainmodel:
(elements+=Type)*;
Type:Class;
// This is the starting point of the grammar
Class:
(packageList+=packageList)*
(packageList+=Import)*
(directives+=Directives) 'class' name=ID ('extends' superType=[Class])? LBRACKET
(directives+=Directives* (features+=Feature | setValues+=SetValues) SEMICOLON)*
RBRACKET;
terminal SEMICOLON:
';'
;
terminal LBRACKET:
'{'
;
terminal RBRACKET:
'}'
;
//Package list consist of all the packages we want to include in our class
packageList:
('package' name=ID) ('.'?) (classes+=[Class])?;
//Import list consist of all the packages we want to include in our class
Import:
'import' importedNamespace=QualifiedNameWithWildcard;
QualifiedNameWithWildcard:
'QualifiedName' '.*'?;
// Features defines the properties of that class
// In our grammar by default we make the property private
// our grammar takes two types of DataType String or Integer
// Name of the property given as per rule ID defined in Terminal file
Feature:
type=DATA_TYPE name=ID ;
// In SetValues we initialize the properties by using SET directive
SetValues:
name=ID ('='?) (INT | STRING);
Directives:
name=DirectiveType;
DirectiveType:
{DirectiveType} value=DIRECTIVES_TYPE;
// From here all are rules which shows that what we can use in our grammar
terminal DATA_TYPE:
('Integer' | 'String');
// These are some directives used by us taken from there website
terminal DIRECTIVES_TYPE:
('SET' | 'WRITE' | 'READ' | 'QUIT' | 'If' | 'ElseIf' | 'Else' | 'EndIf' | 'Do' | 'while' | 'private' | 'public' | 'do');
terminal LOOP_CODITION_CHECK:
('=' | '>' | '<' | '!')+;
terminal GUESS_COMMENT : '//' !('\n'|'\r')* ('\r'? '\n');
Details of what i changed:
- Removed (useless?) greeting rule.
- Made line/block separators terminals.
- Moved directive to parent rule.
Sample code for editor:
public
class MyPoc {
private
String firstTitle ;
privatee
String title ;
private
String secondTitle ;
private
Integer firstAmount ;
private
Integer secondAmount ;
SET
firstTitle = "Ramesh";
SET
secondTitle = "Suresh";
SET
firstAmount = 100;
SET
secondAmount = 200;
}

Inno Setup Microsoft Word Templates Subdirectory Install

I am a programmer for the Small Business Administration. SBA creates and provides free some programs to borrowers and lenders. The install code below is for one off our free programs for the end-user machine.
I searched for a week for a solution to my problem and then decided I needed to learn how to program what I needed. I am a complete novice at Pascal and Inno Setup. I stole code and tested all of the examples in this site and others. Thanks for all the help. I modified and tested over and over. Below is my code.
Note: I am providing this in the hope that it will be useful to others, but it doesn't quite work as planned. Hopefully there will be a working version posted with help from others. I have provided the entire code to help others understand what I struggled to learn. ;-)
Question: The install works perfectly EXCEPT the the [Code] section repeats. It runs the GetInstallDirectory at least once before reaching the {code:GetInstallDirectory} call in the [Files] section.
It runs through the If statement for Word 14 once, and then runs through the [Files] section once. My message "MsgBox ('1. Setup..." appears twice. Previously it was appearing 3 or 4 times, but I made a change which I can't remember and now it runs only twice.
Can anyone help me find the problem? Perhaps it is with the If,Then,Else statements. Then again it could be the order of the statements. Thanks in advance.
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
; Script written by Daniel H. Smith, Lead Programmer/Analyst, Base Technologies
; Washington, DC
#define MyAppName "IDAP mini-Wizard"
#define MyAppVersion "v 2013"
#define MyAppPublisher "Small Business Administration"
#define MyAppURL "http://www.sba.gov/"
#define MyAppExeName "IDAPv2013.exe"
#define MyAppCopyright "(c) 2013-2014"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{459E8784-C110-4348-A294-229C58CB00D2}
AppName={#MyAppPublisher}'s {#MyAppName} {#MyAppVersion}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
AppCopyright={#MyAppCopyright}
DefaultDirName={app}
CreateAppDir=false
OutputDir=C:\Users\Dan\Documents\IDAP Development\Setup
OutputBaseFilename=IDAPv2013Setup
SetupIconFile=C:\Users\Dan\Documents\My Shapes\_private\folder.ico
Compression=lzma
SolidCompression=true
UsePreviousGroup=false
UsePreviousAppDir=false
InfoBeforeFile=C:\Users\Dan\Documents\IDAP Development\Setup\IDAPInfo.txt
WizardImageFile=compiler:WizModernImage-IS.bmp
WizardSmallImageFile=compiler:WizModernSmallImage-IS.bmp
InfoAfterFile=C:\Users\Dan\Documents\IDAP Development\Setup\IDAPWord.txt
AppContact=Auth-IDAP#sba.gov
PrivilegesRequired=none
RestartIfNeededByRun=False
VersionInfoVersion=1.0
VersionInfoCompany={#MyAppPublisher}
VersionInfoCopyright={#MyAppCopyright}
[Languages]
Name: english; MessagesFile: compiler:Default.isl
[Files]
Source: "C:\Users\Dan\Documents\IDAP Development\IDAP 2013 Boilerplate_V10.Clean.pdf"; DestDir: "{userdocs}\IDAP Documents"; Flags: ignoreversion
Source: "C:\Users\Dan\Documents\IDAP Development\IDAP Borrower Certification (9-25-12)(initials) final.docx"; DestDir: "{userdocs}\IDAP Documents"; Flags: ignoreversion
Source: "C:\Users\Dan\Documents\IDAP Development\IDAP Loan Note (Draft July 25).docx"; DestDir: "{userdocs}\IDAP Documents"; Flags: ignoreversion
Source: "C:\Users\Dan\AppData\Roaming\Microsoft\Templates\IDAPLoan2013.dotm"; DestDir: "{code:GetInstallDirectory}"; DestName: "IDAPWizard2013v111.dotm"; Flags: confirmoverwrite
[Messages]
// define wizard title and tray status msg
// both are normally defined in innosetup's default.isl (install folder)
SetupAppTitle = Install {#MyAppName} {#MyAppVersion}
SetupWindowTitle = Install {#MyAppName} {#MyAppVersion} -- {#MyAppPublisher}
[Run]
//Opens Word if User leaves checkbox checked on Finish
Filename: "{code:GetWordDirectory}"; Flags: postinstall; Description: "Open Word after Finish"; StatusMsg: "Opening Word ..."
[Code]
function GetWordDirectory(S: string) : String;
Begin
IF RegKeyExists(HKCU, 'Software\Microsoft\Office\14.0\Common\General') THEN BEGIN
Result := ExpandConstant('{pf32}\Microsoft Office\Office14\winword.exe');
end Else begin
Result := ExpandConstant('{pf32}\Microsoft Office\Office12\winword.exe');
end;
End;
//--------
function GetInstallDirectory(S: String) : String;
var installDirectory : String;
Version: TWindowsVersion;
Begin
//Word 14 Start
IF RegKeyExists(HKCU, 'Software\Microsoft\Office\14.0\Common\General') THEN BEGIN
GetWindowsVersionEx(Version);
if RegQueryStringValue(HKCU, 'Software\Microsoft\Office\14.0\Common\General', 'UserTemplates', installDirectory) then begin
//Successfully read the registry value
If (Version.Major = 6) and (Version.Minor = 1) and (Version.ProductType = VER_NT_WORKSTATION) //Windows 7
then begin
MsgBox ('1. Setup is installing to Word User Templates directory for: '+#10+#13+#10+#13+'---> Word 2010 and Windows 7 <---', mbInformation, MB_OK);
Result := installDirectory
end else begin //begin c2
//FAILED READING USERTEMPLATES DIRECTORY
//Windows <= 6.0 Not Windows 7 and above
If (Version.Major = 6) and (Version.Minor = 0) and (Version.ProductType = VER_NT_WORKSTATION) //Windows Vista or XP
then begin
MsgBox ('2. Setup is installing to Word default template directory for: '+#10+#13+#10+#13+'---> Word 2010 and Windows 7 <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Users\{username}\AppData\Roaming\Microsoft\Templates');
end else
MsgBox ('3. Setup is installing to Word default template directory for: '+#10+#13+#10+#13+'---> Word 2010 and Windows Vista/XP <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Documents and Settings\{username}\Application Data\Microsoft\Templates');
end;
end;
//End Word 14
//Start Word 12
END ELSE IF RegKeyExists(HKCU, 'Software\Microsoft\Office\12.0\Common\General') THEN BEGIN
GetWindowsVersionEx(Version);
if RegQueryStringValue(HKCU, 'Software\Microsoft\Office\12.0\Common\General', 'UserTemplates', installDirectory) then begin
// Successfully read the value
If (Version.Major = 6) and (Version.Minor = 0) and (Version.ProductType = VER_NT_WORKSTATION) //Windows 7
then begin //c3
MsgBox ('4. Setup is installing to Word User Templates directory for: '+#10+#13+#10+#13+'---> Word 2007 and Windows 7 <---', mbInformation, MB_OK);
Result := installDirectory
end else begin //begin c4
//FAILED READING VALUE
If (Version.Major = 6) and (Version.Minor = 0) and (Version.ProductType = VER_NT_WORKSTATION) //Windows Vista/XP
then begin
MsgBox ('5. Setup is installing to Word User Templates directory for: '+#10+#13+#10+#13+'---> Word 2007 and Windows 7 <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Users\{username}\AppData\Roaming\Microsoft\Templates');
end else
MsgBox ('6. Setup is installing to Word default templates directory for: '+#10+#13+#10+#13+'---> Word 2007 and Windows XP or Vista <---', mbInformation, MB_OK);
Result := ExpandConstant('C:\Documents and Settings\{username}\Application Data\Microsoft\Templates');
end;
end;
END;
//End Word 12
end; //Function GetInstallDirectory end
//End All
//------------------------------------------------------------------------------
//Following are reference notes
//How to code a if-elseif-else
// if condition then begin
// ...
// end else if condition then begin
// ...
// end else begin
// ...
// end;
//+------------+------------+-------+-------+---------+
//| Version | PlatformId | Major | Minor | Release |
//+------------+------------+-------+-------+---------+
//| Win32s | 0 | ? | ? | |
//| Win95 | 1 | 4 | 0 | 1995 08 |
//| Win98 | 1 | 4 | 10 | 1998 06 |
//| WinME | 1 | 4 | 90 | 2000 09 |
//| WinNT351 | 2 | 3 | 51 | 1995 04 |
//| WinNT4 | 2 | 4 | 0 | 1996 07 |
//| Win2000 | 2 | 5 | 0 | 2000 02 |
//| WinXP | 2 | 5 | 1 | 2001 10 |
//| Win2003 | 2 | 5 | 2 | 2003 04 |
//| WinXPx64 | 2 | 5 | 2 | 2003 03 |
//| WinCE | 3 | ? | ? | |
//| Vista | 2 | 6 | 0 | 2007 01 |
//| Win2008 | 2 | 6 | 0 | 2008 02 |
//| Win2008R2 | 2 | 6 | 1 | 2009 10 |
//| Win7 | 2 | 6 | 1 | 2009 10 |
//+------------+------------+-------+-------+---------+
//Word Templates default subdirectory before Windows 7
//C:\Documents and Settings\"user name"\Application Data\Microsoft\Templates\
//Word Templates default subdirectory after Windows 7
//C:\Users\"username"\AppData\Roaming\Microsoft\Templates\
//
//For MS Word WinWord.Exe location and registry entry
//HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Word\Options
//C:\Program Files (x86)\Microsoft Office\Office14\ -- For Word 14
//MS Word 14 UserTemplates registry
//HKCU, 'Software\Microsoft\Office\14.0\Common\General', 'UserTemplates'
//MS Word 12 UserTemplates registry
//HKCU, 'Software\Microsoft\Office\12.0\Common\General', 'UserTemplates'
//------------------------------------------------------------------------------
Update:
You're right, code function called from the DestDir parameter is being called twice. First time when the installation is being prepared and second time when the files are processed. I don't know the reason (it's quite deep in the source code), but you don't actually need to care about that. That parameter function's used to be just for getting destination directory, not for any kind of user interaction, so you don't need to worry that it's called twice.
Although I've made a review to your code. Note that it's untested written from resources listed below. Something to point out:
use constants, it's easier to maintain one constant instead of many places in the script
don't rely on that all users will have Office installed in the default Program Files directory
the default template directory is in user's application data, which in InnoSetup you can substitute by the {userappdata} constant, that is in your case safe to use, since you're running your setup with PrivilegesRequired=none, so there's no risk with the forced administrator user difference
if you your users must have at least one of the Word versions installed check that before you show the wizard form, see the InitializeSetup event function below
...
[Run]
; the Check parameter here use only when you remove the InitializeSetup event
; function (see below), otherwise it's useless because the setup won't even start
; when there's not at least Word 2007 or 2010 version installed
Filename: "{code:GetLatestWordAppPath}"; Flags: postinstall; Description: "Open Word after Finish"; StatusMsg: "Opening Word ..."; Check: IsWordInstalled('12.0') or IsWordInstalled('14.0');
[Code]
// better for maintenance is to use constants
const
PathDefaultUserTemplates = '{userappdata}\Microsoft\Templates';
RegKeyOfficeTemplatesPath = 'SOFTWARE\Microsoft\Office\%s\Common\General';
RegKeyWordInstallRootPath = 'SOFTWARE\Microsoft\Office\%s\Word\InstallRoot';
// helper function for checking if the Word version specified by the input parameter
// is installed or not; inspired by https://stackoverflow.com/a/3267832/960757
function IsWordInstalled(const Version: string): Boolean;
begin
Result := RegValueExists(HKEY_LOCAL_MACHINE,
Format(RegKeyWordInstallRootPath, [Version]), 'Path');
end;
// this is used for the application path for the check box from the [Run] section
// you shouldn't rely on that everyone installs Office to Program Files directory
function GetLatestWordAppPath(S: string): string;
begin
Result := '';
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
Format(RegKeyWordInstallRootPath, ['14.0']), 'Path', Result)
then
RegQueryStringValue(HKEY_LOCAL_MACHINE,
Format(RegKeyWordInstallRootPath, ['12.0']), 'Path', Result);
end;
// helper function for getting the Office user templates path (if available)
function GetUserTemplatesPath(const Version: string; var Path: string): Boolean;
begin
Result := RegQueryStringValue(HKEY_CURRENT_USER,
Format(RegKeyOfficeTemplatesPath, [Version]), 'UserTemplates', Path);
end;
// function returning target installation directory
function GetInstallDirectory(S: string): string;
var
InstallDirectory: string;
begin
// initialize the Result first
Result := '';
// check if Word 2010 application is installed and if so, then...
if IsWordInstalled('14.0') then
begin
// try to get the Office 2010 user templates location from registry, if succeed then...
if GetUserTemplatesPath('14.0', InstallDirectory) then
begin
Result := InstallDirectory;
MsgBox('Office 2010 user templates directory was found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end
else
// the Office 2010 user templates location was not found in registry, so...
begin
// you're running your setup with PrivilegesRequired=none, so you can safely use {userappdata}
// constant with no risk of getting different user path (forced administrator)
Result := ExpandConstant(PathDefaultUserTemplates);
MsgBox('Office 2010 user templates directory was not found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end;
end
else
// Word 2010 was not found, check for Word 2007 and if it's found, then...
if IsWordInstalled('12.0') then
begin
// try to get the Office 2007 user templates location from registry, if succeed then...
if GetUserTemplatesPath('12.0', InstallDirectory) then
begin
Result := InstallDirectory;
MsgBox('Office 2007 user templates directory was found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end
else
// the Office 2007 user templates location was not found in registry, so...
begin
// you're running your setup with PrivilegesRequired=none, so you can safely use {userappdata}
// constant with no risk of getting different user path (forced administrator)
Result := ExpandConstant(PathDefaultUserTemplates);
MsgBox('Office 2007 user templates directory was not found in registry!' + #10#13 + #10#13 +
Result, mbInformation, MB_OK);
end;
end;
end;
// this event function is for the first check before the wizard form is shown if you
// return False to this event function, the setup will exit, what is here conditioned
// by presence of Word 2007 or Word 2010
function InitializeSetup: Boolean;
begin
Result := IsWordInstalled('12.0') or IsWordInstalled('14.0');
if not Result then
MsgBox('You don''t have installed neither Word 2007 nor Word 2010.' + #10#13 +
'Setup will now exit.', mbCriticalError, MB_OK);
end;
How to get path to the MS Office executables ?
How to manage templates in 2007 Office programs and in 2010 Office programs