2011-06-14 46 views
6

Tengo un programa de C# donde tengo que obtener el código de producto de un msi instalado. Solo tengo el nombre msi como entrada. ¿Se puede hacer esto programáticamente?Obtener código de producto de Msi instalado

+0

¿Utilizó Regedit para buscar el registro en una máquina con el software instalado? –

+0

¿alguna solución final con código fuente completo al respecto? – Kiquenet

+0

** [DTF (Deployment Tools Foundation)] (https://serverfault.com/questions/577969/is-it-possible-to-disable-msiexec-help-gui/596519#596519) ** es un grupo de Las clases .NET se entregan con la descarga de WiX, que le proporcionará una forma fácil de acceder y modificar archivos MSI sin interoperabilidad COM ni clunk. Vea la información en el enlace provisto para un poco más de contexto. ** [Descargar WiX] (http://wixtoolset.org/) ** para obtener los componentes DTF. –

Respuesta

4

Este es el código que utiliza para obtener la UninstallString de cualquier MSI.

private string GetUninstallString(string msiName) 
{ 
    Utility.WriteLog("Entered GetUninstallString(msiName) - Parameters: msiName = " + msiName); 
    string uninstallString = string.Empty; 
    try 
    { 
     string path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products"; 

     RegistryKey key = Registry.LocalMachine.OpenSubKey(path); 

     foreach (string tempKeyName in key.GetSubKeyNames()) 
     { 
      RegistryKey tempKey = key.OpenSubKey(tempKeyName + "\\InstallProperties"); 
      if (tempKey != null) 
      { 
       if (string.Equals(Convert.ToString(tempKey.GetValue("DisplayName")), msiName, StringComparison.CurrentCultureIgnoreCase)) 
       { 
        uninstallString = Convert.ToString(tempKey.GetValue("UninstallString")); 
        uninstallString = uninstallString.Replace("/I", "/X"); 
        uninstallString = uninstallString.Replace("MsiExec.exe", "").Trim(); 
        uninstallString += " /quiet /qn"; 
        break; 
       } 
      } 
     } 

     return uninstallString; 
    } 
    catch (Exception ex) 
    { 
     throw new ApplicationException(ex.Message); 
    } 
} 

Esto le dará un resultado como este:

MsiExec.exe /I{6BB09011-69E1-472F-ACAD-FA0E7DA3E2CE} 

De esta cadena, se puede tomar la subcadena dentro de las llaves {}, que serán 6BB09011-69E1-472F-ACAD-FA0E7DA3E2CE. Espero que este sea el código de producto.

+2

Esto fallará si usa este código en una máquina de 64 bits y su aplicación es de 32 bits, ya que lo redireccionará al 'Wow6432Node' en el software. Use 'RegistryKey.OpenBaseKey (RegistryHive.LocalMachine, RegistryView.Registry64) .OpenSubKey (path)' en su lugar. – modiX

6

¿Las respuestas a this question ayuda? Quieren obtener el nombre del producto, pero tal vez también funcione para el código del producto.

EDITAR
Si usted no tiene el archivo MSI en sí para acceder a la base de datos (como se sugiere en el enlace anterior para la otra pregunta), puede intentar buscar la ruta de acceso del Registro siguiente para el nombre de su MSI archivo:

HKEY_CLASSES_ROOT\Installer\Products\*\SourceList 

Hay muchas entradas bajo la rama Products. Cada uno de ellos es una clave de producto. Cada rama debe contener el nodo SourceList, que a su vez debe contener el valor PackageName. Ese valor tiene el nombre del archivo MSI.

Así que lo que haría es:

for each key in Products 
{ 
    open SourceList subkey 
    read PackageName value 
    if name equals my msi file name 
    { 
     return key-name formatted as GUID 
    } 
} 
+0

ok: -1 porque ...? –

+0

Sin embargo, el nombre de la clave es en realidad una versión codificada del código del producto. Consulte aquí para obtener ayuda al pasar de un código de producto al formato de clave de registro (http://stackoverflow.com/questions/1881643/msi-product-code-from-product-id/1881818#1881818). Solo tendrá que invertir eso para descifrar el nombre de la tecla. –

1

Este código obtiene el código de producto directamente de un archivo MSI. Esto permite leer el código sin instalar el archivo.

class MsiHandle : SafeHandleMinusOneIsInvalid 
{ 
    public MsiHandle() 
     : base(true) 
    { } 

    protected override bool ReleaseHandle() 
    { 
     return NativeMethods.MsiCloseHandle(handle) == 0; 
    } 
} 

class NativeMethods 
{ 
    const string MsiDll = "Msi.dll"; 

    [DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)] 
    public extern static uint MsiOpenPackageW(string szPackagePath, out MsiHandle product); 

    [DllImport(MsiDll, ExactSpelling=true)] 
    public extern static uint MsiCloseHandle(IntPtr hAny); 

    [DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)] 
    static extern uint MsiGetProductPropertyW(MsiHandle hProduct, string szProperty, StringBuilder value, ref int length); 


    [DllImport(MsiDll, ExactSpelling = true)] 
    public static extern int MsiSetInternalUI(int value, IntPtr hwnd); 

    public static uint MsiGetProductProperty(MsiHandle hProduct, string szProperty, out string value) 
    { 
     StringBuilder sb = new StringBuilder(1024); 
     int length = sb.Capacity; 
     uint err; 
     value = null; 
     if(0 == (err = MsiGetProductPropertyW(hProduct, szProperty, sb, ref length))) 
     { 
      sb.Length = length; 
      value = sb.ToString(); 
      return 0; 
     } 

     return err; 
    } 
} 

static class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static int Main(string[] args) 
    { 
     string msiFile = args[0]; 

     NativeMethods.MsiSetInternalUI(2, IntPtr.Zero); // Hide all UI. Without this you get a MSI dialog 

     MsiHandle msi; 
     uint err; 
     if (0 != (err = NativeMethods.MsiOpenPackageW(args[0], out msi))) 
     { 
      Console.Error.WriteLine("Can't open MSI, error {0}", err); 
      return 1; 
     } 

     // Strings available in all MSIs 
     string productCode; 
     using (msi) 
     { 
      if (0 != NativeMethods.MsiGetProductProperty(msi, "ProductCode", out productCode)) 
       throw new InvalidOperationException("Can't obtain product code"); 

      Console.WriteLine(productCode); 
      return 0; 
     } 
    } 
} 

Ejemplo completo en Subversion en http://ankhsvn.open.collab.net/svn/ankhsvn/trunk/src/tools/Ankh.Chocolatey/ usamos nombre de usuario 'invitado' y sin contraseña.

2

Existe la forma más rápida y sencilla: utilizar WMI con cadena de consulta condicional.

public string GetProductCode(string productName) 
    { 
     string query = string.Format("select * from Win32_Product where Name='{0}'", productName); 
     using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query)) 
     { 
      foreach (ManagementObject product in searcher.Get()) 
       return product["IdentifyingNumber"].ToString(); 
     } 
     return null; 
    } 
+1

este procedimiento es bueno, pero consumió mucho tiempo –

0
private static bool GetUninstallString(string ProductName) 
{ 
    try 
    { 
     RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64); 
     var key = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") ?? 
      localKey.OpenSubKey(
       @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"); 

     if (key == null) 
       return false; 

     return key.GetSubKeyNames() 
       .Select(keyName => key.OpenSubKey(keyName)) 
       .Select(subkey => subkey.GetValue("DisplayName") as string) 
       .Any(displayName => displayName != null && displayName.Contains(ProductName)); 
    } 
    catch 
    { 
     // Log message     
     return false; 
    } 
} 

Esto es muy útil para cadena de búsqueda por nombre del producto

Cuestiones relacionadas