cannot find document just created in a trigger - triggers

I have the following issue.
We use the OBI framework of MarkLogic
I use a trigger in the database to monitor incomming sensor data.
As soon as a new OBI source is loaded I check some stuff.
If I find something I create an "Alert" object.
Then I would like to use that object the generate a"payload" message to send out to mobile devices...
Now trigger works.
In the trigger I use a xdmp:eval with a different transcaction to make sure I can use the document created in the same trigger code...
BUT if I search for the newly created object it cannot be found...
Can I create a document and use it in the same trigger code?
I am afraid it is hard to create a minimal working example here but this is a try:
Relevant trigger code:
(: fire trigger ALWAYS :)
let $_ := xdmp:log(fn:concat('TP-SENSORTRIGGER-ACTION : Source ', $trgr:uri, ' triggers base rule...'))
(: create alert object plus link to source :)
let $object-id := xdmp:eval(concat('
xquery version "1.0-ml";
import module namespace scl = "http://example.com/sccs/lib" at "/lib/sccss-lib.xqy";
declare variable $source-id external;
let $object-id := scl:create-alert-object($source-id)
let $_ := xdmp:log("***** test *****")
return $object-id
'), (xs:QName('source-id'), $source-id),
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>)
let $_ := xdmp:log($object-id)
(: create payload from alert object :)
(:let $payload := scl:create-payload-from-alert-object($object-id)
:)
let $object := obj:find-object($object-id)
let $_ := xdmp:log($object)
The function to create the OBI object is in a lib. I can share if needed.
I have added aa log line "test" and "test B" to make sure I use the proper triggers. And to make sure they are rebuild on deploy in app-specific.rb.
Then from the logs it is clear the object cannot be found:
2015-10-20 15:33:02.860 Info: example-app: ******** Ingest transform Started ***************
2015-10-20 15:33:04.196 Info: TaskServer: TP-SENSORTRIGGER /marklogic.solutions.obi/source/81a3591a-a885-4f85-a781-b066e706ff41.xml was created, start trigger action...
2015-10-20 15:33:04.291 Info: TaskServer: TP-SENSORTRIGGER-ACTION : Source /marklogic.solutions.obi/source/81a3591a-a885-4f85-a781-b066e706ff41.xml triggers base rule...
2015-10-20 15:33:07.267 Info: TaskServer: ***** test B *****
2015-10-20 15:33:07.268 Info: TaskServer: ***** test *****
2015-10-20 15:33:07.273 Info: TaskServer: 4d1fd4e4-2911-40b0-848c-ccf8eaa39229
2015-10-20 15:33:07.277 Info: TaskServer:
After the trigger has run I can of course find the object with id 4d1fd4e4-2911-40b0-848c-ccf8eaa39229 from the QC.
So this has to do with the transaction model of MarkLogic I guess.
So again the question: Why cant I find the object I just created in a xdmp:eval ?

SOLUTION
It turned out you have to wrap ALL functions downstream in a xdmp:eval() for this to work.
(: fire trigger ALWAYS :)
let $_ := xdmp:log(fn:concat('TP-SENSORTRIGGER-ACTION : Source ', $trgr:uri, ' triggers base rule...'))
(: create alert object plus link to source :)
let $object-id := xdmp:eval(concat('
xquery version "1.0-ml";
import module namespace scl = "http://example.com/sccs/lib" at "/lib/example-lib.xqy";
declare variable $source-id external;
declare variable $actions external;
declare variable $lastseen external;
let $object-id := scl:create-alert-object($source-id,$actions,$lastseen)
return $object-id
'), (xs:QName('source-id'), $source-id,xs:QName('actions'), $actions,xs:QName('lastseen'), $lastseen),
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>)
let $_ := xdmp:log($object-id)
(: create payload from alert object :)
(:let $payload := scl:create-payload-from-alert-object($object-id)
:)
let $payload := xdmp:eval(concat('
xquery version "1.0-ml";
import module namespace scl = "http://example.com/sccs/lib" at "/lib/sccss-lib.xqy";
declare variable $object-id external;
let $payload := scl:create-payload-from-alert-object($object-id)
return $payload
'), (xs:QName('object-id'), $object-id),
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>)
let $_ := xdmp:log("************* PAYLOAD ****************")
let $_ := xdmp:log($payload)
(: send alert to middletier :)
let $result := xdmp:eval(concat('
xquery version "1.0-ml";
import module namespace scl = "http://example.com/sccs/lib" at "/lib/sccss-lib.xqy";
declare variable $payload external;
let $res := scl:send-alert-notification($payload)
return $res
'), (xs:QName('payload'), $payload),
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>)
let $_ := xdmp:log("************* RESULT ****************")
let $_ := xdmp:log($result)
This is not clear from the documentation of the xdmp:eval() function :
" When set to different-transaction, the statement is evaluated in a
separate transaction from the one in which it is called, making those
updates available to subsequent expressions in the calling statement
(assuming the calling statement is an update statement; if the calling
statement is not an update, then subsequent expressions will see the
version of the database at the system timestamp when the calling
statement begins its evaluation)."

Related

Unable to create MarkLogic scheduled tasks from within CPF action module

I have a MarkLogic database with Content Processing Framework (CPF) installed and the CPF pipeline is such that:
Whenever a document is inserted then it grabs the value of execution-date from the document and schedule a task for that time.
Example:
Sample document:
<sample>
<execution-date>2014-10-20T12:29:10</execution-date>
</sample>
when inserted triggers the CPF action module which reads the value of execution-date field and creates a scheduled task to be executed on the time read from execution-date field.
Following is the XQuery code snippet from the CPF action module that creates the scheduled task:
let $doc := fn:doc( $cpf:document-uri )
let $releasedon := xs:string($doc/sample/execution-date/text())
let $config := admin:get-configuration()
let $group := admin:group-get-id($config, "Default")
let $new-task :=
admin:group-one-time-scheduled-task(
"/tasks/task.xqy",
"/",
xs:dateTime($releasedon),
xdmp:database("SampleDB"),
xdmp:database("Modules"),
xdmp:user("admin"),
(),
"normal")
let $addTask := admin:group-add-scheduled-task($config,$group, $new-task)
return
admin:save-configuration($addTask),
xdmp:log(fn:concat("Task for document Uri: ", $cpf:document-uri, " created"))
Now, when I insert single document then everything works as expected, that is:
Document inserted successfully
the CPF action module is triggered successfully
Scheduled task created successfully.
But, when I try to insert multiple documents using:
xdmp:document-insert("/1.xml",
<sample>
<execution-date>2014-10-21T10:00:00</execution-date>
</sample>,
xdmp:default-permissions(),
("documents"))
,
xdmp:document-insert("/2.xml",
<sample>
<execution-date>2014-10-20T11:00:00</execution-date>
</sample>,
xdmp:default-permissions(),
("documents"))
CPF action module gets triggered successfully (log message can be seen in logs) BUT
ONLY one scheduled task gets created.
When looking in MarkLogic Admin Interface I can only find a single scheduled task which is scheduled to run at 2014-10-20T11:00:00
Please let me know what am I doing wrong or is there any configuration I am missing.
Any suggestions are welcomed.
Thanks!
The fundamental issue here is that the admin configuration manipulation APIs are not transactionally protected operations, so when you run two in parallel each one sees the initial state of the configuration files, then writes their bit to add the scheduled task, and then saves it, and only one of them wins. You can force this to behave in a transactionally protected way by forcing a lock on some URI It doesn't matter what it is. It doesn't even have to be in the database. As long as everything that is doing this is locking on the same URI you are fine. xdmp:lock-for-update("my.example.uri") will do this.
The following CPF action module is now working as expected:
xquery version "1.0-ml";
import module namespace cpf = "http://marklogic.com/cpf" at "/MarkLogic/cpf/cpf.xqy";
import module namespace admin = "http://marklogic.com/xdmp/admin" at "/MarkLogic/admin.xqy";
declare variable $cpf:document-uri as xs:string external;
declare variable $cpf:transition as node() external;
declare function local:scheduleTask()
{
xdmp:lock-for-update("/sample.xml"),
if (cpf:check-transition($cpf:document-uri,$cpf:transition)) then try
{
let $doc := fn:doc( $cpf:document-uri )
let $releasedon := xs:string($doc/sample/execution-date/text())
let $config := admin:get-configuration()
let $group := admin:group-get-id($config, "Default")
let $new-task :=
admin:group-one-time-scheduled-task(
"/tasks/task.xqy",
"/",
xs:dateTime($releasedon),
xdmp:database("SampleDB"),
xdmp:database("Modules"),
xdmp:user("admin"),
(),
"normal")
let $addTask := admin:group-add-scheduled-task($config,$group, $new-task)
return
admin:save-configuration($addTask),
xdmp:log(fn:concat("Task for document Uri: ", $cpf:document-uri, " created"))
}
catch ($e) {
cpf:failure( $cpf:document-uri, $cpf:transition, $e, () )
}
else ( )
};
local:scheduleTask()

Using Word OLE in Lazarus FreePascal

Im trying to get FreePascal to open up a word document, append some text and data to it and then close it. I've managed to get connected and can write a single line to the document but anything over that is defeating me. Currently I am attempting methods details in this Visual Basic reference, which is pretty similar to how I would expect FreePascal to handle things.
Basically I think I have misunderstood how the relationship between Lazarus and the Word OLE actually works, can anyone offer me any examples on how to construct a simple document that I can build on?
The following code, opens the document but then completely replaces its contents
program officAuto;
{$IFDEF FPC}
{$MODE Delphi}
{$ELSE}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
SysUtils, Variants, ComObj;
const
ServerName = 'Word.Application';
var
Server, Doc : Variant;
oPara : Variant;
w:widestring;
begin
if Assigned(InitProc) then
TProcedure(InitProc);
try
Server := CreateOleObject(ServerName);
except
WriteLn('Unable to start Word.');
Exit;
end;
w:= UTF8Decode('c:\mydoc.docx');
Server.Visible := True; {Make Word visible}
Doc := Server.Documents.Open(w);
Doc.Range.Text := 'This is a Heading';
Doc.Range.Font.Bold := True;
Doc.Format.SpaceAfter := 24;
end.
Whereas this, based on the code above, in attempting to print a string at a bookmark, opens the document, retains the contents, moves to the bookmark and then does nothing.
w:= UTF8Decode('c:\mydoc.docx');
Server.Visible := True;
Doc := Server.Documents.Open(w);
oPara := Doc.Content.Paragraphs.Add(Doc.Bookmarks.Item('\Bookmark1').Range);
oPara := Doc.Range.Text('Where will this appear if at all!');
Ah I worked it out. The following code works as expected:
program officAuto;
{$IFDEF FPC}
{$MODE Delphi}
{$ELSE}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
SysUtils, Variants, ComObj;
var
Server, Connect : Variant;
oWord, oPara1, oPara2 : Variant;
w:widestring;
begin
if Assigned(InitProc) then
TProcedure(InitProc);
try
Server := CreateOleObject('Word.Application');
except
WriteLn('Unable to start Word.');
Exit;
end;
// oWord := Server.Documents.Add;
w:= UTF8Decode('c:\mydoc.docx');
Server.Visible := True;
Server.Documents.Open(w);
oPara1 := Server.ActiveDocument.Content.Paragraphs.Add;
oPara1.Range.Text := 'This is a Heading';
oPara1.Range.Font.Bold := True;
oPara1.Format.SpaceAfter := 24;
oPara1.Range.InsertParagraphAfter();
oPara2 := Server.ActiveDocument.Content.Paragraphs.Add;
oPara2.Range.Text := 'Where will this appear if at all!';
oPara2.Range.Font.Bold := False;
oPara2.Format.SpaceAfter := 24;
oPara2.Range.InsertParagraphAfter();
end.

If Condition followed by a for loop not executing in Xquery

In one of the SOAP responses, I was trying to use the following Xquery code to check a condition followed by for loop. I was trying to get a count of some element and then use the if condition and based on that if condition, it should execute the for loop. However there is an exception that shows up .
Here is my Xquery bit in the SOAP UI.
declare variable $datesList := ("2013-01-01-00.30.00","2013-01-01-01.00.00","2013-01-01-01.30.00","2013-01-01-02.00.00","2013-01-01-02.30.00","2013-01-01-03.00.00","2013-01-01-03.30.00","2013-01-01-04.00.00");
<res>
{
let $mcId1 :=count(//ZZQAD2UsageTransactionSVC/usagePeriods/usagePeriodsList/SQs/SQsList[1]/mL)
let $mcId2 :=count(//ZZQAD2UsageTransactionSVC/usagePeriods/usagePeriodsList/SQs/SQsList[2]/mL)
if($mcId1=8)
{
for $mlList in //ZZQAD2UsageTransactionSVC/usagePeriods/usagePeriodsList/SQs/SQsList[1]/intervals/mL
return(if($mcId1 > $mcId2)
then <text>true</text>
else <text>false</text>)
}
}
Here is the exception that appears during run time.
RuntimeException:java.lang.reflect.InvocationTargetException
So I want to seek advice from the seniors and gurus, if the piece of Xquery code is correct?
Thanks much in advance.
There are multiple syntax errors in your query:
let clauses have to be part of a FLWOR expression, which always ends with a return clause.
if cannot be used without then and else and does not use curly braces.
The opening tag <res> needs a matching closing tag </res> at the end of the query.
The corrected query looks like this:
declare variable $datesList := (
"2013-01-01-00.30.00", "2013-01-01-01.00.00",
"2013-01-01-01.30.00", "2013-01-01-02.00.00",
"2013-01-01-02.30.00", "2013-01-01-03.00.00",
"2013-01-01-03.30.00", "2013-01-01-04.00.00"
);
<res>{
let $mcId1 := count(//ZZQAD2UsageTransactionSVC/usagePeriods/usagePeriodsList/SQs/SQsList[1]/mL)
let $mcId2 := count(//ZZQAD2UsageTransactionSVC/usagePeriods/usagePeriodsList/SQs/SQsList[2]/mL)
return if($mcId1 = 8) then (
for $mlList in //ZZQAD2UsageTransactionSVC/usagePeriods/usagePeriodsList/SQs/SQsList[1]/intervals/mL
return if($mcId1 > $mcId2)
then <text>true</text>
else <text>false</text>
) else ()
}</res>

InnoSetup Dynamic ComboBox, check which item is selected and execute program

In InnoSetup I want to disply a ComboBox on the Finished Page which shows the Components that were installed.
You can choose "None" or any of the installed Components and start the associated program when clicking on finish.
This is my code so far:
procedure CurPageChanged(CurPageID: Integer);
var
NewComboBox1: TNewComboBox;
begin
if (CurPageID = wpFinished) then begin
NewComboBox1 := TNewComboBox.Create(WizardForm);
with NewComboBox1 do begin
Parent := WizardForm.FinishedPage;
Left := ScaleX(256);
Top := ScaleY(208);
Width := ScaleX(145);
Height := ScaleY(21);
ItemIndex := 0;
Style := csDropDownList;
Items.Add('None');
if IsComponentSelected('1') then
Items.Add('Component 1');
if IsComponentSelected('2') then
Items.Add('Component 2');
if IsComponentSelected('3') then
Items.Add('Component 3');
end;
end;
end;
First I want to set "None" as automatically selected. when the page is shown. I have looked up many Pascal forums but none of the solutions worked, like NewComboBox1.ItemSelected=0 (or similar, don't remember correctly...). So how do I achieve this?
Then I don't know how to make a program start when clicking on Finish. I thought
function NextButtonClick
might help but then no Next button worked in the setup.
Maybe there is also a problem because the list is created depending on which Components were selected, so item 1 is not Component 1, if Component 1 was not selected but Component 2 for instance.
I thought one might solve this by making the items invisible instead of not creating them at all.
I looked in the Support Classes Reference in the IS help file but didn't find anything that would help me.
I am looking forward to your answers!
There's no simple way to do this due to a lack of missing access to the file name and destination directory which the component is binded to. Even TSetupComponentEntry internal record doesn't contain this information, but even if would, you won't be able to access it. So, the following script uses its own separate array which contains the component/file linkage needed for this task:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Components]
Name: "program_32"; Description: "Program 32-bit"
Name: "program_x64"; Description: "Program 64-bit"
Name: "program_ia64"; Description: "Program IA 64-bit"
[Files]
Source: "MyProg.exe"; DestDir: "{app}"; Components: program_32
Source: "MyProg-x64.exe"; DestDir: "{app}"; Components: program_x64
Source: "MyProg-IA64.exe"; DestDir: "{app}"; Components: program_ia64
[Code]
type
TFileData = record
Component: string;
Description: string;
FileName: string;
Parameters: string;
end;
var
ComponentCombo: TNewComboBox;
ComponentArray: array of TFileData;
SelectionArray: array of TFileData;
procedure InitializeWizard;
begin
// this is a weakness of this solution - you need to fill the array
// of components that can be added to the final combo box when they
// are selected on component selection page. This is needed because
// you can't get neither file name nor destination directory of the
// file for the component from script. As first, set how many items
// you want to add to your component array storage
SetArrayLength(ComponentArray, 2);
// the Component member must match to the "Name" parameter from the
// [Components] section item since it's used in IsComponentSelected
// function call
ComponentArray[0].Component := 'program_32';
// the Description member is the text displayed in the combo item
ComponentArray[0].Description := 'Program 32-bit';
// the FileName member is the name of the file including path. This
// member may contain InnoSetup constants
ComponentArray[0].FileName := '{app}/MyProg.exe';
// the Parameters member contains execution parameters
ComponentArray[0].Parameters := '-a';
// this is the second item that can be added to the combo box, note
// that the program_ia64 component is not added to this array, what
// means, that it cannot be added to the "run" combo box. It's such
// kind of a filter for components like help files etc.
ComponentArray[1].Component := 'program_x64';
ComponentArray[1].Description := 'Program 64-bit';
ComponentArray[1].FileName := '{app}/MyProg-x64.exe';
ComponentArray[1].Parameters := '-b';
end;
procedure CurPageChanged(CurPageID: Integer);
var
I: Integer;
begin
if (CurPageID = wpFinished) then
begin
ComponentCombo := TNewComboBox.Create(WizardForm);
ComponentCombo.Parent := WizardForm.FinishedPage;
ComponentCombo.Left := ScaleX(256);
ComponentCombo.Top := ScaleY(208);
ComponentCombo.Width := ScaleX(145);
ComponentCombo.Height := ScaleY(21);
ComponentCombo.Style := csDropDownList;
ComponentCombo.Items.Add('None');
for I := 0 to GetArrayLength(ComponentArray) - 1 do
if IsComponentSelected(ComponentArray[I].Component) then
begin
ComponentCombo.Items.Add(ComponentArray[I].Description);
SetArrayLength(SelectionArray, GetArrayLength(SelectionArray) + 1);
SelectionArray[High(SelectionArray)] := ComponentArray[I];
end;
ComponentCombo.ItemIndex := 0;
end;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
FileData: TFileData;
ResultCode: Integer;
begin
Result := True;
if (CurPageID = wpFinished) and (ComponentCombo.ItemIndex > 0) then
begin
FileData := SelectionArray[ComponentCombo.ItemIndex - 1];
Exec(ExpandConstant(FileData.FileName), FileData.Parameters, '', SW_SHOW,
ewNoWait, ResultCode);
end;
end;

Is it possible to accept custom command line parameters with Inno Setup

I am preparing an installer with Inno Setup. But I'd like to add an additional custom (none of the available parameters) command line parameters and would like to get the value of the parameter, like:
setup.exe /do something
Check if /do is given, then get the value of something. Is it possible? How can I do this?
With InnoSetup 5.5.5 (and perhaps other versions), just pass whatever you want as a parameter, prefixed by a /
c:\> myAppInstaller.exe /foo=wiggle
and in your myApp.iss:
[Setup]
AppName = {param:foo|waggle}
The |waggle provides a default value if no parameter matches. Inno setup is not case sensitive. This is a particularly nice way to handle command line options: They just spring into existence. I wish there was as slick a way to let users know what command line parameters the installer cares about.
BTW, this makes both #knguyen's and #steve-dunn's answers somewhat redundant. The utility functions do exactly what the built-in {param: } syntax does.
Further to #DanLocks' answer, the {param:*ParamName|DefaultValue*} constant is documented near the bottom of the Constants page:
http://www.jrsoftware.org/ishelp/index.php?topic=consts
I found it quite handy for optionally suppressing the license page. Here is all I needed to add (using Inno Setup 5.5.6(a)):
[code]
{ If there is a command-line parameter "skiplicense=true", don't display license page }
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False
if PageId = wpLicense then
if ExpandConstant('{param:skiplicense|false}') = 'true' then
Result := True;
end;
Inno Setup directly supports switches with syntax /Name=Value using {param} constant.
You can use the constant directly in sections, though this use is quite limited.
An example:
[Registry]
Root: HKCU; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; \
ValueName: "Mode"; ValueData: "{param:Mode|DefaultMode}"
You will more likely want to use switches in Pascal Script.
If your switch has the syntax /Name=Value, the easiest way to read its value is using ExpandConstant function.
For example:
if ExpandConstant('{param:Mode|DefaultMode}') = 'DefaultMode' then
begin
Log('Installing for default mode');
end
else
begin
Log('Installing for different mode');
end;
If you want to use a switch value to toggle entries in sections, you can use Check parameter and a auxiliary function, like:
[Files]
Source: "Client.exe"; DestDir: "{app}"; Check: SwitchHasValue('Mode', 'Client')
Source: "Server.exe"; DestDir: "{app}"; Check: SwitchHasValue('Mode', 'Server')
[Code]
function SwitchHasValue(Name: string; Value: string): Boolean;
begin
Result := CompareText(ExpandConstant('{param:' + Name + '}'), Value) = 0;
end;
Ironically it is more difficult to check for a mere presence of switch (without a value).
Use can use a function CmdLineParamExists from #TLama's answer to Passing conditional parameter in Inno Setup.
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Exit;
end;
end;
You can obviously use the function in Pascal Script:
if CmdLineParamExists('/DefaultMode') then
begin
Log('Installing for default mode');
end
else
begin
Log('Installing for different mode');
end;
But you can even use it in sections, most typically using Check parameter:
[Files]
Source: "MyProg.hlp"; DestDir: "{app}"; Check: CmdLineParamExists('/InstallHelp')
A related problem:
Add user defined command line parameters to /? window
If you want to parse command line arguments from code in inno, then use a method similar to this. Just call the inno script from the command line as follows:
c:\MyInstallDirectory>MyInnoSetup.exe -myParam parameterValue
Then you can call the GetCommandLineParam like this wherever you need it:
myVariable := GetCommandLineParam('-myParam');
{ ================================================================== }
{ Allows for standard command line parsing assuming a key/value organization }
function GetCommandlineParam (inParam: String):String;
var
LoopVar : Integer;
BreakLoop : Boolean;
begin
{ Init the variable to known values }
LoopVar :=0;
Result := '';
BreakLoop := False;
{ Loop through the passed in arry to find the parameter }
while ( (LoopVar < ParamCount) and
(not BreakLoop) ) do
begin
{ Determine if the looked for parameter is the next value }
if ( (ParamStr(LoopVar) = inParam) and
( (LoopVar+1) <= ParamCount )) then
begin
{ Set the return result equal to the next command line parameter }
Result := ParamStr(LoopVar+1);
{ Break the loop }
BreakLoop := True;
end;
{ Increment the loop variable }
LoopVar := LoopVar + 1;
end;
end;
This is the function I wrote, which is an improvement of Steven Dunn's answer. You can use it as:
c:\MyInstallDirectory>MyInnoSetup.exe /myParam="parameterValue"
myVariable := GetCommandLineParam('/myParam');
{ util method, equivalent to C# string.StartsWith }
function StartsWith(SubStr, S: String): Boolean;
begin
Result:= Pos(SubStr, S) = 1;
end;
{ util method, equivalent to C# string.Replace }
function StringReplace(S, oldSubString, newSubString: String): String;
var
stringCopy: String;
begin
stringCopy := S; { Prevent modification to the original string }
StringChange(stringCopy, oldSubString, newSubString);
Result := stringCopy;
end;
{ ================================================================== }
function GetCommandlineParam(inParamName: String): String;
var
paramNameAndValue: String;
i: Integer;
begin
Result := '';
for i := 0 to ParamCount do
begin
paramNameAndValue := ParamStr(i);
if (StartsWith(inParamName, paramNameAndValue)) then
begin
Result := StringReplace(paramNameAndValue, inParamName + '=', '');
break;
end;
end;
end;
Yes it is possible, you can use the ParamStr function in PascalScript to access all the commandline parameters. The ParamCount function will give you the number of commandline parameters.
Another possibility is to use GetCmdTail
In response to:
"With InnoSetup 5.5.5 (and perhaps other versions), just pass whatever you want as a parameter, prefixed by a /"
"#NickG, yes, every constant you can expand by the ExpandConstant function"
This is not the case. Trying to use a command line parameter in ExpandConstant in InnoSetup 5.5.6 results in a runtime error.
PS: I would have added a comment directly but apparently I dont have enough "reputation"
I've modified a little bit knguyen's answer. Now it's case insensitive (you can write en console /myParam or /MYPARAM) and it can accept default value. Also I fixed the case when you receive larger parameter then expected (for ex: /myParamOther="parameterValue" in place of /myParam="parameterValue". Now myParamOther doesn't match).
function GetCommandlineParam(inParamName: String; defaultParam: String): String;
var
paramNameAndValue: String;
i: Integer;
begin
Result := defaultParam;
for i := 0 to ParamCount do
begin
paramNameAndValue := ParamStr(i);
if (Pos(Lowercase(inParamName)+'=', AnsiLowercase(paramNameAndValue)) = 1) then
begin
Result := Copy(paramNameAndValue, Length(inParamName)+2, Length(paramNameAndValue)-Length(inParamName));
break;
end;
end;
end;
I found the answer: GetCmdTail.
You can pass parameters to your installer scripts. Install the Inno Setup Preprocessor and read the documentation on passing custom command-line parameters.