I am running a VBScript as a Custom Action at the Commit part of an MSI installation. The script calls an .exe that installs drivers for a ZB device. What I want to do is check the file system first to see if the drivers are already there and skip the installation if they are.
So far the script looks like this:
Sub Run(ByVal sFile)
Dim shell
Set shell = CreateObject("WScript.Shell")
shell.Run Chr(34) & sFile & Chr(34), 1, false
Set shell = Nothing
End Sub
Set objFSO = CreateObject("Scripting.FileSystemObject")
IF objFSO.fileExists("c:\windows\system32\drivers\ftser2k.sys") THEN
MsgBox("You already have the drivers installed.")
ELSEIF objFSO.fileExists("c:\windows\system32\ftserui2.dll") THEN
MsgBox("You already have the drivers installed.")
ELSE
Run Session.Property("CustomActionData") & "CDM20600.exe"
END IF
These files do exist on my machine. So if I double click the vbs file I get the MsgBox coming saying that I already have the file. However, when I run the msi installation, no matter what it installs the driver as if the first two conditional statements weren't even there. I did read that you cannot use the WScript object in MSI, so I took out the WScript.Echo lines and replaced them with MsgBox. I was wondering if maybe you can't use the FileSystemObject in msi either.
My ultimate goal is not to have any message come up. I just want the driver install to be skipped if the files are present on the system. The messages are there just for debug purposes right now.
If it helps, the msi package was built in Visual Studio 2010. Also the CustomActionData is the TARGETDIR.
I am new to both VBScript and install packages, so please be gentle :)
I have to be honest, I have many concerns about your proposed solution:
1) VB/JScript CA's Suck. I would read the link and take it to heart.
2) I've seen many machines in my career where the FSO was broken.
3) You've hard coded the path to System32 instead of using SystemFolder or System64Folder.
4) Commit custom actions don't execute when rollback is disabled.
5) You are running double out of process with no error logging of the EXE call.
6) Visual Studio Deployment Projects suck in so many ways that I can't count. Evidence is that Micrsoft has killed them in Visual Studio 11.
If it was me, I'd ask if you have to use this EXE to install the driver package or if there's an INF file to go along with the SYS/DLL files. If so, I'd look at create a WiX merge module that uses the DifxAppExtension. This allows you to encapsulate the behavior of driver installation in a discrete module and then add it to your VDPROJ installer or even better a WiX or InstallShield Limited Edition ( free ) installer.
Here are several blog articles that should help you understand what I mean:
Augmenting InstallShield using Windows Installer XML - Certificates
Augmenting InstallShield using Windows Installer XML - Windows Services
Redemption of Visual Studio Deployment Projects
Related
When I run an installer that allows a custom install location/path, the files will be correctly placed at the location that I select.
When I run the same MSI and select remove (or uninstall from add/remove programs), how does it know the install location so the correct files are removed?
I thought it would be stored at 'Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall{GUID}', but when I look at that location for my installed software, the 'InstallLocation' key is empty.
However, no matter how I uninstall it, it knows which folder to go remove, no matter where I put it. Is that information stored elsewhere in the registry, or in the MSI file itself?
This is a very complicated question as MSI can be configured to drop its uninstall files anywhere you tell it. Usually by default though it will create an uninstall .msi file with a specific name in C:\Windows\Installer.
But don't depend on the uninstall .msi being placed in this directory and don't rely on there being an uninstall path in the Uninstall registry key. This key is as much about convenience for the end-user as anything else.
The uninstall information is usually contained within the MSI file, but it need not be and during installation it can create keys to aid upgrading and uninstallation. The information that an installation will leave in the registry is entirely down to how you configure the .msi database.
Adding a few more things... many installers like Nullsoft, InstallAware and InstallShield like to do their own stuff and put their uninstall information in other places. So InstallShield likes to create an InstallShield Installation Information folder and Nullsoft likes to create .dat files and an uninstall.exe. But beyond all this, these installers are still invoking MSI and creating installation tables and database. So where the uninstall information is actually located it not an exact science!
UPDATE:
Find Component's Installation Location: Is there way to detect install location without uninstall registry nor C:\Windows\Installer?
Implementation Details: How MSI stores these things are implementation details that should not be meddled with, attempted modified or used directly for any purpose - just so that is clear. You should go through the MSI API which is implemented as Win32 functions with complementary COM wrappers for access via scripting languages.
Registry: The MSI database is stored mostly in the registry, but there also components on disk - some of which you refer to - for example %SystemDrive%\Windows\Installer (a super-hidden folder that should not be modified in any way). The MSI database is stored in numerous locations throughout the registry:
HKCR\Installer
HKCU\Software\Microsoft\Installer
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Installer
Etc...
Some of these are real, some are aliases, some are merges. It is all a bit fuzzy. Again: implementation details - a well-known euphemism for all of us to: "give up right now, will you"? :-). Just apply the MSI API to acquire the information you need.
MSI API: A lot of stuff to read above to get to the point, go via the MSI API to get your information on directory resolution. What we have to do is a bit exotic, we have to spin up a session object for the installed product and run two standard actions (built-in MSI actions from Microsoft) in order to resolve the directory table and installation directories of the MSI in question (about "costing"). Below is a practical sample:
For the record:
How can I find the product GUID of an installed MSI setup?
How can I compare the content of two (or more) MSI files? (on Orca and other, free MSI tools)
Uninstalling an MSI file from the command line without using msiexec (a myriad of ways to uninstall MSI packages)
Set installer = CreateObject("WindowsInstaller.Installer")
' Other test product codes: {2F73A7B2-E50E-39A6-9ABC-EF89E4C62E36}
productcode = Trim(InputBox("Please paste or type in the product code you want to look up details for:", _
"Find Product Details (test GUID provided):", "{766AD270-A684-43D6-AF9A-74165C9B5796}"))
If search = vbCancel Or Trim(productcode) = "" Then
WScript.Quit(0)
End If
Set session = installer.OpenProduct(productcode)
' Crucially, resolve the directory table and properties by running "MSI Costing"
session.DoAction("CostInitialize")
session.DoAction("CostFinalize")
' Can be any directory property from the Directory table in the MSI:
MsgBox session.Property("INSTALLFOLDER")
' Open the MSI in Orca to find the directory folder property names
Throwing in a link to an old answer on how to list tables inside an MSI file.
Resolve All: Got a bit carried away and made one more update to resolve ALL directories for any installed package. Here is a script (not tested much):
' https://stackoverflow.com/questions/17543132/how-can-i-resolve-msi-paths-in-vbscript
' On Error resume Next
Set installer = CreateObject("WindowsInstaller.Installer")
' Other test product codes: {2F73A7B2-E50E-39A6-9ABC-EF89E4C62E36}
const READONLY = 0
Dim DirList
productcode = Trim(InputBox("Please paste or type in the product code you want to look up details for:", _
"Find Product Details (test GUID provided):", "{766AD270-A684-43D6-AF9A-74165C9B5796}"))
If search = vbCancel Or Trim(productcode) = "" Then
WScript.Quit(0)
End If
Set session = installer.OpenProduct(productcode)
session.DoAction("CostInitialize")
session.DoAction("CostFinalize")
set view = session.Database.OpenView("SELECT * FROM Directory")
view.Execute
set record = view.Fetch
Do until record is Nothing
ResolvedDir = session.Property(record.StringData(1))
DirList = DirList + record.StringData(1) + " => " + ResolvedDir + vbCrLf
set record = view.Fetch
Loop
' Dismiss dialog with ESC key if it falls off screen
WScript.Echo DirList ' Use WScript.Echo due to MsgBox restrictions (number of characters)
Links:
How can I resolve MSI paths in VBScript?
I want get dir my setup create by install shield.
I use command parametter Setup.exe /path=[SETUPEXEDIR]\log.txt
My setup location is Desktop\myapp\Setup.exe
When use [SETUPEXEDIR] return temp folder
I want when use [SETUPEXEDIR] return me my Setup.exe location.
I use installshield 2016 version 23 SP 2.
I use MSI Script.
I want get location and use in command parameter prerequisites.
A little hard to comprehend exactly what you are asking, but as far as I understand you want to know the location where setup.exe is running from?
Variables
The first question is: what version of Installshield are you using?
The second question is: are you using Basic MSI or Installscript MSI?
The third question is: what type of release media are you using?
There may be more relevant questions...
A word to the wise: if you are indeed using Installscript MSI you should know that it is a very buggy type of project, and you should seriously consider switching to Basic MSI to save yourself grief. I can provide more information on this if you like. I had to abandon Installscript MSI entirely to make my deployment problems go away.
Installshield Properties
It seems different versions of Installshield may behave differently and feature varying support for these folder properties / variables. It also seems the properties may not work with all types of release media. And finally they may only work in Basic MSI or Installscript MSI respectively. The properties I have found are: PACKAGE_LOCATION, SETUPEXEDIR and SRCDIR. There also appears to be an Installscript method called GetCurrentDir() available in recent versions of Installshield, but the documentation warns about using it (see link).
Please visit the links above in sequence and read in detail about each property's (or method's) limitations. It is very important that you use the option (if any) that matches your requirements and scenario. For example PACKAGE_LOCATION works only for Installscript MSIs, SETUPEXEDIR is set by Setup.exe. If the end user runs the .msi package directly, SETUPEXEDIR is not set.
MSI Built-in Property
It seems to me that getting the built-in MSI property SourceDir might be an option to try. My quick test indicates that it works for both InstallScript and Basic MSI. However, I do not know if this works for all versions of Windows Installer. Please test on various Windows versions to be sure.
You should also be aware of the potential problem using SourceDir which is described in the documentation for SETUPEXEDIR. This goes for setups that are compiled into a single, compressed setup.exe containing all files - this launcher will extract the MSI file to a temp location and run from there. When I tried with an uncompressed network image it worked fine to use SourceDir.
Finally, if you use a setup.exe to compress all files and enable the caching of the MSI on the system, then you will be running from somewhere inside: C:\WINDOWS\Downloaded Installations\{GUID}\.
All of this could be different on newer versions of Installshield. I am testing with an ancient version I have available. Please test thoroughly on your version.
I should also mention the OriginalDatabase built-in MSI property. Check the link for documentation on how it will be set.
Some links:
Installscript project - Get Setup.exe location.
How to find the setup.exe directory?
Installscript Function For Testing
And just for reference, here is a quick and dirty function to test these properties from an Installshield custom action (this is for other people who may find this without having tested as much as you):
function TestFolderProperties(hMSI)
STRING svName;
NUMBER nvSize;
begin
// MSI properties
nvSize = 256;
MsiGetProperty (hMSI, "SETUPEXEDIR", svName, nvSize);
MessageBox ("SETUPEXEDIR: " + svName, INFORMATION);
MsiGetProperty (hMSI, "SourceDir", svName, nvSize);
MessageBox ("SourceDir: " + svName, INFORMATION);
MsiGetProperty (hMSI, "OriginalDatabase", svName, nvSize);
MessageBox ("OriginalDatabase: " + svName, INFORMATION);
// System Variables
MessageBox ("SRCDIR: " + SRCDIR, INFORMATION);
// PACKAGE_LOCATION is not available in my version of Installshield, enable and test
//MessageBox ("PACKAGE_LOCATION: " + PACKAGE_LOCATION, INFORMATION);
end;
Remember to add the export to the top of the setup.rul file:
export prototype TestFolderProperties(HWND);
Test compile to verify, and then create an Installscript custom action and put it in a sequence. Make "Return Processing" Synchronous (Ignores exit code) for the custom action. I put it right before InstallFinalize in the sequence, using immediate mode execution. Rebuild your release and run it. Try different release build configurations (msi with external source files, MSI only with compressed files inside, setup.exe launcher with external files, setup.exe with all files compressed inside, setup.exe with caching, setup.exe without caching, etc... the behavior might be different).
I have a project with lot of plugins and config files for them. Now I am doing a Visual Studio Setup Project for it.
I don't want to add each config file manually to the setup project, so I thought to do this:
create an empty zip file, say config.zip, and add it to the setup project
Add a pre-build action to zip all the config files into config.zip
Add a custom action that runs a vbs script that unzip config.zip to the right folder and deletes it.
The vbs script is the following:
sArchiveName = "Config.zip"
sLocation = "C:\Data\Configurations"
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oShell = CreateObject("Wscript.Shell")
oShell.Run """" & s7zLocation & "7z.exe"" x " & sLocation & "\" & sArchiveName & " -aoa -o" & sLocation, 1, True
'--- If I uncomment the following 2 lines,
'--- as I click on the shortcuts the installation rollbacks.
'--- If I leave them the shortcuts work fine.
'Set f = oFSO.GetFile(sLocation & "\" & sArchiveName)
'f.Delete True
My problem is that the shortcuts that I add in the programs menu causes the rollback of the installation. The reason is the deletion of config.zip at the end of the installation process. If I leave it everything works fine.
I have googled for a solution but cannot find anything, can someone help me?
This seems to be a self-repair problem.
Self-repair is a core Windows Installer feature which checks that your software is correctly installed whenever you launch it via an "advertised shortcut" (essentially a special type of shortcut that points to a Windows Installer feature and not directly to a file - see more information in the link provided).
Windows Installer tracks your zip file and discovers that it is missing, and hence correctly triggers a repair of your installation.
There is no good fix for this, because your deployment design is not sound. MSI is specifically designed to behave this way, and you can not disable this "self-repair" feature. Once you install the zip file it is tracked.
If you really want to understand self-repair there is a long answer here, but it is generally too detailed to understand without some prior idea of what it is and how it works. Still adding the link for reference: How can I determine what causes repeated Windows Installer self-repair?
The easiest way to deal with this is to install the config files properly via your MSI file, and remove the whole zip file. Will these config files be updated after installation, or will they be treated as read-only?
Visual Studio Setup Projects are very basic. The recommended way to create MSI files these days is by using the WiX toolkit. This is a new framework for creating MSI files (Windows Installer files), and it allows you to author the installation as an XML file that is then compiled into an MSI binary. It is a very elegant toolkit, but there is a learning curve.
Perhaps check this answer for more context on WiX: Wix generate single component id for entire tree. If you download and install the WiX toolkit and the Visual Studio plugin you will get new project types that allow the creation of a WiX XML package file.
Here is an answer on the "origin of WiX": Windows Installer and the creation of WiX. In essence the rationale behind its creation.
Those symptoms almost certainly mean that your custom action is failing, so the install will roll back. You'd need to post your VBScript for us to have a look.
A verbose log should show the place that the script is failing. Do an msiexec /I [path to msi file] /l*vx [path to a log file]
A common problem in VBScript custom actions is to use the WScript object, such as WScript.CreateObject. This will fail because the WScript object is provided when running in the WSH environment, but that's not happening during a call from Windows Installer.
In the script you posted, s7zLocation seems to be uninitialized.
Also, note that the code is being called from msixec.exe, running with the system account, and does not have any of the infrastructure that would see if you were running from an interactive user explorer environment (such as working directory). You need to specify the full path to all executables and files being used.
Using Installshield 2010 and Basic MSI project.
I have an exe that was previously installed by my installer. That exe needs to be running during an installer upgrade. Is there a way to guarantee that the installer won't try shutdown the process? Basically, I would like the behavior to be : If file doesn't exist, lay it down, otherwise ignore it.
I have made the exe a key file in a component and set it 'Never Overwrite' to true. Should this give me my desired behavior ?
Never Overwrite will be used by future installers to determine if the file will be overwritten or not by other MSI packages. Basically, this attribute should have been set for the installed EXE.
A good approach is to use a file search to determine if the EXE exists. The search property can then be used to condition the new component.
Windows Installer doesn't automatically close applications, but it does show a FilesInUse dialog which offers this option to the user.
In an InstallShield project I have a VBScript custom action that conditionally needs to execute a certain file packaged with the install.
Normally I get the current directory of a vbs using code such as
sCurPath = CreateObject("Scripting.FileSystemObject").GetAbsolutePathName(".")
Which, if it returned the location of the Setup.exe that initiated the install, should work.
However when running the install, the current path (on XP) is C:\Windows\system32 instead of the location of the Setup.exe file I was expecting.
Assuming the output of my InstallShield build looks like the following
Disk1
->Setup.exe
->ISSetupPrerequisites
-->Req1
-->Req2
-->...
->OtherReqs
-->ConditionallyRunMe.exe
How could I run "\OtherReqs\ConditionallyRunMe.exe" from a VBScript custom action?
Thanks!!
(Note: I realize there are ways to conditionally run exe files from withing InstallShield, but in this case the requirements are not supported by InstallShield - unless there is a way to use a VBScript custom action return value as a condition to run another file?)
After a lot of messing around, I got it (actually found it in the InstallShield manual, and not Google, go figure :))!
This line of VBScript does the trick
disk1Path = Session.Property("SETUPEXEDIR")
The line above points to where ever the setup exe file was, so from there it's trivial to run any exe included with your install media.
Other useful ones I found, which I'll past here for reference are
'points to app data\downloaded install directory
MsgBox Session.Property("SourceDir")
'where the software wants to install to on the users system
MsgBox Session.Property("INSTALLDIR")
Not sure why it's so hard to find a good reference on MSI Standard properties (even just a list). The closest I found was this, but not all of them work (and not specifically for InstallShield at all). If anyone finds a good link with documentation about MIS Standard properties and their description please add a link here, so no one has to waste as much time on this as I did :).