.net 安装程序自定义操作可以';无法读取所有注册表值

.net 安装程序自定义操作可以';无法读取所有注册表值,.net,vb.net,windows,visual-studio-2015,registry,.net,Vb.net,Windows,Visual Studio 2015,Registry,使用Visual Studio 2015。我正在尝试构建一个安装程序自定义操作,该操作将在卸载时“注销”Excel加载项。基本上,它需要查看HKCU\Software\Microsoft\Office中的键,找到任何版本号的子键(16.0e.g.),然后查看Excel\Options子键(如果存在)并在其中一个OPEN值中检查外接程序名称(Excel使用OPEN、OPEN1、OPEN2等枚举注册表中的加载项) 当我调试自定义操作时,它似乎无法查看所有注册表值。例如,它报告在HKCU\Softwa

使用Visual Studio 2015。我正在尝试构建一个安装程序自定义操作,该操作将在卸载时“注销”Excel加载项。基本上,它需要查看
HKCU\Software\Microsoft\Office
中的键,找到任何版本号的子键(
16.0
e.g.),然后查看
Excel\Options
子键(如果存在)并在其中一个
OPEN
值中检查外接程序名称(Excel使用
OPEN
OPEN1
OPEN2
等枚举注册表中的加载项)

当我调试自定义操作时,它似乎无法查看所有注册表值。例如,它报告在
HKCU\Software\Microsoft\Office
下有8个子项,而实际上有10个子项。我猜这是由于注册表虚拟化,所以我尝试强制应用程序打开一个指定项ic注册表视图如下所示:

x64:
RegistryKey.OpenBaseKey(RegistryHive.CurrentUser,RegistryView.Registry64)

x86:
RegistryKey.OpenBaseKey(RegistryHive.CurrentUser,RegistryView.Registry32)

使用这两个调用中的任何一个都会产生完全相同的受限注册表视图(我仍然看到8个键而不是10个键)。因此,出于某种原因,安装程序自定义操作似乎无法强制特定注册表视图

我构建了一个控制台应用程序来做一些额外的测试,控制台应用程序可以看到完整的10个键,而不管它是编译到x64还是x86平台

我在这里有点不知所措。这是VS2015安装项目的已知问题吗?它们查看注册表某些部分的能力是否受到限制?或者,这只是我的一个代码错误

以下是我试图用来注销外接程序的代码(如果有帮助的话)。我在卸载过程中添加了大量错误检查,因为这会导致致命错误。卸载现在可以工作(只要不会导致崩溃)但实际上并没有注销外接程序,因为如前所述,它显然看不到完整的注册表

If Registry.CurrentUser.OpenSubKey("Software\Microsoft\Office", True) IsNot Nothing Then
    Dim regCUOffice As RegistryKey = Registry.CurrentUser.OpenSubKey("Software\Microsoft\Office", True)
    If regCUOffice.GetSubKeyNames.Count > 0 Then
        For Each strKeyName As String In regCUOffice.GetSubKeyNames
            If regCUOffice.OpenSubKey(strKeyName & "\Excel\Options", True) IsNot Nothing Then
                Dim regExcelOptionsKey As RegistryKey = regCUOffice.OpenSubKey(strKeyName, True).OpenSubKey("Excel\Options", True)
                For Each strValueName As String In regExcelOptionsKey.GetValueNames
                    If strValueName IsNot Nothing Then
                        If (strValueName.Equals("OPEN") Or strValueName.StartsWith("OPEN")) Then
                            If regExcelOptionsKey.GetValue(strValueName) IsNot Nothing Then
                                If regExcelOptionsKey.GetValue(strValueName).Equals("MyAddIn.xll") Then
                                    regExcelOptionsKey.DeleteValue(strValueName)
                                End If
                            End If
                        End If
                    End If
                Next
                regExcelOptionsKey.Close()
            End If
        Next
    End If
    regCUOffice.Close()
End If

经过大量的研究和工作,我相信我已经解决了这个问题

显然,Visual Studio中安装项目中构建的任何自定义操作都作为通用系统帐户运行。因此,HKCU注册表配置单元是系统帐户的配置单元,而不是当前登录的用户的配置单元。但是,可以绕过此行为

我遇到的最可行的解决方案是在构建MSI后翻转MSI中的模拟标志。这允许安装自定义操作模拟当前登录的用户。不幸的是,翻转此标志不是特别直观。我最终遇到了这个脚本,它为您完成了所有的工作:

// CustomAction_Impersonate.js <msi-file>
// Performs a post-build fixup of an msi to change all deferred custom actions to Impersonate
// Constant values from Windows Installer
var msiOpenDatabaseModeTransact = 1;

var msiViewModifyInsert         = 1
var msiViewModifyUpdate         = 2
var msiViewModifyAssign         = 3
var msiViewModifyReplace        = 4
var msiViewModifyDelete         = 6

var msidbCustomActionTypeInScript       = 0x00000400;
var msidbCustomActionTypeNoImpersonate  = 0x00000800

if (WScript.Arguments.Length != 1)
{
       WScript.StdErr.WriteLine(WScript.ScriptName + " file");
       WScript.Quit(1);
}

var filespec = WScript.Arguments(0);
var installer = WScript.CreateObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);

var sql
var view
var record

try
{
       sql = "SELECT `Action`, `Type`, `Source`, `Target` FROM `CustomAction`";
       view = database.OpenView(sql);
       view.Execute();
       record = view.Fetch();
    //Loop through all the Custom Actions
       while (record)
       {
           if (record.IntegerData(2) & msidbCustomActionTypeInScript)
           {
               //We must flip the msidbCustomActionTypeNoImpersonate bit only for deferred custom actions
               record.IntegerData(2) = record.IntegerData(2) & ~msidbCustomActionTypeNoImpersonate;
              view.Modify(msiViewModifyReplace, record);
           }
        record = view.Fetch();
       }

       view.Close();
       database.Commit();
}
catch(e)
{
       WScript.StdErr.WriteLine(e);
       WScript.Quit(1);
}
//CustomAction\u Impersonate.js
//对msi执行生成后修复,以将所有延迟的自定义操作更改为模拟
//Windows Installer中的常量值
var msiOpenDatabaseModeTransact=1;
var msiViewModifyInsert=1
var msiViewModifyUpdate=2
变量msiViewModifyAssign=3
变量msiViewModifyReplace=4
var msiViewModifyDelete=6
var msidbCustomActionTypeInScript=0x00000400;
变量msidbCustomActionTypeNoImpersonate=0x0000080
if(WScript.Arguments.Length!=1)
{
WScript.StdErr.WriteLine(WScript.ScriptName+“文件”);
WScript.Quit(1);
}
var filespec=WScript.Arguments(0);
var installer=WScript.CreateObject(“WindowsInstaller.installer”);
var database=installer.OpenDatabase(filespec,msiOpenDatabaseModeTransact);
var-sql
变量视图
var记录
尝试
{
sql=“从“CustomAction”中选择“Action”、“Type”、“Source”、“Target”;
视图=数据库.OpenView(sql);
view.Execute();
record=view.Fetch();
//循环执行所有自定义操作
while(记录)
{
if(record.IntegerData(2)&msidbCustomActionTypeInScript)
{
//我们必须仅为延迟的自定义操作翻转msidbCustomActionTypeNoImpersonate位
record.IntegerData(2)=record.IntegerData(2)&~msidbCustomActionTypeNoImpersonate;
查看.修改(msiViewModifyReplace,记录);
}
record=view.Fetch();
}
view.Close();
Commit();
}
捕获(e)
{
WScript.StdErr.WriteLine(e);
WScript.Quit(1);
}
将此脚本另存为安装项目的项目文件夹中的
CustomAction\u Impersonate.js
(即
SetupProjectName.vdproj
文件所在的位置)。然后,在Visual Studio中,选择安装项目并打开属性窗口。在PostBuildEvent属性中,添加
cscript.exe“$(ProjectDir)CustomAction\u Impersonate.js“$(BuiltoputPath)”

基本上,这一行告诉Visual Studio生成项目,并在成功生成项目后运行保存的脚本。该脚本翻转模拟标志,以允许安装程序自定义操作以登录用户身份运行

在我的初步测试中,这似乎起到了作用。如果我发现此解决方案因某些原因不可行,我将在此处更新。我只是想分享答案,以防其他人遇到此问题