InstallShield Tips and Techniques

July 16, 2007

MSI Tip: Writing to a MSI Log file from InstallScript Custom Action

Filed under: Reference Materials — shieldmaster @ 10:44 pm

Much has been written about logging to the MSI log, including new requirements levied by Windows Vista – custom actions should write startup and shutdown information.

InstallShield has created a TIP topic “MSI Tip: Writing to the Log File from a Custom Action” and discussed various methods available.

I have been using the MSI log for diagnostic analysis since DevStudio v9 – granted it has a complex structure, but with practice comes an easier ability to delve in the log to solve install issues. 

But what if many times, analysis could be done with a simpler install log?  I have found that the majority of problems can easily be identified if you log critical information into an install log, which is always created when your install kicks off.  Many times when your install is running onsite, they don’t have MSI Logging activated (see previous posting) – so a valuable technical tool has been lost. 

Even more valuable to the InstallShield Specialist is the ability to review an install log and verify that each Custom Action performed as expected – even if the install was successful you could have scripting errors that miss conditions critical to success.

Sample Install Log Output

ISI–> ***************************************************************
ISI–> ** LogFile Created by ABC Skeleton Package Install
ISI–> ** LogFile Created on 6-23-2007 at 12:59:43
ISI–> ***************************************************************
ISI–> INFO: Entering ISI_Fn_AppSearch Function
ISI–> ABC AppSearch – Must find the ABC ‘ABC_Skeleton’ application
with registry key file of ‘Motorola\ABC_Skeleton’
ISI–> ABC AppSearch – Found the ABC ‘ABC_Skeleton’ application with
registry key file of ‘Motorola\ABC_Skeleton’
ISI–> INFO: Entering ISI_Fn_SkeletonPreReq Function

ISI–> INFO: Entering ISI_Fn_CheckWin2003Svr Function
ISI–> Operating System is Windows 2003 Server Service pack installed
was ‘1′ which is approved!
ISI–> PASSED – Windows 2003 Server SP1 (or greater) was found! Passed Windows 2003 Server Check!
ISI–> INFO: Exiting ISI_Fn_CheckWin2003Svr Function

ISI–> INFO: Entering ISI_Fn_Verify_WSE_Installed Function
ISI–> Microsoft WSE ‘Microsoft.Web.Services3.dll’ will be searched in
directory or sub-directories
‘C:\Program Files\Microsoft WSE\v3.0′.
ISI–> PASSED – Microsoft WSE version 3.0.5305.0 was found installed! ISI–> INFO: Exiting ISI_Fn_Verify_WSE_Installed

ISI–> INFO: Entering ISI_Fn_RegToGAC Function
ISI–> INFO: Retrieved value of InstallDir, which was
‘C:\Program Files\Motorola\CSR\’ from registry using key of
‘SOFTWARE\Motorola\CSR_Framework’
ISI–> ERROR: Bad return code: ‘2′ from GAC Registration of file: ‘”C:\Program Files\ABC\CSR\ABC.Skeleton.Ajax.dll”‘
ISI–> ERROR: Bad return code: ‘2′ from GAC Registration of file: ‘”C:\Program Files\ABC\CSR\ABC.Skeleton.WebSpider.dll”‘
ISI–> ERROR: Bad return code: ‘2′ from GAC Registration of file: ‘”C:\Program Files\ABC\CSR\ABC.Skeleton.WSE.dll”‘
ISI–> INFO: Exiting ISI_Fn_RegToGAC Function

For Extra Credit! 

When you create the log entry for the Install Log, you can also trigger an entry into the MSI Log using the ‘SprintfMsiLog’ command.  That way the log comments are found in both locations!

Hope this helps!

Charles

p.s. if anyone wants some sample script that accomplishes this, please send an email to charles@installsolutionsinc.com and I will markup some script for you use!

July 14, 2007

Should you use CustomActionData?

Filed under: Reference Materials — shieldmaster @ 10:58 am

InstallShield 12 can be the bane of Deployment architects everywhere if there are existing MSI projects with InstallScript Custom Actions that need to be migrated to this new version!  All of their carefully worked Custom Action scripts created for earlier IS versions, will need to be re-evaluated after migrating to version 12.A severe problem that I have seen is that Custom Action written in InstallScript that are set to run Deferred will not have access to any MSI Property – there are very few exceptions, one MSI property accessable will be “CustomActionData”. So MSI Properties like “INSTALLDIR” and “PROGRAMFILES” are not accessible – no warning will be issues, just any attempt to retrieve data will result in a NULL value received.  Kind of destroys your script coding logic!

In my scripts, I tend to evaluate the workstation using InstallScript Custom Actions (Immediate Execution) and stash data in public MSI properties.  For example if I determine that Adobe Acrobat was installed, I store a boolean value (TRUE/FALSE) in an MSI Property, and the install path in another MSI Property.  Later when I want to launch the release notes, I have a InstallScript Custom Action set to Deferred to evaluate the MSI properties and take appropriate action.

With InstallShield 12 these InstallScript Custom Actions will no longer work correctly.  I will need to rework the Custom Action to store the data in the MSI Property CustomActionData.  Check out Christopher Painter’s blog notes on CustomActionData and its issues.  Normally the field would be used for a single value, but Christopher offers a technique to ’stuff’ multiple values in the field for retrieval. 

Technically a great solution, but hard to implement and a bear in long term maintenance.I feel that there is a better solution for modifying existing/legacy scripts, simply by using the registry as your sandbox.  For example, take a section of the HKLM\SOFTWARE, which is normally suggested that you place specs on your Company Name (see entries for Adobe).  Use it as a sandbox and stash installation variables gleaned during the UI Sequence and later retrieve them during the Install Execute sequence.   Here is an example of the technique that I use:

Prime RegistryJust after the dialogs have completed create a InstallScript Custom Action that will allow you to gather up all of your special MSI Properties that were initialized during previous CA’s and write them to the registry.  Here is a script extract:

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE ); 
szRegKey = “SOFTWARE\\Install Solutions\\Framework”; 
nvType = REGDB_STRING;

szRegSubKey =   “InstallDir”;
szProgramFilesDir = INSTALLDIR;
nResult = RegDBSetKeyValueEx(szRegKey, szRegSubKey, nvType, szInstallDir,-1);
if (nResult < 0 )then
   szBuffer = “ISI–>  ERROR – szInstallDir Directory” + could not be updated in registry:  ‘%s’” ;
 Sprintf (szLogMessage, szBuffer, szRegKey);
 ISI_Fn_WriteLogFile (szLogMessage);      
else
 szBuffer = “ISI–>  INFO – szInstallDir Directory location ‘%s’ updated in registry: ‘%s’ with key of ‘%s’ “;
 Sprintf (szLogMessage, szBuffer, szInstallDir, szRegKey, szRegSubKey);
 ISI_Fn_WriteLogFile (szLogMessage);      
endif;   
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE ); 
szRegKey = “SOFTWARE\\Install Solutions\\Framework”; 
nvType = REGDB_STRING;

// Obtain MSI Property for CustomerName and update Registry
nBuffSize = 256;
szMSIProperty = “ISI_CUSTNAME”;
szRegSubKey   = “CustomerName”;
  MsiGetProperty (ISMSI_HANDLE, szMSIProperty, svValue, nBuffSize);
  //Update the CustomerName Key.
  nResult = RegDBSetKeyValueEx(szRegKey, szRegSubKey, nvType, svValue, -1);
 if (nResult < 0 )then
   szBuffer=”ISI–>  ERROR-MSI Property ‘%s’ could not be updated in registry:  ‘%s’” ;
  Sprintf (szLogMessage, szBuffer, szMSIProperty, szRegKey);
  ISI_Fn_WriteLogFile (szLogMessage);      
 else
  szBuffer=”ISI–>INFO-MSI Property ‘%s’ updated in registry: ‘%s’ w/value of ‘%s’ “;
  Sprintf (szLogMessage, szBuffer, szMSIProperty, szRegKey, svValue);
  ISI_Fn_WriteLogFile (szLogMessage);      
 endif;   

Retrieve Registry ValuesIn each InstallScript Custom Action, simply add this script at the beginning to retrieve the values from the registry and store them into variables.  It should flow seamlessly into your existing script – here is some sample script that I use:

nvSize=256;  

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE); 

szKey=”SOFTWARE\\Install Solutions\\Framework”;nType=REGDB_STRING;  

//Get the InstallDir Key.

RegDBGetKeyValueEx(szKey,”InstallDir”, nType, svInstallDir, nvSize);     

szBuffer = “Retrieved from registry key ‘%s’ the InstallDir key which was ‘%s’ “;      Sprintf (szLogMessage, szBuffer, szKey, svInstallDir );     

ISI_Fn_WriteLogFile (szLogMessage);               

//Get the Customer Name.RegDBGetKeyValueEx(szKey,”ISI_CustName”,nType,szCustName,nvSize);     

szBuffer=”Retrieved from registry key ‘%s’ the ISI_CustName key which was ‘%s’ “;     

Sprintf (szLogMessage, szBuffer, szKey, szCustName );     

ISI_Fn_WriteLogFile (szLogMessage);    

After the install has been completed, should the install variables placed in the registry be deleted?  Not necessarily, it could have use in the uninstall process.  But remember to delete them when the uninstall has completed. 

Hope this helps! 

Charles  

Enable MSI logging

Filed under: Reference Materials — shieldmaster @ 12:07 am

The use of the MSI log cannot be underestimated in its ability to resolve technical issues.  It can be complicated to review the contents, so much so there are tools available to assist you.  

But first you must ensure that a MSI log is generated for your installs – here is the best method  to have one generated each time your install is run.  Add this registry entry to the workstation/server:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer]
“Logging”=”voicewarmup”

Hint:  take the above text and save as “MSILogging.reg” and then double-click it.

Now whenever a MSI install is run – even if you perform a Modify/Repair/Remove from the Add/Remove Program Control Panel, a verbose MSI log is generated.  The log is created in the Windows Temp directory (use %TEMP% in the Start | Run dialog box to access).  Look for a recent file named MSI9999.log (where 9999 is a random number).

This is much easier to accomplish instead of creating the complex batch statement to create a MSI log during execution.

Charles

Updated:  It was noted that the correct phrase should be “voicewarmup” – thanks Colin!

July 8, 2007

Soliciting suggestions!

Filed under: Uncategorized — shieldmaster @ 11:32 pm

I have just a few ideas for tips and techniques remaining that I will be posting soon. 

If anyone has a special request for something that I may be able to assist – please just add a comment and request it.

Best Regards

Charles

July 6, 2007

Creating Common Scripts for InstallShield projects

Filed under: Techniques — shieldmaster @ 12:30 am

Let’s talk about creating common scripts for InstallShield projects.  EVERYBODY creates script functions in the same directory as the Setup.RUL – but when you have multiple projects that use the same script functions, then the replication will cause code regression issues.  For example:

I have 9 active InstallShield projects, everyone of them uses the same script function “Adobe.rul”.  This script function is run as a Custom Action and will look to see if Adobe is installed.  If installed, it will verify the specific version of Adobe installed – our documentation uses features that won’t work for anything prior to version 6.0 of Adobe Reader.  The script did other things, like snare the installdir value for the launching of the ReadMe at the end – it also hid the checkbox/text field if they did not have Adobe, and elected not to install it. 

So I found myself with 9 copies of the same RUL file in 9 locations.  Obviously I would encounter a logic bug!  I found out later that I only corrected the logic bug in 7 of the 9 copies!  After the release, I was determine to come up with a better solution.

Common Scripts

I wanted to store the Adobe.RUL script function only once and reference it with all of the projects.  The critical thing is that I wanted to have access to the script function in the InstallShield IDE – which is what was a really issue to contend with! 

I ended up creating a directory “Common Scripts” under my InstallShield Reference directory – and stored all of the common scripts there.  I defined common script to the extreme – every script function would be stored there, the only exception was the Setup.RUL would be stored with the InstallShield project.

I did create a naming standard – to differenciate between the scripts used for any project versus the scripts that were specific to one project.  For example:

  • ISI_Adobe.RUL
  • ISI_Engine_RegFiles.RUL
  • ISI_Fn_RegFiles.RUL

My naming standard added Fn to indicate a that the script was called by another script function, such as the Custom Action called “ISI_Engine_RegFiles”, which registered certain Engine files by calling the routine “ISI_Fn_RegFiles” which had the generic registering script.  Another Custom Action called “ISI_Adobe” which performed the script as described above. 

Usage in InstallShield Project 

There is no difference in the usage of the Prototype statements:

export prototype ISI_Adobe(HWND);
export prototype ISI_Engine_RegFiles(HWND);
prototype ISI_Fn_RegFiles (BOOL, STRING, STRING, BOOL);

There is a difference in how you include the external script file for compiling:

#include “..\..\..\Reference Files\CommonScripts\ISI_Adobe.rul”
#include “..\..\..\Reference Files\CommonScripts\ISI_Engine_RegFiles.rul”
#include “..\..\..\Reference Files\CommonScripts\ISI_Fn_RegFiles.rul”

Notice that I used the relative DOS Path reference to locate the script functions – critical difference is that I need to offset from the location of the Setup.RUL – not the .ISM file.  (See earlier post for how to accomplish this).

Finally – if you want to see the script file in your IDE, you need to right-click on the Setup.rul and select “Insert Script files”, and navigate to the Common Scripts directory and select the ”.RUL” to add.  Now the script will appear exactly like any normal included script for viewing and editing by the IDE.

Issues 

Of course there will be issues!  Biggest one is that when the InstallShield project is open, and you made edits to the scripts then when you compile the scripts are rewritten back into the Common Files directory.  Not a big deal – but if you have two project open at once, then when you compile the second project – it will overwrite the changes you just did in the first project.   Apparently a copy is stored in memory and will refresh/rewrite the scripts back. 

So open and change only one at at time.  NEVER open a project and then use another editor to quickly apply a change to the common script.  Again it will be overwritten!

Hope this helps!

CharlesInstallShield Tips and Techniques
InstallShield Consulting
InstallShield Tips and Techniques
InstallShield Consulting
InstallShield Tips and Techniques
InstallShield Consulting
InstallShield Tips and Techniques
InstallShield Consulting
InstallShield Tips and Techniques
InstallShield Consulting

July 3, 2007

Make an MSI run multiple times

Filed under: Techniques — shieldmaster @ 11:55 pm

One of the options that was available to you with installations prior to Windows Installer emergence was the ability to rerun an installation multiple times.  By this I mean running the setup again without entering Maintenance Mode – as if it had never been run before.

You can still use InstallShield to create an InstallScript using the  option and then select the Multi-Instance option which lets your end users rerun an installation multiple times as a first-time installation rather than as a maintenance installation. 

But as you know, I studiously avoid InstallScript projects – I use Basic MSI Projects, so how do you make a Basic MSI Project eligible to be rerun?

Look at this dialog extract from the Sequences Table for the Install Execute Sequence:

 rerun.jpg

Note that there are four Standard MSI actions that are the primary method by which the MSI Application is registered under the Windows Installer that we need to be concerned with: 

  • RegisterUser
  • RegisterProduct
  • PublishFeatures
  • PublishProduct

Alter the condition on each of these Standard Actions to use zero – which means the condition will never be set true and the Standard Action will never run.  I recommend you alter the comment to reflect the change – as I have shown above.

Impact

The usage of this technique means the product will not be registered under Windows Installer – nor will it appear in the Add/Remove Programs Control Panel.   Use only if you have a specific requirement!

Usage Scenarios

I have encountered two separate situation where this technique served admirably.  

Once a vendor was creating unique sales video/slideshow presentations to sell to his customers.  After a few customers used his services, he encountered a situation where the same end-client was attempting to install the presentations from two of his customers.  The dreaded Maintenance mode was disrupting the end-client from installing the second presentation.   I was able to make the install rerunable – and used a scripted Custom Action to read a special file delivered with each presentation.  This information allowed me to install the unique files, create shortcuts to access the presentation and create a uninstall technique to allow the end-user to remove the files when no longer required.

Another vendor needed to be able to allow an unlimited amount of customers to be established on the server.  Each customer would have a unique IIS Virtual Directory with the same files installed.  XML documents that were specially configured during the install would maintain the customer data within the Virtual Directory.  A very complex Visual Studio .NET solution was involved to support the customer processes.  Each customer install used a rerunable Basic MSI Webproject to deliver the files and create the Virtual Directory.

Warning!

I don’t recommend you do this to simply to avoid the Maintenance Mode!  A great technique if you have a specific requirement – but you will lose so much functionality that Windows Installer/MSI packages offer.
Charles

Blog at WordPress.com.