2013-07-29 17 views
10

WIX ToolsetのWindowsインストーラAPIにC#ラッパーを使用しています。 ProductInstallationクラスを使用して、インストールされた製品に関する情報(製品コードや製品名など)を取得します。例えばインストールされたアプリケーションのアップグレードコードをC#で見つけるにはどうすればよいですか?

  • 商品名 - "私のテストアプリケーション"
  • 商品コード - {F46BA620-C027-4E68-9069-5D5D4E1FF30A}
  • 製品バージョン - 1.4.0

内部的にこのラッパーはMsiGetProductInfo機能を使用します。残念ながら、この関数は製品のアップグレードコードを返しません。

C#を使用してインストールされたアプリケーションのアップグレードコードを取得するにはどうすればよいですか?

+0

のではなく、以下の提案として、レジストリを使用して、**私はこの答えで説明したように、あなたが** WMIを使用することを提案している:[**どのようにすることができますインストールされたMSIファイルのアップグレードコードを探しますか?**](https:// stackoverflow。com/questions/46637094/msiファイルがインストールされたアップグレードコードを探す方法/ 46637095#46637095)。これにより、適切なアップグレードコードが確実に取得され、変換や解釈が不要になります。 **実際のアップグレードコードを適切なフォーマットに戻します**。 –

答えて

22

アップグレードコードが次のレジストリの場所に格納されていることを発見しました。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes 

レジストリキー名はアップグレードコードであり、レジストリキー値nameは製品コードです。私は簡単にこれらの値を抽出することができますが、コードは別の形式で保存されます。赤い丸はフォーマットされたアップグレードコードを示し、青い丸はregedit.exeで表示したときのフォーマットされた製品コードを示しています。

Red circle is the formatted upgrade code, the blue circle the formatted product code

ハイフンはGuidからストリッピングされた後、文字列の反転のシリーズが行われます。最初の8文字が反転され、次に4文字、次に4文字、残りの文字列が2文字のセットで反転されます。通常、文字列を反転するときは、コントロールと特殊文字が正しく処理されるように注意する必要がありますが(see Jon Skeet's aricle here)、この場合はGuid文字列を扱うので、文字列が正しく逆転されると確信できます。

以下は、レジストリから既知の製品コードのアップグレードコードを抽出するために使用した完全なコードです。

internal static class RegistryHelper 
{ 
    private const string UpgradeCodeRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes"; 

    private static readonly int[] GuidRegistryFormatPattern = new[] { 8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2 }; 

    public static Guid? GetUpgradeCode(Guid productCode) 
    { 
     // Convert the product code to the format found in the registry 
     var productCodeSearchString = ConvertToRegistryFormat(productCode); 

     // Open the upgrade code registry key 
     var localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); 
     var upgradeCodeRegistryRoot = localMachine.OpenSubKey(UpgradeCodeRegistryKey); 

     if (upgradeCodeRegistryRoot == null) 
      return null; 

     // Iterate over each sub-key 
     foreach (var subKeyName in upgradeCodeRegistryRoot.GetSubKeyNames()) 
     { 
      var subkey = upgradeCodeRegistryRoot.OpenSubKey(subKeyName); 

      if (subkey == null) 
       continue; 

      // Check for a value containing the product code 
      if (subkey.GetValueNames().Any(s => s.IndexOf(productCodeSearchString, StringComparison.OrdinalIgnoreCase) >= 0)) 
      { 
       // Extract the name of the subkey from the qualified name 
       var formattedUpgradeCode = subkey.Name.Split('\\').LastOrDefault(); 

       // Convert it back to a Guid 
       return ConvertFromRegistryFormat(formattedUpgradeCode); 
      } 
     } 

     return null; 
    } 

    private static string ConvertToRegistryFormat(Guid productCode) 
    { 
     return Reverse(productCode, GuidRegistryFormatPattern); 
    } 

    private static Guid ConvertFromRegistryFormat(string upgradeCode) 
    { 
     if (upgradeCode == null || upgradeCode.Length != 32) 
      throw new FormatException("Product code was in an invalid format"); 

     upgradeCode = Reverse(upgradeCode, GuidRegistryFormatPattern); 

     return Guid.Parse(upgradeCode); 
    } 

    private static string Reverse(object value, params int[] pattern) 
    { 
     // Strip the hyphens 
     var inputString = value.ToString().Replace("-", ""); 

     var returnString = new StringBuilder(); 

     var index = 0; 

     // Iterate over the reversal pattern 
     foreach (var length in pattern) 
     { 
      // Reverse the sub-string and append it 
      returnString.Append(inputString.Substring(index, length).Reverse().ToArray()); 

      // Increment our posistion in the string 
      index += length; 
     } 

     return returnString.ToString(); 
    } 
} 
+0

上記のプログラムのAlexが対応する製品コードのNULLを戻しています。すべてのアップグレードコードが製品コードでそのアップグレードコードにエントリしていますか?製品コードからバックセットを削除しましたが、ハイフン( - )は削除していません。それでも動作していません。 – Keshav

+0

デバッグ後レジストリが特定の場所に存在することがわかりましたが、upgradeCodeRegistryRootがNULL値を取得しています – Keshav

+0

"レジストリキー名はアップグレードコードです"というのは間違っています!レジストリキーはアップグレードコードと同じではありません! –

4

InstallPackageクラスにはLocalPackageというプロパティがあります。これを使用して、C:\ Windows \ InstallerにキャッシュされたMSIデータベースを照会し、おそらく知りたいことがあるものを得ることができます。

+0

はい、正しいですが、信頼性が高くありません。 MSIパッケージがまだ存在する限り、役立ちます。いくつかのPCの簡単なテストで、私はMSIが存在しないことが非常に一般的であることを発見しました(テストされた593パッケージから27が欠落しているか〜5%です)。これらのテストでは、すべてのアップグレードコードがレジストリから引き続き入手できました。 –

+1

これらのMSIは常に存在する必要があります。そうしないと、修理、アンインストール、広告されたショートカットなどの問題が発生します。 http://blogs.msdn.com/b/sqlserverfaq/archive/2013/04/30/do-not-delete-files-from-the-windows-installer-folder.aspx –

+0

私は同意します。彼らはそこにいなければならない。残念ながら時にはそうではありません。私は、これがクライアントPCにインストールされる在庫スタイルのサービスのためのものであることを確かめて、より信頼性の高い方法が必要でした。 –

1

これは、UpgradeCodeからProductCodeを取得するための逆の方法です。誰かにとって役に立ちます。

using Microsoft.Win32; 
using System; 
using System.IO; 
using System.Linq; 
using System.Text; 

internal static class RegistryHelper 
{ 
    private const string UpgradeCodeRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes"; 

    private static readonly int[] GuidRegistryFormatPattern = new[] { 8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2 }; 


    public static Guid? GetProductCode(Guid upgradeCode) 
    { 
     // Convert the product code to the format found in the registry 
     var productCodeSearchString = ConvertToRegistryFormat(upgradeCode); 

     // Open the upgrade code registry key 
     var upgradeCodeRegistryRoot = GetRegistryKey(Path.Combine(UpgradeCodeRegistryKey, productCodeSearchString)); 

     if (upgradeCodeRegistryRoot == null) 
      return null; 

     var uninstallCode = upgradeCodeRegistryRoot.GetValueNames().FirstOrDefault(); 
     if (string.IsNullOrEmpty(uninstallCode)) 
     { 
      return null; 
     } 

     // Convert it back to a Guid 
     return ConvertFromRegistryFormat(uninstallCode); 
    } 





    private static string ConvertToRegistryFormat(Guid code) 
    { 
     return Reverse(code, GuidRegistryFormatPattern); 
    } 

    private static Guid ConvertFromRegistryFormat(string code) 
    { 
     if (code == null || code.Length != 32) 
      throw new FormatException("Product code was in an invalid format"); 

     code = Reverse(code, GuidRegistryFormatPattern); 

     return Guid.Parse(code); 
    } 

    private static string Reverse(object value, params int[] pattern) 
    { 
     // Strip the hyphens 
     var inputString = value.ToString().Replace("-", ""); 

     var returnString = new StringBuilder(); 

     var index = 0; 

     // Iterate over the reversal pattern 
     foreach (var length in pattern) 
     { 
      // Reverse the sub-string and append it 
      returnString.Append(inputString.Substring(index, length).Reverse().ToArray()); 

      // Increment our posistion in the string 
      index += length; 
     } 

     return returnString.ToString(); 
    } 

    static RegistryKey GetRegistryKey(string registryPath) 
    { 
     var hklm64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); 
     var registryKey64 = hklm64.OpenSubKey(registryPath); 
     if (((bool?)registryKey64?.GetValueNames()?.Any()).GetValueOrDefault()) 
     { 
      return registryKey64; 
     } 

     var hklm32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); 
     return hklm32.OpenSubKey(registryPath); 
    } 
} 
+2

@Alex Wieseコードがうまく動作します。このコードはありません。ただし、指定されたUpgradeCodeのProductCodeを検索することによって反対のルックアップを実行します。 – harlam357

+0

@ harlam357コメントありがとうございます。あなたが正しいです。私は受け入れられた答えを補完する答えを更新しました。 –

0

また、ヘルパーは、.Net3.5 32ビットアプリケーションでも動作するように変更されています。 .net 3.5は、32ビットと64ビットのエントリの間でレジストリが分割されていることを認識していないため、特別な扱いが必要です。私の解決策は、To64BitPathを使って64ビットの部分をブラウズするだけです。そのためDllImportsを使用しています偉大なチュートリアルもあります:https://www.rhyous.com/2011/01/24/how-read-the-64-bit-registry-from-a-32-bit-application-or-vice-versa/

class RegistryHelper 
{ 
    private const string UpgradeCodeRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes"; 
    private const string UninstallRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; 

    private static readonly int[] GuidRegistryFormatPattern = new[] { 8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2 }; 



    public static string To64BitPath(string path) 
    { 
     return path.Replace("SOFTWARE\\Microsoft", "SOFTWARE\\WOW6432Node\\Microsoft"); 
    } 

    private static RegistryKey GetLocalMachineRegistryKey(string path) 
    { 
     return RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, string.Empty).OpenSubKey(path); 
    } 

    public static IEnumerable<Guid> GetUpgradeCodes() 
    { 
     var list = new List<Guid>(); 

     var key = GetRegistryKey(UpgradeCodeRegistryKey); 
     if (key != null) 
     { 
      list.AddRange(key.GetSubKeyNames().Select(ConvertFromRegistryFormat)); 
     } 

     return list; 
    } 

    public static Guid? GetProductCode(Guid upgradeCode) 
    { 
     // Convert the product upgradeCode to the format found in the registry 
     var productCodeSearchString = ConvertToRegistryFormat(upgradeCode); 

     // Open the upgradeCode upgradeCode registry key 
     var upgradeCodeRegistryRoot = GetRegistryKey(Path.Combine(UpgradeCodeRegistryKey, productCodeSearchString)); 

     if (upgradeCodeRegistryRoot == null) 
      return null; 

     var uninstallCode = upgradeCodeRegistryRoot.GetValueNames().FirstOrDefault(); 
     if (string.IsNullOrEmpty(uninstallCode)) 
     { 
      return null; 
     } 

     // Convert it back to a Guid 
     return ConvertFromRegistryFormat(uninstallCode); 
    } 

    public static string ConvertToRegistryFormat(Guid code) 
    { 
     return Reverse(code, GuidRegistryFormatPattern); 
    } 

    private static Guid ConvertFromRegistryFormat(string code) 
    { 
     if (code == null || code.Length != 32) 
      throw new FormatException("Product upgradeCode was in an invalid format"); 

     code = Reverse(code, GuidRegistryFormatPattern); 

     return new Guid(code); 
    } 

    private static string Reverse(object value, params int[] pattern) 
    { 
     // Strip the hyphens 
     var inputString = value.ToString().Replace("-", ""); 

     var returnString = new StringBuilder(); 

     var index = 0; 

     // Iterate over the reversal pattern 
     foreach (var length in pattern) 
     { 
      // Reverse the sub-string and append it 
      returnString.Append(inputString.Substring(index, length).Reverse().ToArray()); 

      // Increment our posistion in the string 
      index += length; 
     } 

     return returnString.ToString(); 
    } 

    static RegistryKey GetRegistryKey(string registryPath) 
    { 
     var registryKey64 = GetLocalMachineRegistryKey(To64BitPath(registryPath)); 
     if (((bool?)registryKey64?.GetValueNames()?.Any()).GetValueOrDefault()) 
     { 
      return registryKey64; 
     } 

     return GetLocalMachineRegistryKey(registryPath); 
    } 


    public static Guid? GetUpgradeCode(Guid productCode) 
    { 
     var productCodeSearchString = ConvertToRegistryFormat(productCode); 
     var upgradeCodeRegistryRoot = GetRegistryKey(UpgradeCodeRegistryKey); 

     if (upgradeCodeRegistryRoot == null) 
     { 
      return null; 
     } 

     // Iterate over each sub-key 
     foreach (var subKeyName in upgradeCodeRegistryRoot.GetSubKeyNames()) 
     { 
      var subkey = upgradeCodeRegistryRoot.OpenSubKey(subKeyName); 

      if (subkey == null) 
       continue; 

      // Check for a value containing the product upgradeCode 
      if (subkey.GetValueNames().Any(s => s.IndexOf(productCodeSearchString, StringComparison.OrdinalIgnoreCase) >= 0)) 
      { 
       // Extract the name of the subkey from the qualified name 
       var formattedUpgradeCode = subkey.Name.Split('\\').LastOrDefault(); 

       // Convert it back to a Guid 
       return ConvertFromRegistryFormat(formattedUpgradeCode); 
      } 
     } 

     return null; 
    } 
} 
関連する問題