使用Visual Studio 2015.我尝试构建一个安装程序自定义操作,该操作将取消注册"卸载时的Excel加载项。本质上,它需要查看HKCU\Software\Microsoft\Office
中的键并查找任何版本号(16.0
例如)的子项,然后查看Excel\Options
子项(如果存在)并检查其中一个OPEN
值中的加载项名称(Excel使用OPEN
,OPEN1
,OPEN2
等枚举注册表中的加载项。
当我调试自定义操作时,看起来它无法查看所有注册表值。例如,当实际有10个子键时,它报告HKCU\Software\Microsoft\Office
下有8个子键。我猜这是由于注册表虚拟化导致的,所以我试图强制应用程序打开一个特定的注册表视图,如下所示:
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
答案 0 :(得分:1)
经过大量的研究和研究,我相信我已经找到了问题。
显然,在Visual Studio中的安装项目中构建的任何自定义操作都作为通用SYSTEM帐户运行。因此,HKCU注册表配置单元是SYSTEM帐户,不当前登录用户。但是,有可能绕过这种行为。
我遇到的最可行的解决方案是在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_Impersonate.js
,位于安装项目的项目文件夹中(即SetupProjectName.vdproj
文件所在的位置)。然后,在Visual Studio中,选择您的安装项目并打开属性窗口。在PostBuildEvent属性中,添加cscript.exe "$(ProjectDir)CustomAction_Impersonate.js" "$(BuiltOuputPath)"
。
基本上,此行告诉Visual Studio构建项目,在成功构建之后,运行您保存的脚本。该脚本将翻转impersonate标志以允许安装程序自定义操作作为登录用户运行。
在我的初步测试中,这似乎可以解决问题。如果我发现此解决方案由于某种原因不可行,我将在此更新。只是想分享答案以防其他人遇到这个问题。