Question

Dans notre processus de construction, il est actuellement le potentiel pour les fichiers non basés sur le code (tels que les fichiers d'image) à ajouter à notre projet web, mais non inclus dans le programme d'installation MSI construit par Wix.

Pour éviter cela, je veux effectuer les opérations suivantes dans la cible AfterBuild pour notre projet WiX:

  • Obtenir un compte de tous les fichiers construits (sortie du projet de déploiement Web)
  • Obtenir un compte de tous les fichiers intégrés dans MSI (à partir du tableau « Fichier » dans MSI)
  • Comparez compte et ne parviennent construire si elles ne correspondent pas

Si je fais feu Orca je peux facilement voir le tableau de fichiers et compter, mais je ne sais pas comment automatiser ce MSBuild. Y at-il une API ou un autre mécanisme pour obtenir cette information d'un MSI?

Je ne me dérange pas d'écrire une tâche MSBuild personnalisé pour extraire le nombre de table de fichier MSI.

Était-ce utile?

La solution

Créer un nouveau projet de Visual Studio, ajouter une référence à et utiliser le c:\windows\system32\msi.dll code suivant pour lire le nombre de fichiers dans un fichier msi:

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);

Ce code utilise l'objet COM WindowsInstaller.Installer, qui est l'entrée point pour l'installation de Windows API d'automatisation. Jetez un oeil à la documentation complète de référence .

modifier : Wix apparemment livré avec des ensembles gérés (en) qui de C:\program files\Windows Installer XML v3\sdk msi.dll wrap. Je suppose que c'est ce que Rob fait référence par « DTF » dans sa réponse. En utilisant les types dans l'ensemble de Microsoft.Deployment.WindowsInstaller et namespace simplifierait l'exemple de code à ceci:

var database = new Database(@"\path\to\some\file.msi");
var list = database.ExecuteQuery("SELECT FileName FROM File");
Console.WriteLine(list.Count);

Autres conseils

fichiers MSI sont petites bases de données de bébé avec un moteur SQL personnalisé. Vous avez juste besoin d'exécuter la requête:

SELECT `File` FROM `File` 

et compter le nombre de lignes qui reviennent. Meilleure façon d'intégrer dans un MSBuild Tâche serait probablement d'utiliser le DTF Wix qui fournit géré les enveloppes pour toutes les API MSI.

La solution sera très simple une fois que vous obtenez tous les outils en place.

Comme il existe plusieurs façons dont vous pouvez mettre en œuvre cela, je réponds à ma propre question avec les résultats que je suis maintenant à l'aide grâce aux réponses de wcoenen et Rob.

Ceci est la tâche MSBuild personnalisée:

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;
    }
}

Couple de choses à noter:

  • J'ai laissé de la documentation par souci de concision.
  • MSBuildHelper.Log est juste un emballage simple pour ITask.BuildEngine.LogMessageEvent pour attraper les tests unitaires en cours d'exécution NullReferenceException.
  • encore place à l'amélioration, par exemple à l'aide ITaskItem au lieu de chaîne pour les propriétés, regex pour la comparaison.
  • La logique de comparaison peut sembler un peu bizarre, mais la table de fichier ne des trucs funky avec répertoire préfixage, et je voulais aussi éviter le cas de bord où un fichier peut être supprimé et un nouveau fichier ajouté, de sorte que le nombre de fichiers est correct, mais le contenu msi sont mal :)

Voici les tests unitaires correspondants, hypothèse est que vous avez Test.msi dans votre projet de test qui est copié dans le répertoire de sortie.

[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);
        }
    }
}

WinRAR identifie le MSI comme CAB archive auto-extractible (après lui avoir donné une extension .rar). Je suppose que vous pouvez copier le fichier quelque part, renommer, décompresser avec WinRAR, puis compter les fichiers. Les fichiers ne seront pas leur nom d'origine, bien que.

Cette semble un peu dépassé et je ne sais pas si elle pouvait être d'aucune aide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top