2

我的问题的概要:

是否可以在 VS2010 的 Windows 安装项目的注册表屏幕中使用您自己的自定义变量(您可以使用 [TARGETDIR] 的方式)?具体来说,我需要在注册表中存储我的程序集的强名称和程序集版本,以便在没有安装用户具有管理员权限的机器上注册 COM 对象。

我已经尝试过使用自定义操作,如果可能的话,我宁愿不要继续走这条路。

以下是具体细节,以及我尝试过的内容:

最近,我的雇主开始盲目地从他们的机器上删除所有员工的管理员权限。

我创建了一个 COM 公开的 C# 类,我一直在我的一些工作站上使用它,它不再能够注册,因为我不再拥有 HKEY_CLASSES_ROOT 下的适当权限。

通过谷歌搜索,我发现了如何在 HKCU* 下注册所有适当的密钥,但现在我想在我的部署项目中实现这一点。

我了解如何在 Windows 安装程序中使用注册表屏幕,但是需要存储自定义键/值(安装文件夹、程序集强名称、版本)。

我可以使用自定义操作,但理想情况下,我希望 Windows 安装程序管理我的注册表设置,因为 (a) 它比我在卸载时自动删除所有正确的键/值要好,(b) 在安装期间,注册表更改是事务性的并在安装错误时回滚,并且 (c) 注册表项安装/删除/事务的逻辑已经由 Microsoft 编写,我不必自己重写它。

该项目直到今天还在 VS2008 中,但我刚刚将其升级到 VS2010,所以可能在 2008 年和 2010 年之间发生了一些变化,可能允许这种行为。

那么,除了使用自定义操作之外,有没有更好的方法来做到这一点?

编辑:我找到了这个答案,这似乎表明您可以在安装项目中访问 Windows 安装“注册表”表。不过,我不确定如何访问它。过去,我似乎记得您可以通过特殊的外部工具 (Orca) 访问 MSI 数据库,但我不知道您是否可以在安装项目中访问这些表。

编辑 2:啊,我可能正在做某事;也许是构建后事件:


* 运行 RegAsm 两次 - 一次使用 /codebase,一次不使用;两次都使用 /regfile 选项。然后将两个文件合并在一起(删除重复项),并将所有 HKCR 引用替换为 HKCU\Software\Classes。

4

1 回答 1

1

是的,这可以做到*。

首先,创建一个控制台可执行文件,它将作为 Windows 安装程序项目的构建后事件的一部分运行。这会修改Registry已由 VS2010 构建的 MSI 文件中的表。

注意:您必须在 COM 下添加对“Microsoft Windows Installer Object Library”的引用,才能编译以下代码。

using System;
using WindowsInstaller;
using System.Runtime.InteropServices;
using System.Reflection;

namespace Post_Setup_Scripting
{
    class Program
    {

        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("Incorrect args.");
                return;
            }

            //arg 1 - path to MSI
            string PathToMSI = args[0];
            //arg 2 - path to assembly
            string PathToAssembly = args[1];

            Type InstallerType;
            WindowsInstaller.Installer Installer;
            InstallerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
            Installer = (WindowsInstaller.Installer)Activator.CreateInstance(InstallerType);

            Assembly Assembly = Assembly.LoadFrom(PathToAssembly);
            string AssemblyStrongName = Assembly.GetName().FullName;
            string AssemblyVersion = Assembly.GetName().Version.ToString();

            string SQL = "SELECT `Key`, `Name`, `Value` FROM `Registry`";
            WindowsInstaller.Database Db = Installer.OpenDatabase(PathToMSI, WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabaseModeDirect);
            WindowsInstaller.View View = Db.OpenView(SQL);
            View.Execute();
            WindowsInstaller.Record Rec = View.Fetch();
            while (Rec != null)
            {
                for (int c = 0; c <= Rec.FieldCount; c++)
                {
                    string Column = Rec.get_StringData(c);
                    Column = Column.Replace("[AssemblyVersion]", AssemblyVersion);
                    Column = Column.Replace("[AssemblyStrongName]", AssemblyStrongName);
                    Rec.set_StringData(c, Column);
                    View.Modify(MsiViewModify.msiViewModifyReplace, Rec);
                    Console.Write("{0}\t", Column);
                    Db.Commit();
                }
                Console.WriteLine();
                Rec = View.Fetch();
            }
            View.Close();

            GC.Collect();
            Marshal.FinalReleaseComObject(Installer);

            Console.ReadLine();
        }
    }
}

我们将在 Windows 安装程序注册表屏幕中使用的“变量”在上述代码的这些行中被替换;这可以适应任何必要的项目。

string Column = Rec.get_StringData(c);
Column = Column.Replace("[AssemblyVersion]", AssemblyVersion);
Column = Column.Replace("[AssemblyStrongName]", AssemblyStrongName);

其次,创建一个 .reg 文件,其中包含您要在安装时创建的注册表项。在上面的代码中,我们在构建后修改 MSI 数据库,将 [AssemblyVersion] 的所有实例替换为程序集版本,并将 [AssemblyStrongName] 替换为程序集的强名称。

[HKEY_CURRENT_USER\Software\Classes\Record\{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\[AssemblyVersion]]
"Class"="MyClass.MyClass"
"Assembly"="[AssemblyStrongName]"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="[TARGETDIR]MyClass.dll"

第三,将.reg文件导入到VS2010的Windows Setup注册表界面,右键“目标机器上的注册表”,点击“导入”。

最后,在安装项目的“PostBuildEvent”属性中调用构建后可执行文件:

"C:\Path\To\Exe\Post-Setup Scripting.exe" [Path to MSI] [Path To DLL to extract strong name/version]

* 这与使用 [TARGETDIR] 有点不同,因为 [TARGETDIR] 在安装时得到解析,而这些“变量”将在构建时得到解析。对于我的解决方案,我需要在构建时解决,因为我的版本号会随着每次构建而增加。

于 2013-06-04T22:23:50.820 回答