Creating a SMS Package from a MSI File using Microsoft's SMS.DLL






4.20/5 (3 votes)
Create a Systems Management Server Package from an MSI file using the SMS toolkit.
Introduction
Normally Systems Management Server(SMS) Packages are created by using the SMS AdminstratorConsole. For automation purposes, sometimes a programmatic way is needed to create a package from a *.msi Installer file. The Systems Management Server 2003 Software Development Kit (see the Microsoft® SMS SDK) only supports the creation of an SMS package from a *.pdf file. To create a package from a *.msi file, a workaround is needed.
The idea is quite simple: reading the attributes out of the MSI database and adding them to a manually created SMS package.
To use the code in the demo project, just include a reference to msi.dll (usually found in system32). If you include it with Visual Studio (Project + Add Reference, COM tab, select "Microsoft Windows Installer Object Library") it generates a wrapper called Interop.WindowsInstaller.dll.
You also need a reference to Microsoft.SystemsManagementServer.Automation.dll which can be downloaded here.
If you download both files (Interop.WindowsInstaller.dll and Microsoft.SystemsManagementServer.Automation.dll) and copy them into the lib folder of the sample project, all references are OK because I set the references to this folder.
Using the Code
First we have to determine the attributes of the MSI database. To read the MSI database, we need to import the Microsoft® Windows Installer (see the Microsoft® Windows Installer Reference). It is defined as a COM import and has to be declared in the class:
[System.Runtime.InteropServices.ComImport(),
System.Runtime.InteropServices.Guid
("000C1090-0000-0000-C000-000000000046")]
class Installer { }
Now we are able to query the MSI file.
As described in other tutorials, we do this in these steps:
- Open the MSI database/file
- Query the database
- Store the Attributes in a
Hashtable
for further use
// snippet of demo projects' "DetermineMsiData()" method
// get the MSI file
FileInfo msiFile = new FileInfo("c:\\MyPath\\MyInstallerFile.msi")
// Hashtable to store the attributes of the MSI
Hashtable msiData = new Hashtable();
// open MSI database
WindowsInstaller.Installer inst = (WindowsInstaller.Installer)new Installer();
Database instDb = inst.OpenDatabase(msiFile.FullName,
WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabaseModeReadOnly);
// query the database
WindowsInstaller.View view = instDb.OpenView
("Select `Property`,`Value` FROM `Property`");
view.Execute(null);
Record record = view.Fetch();
//fetch the data and store it in a hash table
while (record != null)
{
//debug
Console.WriteLine(record.get_StringData(1)+"|"+record.get_StringData(2));
//add data to hash table
msiData.Add(record.get_StringData(1), record.get_StringData(2));
record = view.Fetch();
}
// close the database
view.Close();
The next thing to do is to build the new SMS package with the data stored in the msiData
Hashtable
. This can be done with the following steps:
- Connect to the SMS server
- Create a new SMS package
- Add the attributes stored in
msiData
// snippet of demo projects' "createPackageFromMsi()" method
SMSProvider smsProvider = null
// connect to the SMS server
try
{
String smsServer="\\\\root\\sms\\site_";
smsProvider = new SMSProvider(smsServer, "myUserName", "myPassword");
}
catch(Exception ex)
{
Console.Error.WriteLine("Could not connect to the SMS Provider.",ex);
MessageBox.Show("Could not connect to the SMS Provider.");
}
// create a new package if a product name is given in the msiData Hashtable
SMSPackage smsPkg;
if(msiData.ContainsKey("ProductName"))
{
smsPkg = smsProvider.Packages.Create(msiData["ProductName"].ToString());
}
else
{
Console.Error.WriteLine("msi contains no product name.");
return null;
}
// add the attributes of the MSI to the new created PDF
if(msiData.ContainsKey("ProductLanguage"))
{
smsPkg.Language = msiData["ProductLanguage"].ToString();
Console.WriteLine("set smsPkg.Language="+
msiData["ProductLanguage"].ToString());
}
// .... add other attributes (Manufacturer, ProductName, Productversion)
// the same way like ProductLanguage. Take a look at the demo project.
// setting the filename and the SMS package source path eg.: c:\temp\sourcen
smsPkg.MIFFileName = msiFile.Name;
Console.WriteLine("set smsPkg.MIFFileName="+ msiFile.Name);
smsPkg.PkgSourcePath = "c:\\temp\\sourcen";
Console.WriteLine("set smsPkg.PkgSourcePath="+ smsPkg.PkgSourcePath);
Now we have to create the install and uninstall programs. Despite a *.pdf file, the *.msi file does not contain the install or uninstall programs.
Therefore we will create them manually.
// also part of the demo projects' "createPackageFromMsi()" method
//create programs: install/uninstall
String name = "Installation (Service)";
String cmd = "msiexec.exe /q ALLUSERS=2 /i \""+msiFile.Name+"\"";
Console.WriteLine("creating install program: Name= "+name+" CMD= "+cmd);
SMSProgram inst = smsPkg.Programs.Create(name,cmd);
inst.RunWithAdminRights = true;
inst.UserRequirements = UserRequirementsFlags.RunWhether
gedOnOrOff;
inst.Save();
name = "Uninstall (Service)";
cmd = "msiexec.exe /q /x \""+msiFile.Name+"\"";
Console.WriteLine("creating install program: Name= "+name+" CMD= "+cmd);
SMSProgram deInst = smsPkg.Programs.Create(name,cmd);
deInst.RunWithAdminRights = true;
deInst.UserRequirements = UserRequirementsFlags.RunWhetherLoggedOnOrOff;
deInst.Save();
Last but not least we have to save the SMS package. The Save
method stores the changes we made in the SMS server.
// also part of the demo projects "createPackageFromMsi()" method
// save the attributes set to the SMS package
smsPkg.Save();
Console.WriteLine("SMS package with id: "+smsPkg.PackageID+" created.")
This is quite a simple solution, but works fine. The Microsoft SMS toolkit is a big help while developing custom SMS solutions. Unfortunately the documentation is not so good and the support for *.msi files is missing.
Points of Interest
If you want to determine the Siteservername out of the registry, you can find it under:
- Microsoft.Win32.Registry.CurrentUser Software\Microsoft\SMS\Admin UI\MRU\Site1\ServerName
or - Microsoft.Win32.Registry.LocalMachine Software\Microsoft\SMS\AdminUI\Connection\Server
First try to get the Servername out of HKCU, then from HKLM.
The demo project demonstrates how to determine the server out of the registry and how to connect to it. If you have questions about the programmatic use of the SMS Server, please feel free to contact me.
References
Please feel free to browse the following reference material: