Update Installshield ISM Product Version During Pre-Build

February 14th, 2008

So this is a nice to have since keeping everything in sync is a pain. What I wanted to do is manage the product version displayed by the Installshield dialogs during the build process. This has an implication of keeping everything in sync without having to edit the ISM project everytime we want to just update the version number. I found this code on the Installshield Community forums so I dont take credit. It is done in VBScript but this could be easily ported to perl or the like:

Dim proj
Set proj = CreateObject(”SAAuto12.ISWiProject”)
proj.OpenProject(”%PROJECT_INSTALLER_DIR%\MyProject.ism”)

if proj.ProductVersion <> “%BUILD_VERSION%” then
proj.ProductVersion = “%BUILD_VERSION%”
proj.SaveProject()
end if
proj.CloseProject()

I hope this helps someone because I know for us keeping the versions in sync is a royal pain.

MSI - Updating the Progress Bar

February 6th, 2008

I spent the better part of three days researching how to control an MSI installation’s progress bar on the fly via an installscript CustomAction. What I needed to do was update the user while the installation was processing stuff through custom actions. So for example lets say i needed to copy an index.html file to another location but i want to tell the user the installation is doing so. I would need to have a progress bar and status text. This post descibes how to do it.

First I created a new ‘Functions.rul’ file to store my two external installscript functions (I did this just to keep things organized). One of these functions will be a ‘deferred custom action’, and the other will be the function I call from inside my CustomActions which will actually update the status text and progress bars. Basically from my understanding the deffered custom action will run silently in the background and be called anytime you need to update the progress bar. I then have my regular installscript custom actions which I have a function call in each to update the progress bar. I’ll try to describe it a bit better below:

To begin let’s start by adding our deferred custom action. I do this by creating a function in installscript called ‘AddTotalTicks’ this function will be our background task. The background task will run quietly in the background while the immediate CustomActions are fired. Below is the code i used:

////////////////////////////////////////////////////////
// Function: AddTotalTicks(hMSI)
//
// Purpose: Resets and adds the number of
// “ticks” to the progress bar.
////////////////////////////////////////////////////////

function AddTotalTicks(hMSI)
OBJECT rec;
NUMBER iResult;
HWND hRec;

begin

set rec = MsiCreateRecord(3);
MsiRecordSetInteger(rec,1,3);
MsiRecordSetInteger(rec,2,5000);
MsiRecordSetInteger(rec,3,0);

MsiProcessMessage(hMSI , INSTALLMESSAGE_PROGRESS, rec);

set rec = NOTHING;

//reset the progress bar
hRec = MsiCreateRecord(3);
MsiRecordSetInteger(hRec, 1, 0);
MsiRecordSetInteger(hRec, 2, 100);
MsiRecordSetInteger(hRec, 3, 0);
iResult = MsiProcessMessage(hMSI, INSTALLMESSAGE_PROGRESS, hRec);

end;

To add the deferred custom action to our project, go to installshield’s custom action editor add a new Installscript Custom Action and set it as deferred. Then set it to fire in the Install Exec Sequence after install files. Again this little CA will run quietly in the background doing nothing until needed.

So now that we have our nifty section that runs in the background we need to have something that can be called anytime to ‘activate’ the deferred custom action. I did this as an external function in installscript. This way if i have many other custom actions that i want to update the progress bar on i can do so simply by adding the following to my CustomActions:

szMsg = “Please wait while we execute the copy command…..”;
SEPERATE_SetProgressBar(szMsg, hMSI);

The script we will use to update the Status Text, and progress bar is as follows:

////////////////////////////////////////////////////////
// Function: SEPERATE_SetProgressBar
////////////////////////////////////////////////////////

function SEPERATE_SetProgressBar(szTitle, hMSI)
STRING szClientCount;
HWND hRec, hProgressRec;
NUMBER nvBufferSize, nIncrement, i;
NUMBER nBuff, nClientCount, nCount;
NUMBER iResult;

begin

hRec = MsiCreateRecord(3);
hProgressRec = MsiCreateRecord(3);

//reset the progress bar
hRec = MsiCreateRecord(3);
MsiRecordSetInteger(hRec, 1, 0);
MsiRecordSetInteger(hRec, 2, 100);
MsiRecordSetInteger(hRec, 3, 0);
iResult = MsiProcessMessage(hMSI, INSTALLMESSAGE_PROGRESS, hRec);

MsiRecordSetString(hRec, 1, “Progress Custom Action”);
MsiRecordSetString(hRec, 2, szTitle);
MsiRecordSetString(hRec, 3, “incrementing tick [1] of [2]”);
iResult = MsiProcessMessage(hMSI, INSTALLMESSAGE_ACTIONSTART, hRec);

// Tell the installer to use explicit progress messages.
MsiRecordSetInteger(hRec, 1, 1);
MsiRecordSetInteger(hRec, 2, 1);
MsiRecordSetInteger(hRec, 3, 0);
iResult = MsiProcessMessage(hMSI, INSTALLMESSAGE_PROGRESS, hRec);

//Specify that an update of the progress bar’s position in
//this case means to move it forward by one increment.

nIncrement = 1;
MsiRecordSetInteger(hProgressRec, 1, 2);
MsiRecordSetInteger(hProgressRec, 2, nIncrement);
MsiRecordSetInteger(hProgressRec, 3, 0);
MsiRecordSetInteger(hRec, 2, 1);

for i = 0 to 100 step 1

//This makes the progress bar glide from left to right.
MsiRecordSetInteger(hRec, 1, i);
MsiProcessMessage(hMSI, INSTALLMESSAGE_PROGRESS, hProgressRec);

endfor;

MsiCloseHandle(hRec);
MsiCloseHandle(hProgressRec);

end;

The function above takes in a string “szMsg” which basically says what to set the status text as. This above actually isn’t a custom action but rather a function that can be called via a custom action. The reason I extracted it out of the custom actions is because it’s like 30 lines of code and I didn’t want to have to cut and paste 30 lines of code every time I wanted a progress bar to change.

The final step is to subscribe the custom actions you want to edit the progress bar to the progress bar itself. This was something that tripped me up because the way to do it is actually in the dialog editor inside Installshield. You go to the ‘Behavior’ area for the ‘SetupProgress’ dialog, and then select the progress bar of ‘ActionProgress95′ and on the right hand pane you will see a little button called ‘Subscriptions’ at the bottom. For each custom action you want to update this bar on you need to subscribe it to the bar itself.

I hope this is some help. Drop me a line if you have any questions.

ClickOnce - Adding Files Post Visual Studio Publish

February 6th, 2008

So ClickOnce as we all know is very picky on how it handles security, manifests, and digital signatures. Everything must be in sync or the installation will fail. Unfortunatly ClickOnce isnt very friendly to the end user about errors it has or problems it sees with the manifests, file hashes and the like.

 Our development group has needed to handle ‘dynamic’ localization with ClickOnce. This means any of our clients or partners will want to localize our software and publish it out to their end users via ClickOnce. Since ClickOnce isnt really designed for doing dynamic anything, this was tricky to implement. What we ended up doing is wrapping the ClickOnce manifest generation tool with an MSI package and having this MSI package wizard the site administrators through the process of ‘Creating’ a ClickOnce publish. I will not describe that process here but really I’d like to go into a lower level on how to add files to the software publish after the manifests already exist and are signed. I will stick with command line examples since they are just relying on MAGE.exe to handle them and this tool is widely available:

mage -u myBinFolderPath\myApp.exe.manifest -fd myBinFolderPath\myBinFolder -cf pathtoCertificate.pfx -pwd certificatePassword

mage -s myBinFolderPath\myApp.exe.manifest -cf pathtoCertificate.pfx -pwd certificatePassword

 Those two lines are all it takes oto add files. Lets describe them a bit:

mage -u: This section says for the mage utilty to update the manifest file. We specify the location of the deployment manifest directly after with (myBinFolderPath\myApp.exe.manifest).

mage -u myBinFolderPath\myApp.exe.manifest -fd myBinFolderPath\myBinFolder -cf pathtoCertificate.pfx -pwd certificatePassword

The next part of the above line actually tells mage to update what it belives to exist in the deployment. The -fd switch actually stands from ‘-FromDirectory’ and tells mage to regenerate whats in the deployment manifest. Since the deployment manifest keeps a master list of files to be deployed this must be updated if we want to add anything to the ClickOnce installation after it has been published via Visual Studio.

The next part of the mage command (-cf) line tells mage to use a specific certificate when updating the deployment manifest. Since we have to use this cert to generate the file security hashes for each file being deployed you need to specify

 The last command line is simple  it simply re-signs the deployment manifest file with the certificate provided. Making it a ‘published’ property. Its pretty simple but Mage is very picky about how you run its commands and belive it or not it IS CaSe SenSitiVe.

Best of luck — Practice and Enjoy

General Review of Build Studio

December 6th, 2007

Upon some research our group decided to utilize Automated QA’s build studio solution to manage and create new builds in our infrastructure. Since I have had about 6 months to test this out and review this product. Below is a detailed review of the Automated QA build Studio project: 

  • POSITIVE:  Good Integration with Existing Tools - A good first point to make on this product is that it integrates pretty tightly with existing industry standard build solutions such as NANT, ANT, MSBUILD etc… this allows you to take an existing build that is already running and wrap build studio around it so your not spending major resources and time re-designing a build to fit with Build Studio.
  • POSITIVE: Large Toolset -Build studio includes a large tool set which allows it to integrate into MOST popular tools. It has built in mechanisms for Copy, Delete, MSBUILS, Dotfuscate and more which makes it a very flexible environment.
  • NEGATIVE: No Rename -It unfortunately has no integration for renaming files or directories. It does have an MSDOS command integration which can perform this task for you but it makes your project look ugly. It also has no integration for a mass rename which may be specialized but I think would be very useful.
  • POSITIVE: Excellent Reporting - The reporting abilities of Build Studio are amazing. It produces a detailed HTML report that can be emailed to any users on your team. This integration is a HUGE plus for this tool.
  • NEGATIVE: Poor Scripting Abilities -While Build Studio does offer VBScript, JavaScript or Delphi-Script it lacks a quality full fledged development language like c-sharp.  The tools provided are good but a higher language support would be a huge nice to have.
  • POSITIVE: Great UI -The ease of use and great flexibility of this product allows even someone who is new to using it get up and running quickly.  This decrease in learning curve is huge for small companies who need a reliable build system that isn’t a beast to learn.
  • NEGATIVE: No Import Logging - Currently it is not possible to import logs from other build studio projects this feature was apparently overlooked.

Overall Build studio has proven to be a reliable, quality product.  It is a bit pricey but well worth the cost. When considering a build solution usually you are faced with two choices. Either build it or buy it. While building it gives much more flexibility it is also costly and then ensures job security for your release engineer due to the learning curve involved in someone new coming into the situation.  For our needs Build Studio works. Granted that we don’t utilize it on ALL products we build we have implemented it and use it regularly on a few key ones.

Installshield Reading a Registry Key

December 5th, 2007

One of our installation kits needs to be installed to the IIS default directory on the system. I have been hunting for a way to automatically figure out where IIS is located. Normally this would be something like C:\inetpub\wwwroot but this can vary based on individual sites and corporations so it would be optimal to find this path automatically. Below is the example installscript which I am using to do this. I hope it can help someone else out there:

function DIALOG_FindIIS(hMSI)

STRING szValue,svValue, WebDir;
NUMBER nReturn,nType,nvSize;

begin

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
svValue = “Software\\Microsoft\\INetStp”;
nReturn = RegDBGetKeyValueEx(svValue,”PathWWWRoot”,nType, szWebDir, nvSize);

if(nReturn < 0) then
szWebDir = WINDISK ^ “inetpub\\wwwroot”^”ClickOnce”;
INSTALLDIR = szWebDir;
endif;

end;

Installshield 12 and the .net 3.0 Prerequisite

December 4th, 2007

I was going to type up a whole post on getting .net 3.0 to be a required pre-requisite but i came across Christopher Painter’s blog entry basically outlining the whole process for me. So I thought I would do my due dilligence and post a nice spiffy link to his article here:

http://blog.deploymentengineering.com/2006/11/using-installshield-12-to-install-net.html

Top 3 Reasons NOT to use Dynamic File Linking

November 16th, 2007

Hello. After a weeks wasted work trying to fight with dynamic file linking. I decided to blog about this terrible feature. My goal is to sway people away from utilizing dynamic file linking in Instalshield. If I can persuade one person not to EVER use dynamic file linking in thier projects then I can die a happy person. Below is my list of top five reasons NOT to use dynamic file linking in Installshield

1.  Dynamic file linking will break upgrade and patching best practices:

This may not sound important now but just wait until you try to patch a kit that was installed utilizing dynamic file linking. It makes adding new components to a feature 100% impossible in basic MSI projects.

2. Dynamic file linking with the ‘include subdirectories’ a pain to find and access sub directories in your installdir:

lets say for example you need to modify an XML file stored in one of your dymaic folders. It will be very hard to figure out the name of that folder while the kit is running.

3. Dynamic file linking is flaky with standard upgrading (minor upgrades):

If your client installs your software and deletes a file, then they try to run a minor upgrade the upgrade will succeed but not lay down the missing file. This could cause a huge support problem for you.

 I hope people will heed this warning and NOT use dynamic file linking at ALL costs.

Working XMLDOM’s and XPath in Installscript

November 1st, 2007

I started out this adventure with the task of making changes to a .net config file via Installshield. There are two paths you can take when traveling this road.  You can use the built in Installshield ‘XML File Changes’ section which will import an XML file and allow you to modify various nodes, or you can use Installscript to wire in the MS XML2 Engine.

I chose to take the route of the second path as the first path seems buggy and unreliable in Installshield 12.0. Basically to begin you will need to wire in a CustomAction which will fire your Installscript Prototype Function. I titled mine appropriately (”SETUP_UpdateXML”). I’ll paste the code of what I did to modify an XML node value, then I’ll go over a few small tips and tricks which are useful in working with XML.

 Below is My Script:
// Set XML Doc Type
set oDoc = CreateObject("Msxml2.DOMDocument.4.0");
oDoc.setProperty("SelectionLanguage", "XPath");
// set up variable with fullpath to config file
szConfigFile = "C:\app.config" ;
// set up the XPath for your target node and setting you are going to change
szXPath = "/configuration/userSettings/Client.Properties.Settings/setting[@name='WebUrl']";

// load the XML document into memory
if oDoc.load(szConfigFile) then

//Set the XPath in the XML
set oNode = oDoc.documentElement.selectSingleNode(szXPath);

//Update the XML Element
oNode.text = szCurrentURI;

endif;

// Save the XML Changes
oDoc.save(szConfigFile);

set oDoc = NOTHING;
Tips and Tricks to Installshield & XML:

I mentioned I would add some additional tips and tricks to working with XPATH and XML files in Installscript. I had a heck of a time getting this all to work. Below are a few items to keep in mind when working with XML files in Installscript:

Tip 1 Debugging:

Remember that XML manipulation in Installscript is VERY similar to XML manipulation in VBScript. Since Installshield’s Debugger is sometimes clumsy and forces you to build the project prior to execution, it can be time consuming to test your scripting changes in Installshield’s debugger. Start simple, and build your script in VBScript (.vbs in a text editor) then you can launch your VBScript from the command line and attach the FULL Visual Studio’s debugger. The command line you would want to execute to attach to the VBScript is as follows:

>WScript //D //X myvbscript.vbs

Tip 2 Listing Nodes:

I found some nice examples of XML Node Listings via the Installshield Community forums. These are not my work but the work of others. However I thought it would be useful to post them here:


listNames = ListCreate (STRINGLIST);
for iList = 0 to oNodeList.length - 1svAttributeValue = oNodeList.Item(iList).Text;
ListAddString (listNames, svAttributeValue, AFTER);
endfor;

Accessing a .NET DLL Via Installshield & MSI

October 25th, 2007

Call a .NET DLL Function /CLASS via Installscript:

So everyone knows you can call MFC win32 dll functions from Installshield or Installscript. But did you know you can call .net 2.0 and 3.0 DLL Classes from a Basic MSI Installshield project?

The Answer:

Well…. there is a little known installscript function called CoCreateObjectDotNet, which can be utilized to expose functions/classes in a .net DLL file. This function exposes the functions/classes to an installscript prototype in an Installshield MSI project, so that they can be activated on-demand via installshield, and return values, strings etc….

What is the benefit of this CoCreateObjectDotNet function?

There are numerous benefits of this, I will list a few: 

  • Test a Database Connection Based on user input provided in a dialog
  • Test a Server to Client Connection for Client Apps
  • Internet Registration Routines can now be called via a DLL (More Secure!)

This is just a few examples of how being able to access .net functionality in Installshield can prove to be very valuable.  Now How to Do it:

  1.  
    1. Create a Custom Action which calls your Installscript function. Activate this custom action however you like (button click, sequence etc..)
    2. Write your Installscript. Below is an example of mine: 


function SETUP_TestEndPoint(hMSI)
OBJECT oObj;
STRING szPublishURL,szTrailSlash,szPROT, sDLL,szReturn,szURI;

begin

sDLL = SourceDir ^ "InstallShieldEndpointTester.dll";
szURI = "nettcp://testURI.com:3001/enterprise/services/clientservice/";

UseDLL (sDLL);
try

set oObj = CoCreateObjectDotNet(sDLL, "InstallShieldEndpointTester.EndpointTester");
nResult = oObj.IsEndpointValidAndActive(szURI);

catch

SprintfBox(INFORMATION,"CoCreateObjectDotNet Error","ERROR: %s NUMBER: %d",Err.Description,Err.Number);

endcatch;

NumToStr ( szReturn, nResult );
MessageBox (szReturn,SEVERE);

end;

My code basically calls a DLL to validate a server exists and is accessible. Yours however can do whatever you want. If you have any questions on this drop me a line. Ill be happy to try and help.

Installshield Minor Upgrade Custom Actions

October 24th, 2007

Recently i have been working on ensuring our new ClickOnce wrapper MSI kit can handle minor upgrades. I am architecting this kit to act as both the initial install as well as the upgrade, this involves running Custom Actions upon upgrade. Without going to into detail the major problem I encountered was getting the custom action to fire with the right conditions, during the upgrade. Below are the settings I used for my Custom Action to get it to run only during upgrade

 Install UI Sequence - After SetupProgress
 Install UI Condition - IS_MINOR_UPGRADE=1

 I hope this helps prevent someone else from pounding sand to get a CA to fire only during a minor upgrade.