كيفية استخراج البيانات (عدد الملفات) من MSI "" الجدول

StackOverflow https://stackoverflow.com/questions/668122

سؤال

في بناء العملية لا يوجد حاليا إمكانية غير مدونة على أساس الملفات (مثل ملفات الصور) أن تضاف إلى مشروع ويب ، ولكنها لم تدرج في المثبت MSI بناها WiX.

للمساعدة على منع هذا ، أريد أن أداء التالية في AfterBuild الهدف لدينا WiX المشروع:

  • الحصول على الاعتماد من جميع الملفات بنيت (الإخراج من نشر ويب المشروع)
  • الحصول على الاعتماد من جميع الملفات المضمنة في MSI (من "ملف" الجدول في MSI)
  • قارن التهم تفشل بناء إذا كانت لا تطابق

إذا كنت اطلاق النار حتى أرك أنا يمكن أن نرى بسهولة جدول الملفات و العد ، ولكن لا أعرف كيفية أتمتة هذا من MSBuild.هناك بعض API أو آلية أخرى للحصول على هذه المعلومات من MSI?

لا مانع من كتابة مخصص MSBuild مهمة استخراج ملف MSI الجدول العد.

هل كانت مفيدة؟

المحلول

إنشاء مشروع visual studio إضافة مرجع إلى c:\windows\system32\msi.dll استخدام التعليمات البرمجية التالية إلى قراءة عدد من الملفات في ملف 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);

يستخدم هذا الرمز WindowsInstaller.Installer COM الكائن الذي هو مدخل إلى نظام التشغيل windows installer أتمتة API.نلقي نظرة على وثائق مرجعية كاملة.

تحرير:على ما يبدو wix يأتي مع إدارة الجمعيات (في C:\program files\Windows Installer XML v3\sdk) الذي التفاف msi.dll.أعتقد أن هذا هو ما روب مشيرا إلى "DTF" في جوابه.باستخدام أنواع في Microsoft.النشر.WindowsInstaller الجمعية مساحة من شأنه تبسيط نموذج التعليمات البرمجية هذا:

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

نصائح أخرى

MSI الملفات الطفل الصغير قواعد البيانات مع SQL مخصص المحرك.تحتاج فقط لتشغيل الاستعلام:

SELECT `File` FROM `File` 

وحساب عدد الصفوف التي تأتي مرة أخرى.أسهل طريقة لدمج في MSBuild المهمة سيكون على الأرجح إلى استخدام WiX هو DTF الذي يوفر إدارة مغلفة لكل من MSI واجهات برمجة التطبيقات.

الحل سوف يكون حقا بسيطة مرة واحدة يمكنك الحصول على جميع الأدوات في المكان.

لأن هناك طرق متعددة يمكن تنفيذ هذا أنا الإجابة على سؤالي مع النتائج أنا الآن باستخدام بفضل إجابات من wcoenen و روب.

هذا هو العرف MSBuild المهمة:

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.سجل هو مجرد التفاف على أنا.BuildEngine.LogMessageEvent للقبض NullReferenceException تشغيل وحدة الاختبارات.
  • لا يزال هناك مجال للتحسين ، على سبيل المثالباستخدام ITaskItem بدلا من سلسلة خصائص regex للمقارنة.
  • المقارنة بين المنطق قد تبدو غريبة قليلا ولكن الملف الجدول يفعل بعض الاشياء غير تقليدي مع الدليل التقديم و أردت أيضا أن تجنب حافة الحالة التي يكون فيها الملف قد حذف ملف جديد المضافة ، وبالتالي فإن عدد الملفات هو الصحيح ولكن msi محتويات خاطئة :)

وهنا المقابلة وحدة الاختبارات ، الافتراض هو أن يكون لديك الاختبار.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);
        }
    }
}

برنامج WinRAR يحدد MSI الذاتي استخراج سيارة أجرة الأرشيف (بعد إعطائها .رر التمديد).أعتقد يمكنك نسخ الملف في مكان ما, تسمية, فك مع ينرر ، ثم عد الملفات.الملفات لن يكون بأسمائها الأصلية ، على الرغم من.

هذا يبدو عفا عليها الزمن قليلا و أنا لا أعرف إذا كان يمكن أن يكون من أي مساعدة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top