MSI properties set during the pre-file transfer events (OnBegin, OnFirstUIBefore, etc.) or set during immediate custom actions will lose their values in post-file transfer events (OnFirstUIAfter, OnEnd, etc.). This is an unintended side-effect due to how InstallScript MSI projects are implemented.
The basic reason for this is the InstallScript engine acts as an external UI handler for the MSI package being installed. In order to interact with Windows Installer, the InstallScript engine opens an installation session through the MsiOpenPackage API. All properties set during pre-file transfer events are backed by this installer session. Just before file-transfer is started, this installer session needs to be closed, otherwise, Windows Installer would be unable to read the MSI package and the install would fail. A consequence of closing the session is any properties set during that session are essentially lost. Properties that are Directory table entries, such as INSTALLDIR, are reinitialized after file-transfer by running the Windows Installer costing actions.
If you need to maintain property values between pre and post file transfer events in InstallScript, global variables should be used in addition to or instead of MSI properties. Properties set during custom actions in the execute sequence would need to be written to a file or registry entries on the machine as there is no way to pass global variables from InstallScript custom actions to InstallScript events.
__________________
Josh Stechnij - Software Engineer - Acresso Software: InstallShield Team
Showing posts with label InstallScript. Show all posts
Showing posts with label InstallScript. Show all posts
Thursday, June 11, 2009
InstallShield and MsiGetProperty
For a long time I have had a problem with MsiGetProperty in that sometimes it would work and sometimes it wouldn't. I posted about it on the InstallShield forums and got this great piece of information:
Tuesday, March 24, 2009
InstallShield Merging Web Sites
I had this problem with my InstallShield project where it was merging my web site into the default web site instead of creating a new web site. This was happening if I was installing the site using port 80 which was also being used by the default web site.
After some research (and help) I found that the problem was caused by the web site number (a value in the IIS config settings in the project). I had set this value to 0 thinking InstallShield would just use the next available web site number. However, this is not so. I don't know why, but if the port was used by another site, it used that site's number. If the port wasn't in use, it would used the next number. As far as I know, there isn't a mechanism to use the next available. So, I wrote a function in InstallScript that calls out the the IIS admin script absutil.vbs to get the numbers in use and return the next one.
Here is the function:
Hopefully this will save you time and pain.
*EDIT* This doesn't quite work. Look for the posted correction.
After some research (and help) I found that the problem was caused by the web site number (a value in the IIS config settings in the project). I had set this value to 0 thinking InstallShield would just use the next available web site number. However, this is not so. I don't know why, but if the port was used by another site, it used that site's number. If the port wasn't in use, it would used the next number. As far as I know, there isn't a mechanism to use the next available. So, I wrote a function in InstallScript that calls out the the IIS admin script absutil.vbs to get the numbers in use and return the next one.
Here is the function:
//---------------------------------------------------------------------------
// GetNextWebSiteNumber
//
// Author: Yevi
//
// Description: This function returns the next available web site number based
// on entries for current web sites in IIS. The admin script adsutil.vbs is
// used the query current web sites.
// If there is an error, the function returns -1.
//---------------------------------------------------------------------------
function GetNextWebSiteNumber()
NUMBER nResult;
NUMBER nvTempWebSiteNumber;
NUMBER nWebSiteNumber;
NUMBER nvFileHandle;
NUMBER nNumPos;
STRING szProgName;
STRING szProgParams;
STRING szFileDir;
STRING szFileName;
STRING svLine;
STRING svSiteNum;
begin
nvTempWebSiteNumber = -1;
nWebSiteNumber = nvTempWebSiteNumber;
szFileDir = SUPPORTDIR;
szFileName = "websiteenum.txt";
// Run the adsutil.vbs script and get all the web sites. Put the
// output into a file.
szProgName = WINDIR^"system32\\cscript.exe";
szProgParams = " C:\\inetpub\\AdminScripts\adsutil.vbs ENUM /p W3SVC > " + szFileDir^szFileName;
if (LaunchAppAndWait(szProgName, szProgParams, WAIT) >= 0) then
// Open the file with the web sites and find the number
// of the last one.
OpenFileMode(FILE_MODE_NORMAL);
if (!OpenFile (nvFileHandle, szFileDir, szFileName) < 0) then
while GetLine (nvFileHandle, svLine) = 0
if (svLine % "W3SVC/") then
nNumPos = StrFind(svLine, "/");
StrSub(svSiteNum, svLine, nNumPos, 10);
StrToNum(nvTempWebSiteNumber, svSiteNum);
if (nvTempWebSiteNumber > nWebSiteNumber) then
nWebSiteNumber = nvTempWebSiteNumber;
endif;
endif;
endwhile;
CloseFile(nvFileHandle);
nWebSiteNumber = nWebSiteNumber + 1;
endif;
endif;
return (nWebSiteNumber);
end;
Hopefully this will save you time and pain.
*EDIT* This doesn't quite work. Look for the posted correction.
Labels:
Default Web Site,
IIS,
InstallScript,
InstallShield,
Web Site Number
Monday, August 4, 2008
SdLogonUserInformation
I discovered a interesting tidbit today -- SdLogonUserInformation only works with one user. It has a single set of properties it uses to gather user information. If run this dialog more than once to get multiple users, the last set of credentials wins.
Friday, August 1, 2008
InstallShield LaunchAppAndWait and File Access
The Task: Grant access to the files I have installed to the users I have created.
In the Files & Folders view, you can right click on a folder and select properties to get access to the permissions for a file or folder. However, this doesn't work so well because you can't add permissions to a file, you can only replace them. I didn't want that as I didn't want to clear out the Administrators group access to my install folder. I just wanted to add a couple of users to the access list. The real answer is to use LaunchAppAndWait to run cacls.exe on the folder.
I have used LaunchAppAndWait in the past and have struggled with it. It is difficult to debug. So, there are some things I would recommend. First, add a MessageBox for debug that shows what you are going to run. If you are not getting desired results, copy exactly what is in your message into a console window on the target system with your files in place and see what happens. You might be surprised. LaunchAppAndWait tells you if it launched the program, but does not tell your if the program had errors.
Of course, that isn't the way I did it the first time. The first time I was convinced LaunchAppAndWait was messing up. So, I created a sanity-check app in Visual Studio and called it from LaunchAppAndWait.

It's very simple. Take the arguments from the command line and put them into a text file. This way, you can use whatever command parameters your real command needs. Just change the program to the test app.
To use LaunchAppAndWait, set up a variable for the program name and a variable for the parameters. Actually, here is my function.
function SetDirectoryPermissions(szDirectoryName, szUserName)
STRING szProgName;
STRING szProgParams;
STRING svTempString;
begin
// Remove any trailing slash from the directory name.
StrSub(svTempString, szDirectoryName, StrLength(szDirectoryName) - 1, 1);
if (svTempString = "\\") then
StrSub(svTempString, szDirectoryName, 0, StrLength(szDirectoryName) - 1);
szDirectoryName = svTempString;
endif;
szProgName = WINDIR^"system32\\cacls.exe";
szProgParams = " " + szDirectoryName + " /E /G " + szUserName + ":F";
if (LaunchAppAndWait(szProgName, szProgParams, WAIT) <>
MessageBox("Unable to launch cacls. File permissions will need to be set manually.", WARNING);
endif;
end;
With all that out there in the open, it shows the issue I had with cacls.exe. If you pass in INSTALLDIR, there is a trailing slash (IS always puts one there). cacls.exe throws an error saying that it can't find the file. So, I had to remove the trailing slash and IT WORKED!
BTW, the command it is running is
cacls.exe C:\InstallDir /e /g DOMAIN\USER:F
Which brings up my last point. You should include the domain with the user name. Windows needs that to properly locate the credentials for the user.
Ok, so my code samples are rough. I am still getting used to blogger.
In the Files & Folders view, you can right click on a folder and select properties to get access to the permissions for a file or folder. However, this doesn't work so well because you can't add permissions to a file, you can only replace them. I didn't want that as I didn't want to clear out the Administrators group access to my install folder. I just wanted to add a couple of users to the access list. The real answer is to use LaunchAppAndWait to run cacls.exe on the folder.
I have used LaunchAppAndWait in the past and have struggled with it. It is difficult to debug. So, there are some things I would recommend. First, add a MessageBox for debug that shows what you are going to run. If you are not getting desired results, copy exactly what is in your message into a console window on the target system with your files in place and see what happens. You might be surprised. LaunchAppAndWait tells you if it launched the program, but does not tell your if the program had errors.
Of course, that isn't the way I did it the first time. The first time I was convinced LaunchAppAndWait was messing up. So, I created a sanity-check app in Visual Studio and called it from LaunchAppAndWait.

It's very simple. Take the arguments from the command line and put them into a text file. This way, you can use whatever command parameters your real command needs. Just change the program to the test app.
To use LaunchAppAndWait, set up a variable for the program name and a variable for the parameters. Actually, here is my function.
function SetDirectoryPermissions(szDirectoryName, szUserName)
STRING szProgName;
STRING szProgParams;
STRING svTempString;
begin
// Remove any trailing slash from the directory name.
StrSub(svTempString, szDirectoryName, StrLength(szDirectoryName) - 1, 1);
if (svTempString = "\\") then
StrSub(svTempString, szDirectoryName, 0, StrLength(szDirectoryName) - 1);
szDirectoryName = svTempString;
endif;
szProgName = WINDIR^"system32\\cacls.exe";
szProgParams = " " + szDirectoryName + " /E /G " + szUserName + ":F";
if (LaunchAppAndWait(szProgName, szProgParams, WAIT) <>
MessageBox("Unable to launch cacls. File permissions will need to be set manually.", WARNING);
endif;
end;
With all that out there in the open, it shows the issue I had with cacls.exe. If you pass in INSTALLDIR, there is a trailing slash (IS always puts one there). cacls.exe throws an error saying that it can't find the file. So, I had to remove the trailing slash and IT WORKED!
BTW, the command it is running is
cacls.exe C:\InstallDir /e /g DOMAIN\USER:F
Which brings up my last point. You should include the domain with the user name. Windows needs that to properly locate the credentials for the user.
Ok, so my code samples are rough. I am still getting used to blogger.
Labels:
cacls,
File Permissions,
InstallScript,
InstallShield,
LaunchAppAndWait
Subscribe to:
Comments (Atom)
