1

I'm having an issue here with the following:

function InitializeSetup(): Boolean;
var
  ResultCode:Integer;
begin
  Result := true;
  if MsgBox('Wanna help?',mbConfirmation, MB_YESNO )= IDYES then
  begin
    CreateBatch();
    Exec('cmd.exe', '/c "' +ExpandConstant('{tmp}\batch.bat'),'',SW_HIDE, ewWaitUntilTerminated, ResultCode);
    Result:= false;
  end;  
end;

in the batch file i got the following :

@ECHO OFF 
D:
cd D:\_INSTALLER\Output
"installer.exe" /SAVEINF="opt.txt"

So it basically re-open the installer , over and over again ... ( infinite loop )

Is there any way to ask : Wanna help? only for the 1st time ? and if the user clicked yes, the batch should be executed , else if the user clicked no it should continue the install normally.

Thanks in advance for the support , BeGiN.

BeGiN
  • 363
  • 3
  • 14
  • Err, are you sure you need a batch file ? What is the exact workflow that you want to achieve ? – TLama Apr 22 '14 at 10:48
  • @TLama the thing that i want to achieve is : Always run the installer with /SAVEINF-parameter :D ( or by confirmation ) – BeGiN Apr 22 '14 at 15:37
  • Hm, so you want to re-run the installer if the `/SAVEINF` parameter is not specified, or what is the condition for asking that question ? I'm just confused from that message dialog asking for help. So, if I will want help, you'll re-run the setup with `/SAVEINF` parameter ? Btw. there is a batch-less way to re-run the setup. And finally, if you want to ask only once, you have to pass the information that you've already asked to the executed setup... Or, if that question should be shown on certain condition (e.g. if that parameter is missing), you must check that before asking. – TLama Apr 22 '14 at 15:49
  • ok let me explain again :D if the user say `Yes` , the installer should run with `/SAVEINF=opt.txt` parameter( so i think the installer gotta restart ) so it can store his choices in a `.txt` and give it to me , so it's easier for me when the user have an issue, else if the user say `No` , the installer should run normally. hope i explained well :D – BeGiN Apr 22 '14 at 15:55
  • I see, so the message will be displayed always in the first started setup instance (the re-runned setup won't display it). Well, that requires to pass the information that you already asked the user to the re-runned setup. Otherwise you won't distinguish between the first instance and the re-runned one. But wait, wouldn't be just easier to check if the `/SAVEINF` parameter is specified ? What if the user intentionally runs the setup with this parameter ? What will you do in that case ? Will you re-run the setup against their will of chosen `/SAVEINF` parameter ? – TLama Apr 22 '14 at 16:01
  • Well , i haven't considered that option .. and also how could i check the parameters, because it's sounds like a good idea. :D – BeGiN Apr 22 '14 at 16:06
  • I did [`something similar`](http://stackoverflow.com/a/21565479/960757) with elevation. There is also a function that checks if a command line parameter is specified (`CmdLineParamExists`). You can also find a way for re-running a setup without batch file (by using `ShellExecute` function). However, that case was more complicated since the re-running was done at directory selection page, where you cannot shutdown the setup easily, so there is an extra use of force exit by `ExitProcess` function, that you don't need (you can simply return `False` to the `Result`). – TLama Apr 22 '14 at 16:16
  • i see , let me see if i achieve this :D – BeGiN Apr 22 '14 at 16:19
  • btw , i should check for parameters in `InitializeSetup` or `InitializeWizard` ? – BeGiN Apr 22 '14 at 16:28
  • Better in `InitializeSetup` since that's the time before the wizard form is not yet created, so you won't waste time by creating it. – TLama Apr 22 '14 at 16:29
  • but i can't use `ShellExecute(WizardForm.Handle, 'open',ExpandConstant('{srcexe}'), Params, '', SW_SHOW);` int initialize setup , because i have there WizardForm.Handle – BeGiN Apr 22 '14 at 16:34
  • Change that parameter to 0. That parameter specifies a handle to the parent window used for displaying a UI or error messages and it's not necessary. – TLama Apr 22 '14 at 16:35
  • 1
    @TLama epic , i did it , i'll post up the code in a bit :D ...What would i do without you ? :D i owe you another beer , i think i should buy a factory ^^ – BeGiN Apr 22 '14 at 16:42

2 Answers2

1

With the help of TLama and his post i achieved my goal by using the following script:

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

type
  HINSTANCE = THandle;
procedure ExitProcess(exitCode:integer);external 'ExitProcess@kernel32.dll stdcall';
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;lpParameters: string; lpDirectory: string; nShowCmd: Integer): HINSTANCE;
  external 'ShellExecute{#AW}@shell32.dll stdcall';

var
  withINF: Boolean;

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;
      Break;
    end;
end;

//Initialize setup
function InitializeSetup(): Boolean;
var
  ResultCode:Integer;
  Params: string;
  RetVal: HINSTANCE;
begin
  Result := true;
  withINF := CmdLineParamExists('/SAVEINF=opt.txt');
  if not withINF then
  begin
    Params := '/SAVEINF=opt.txt';
    ShellExecute(0, 'open',ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
    ExitProcess(0);
  end;
end;

L.E: A shorter code for doing the same thing also made by TLama(multilingual support added):

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
ShowLanguageDialog = yes

[Languages]
Name: "en"; MessagesFile: "compiler:Default.isl"
Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl"

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

type
  HINSTANCE = THandle;
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;lpParameters: string; lpDirectory: string; nShowCmd: Integer): HINSTANCE;
  external 'ShellExecute{#AW}@shell32.dll stdcall';


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;
      Break;
    end;
end;


function InitializeSetup: Boolean;
var
  Params: string;
begin
  // prepare Params variable for reusing
  if ExpandConstant('{language}') = 'en' then begin
    Params := '/SAVEINF=opt.txt /LANG=en';
  end;
  if ExpandConstant('{language}') = 'nl' then begin
    Params := '/SAVEINF=opt.txt /LANG=nl';
  end;
  // allow this setup to run if the expected parameter is specified; or, if it is not, allow to run it
  // when ShellExecute fails; it works like this - first evaluates the CmdLineParamExists function and
  // if that returns True, the second part of the statement (ShellExecute) won't run (evaluate); when
  // the parameter is not found, the CmdLineParamExists returns False and statement evaluation goes on,
  // ShellExecute attempts to run the setup and to the Result returns True (allow this instance to run)
  // when the function fails for some reason (the returned value <= 32); in other words, you will allow
  // this setup instance to run if executing of the new setup instance fails
  Result := CmdLineParamExists('/SAVEINF=opt.txt') or (ShellExecute(0, '', ExpandConstant('{srcexe}'), Params, '', SW_SHOW) <= 32);
end;

Regards, BeGiN

Community
  • 1
  • 1
BeGiN
  • 363
  • 3
  • 14
  • You don't need the `ExitProcess` since in `InitializeSetup` event method you can decide whether to run the setup or exit by the `Result` variable. Also, you should allow the setup to run when the `ShellExecute` fails for some reason (better than run nothing). The `withINF` is not necessary. And finally, I'm missing that message dialog here... :) – TLama Apr 22 '14 at 23:12
  • Btw. including the `ShellExecute` failure fuse you can shorten your `InitializeSetup` code to [`this`](http://pastebin.com/U0NddG0Q) ;) – TLama Apr 22 '14 at 23:50
  • Thanks! :) But there's nothing unusual. That code uses so called [`short-circuit evaluation`](http://en.wikipedia.org/wiki/Short-circuit_evaluation). With the OR operator, when the first operand returns True, there's no need to evaluate the second one because it won't change the overall result (`True or False` will be still True, same as `True or True` so it's just enough to know the first True). When the first operand returns False, the evaluation continues to give the statement a chance to be True. – TLama Apr 23 '14 at 00:10
0

I see not reason for this but you can simply solve this by creating temp file.

If user clicks Yes (first time) then create some file (any file with some random content) just before CreateBatch() is called.

Then simply check for existence of this file - if it exists user already clicked Yes (once) and do what ever you want.

Slappy
  • 5,250
  • 1
  • 23
  • 29