I found a nice Powershell function on GitHub which uses the WindowsInstaller.Installer COM object to query applications installed and lists all the details and properties quite beautifully. However, I don't want to use Powershell I want to create an exe.
I don't want to search the registry and I don't want to use WMI. I want to use the same exact method used in the powershell script and another vbscript I found. There does exist a COM object called WindowsInstaller.Installer. It definitely exists and yet for some reason I can't find a single example of how it would be accessed after importing the msi.dll in Visual Studio using C#.
Does anyone know the answer to this question?
In Visual Studio, the WindowsInstaller.Installer reference from adding the COM reference for WindowsInstaller is just a type and nothing more. It contains no method called "GetType" and trying to convert this PowerShell to C# is not working right.
I also don't know what @{}
means but I guess it means Hashtable.
My sad attempt at forcing the situation below:
private void Form1_Load(object sender, EventArgs e)
{
Type installerType = Type.GetType("WindowsInstaller.Installer");
Installer installerObj = (Installer)Activator.CreateInstance(installerType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
var type = installer.GetType();
var Products = type.InvokeMember("Products", System.Reflection.BindingFlags.GetProperty, null, installer, null) as IEnumerable<object>;
foreach (var product in Products)
{
Hashtable hash = new Hashtable();
hash.Add("ProductCode", product);
string[] Attributes = { "Language", "ProductName", "PackageCode", "Transforms", "AssignmentType", "PackageName", "InstalledProductName", "VersionString", "RegCompany", "RegOwner", "ProductID", "ProductIcon", "InstallLocation", "InstallSource", "InstallDate", "Publisher", "LocalPackage", "HelpLink", "HelpTelephone", "URLInfoAbout", "URLUpdateInfo" };
foreach (var attribute in Attributes)
{
object[] thing = { product, attribute };
var details = type.InvokeMember("ProductInfo", System.Reflection.BindingFlags.GetProperty, null, installer, thing);
hash.Add(attribute, details);
}
new ??????????
}
}
You're right about @{}
, its a new hashtable. I think for what you want you can just create a list and every product that gets iterated through to collect properties you can add it to that list
Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installerObj = (Installer)Activator.CreateInstance(installerType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
var Products = installerObj.Products;
List<Hashtable> ProductCollection = new List<Hashtable>();
foreach (var product in Products)
{
Hashtable hash = new Hashtable();
hash.Add("ProductCode", product);
string[] Attributes = { "Language", "ProductName", "PackageCode", "Transforms", "AssignmentType", "PackageName", "InstalledProductName", "VersionString", "RegCompany", "RegOwner", "ProductID", "ProductIcon", "InstallLocation", "InstallSource", "InstallDate", "Publisher", "LocalPackage", "HelpLink", "HelpTelephone", "URLInfoAbout", "URLUpdateInfo" };
foreach (var attribute in Attributes)
{
try
{
var details = installer.ProductInfo[product.ToString(), attribute.ToString()];
hash.Add(attribute, details);
}
catch
{
}
}
ProductCollection.Add(hash);
}
Now just reference ProductCollection to get your product details. If you want to go a step further you can create a class for each MSI and have your process create an object for each product.
public class MSIInfo
{
public string ProductCode { get; set; }
public string Language { get; set; }
public string ProductName { get; set; }
public string PackageCode { get; set; }
public string Transforms { get; set; }
public string AssignmentType { get; set; }
public string PackageName { get; set; }
public string InstalledProductName { get; set; }
public string VersionString { get; set; }
public string RegCompany { get; set; }
public string RegOwner { get; set; }
public string ProductID { get; set; }
public string ProductIcon { get; set; }
public string InstallLocation { get; set; }
public string InstallSource { get; set; }
public string InstallDate { get; set; }
public string Publisher { get; set; }
public string LocalPackage { get; set; }
public string HelpLink { get; set; }
public string HelpTelephone { get; set; }
public string URLInfoAbout { get; set; }
public string URLUpdateInfo { get; set; }
public override string ToString()
{
return $"{ProductName} - {ProductCode}";
}
public static IEnumerable<MSIInfo> GetProducts()
{
Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installerObj = (Installer)Activator.CreateInstance(installerType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
var Products = installerObj.Products;
List<MSIInfo> ProductCollection = new List<MSIInfo>();
foreach (var product in Products)
{
MSIInfo msi = new MSIInfo();
msi.ProductCode = product.ToString();
foreach (var property in msi.GetType()?.GetProperties())
{
try
{
if (property.Name != "ProductCode")
{
string val = installer.ProductInfo[product.ToString(), property.Name];
property.SetValue(msi, val);
}
}
catch (System.Runtime.InteropServices.COMException)
{
}
}
ProductCollection.Add(msi);
}
return ProductCollection;
}
}
Once you have that class you can get the collection from your code like this
var Products = MSIInfo.GetProducts();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With