3

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'
//------------------------------------------------------------------------------
TLama
  • 75,147
  • 17
  • 214
  • 392
  • What you're describing is impossible. That message box must be shown exactly once. I'll review your code a little bit, but cannot answer you what is impossible :-) – TLama Nov 24 '12 at 14:23
  • Guess I was not quite clear. Sorry. It appears twice and it should only appear once. I can't find the problem. Thanks for your help. – user1848350 Nov 24 '12 at 16:19

1 Answers1

0

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;
Community
  • 1
  • 1
TLama
  • 75,147
  • 17
  • 214
  • 392
  • Thank you very much. Elegant code. I substituted your changes, and pretty much was amazed at how it flowed. However, the original problem still exists. The code GetInstalDirectory runs twice. – user1848350 Nov 25 '12 at 16:22
  • Here's the sequence: InitializeSetup > IsWordInstalled GetInstallDirectory > IsWordInstalled > GetUserTemplatesPath Message is: Office 2010 user templates directory was found in registry! [Files] section is run … extracts 3 files then on 4th file, GetInstallDirectory > IsWordInstalled > GetUserTemplatesPath Message is: Office 2010 user templates directory was found in registry! Everything else works fine. – user1848350 Nov 25 '12 at 16:31
  • Sorry, taking back what I've said about that multiple function call (I wrote it before trying). You're right, code function called from the `DestDir` parameter is called twice. First time at the installation prepare stage and second time when the files are being processed. I don't know the exact reason (it's quite deep in the source code), but you don't actually need to care about it. That function is just for getting destination directory, not for showing any message, so you don't need to worry that it's called twice. – TLama Nov 26 '12 at 18:45
  • If you'd still want to have it called once, make a string variable like `InstallDirectory`, in some event function e.g. in that `InitializeSetup` call the `GetInstallDirectory` storing the result to the `InstallDirectory` variable and for the `DestDir` parameter use some helper function like `GetInstallDirectoryValue` which will just return the value of the `InstallDirectory` variable. But I wouldn't personally overcomplicate it. There's no performance risk at all, so there's no reason why don't this function can't be called twice. – TLama Nov 26 '12 at 18:51
  • Thanks for the help. I've been trying to figure out how to get rid of the messages and place the current installing version of Word and the subdirectory for the templates on a page just as information before install. Of course that means a new page and controls. I'll keep trying your suggestion of 11/26. Shame it runs twice because the message covers the progress bar which helps the user know something was done. – user1848350 Nov 29 '12 at 22:11
  • Sorry, but I'm afraid I don't get the last two lines of your comment. Do you just want to have the information on the *ready to install* page in that memo box ? What do you mean by *the message covers the progress bar* ? Could you elaborate this ? – TLama Nov 29 '12 at 22:48
  • The first message appears in front of the "Ready to Install" panel. I can live with that. But the next is "Installing, Extracting files" panel, which has the progress bar for the files being copies. However, the second message covers that progress bar. I only put the messages in so I could debug the code. Now the second one is a problem. If I could just suppress the second time the message appears. – user1848350 Nov 30 '12 at 00:26
  • Then don't use message boxes for debugging purposes like this :-) I'm personally using them here on SO just for questioners to see a certain value in the code sample. Otherwise I'm using the [`Log()`](http://jrsoftware.org/ishelp/topic_isxfunc_log.htm) procedure and you should do the same. When you're running your setup in debugger, the log messages are logged to the bottom message pane of the InnoSetup editor and except that it allows you to even log the standalone setup executed without debugger at certain circumstances described in the `Log()` procedure reference. – TLama Nov 30 '12 at 01:36
  • So, how is it going :-) ? I'm just poking, since you've asked why the message box from a code function called for the `DestDir` parameter of the `[Files]` section is shown twice for just one entry on which I've answered, that it's by design; never interact with user from these parameter functions. So do you want me to trace in the InnoSetup source code the exact reason why it's fired twice to answer you exactly (even if it won't be useful information) ? – TLama Dec 09 '12 at 08:02
  • Otherwise I think this Q&A might be finished. If you feel the same, [`accept the answer`](http://meta.stackexchange.com/a/5235/179541). If not, leave a comment and I'll try to find that exact reason. Thanks! – TLama Dec 09 '12 at 08:07