代码之家  ›  专栏  ›  技术社区  ›  si618

如何从MSI“文件”表中提取数据(文件计数)

  •  10
  • si618  · 技术社区  · 16 年前

    为了防止这种情况,我想在我们的WiX项目的AfterBuild目标中执行以下操作:

    • 获取所有已构建文件的计数(来自web部署项目的输出)

    4 回复  |  直到 16 年前
        1
  •  9
  •   Stein Åsmul    11 年前

    创建一个新的visual studio项目,添加一个引用 c:\windows\system32\msi.dll

    Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
    var installer =
       (WindowsInstaller.Installer)Activator.CreateInstance(installerType);
    var msi = installer.OpenDatabase(@"path\to\some\file.msi", 0);
    var fileView = msi.OpenView("SELECT FileName FROM File");
    fileView.Execute(null);
    int fileCount = 0;
    while (fileView.Fetch() != null)
    {
       fileCount++;
    }
    Console.WriteLine(fileCount);
    

    WindowsInstaller.Installer COM对象,它是windows安装程序自动化API的入口点。看看 complete reference documentation .

    C:\program files\Windows Installer XML v3\sdk

    var database = new Database(@"\path\to\some\file.msi");
    var list = database.ExecuteQuery("SELECT FileName FROM File");
    Console.WriteLine(list.Count);
    
        2
  •  5
  •   Rob Mensching    16 年前

    SELECT `File` FROM `File` 
    

    一旦你把所有的工具都准备好了,解决方案就会变得非常简单。

        3
  •  4
  •   Stein Åsmul    11 年前

    public class VerifyMsiFileCount : Task
    {
        [Required]
        public string MsiFile { get; set; }
    
        [Required]
        public string Directory { get; set; }
    
        public override bool Execute()
        {
           Database database = new Database(MsiFile, DatabaseOpenMode.ReadOnly);
            IList msiFiles = database.ExecuteQuery("SELECT FileName FROM File", new Record(0));
            IList<string> files = new List<string>(
                System.IO.Directory.GetFiles(Directory, "*", SearchOption.AllDirectories));
            return compareContents(msiFiles, files);
        }
    
        bool compareContents(IList msiFiles, IList<string> files)
        {
            // Always false if count mismatch, but helpful to know which file(s) are missing
            bool result = msiFiles.Count == files.Count;
    
            StringBuilder sb = new StringBuilder(msiFiles.Count);
            foreach (string msiFile in msiFiles)
            {
                sb.AppendLine(msiFile.ToUpper());
            }
            string allMsiFiles = sb.ToString();
    
            // Could be optimized using regex - each non-matched line in allMsiFiles
            string filename;
            foreach (string file in files)
            {
                filename = file.ToUpper();
                // Strip directory as File table in MSI does funky things with directory prefixing
                if (filename.Contains(Path.DirectorySeparatorChar.ToString()))
                {
                    filename = filename.Substring(file.LastIndexOf(Path.DirectorySeparatorChar) + 1);
                }
                if (!allMsiFiles.Contains(filename))
                {
                    result = false;
                    MSBuildHelper.Log(this, file + " appears to be missing from MSI File table",
                        MessageImportance.High);
                }
            }
            return result;
        }
    }
    

    • 为了简洁起见,我省略了文档。
    • MSBuildHelper。Log只是ITask的一个简单包装器。构建引擎。LogMessageEvent用于捕获运行单元测试的NullReferenceException。
    • 比较逻辑可能看起来有点奇怪,但File表在目录前缀方面做了一些奇怪的事情,我还想避免可能删除文件并添加新文件的边缘情况,因此文件计数是正确的,但msi内容是错误的:)

    以下是相应的单元测试,假设你有 Test.msi

    [TestFixture]
    public class VerifyMsiFileCountFixture
    {
        VerifyMsiFileCount verify;
    
        [SetUp]
        public void Setup()
        {
            verify = new VerifyMsiFileCount();
        }
    
        [Test]
        [ExpectedException(typeof(InstallerException))]
        public void Execute_ThrowsInstallerException_InvalidMsiFilePath()
        {
            verify.Directory = Environment.CurrentDirectory;
            verify.MsiFile = "Bogus";
            verify.Execute();
        }
    
        [Test]
        [ExpectedException(typeof(DirectoryNotFoundException))]
        public void Execute_ThrowsDirectoryNotFoundException_InvalidDirectoryPath()
        {
            verify.Directory = "Bogus";
            verify.MsiFile = "Test.msi";
            verify.Execute();
        }
    
        [Test]
        public void Execute_ReturnsTrue_ValidDirectoryAndFile()
        {
            string directory = Path.Combine(Environment.CurrentDirectory, "Temp");
            string file = Path.Combine(directory, "Test.txt");
            Directory.CreateDirectory(directory);
            File.WriteAllText(file, "Temp");
            try
            {
                verify.Directory = directory;
                verify.MsiFile = "Test.msi";
                Assert.IsTrue(verify.Execute());
            }
            finally
            {
                File.Delete(file);
                Directory.Delete(directory);
            }
        }
    
        [Test]
        public void Execute_ReturnsFalse_NoFileDefined()
        {
            string directory = Path.Combine(Environment.CurrentDirectory, "Temp");
            Directory.CreateDirectory(directory);
            try
            {
                verify.Directory = directory;
                verify.MsiFile = "Test.msi";
                Assert.IsFalse(verify.Execute());
            }
            finally
            {
                Directory.Delete(directory);
            }
        }
    
        [Test]
        public void Execute_ReturnsFalse_IncorrectFilename()
        {
            string directory = Path.Combine(Environment.CurrentDirectory, "Temp");
            string file = Path.Combine(directory, "Bogus.txt");
            Directory.CreateDirectory(directory);
            File.WriteAllText(file, "Temp");
            try
            {
                verify.Directory = directory;
                verify.MsiFile = "Test.msi";
                Assert.IsFalse(verify.Execute());
            }
            finally
            {
                File.Delete(file);
                Directory.Delete(directory);
            }
        }
    
        [Test]
        public void Execute_ReturnsFalse_ExtraFileDefined()
        {
            string directory = Path.Combine(Environment.CurrentDirectory, "Temp");
            string file1 = Path.Combine(directory, "Test.txt");
            string file2 = Path.Combine(directory, "Bogus.txt");
            Directory.CreateDirectory(directory);
            File.WriteAllText(file1, "Temp");
            File.WriteAllText(file2, "Temp");
            try
            {
                verify.Directory = directory;
                verify.MsiFile = "Test.msi";
                Assert.IsFalse(verify.Execute());
            }
            finally
            {
                File.Delete(file1);
                File.Delete(file2);
                Directory.Delete(directory);
            }
        }
    }
    
        4
  •  0
  •   cdonner    16 年前