msiOpenDataBaseModes != 0 导致异常
msiOpenDataBaseModes != 0 causes exception
这个我完全不懂
当我尝试以非只读模式打开 MSI 文件时,出现异常:
System.Runtime.InteropServices.COMException was unhandled by user code
HelpLink=Msi.chm#9006 HResult=-2147467259
Message=OpenDatabase,DatabasePath,OpenMode Source=Msi API Error
ErrorCode=-2147467259 StackTrace: at
System.RuntimeType.ForwardCallToInvokeMember(String memberName,
BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData&
msgData) at WindowsInstaller.Installer.OpenDatabase(String
DatabasePath, Object OpenMode) at web based release
manager.AjaxFileHandler.updateMSIProperty(String msiFile, String
msiProperty, String value) in C:\Users\obfuscated\documents\visual
studio 2010\Projects\web based release
manager\AjaxFileHandler.ashx.cs:line 28 at web based release
manager.AjaxFileHandler.ProcessRequest(HttpContext context) in
C:\Users\obfuscated\documents\visual studio 2010\Projects\web based
release manager\AjaxFileHandler.ashx.cs:line 143 at
System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step,
Boolean& completedSynchronously)
我可以使用下面的工作代码从msi读取属性,所以我知道文件路径是正确的:
public static string GetMSIProperty(string msiFile, string msiProperty)
{
string retVal = string.Empty;
Type classType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Object installerObj = Activator.CreateInstance(classType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
Database database = installer.OpenDatabase(msiFile, 0);
string sql = String.Format("SELECT `Value` FROM `Property` WHERE `Property`='{0}'", msiProperty);
View view = database.OpenView(sql);
WindowsInstaller.Record record = null;
view.Execute(record);
record = view.Fetch();
if (record != null)
{
retVal = record.get_StringData(1).ToString();
}
else
retVal = "Property Not Found";
Marshal.FinalReleaseComObject(installer);
return retVal;
}
导致问题的代码:
public void updateMSIProperty(string msiFile, string msiProperty, string value)
{
Type classType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Object installerObj = Activator.CreateInstance(classType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
var mode = MsiOpenDatabaseMode.msiOpenDatabaseModeDirect;
Database database = installer.OpenDatabase(msiFile, mode); //throws the exception!
string sql = String.Format("UPDATE `Property` SET `Value`='{0}' WHERE `Property`='{1}'", value, msiProperty);
View view = database.OpenView(sql);
view.Execute();
return;
}
这两个函数 运行 来自同一段代码:
if(GetMSIProperty(path, "UpgradeCode") != theProductsUpgradeCode)
updateMSIProperty(path, "UpgradeCode",theProductsUpgradeCode);
我工作的公司正在转向以 msi 的形式发布软件。
没问题,除了,公司由很多擅长他们所做的事情的工程师组成,其中一部分是用于计算的编程工具。
他们不是计算机科学家,他们中的大多数人不知道 OS2 和 Office 365 之间的区别...
到目前为止,大多数部门已经创建了一些发布系统,可以创建 msi 并安装产品,但是他们还没有真正掌握所有这些 product/package 属性的作用。
我想我会帮助他们,在我们的网络前端,他们通过替换 UpgradeCodes (guid) 来发布 msi 的更正,并插入一些他们通常忘记的其他数据,例如制造商..证书等..
但我无法获取更新 MSI 的代码。
更新:
- 混淆产品名称,以保护工作。
- 尝试添加
Marshal.FinalReleaseComObject(installer)
但没有成功
Windows 安装程序 XML (WiX) 有一个称为部署工具基础 (DTF) 的功能,它有一个非常好的 MSI 互操作程序集 Microsoft.Deployment.WindowsInstaller.dll。这个程序集有一个名为 Database 的 class,它有一个构造函数,如:
public Database(
string filePath,
DatabaseOpenMode mode
)
一个简单的例子是:
using Microsoft.Deployment.WindowsInstaller;
using(Database database = new Database(@"C:\test.msi", DatabaseOpenMode.Direct))
{
...
}
还有一个 QDatabase class 实现了 LINQ to MSI 模式,可以轻松地将属性 table 视为一个实体并进行相应的查询/更新。
using(var database = new QDatabase(@"C:\test.msi", DatabaseOpenMode.Direct))
// or in custom action
using(var qdatabase = session.Database.AsQueryable() )
我强烈建议这样做,这样您就可以专注于您尝试编写的代码,而不是如何与 MSI 互操作。
除了 Chris 的建议,我会远离整个 COM 类型的激活,因为它完全没有必要。有一个非常好的 Win32 API 可以通过 p/invoke 使用。这是我曾经使用过的一个最小示例:
public class MsiInvoke
{
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiOpenDatabase(string filename, int persist, out IntPtr dbhandle);
public const int MSIDBOPEN_DIRECT = 2;
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiCloseDatabase(string filename, int persist, out IntPtr dbhandle);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiDatabaseCommit(IntPtr hDb);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiViewClose(IntPtr hView);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiDatabaseOpenView(IntPtr hDb, string query, out IntPtr hView);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiViewExecute (IntPtr hView, IntPtr hRec);
}
class Program
{
static void Main(string[] args)
{
IntPtr hDb = IntPtr.Zero;
int res = MsiInvoke.MsiOpenDatabase("setup.msi",MsiInvoke.MSIDBOPEN_DIRECT, out hDb);
string qinsert = "UPDATE `Control` set `Control`.`Text`= 'Something' WHERE `Dialog_`='License_Dialog' AND `Control`='License'";
IntPtr hView=IntPtr.Zero;
res = MsiInvoke.MsiDatabaseOpenView(hDb, qinsert, out hView);
res = MsiInvoke.MsiViewExecute(hView, IntPtr.Zero);
res = MsiInvoke.MsiViewClose(hView);
res = MsiInvoke.MsiDatabaseCommit(hDb);
}
}
请注意,这个惰性程序应该在每个句柄上包含对 MsiCloseHandle() 的调用,但没有,因为它无论如何都会在完成时发生。
这个我完全不懂
当我尝试以非只读模式打开 MSI 文件时,出现异常:
System.Runtime.InteropServices.COMException was unhandled by user code HelpLink=Msi.chm#9006 HResult=-2147467259 Message=OpenDatabase,DatabasePath,OpenMode Source=Msi API Error ErrorCode=-2147467259 StackTrace: at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) at WindowsInstaller.Installer.OpenDatabase(String DatabasePath, Object OpenMode) at web based release manager.AjaxFileHandler.updateMSIProperty(String msiFile, String msiProperty, String value) in C:\Users\obfuscated\documents\visual studio 2010\Projects\web based release manager\AjaxFileHandler.ashx.cs:line 28 at web based release manager.AjaxFileHandler.ProcessRequest(HttpContext context) in C:\Users\obfuscated\documents\visual studio 2010\Projects\web based release manager\AjaxFileHandler.ashx.cs:line 143 at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
我可以使用下面的工作代码从msi读取属性,所以我知道文件路径是正确的:
public static string GetMSIProperty(string msiFile, string msiProperty)
{
string retVal = string.Empty;
Type classType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Object installerObj = Activator.CreateInstance(classType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
Database database = installer.OpenDatabase(msiFile, 0);
string sql = String.Format("SELECT `Value` FROM `Property` WHERE `Property`='{0}'", msiProperty);
View view = database.OpenView(sql);
WindowsInstaller.Record record = null;
view.Execute(record);
record = view.Fetch();
if (record != null)
{
retVal = record.get_StringData(1).ToString();
}
else
retVal = "Property Not Found";
Marshal.FinalReleaseComObject(installer);
return retVal;
}
导致问题的代码:
public void updateMSIProperty(string msiFile, string msiProperty, string value)
{
Type classType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Object installerObj = Activator.CreateInstance(classType);
WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;
var mode = MsiOpenDatabaseMode.msiOpenDatabaseModeDirect;
Database database = installer.OpenDatabase(msiFile, mode); //throws the exception!
string sql = String.Format("UPDATE `Property` SET `Value`='{0}' WHERE `Property`='{1}'", value, msiProperty);
View view = database.OpenView(sql);
view.Execute();
return;
}
这两个函数 运行 来自同一段代码:
if(GetMSIProperty(path, "UpgradeCode") != theProductsUpgradeCode)
updateMSIProperty(path, "UpgradeCode",theProductsUpgradeCode);
我工作的公司正在转向以 msi 的形式发布软件。 没问题,除了,公司由很多擅长他们所做的事情的工程师组成,其中一部分是用于计算的编程工具。 他们不是计算机科学家,他们中的大多数人不知道 OS2 和 Office 365 之间的区别...
到目前为止,大多数部门已经创建了一些发布系统,可以创建 msi 并安装产品,但是他们还没有真正掌握所有这些 product/package 属性的作用。
我想我会帮助他们,在我们的网络前端,他们通过替换 UpgradeCodes (guid) 来发布 msi 的更正,并插入一些他们通常忘记的其他数据,例如制造商..证书等..
但我无法获取更新 MSI 的代码。
更新:
- 混淆产品名称,以保护工作。
- 尝试添加
Marshal.FinalReleaseComObject(installer)
但没有成功
Windows 安装程序 XML (WiX) 有一个称为部署工具基础 (DTF) 的功能,它有一个非常好的 MSI 互操作程序集 Microsoft.Deployment.WindowsInstaller.dll。这个程序集有一个名为 Database 的 class,它有一个构造函数,如:
public Database(
string filePath,
DatabaseOpenMode mode
)
一个简单的例子是:
using Microsoft.Deployment.WindowsInstaller;
using(Database database = new Database(@"C:\test.msi", DatabaseOpenMode.Direct))
{
...
}
还有一个 QDatabase class 实现了 LINQ to MSI 模式,可以轻松地将属性 table 视为一个实体并进行相应的查询/更新。
using(var database = new QDatabase(@"C:\test.msi", DatabaseOpenMode.Direct))
// or in custom action
using(var qdatabase = session.Database.AsQueryable() )
我强烈建议这样做,这样您就可以专注于您尝试编写的代码,而不是如何与 MSI 互操作。
除了 Chris 的建议,我会远离整个 COM 类型的激活,因为它完全没有必要。有一个非常好的 Win32 API 可以通过 p/invoke 使用。这是我曾经使用过的一个最小示例:
public class MsiInvoke
{
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiOpenDatabase(string filename, int persist, out IntPtr dbhandle);
public const int MSIDBOPEN_DIRECT = 2;
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiCloseDatabase(string filename, int persist, out IntPtr dbhandle);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiDatabaseCommit(IntPtr hDb);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiViewClose(IntPtr hView);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiDatabaseOpenView(IntPtr hDb, string query, out IntPtr hView);
[DllImport("msi", CharSet = CharSet.Auto)]
public static extern int MsiViewExecute (IntPtr hView, IntPtr hRec);
}
class Program
{
static void Main(string[] args)
{
IntPtr hDb = IntPtr.Zero;
int res = MsiInvoke.MsiOpenDatabase("setup.msi",MsiInvoke.MSIDBOPEN_DIRECT, out hDb);
string qinsert = "UPDATE `Control` set `Control`.`Text`= 'Something' WHERE `Dialog_`='License_Dialog' AND `Control`='License'";
IntPtr hView=IntPtr.Zero;
res = MsiInvoke.MsiDatabaseOpenView(hDb, qinsert, out hView);
res = MsiInvoke.MsiViewExecute(hView, IntPtr.Zero);
res = MsiInvoke.MsiViewClose(hView);
res = MsiInvoke.MsiDatabaseCommit(hDb);
}
}
请注意,这个惰性程序应该在每个句柄上包含对 MsiCloseHandle() 的调用,但没有,因为它无论如何都会在完成时发生。