5

I'm automating unattended retrieval & installation of specified .msi packages with a powershell script but if the command is invoked with syntax errors the msiexec will wait indefinitely for an OK click on its help display despite the presence of /quiet and/or /passive.

At the moment I'm invoking it with:

(start-process -FilePath "msiexec" -ArgumentList "/i <path_to_package> /quiet /passive" -PassThru -Wait).ExitCode

Is there any way of disabling the msiexec help display?

snoweagle
  • 121
  • 1
  • 2
  • 8
  • Why would there be syntax errors? You're calling the same command, is it an issue of a space ' ' in your path name? we can workaround that. – MDMoore313 Feb 24 '14 at 21:41
  • 1
    The -ArgumentList string is being populated by reading in an entry from a csv file against a given package file. – snoweagle Feb 24 '14 at 22:12

4 Answers4

5

There is no way to disable this behavior for an msiexec command containing a syntax error. You could wrap the command into something like the following. It uses .NET Automation to look for the "usage" window and deal with it in-script.

Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes

# Note the invalid argument '/badswitch'
$mse = Start-Process -FilePath 'msiexec' -ArgumentList "/i package.msi /badswitch /quiet /passive" -PassThru

# Let msiexec at least get off the ground
[void] $mse.WaitForInputIdle()

# Create an AutomationElement from $mse's handle
$mseAuto = [Windows.Automation.AutomationElement]::FromHandle($mse.MainWindowHandle)

# A PropertyCondition for findAll()
$pane = New-Object Windows.Automation.PropertyCondition -ArgumentList (
    [Windows.Automation.AutomationElement]::ControlTypeProperty,
    [Windows.Automation.ControlType]::Pane
)

# Search for a child $pane element.
$findResult = $mseAuto.FindFirst(
    [System.Windows.Automation.TreeScope]::Children,
    $pane
)

# If there's a pane element in $mseAuto, and it contains "usage" string, it's an msiexec syntax issue, so close $mse's window.
if ( $findResult.Current.Name -match 'msiexec /Option <Required Parameter>' ) {
    [void] $mse.CloseMainWindow()
} else {
    # You should put something more sane here to handle waiting for "good" installs to complete.
    $mse.WaitForExit()    
}

$mse.ExitCode

This too has problems. With /quiet a progress dialog is still displayed during install. You may consider using /qn instead would hide all msiexec UI elements. As well the MSI may trigger other errors, unhandled, which would pause execution infidelity. Perhaps include a time out value? And what of external process launched from the CustomAction table? Sorry, I'm close to rambling now...

jscott
  • 24,204
  • 8
  • 77
  • 99
  • I believe all these GUI problems could be avoided by going via the Windows Installer API as described in my answer below. How do you run this script of yours? Via PowerShell? I am in deployment, right between development and system administration so I tend to avoid GUI whenever I can. – Stein Åsmul May 16 '14 at 14:41
  • @Glytzhkof Yes, my answer is using Powershell, as is the the OP's question. Your answer, +1 btw, is quite clever, I've not worked with the API directly. I would imagine if the OP *requires* using Powershell, your C# example could be re-written in PS. – jscott May 16 '14 at 14:45
  • I would think so yes. Unfortunately I tend to use C# or VBScript and not PowerShell. I guess VBScript is very outdated. I am not familiar with calling .NET dll's from PowerShell. – Stein Åsmul May 16 '14 at 15:17
3

Sadly, I think the only way to avoid the help display is to...

...not make any typos/syntax errors.

I wish I had a better answer for you, but...

Katherine Villyard
  • 18,510
  • 4
  • 36
  • 59
3

I would just avoid going via msiexec.exe altogether. This is possible by going via the Windows Installer API using scripts or code.

You can go via COM automation using VBScript / VBA / VB, or using DTF which is a .NET wrapper for the Windows Installer API that's easier to work with from .NET languages such as C#.

You could even go directly via C++ to the raw Win32 API calls, but that's just a waste of time since you have both COM and .NET equivalents that call the raw Win32 API as part of their operation.


Solution 1: VBScripts and COM Automation

If using automation is an option you should be able to go via the Windows Installer COM automation and automate installation / uninstallation that way. Here is a VBScript you can put in a file and run (update the MSI path name obviously):

Const msiUILevelEndDialog = 128

Set msi = CreateObject("WindowsInstaller.Installer")
msi.UILevel = msiUILevelEndDialog
msi.InstallProduct( "C:\msifile.msi")
Set msi = Nothing

Solution 2: DTF - Deployment Tools Foundation - .NET

DTF (Deployment Tools Foundation) is essentially a .NET wrapper for the Windows Installer API - it features such a powerful collection of .NET assemblies to work directly with aspects of Windows Installer, that I just want to add it here for reference for those who search for a solution with a lot of fine-grained deployment control to solve their administration problem. Very simple code inside a C# application provides full control of the installation process. Here is a rough mock-up:

using Microsoft.Deployment.WindowsInstaller.Installer;

Installer.SetInternalUI(InstallUIOptions.Silent);
Installer.InstallProduct(msiFilename, "ACTION=INSTALL ALLUSERS=1");

You can get hold of DTF via the WIX toolkit - which is an overall solution for creating MSI files from XML source files. You will find excellent documentation in DTF.chm and DTFAPI.chm and the actual files are in the main installation folder. The two last ones are generally the ones you need:

  • Microsoft.Deployment.Compression.dll - Framework for archive packing and unpacking.
  • Microsoft.Deployment.Compression.Cab.dll - Implements cabinet archive packing and unpacking.
  • Microsoft.Deployment.Resources.dll - Classes for reading and writing resource data in executable files.
  • Microsoft.Deployment.WindowsInstaller.dll - Complete class library for the Windows Installer APIs.
  • Microsoft.Deployment.WindowsInstaller.Package.dll - Extended classes for working with Windows Installer installation and patch packages.

Just create a C# project, reference these files, and code your own deployment application with whatever control you desire and need. I am not set up with the tools for DTF at the moment, but see this sample for a general idea of how a C# program would work.


Solution 3: C++ Installer Functions

I just figured I'd add this - it is largely irrelevant for system administrators I believe, but it might help to understand the MSI technology better. Besides COM automation, there is also a Win32 API with functions accessible from C++. Much better performance of course (the COM automation obviously calls these Win32 functions under the hood - COM is just a wrapper on top of these "real" functions of course).

I don't have any C++ samples available for this right now, but here is the SDK documentation: Windows Installer Reference. And a direct link to the list of actual installer functions. This list of functions should give you a quick idea of what it is like to use this technology.

UPDATE: I added a C++ snippet sample in this stackoverflow answer on different ways to uninstall an MSI package (section 14 at the bottom of the answer).

System administrators wouldn't use this option, but commercial tools would in order to access the system's MSI database (stored in a few locations in the registry and with a cache folder on disk - %SystemRoot%\Installer - and some "working folders"). For example your SCCM or similar deployment system will use them "under the hood".


Solution 4: WMI

Just adding that there are a few WMI classes that can be used to automate and query MSI. Win32_Product and a couple of other ones.

Stein Åsmul
  • 2,566
  • 4
  • 25
  • 38
0

Have you tried the /qn switch? it should suppress all user interface prompts.

http://technet.microsoft.com/en-us/library/cc759262(v=ws.10).aspx#BKMK_SetUI

Couradical
  • 376
  • 1
  • 6
  • You will still see the "usage" dialog with `/qn` if you make a syntax error in your command line. e.g `msiexec /i some.msi typo /qn` – jscott Feb 24 '14 at 20:51
  • 1
    Just tested this, and what do you know, you're right. What about building a cscript shell in VBScript, then executing from there? – Couradical Feb 24 '14 at 21:07