C# msiOpenDataBaseModes!=0导致异常

C# msiOpenDataBaseModes!=0导致异常,c#,windows-installer,C#,Windows Installer,我一点也不明白 当我尝试以只读模式以外的任何方式打开MSI文件时,会出现异常: 用户代码未处理System.Runtime.InteropServices.COMException HelpLink=Msi.chm#9006 HResult=-2147467259 Message=OpenDatabase,DatabasePath,OpenMode Source=Msi API错误 ErrorCode=-2147467259 StackTrace:at System.RuntimeType.Fo

我一点也不明白

当我尝试以只读模式以外的任何方式打开MSI文件时,会出现异常:

用户代码未处理System.Runtime.InteropServices.COMException HelpLink=Msi.chm#9006 HResult=-2147467259 Message=OpenDatabase,DatabasePath,OpenMode Source=Msi API错误 ErrorCode=-2147467259 StackTrace:at System.RuntimeType.ForwardCallToInvokeMember(字符串memberName, BindingFlags标志、对象目标、Int32[]aWrapperTypes、MessageData& WindowsInstaller.Installer.OpenDatabase(字符串 基于web的版本中的数据库路径(对象OpenMode) manager.AjaxFileHandler.updateMSIProperty(字符串msiFile,字符串 msiProperty,字符串值),在C:\Users\obfuscated\documents\visual中 studio 2010\Projects\web版 manager\AjaxFileHandler.ashx.cs:web版第28行 中的manager.AjaxFileHandler.ProcessRequest(HttpContext上下文) C:\Users\obfuscated\documents\visual studio 2010\Projects\web-based release manager\AjaxFileHandler.ashx.cs:at的第143行 System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 在System.Web.HttpApplication.ExecuteStep(IExecutionStep步骤, 布尔值(同步完成(&C))

我可以使用下面的工作代码从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并安装产品,但是他们还没有真正掌握所有这些产品/包属性的功能

我想我会帮助他们,在我们的Web前端,他们发布msi的更正,替换升级码(guid),插入一些他们通常忘记的其他数据,例如制造商。。证书等

但我无法获取更新MSI的代码

更新:

  • 模糊产品名称多一点,以保护工作
  • 已尝试添加
    Marshal.finalEleaseComObject(安装程序)
    ,但未成功

Windows安装程序XML(WIX)有一个称为部署工具基金会(DTF)的特性,它有一个非常好的MSI互操作程序集,称为MySoop.AdvestMy.WINDOWSINSTALLAR.DLL。此程序集有一个名为Database的类,其构造函数如下:

public Database(
    string filePath,
    DatabaseOpenMode mode
)
一个简单的例子是:

using Microsoft.Deployment.WindowsInstaller;    
using(Database database = new Database(@"C:\test.msi", DatabaseOpenMode.Direct))
{
  ...
}
还有一个QDatabase类,它实现了LINQ到MSI模式,可以轻松地将Properties表视为一个实体并进行相应的查询/更新

using(var database = new QDatabase(@"C:\test.msi", DatabaseOpenMode.Direct))
// or in custom action
using(var qdatabase = session.Database.AsQueryable() )

我高度建议这一点,这样您就可以专注于编写的代码,而不是如何与MSI进行交互。

< P> Windows安装程序XML(WIX)有一个称为部署工具基金会(DTF)的功能,它有一个非常好的MSI互操作程序集,称为MySoop.AdvestMy.WINDOWSINSTALLAR.DLL。此程序集有一个名为Database的类,其构造函数如下:

public Database(
    string filePath,
    DatabaseOpenMode mode
)
一个简单的例子是:

using Microsoft.Deployment.WindowsInstaller;    
using(Database database = new Database(@"C:\test.msi", DatabaseOpenMode.Direct))
{
  ...
}
还有一个QDatabase类,它实现了LINQ到MSI模式,可以轻松地将Properties表视为一个实体并进行相应的查询/更新

using(var database = new QDatabase(@"C:\test.msi", DatabaseOpenMode.Direct))
// or in custom action
using(var qdatabase = session.Database.AsQueryable() )

我高度建议这一点,这样您就可以专注于编写的代码,而不是如何与MSI进行交互。

< P> Windows安装程序XML(WIX)有一个称为部署工具基金会(DTF)的功能,它有一个非常好的MSI互操作程序集,称为MySoop.AdvestMy.WINDOWSINSTALLAR.DLL。此程序集有一个名为Database的类,其构造函数如下:

public Database(
    string filePath,
    DatabaseOpenMode mode
)
一个简单的例子是:

using Microsoft.Deployment.WindowsInstaller;    
using(Database database = new Database(@"C:\test.msi", DatabaseOpenMode.Direct))
{
  ...
}
还有一个QDatabase类,它实现了LINQ到MSI模式,可以轻松地将Properties表视为一个实体并进行相应的查询/更新

using(var database = new QDatabase(@"C:\test.msi", DatabaseOpenMode.Direct))
// or in custom action
using(var qdatabase = session.Database.AsQueryable() )

我高度建议这一点,这样您就可以专注于编写的代码,而不是如何与MSI进行交互。

< P> Windows安装程序XML(WIX)有一个称为部署工具基金会(DTF)的功能,它有一个非常好的MSI互操作程序集,称为MySoop.AdvestMy.WINDOWSINSTALLAR.DLL。此程序集有一个名为Database的类,其构造函数如下:

public Database(
    string filePath,
    DatabaseOpenMode mode
)
一个简单的例子是:

using Microsoft.Deployment.WindowsInstaller;    
using(Database database = new Database(@"C:\test.msi", DatabaseOpenMode.Direct))
{
  ...
}
还有一个QDatabase类,它实现了LINQ到MSI模式,可以轻松地将Properties表视为一个实体并进行相应的查询/更新

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()的调用,但不会,因为不管怎样,当它完成时都会发生这种情况

正如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()的调用,但不会,因为不管怎样,当它完成时都会发生这种情况

正如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()的调用,但不会,因为不管怎样,当它完成时都会发生这种情况

也是