2009-03-20 11 views
10

हमारी निर्माण प्रक्रिया में वर्तमान में गैर-कोड आधारित फ़ाइलों (जैसे छवि फ़ाइलों) के लिए हमारी वेब प्रोजेक्ट में जोड़ा जा सकता है, लेकिन इसमें शामिल नहीं है वाईएक्स द्वारा निर्मित एमएसआई इंस्टॉलर।एमएसआई "फ़ाइल" तालिका से डेटा (फ़ाइल गिनती) निकालने के लिए कैसे करें

मदद करने के लिए इसे रोकने, मैं हमारे WiX परियोजना के लिए AfterBuild लक्ष्य में निम्नलिखित निष्पादित करना चाहते हैं:

  • बनाया (वेब ​​तैनाती परियोजना से उत्पादन)
  • एक संख्या प्राप्त सभी फ़ाइलों की एक संख्या प्राप्त करें एमएसआई में निर्मित सभी फाइलों का (MSI में "फाइल" तालिका से)
  • मायने रखता है तुलना करें और यदि वे

मेल नहीं खाते हैं मुझे लगता है मैं आसानी से फ़ाइल तालिका देख सकते हैं और भरोसा कर सकते हैं ओर्का ऊपर आग का निर्माण असफल, परंतु मुझे नहीं पता कि एमएसबिल्ड से इसे कैसे स्वचालित किया जाए। क्या एमएसआई से यह जानकारी प्राप्त करने के लिए कुछ एपीआई या अन्य तंत्र है?

मुझे एमएसआई फ़ाइल तालिका गिनती निकालने के लिए कस्टम एमएसबिल्ड कार्य लिखने पर कोई फर्क नहीं पड़ता।

उत्तर

8

एक नया दृश्य स्टूडियो परियोजना बनाएँ, 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 ऑब्जेक्ट का उपयोग करता है, जो है विंडोज इंस्टालर ऑटोमेशन एपीआई के लिए एंट्री पॉइंट। complete reference documentation पर एक नज़र डालें।

संपादित: जाहिरा तौर पर Wix (C:\program files\Windows Installer XML v3\sdk में) में कामयाब रहे विधानसभाओं के साथ आता है जो चादर Msi.dll। मुझे लगता है कि रॉब अपने जवाब में "डीटीएफ" का जिक्र कर रहा है। Microsoft.Deployment.WindowsInstaller विधानसभा और नाम स्थान में प्रकार का उपयोग करना यह करने के लिए कोड का नमूना को आसान बनाने में होगा:

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

विनर एमएसआई को स्वयं निकालने वाले सीएबी संग्रह के रूप में पहचानता है (इसे एक .rar एक्सटेंशन देने के बाद)। मुझे लगता है कि आप फ़ाइल को कहीं कॉपी कर सकते हैं, इसका नाम बदल सकते हैं, WinRAR के साथ इसे अनपैक कर सकते हैं, फिर फ़ाइलों को गिनें। हालांकि, फाइलों के मूल नाम नहीं होंगे।

This थोड़ा पुराना लगता है और मुझे नहीं पता कि यह किसी भी मदद से हो सकता है या नहीं।

+0

मैं इस नीचे मतदान नहीं करेगा क्योंकि इसमें वास्तव में अगर WinRAR कॉम संरचित भंडारण फ़ाइल (जो क्या एक MSI फ़ाइल है) पढ़ सकते हैं काम कर सकते हैं, लेकिन यह निश्चित रूप से जिस तरह से एक फ़ाइल गिनती लेने के लिए नहीं है, देखने के लिए मेरी राय में रॉब मेन्शिंग के जवाब पर। यदि आप चाहते हैं कि फ़ाइलों को निकालना है, तो आप एक कमांड प्रॉम्प्ट से व्यवस्थापक इंस्टॉल कर सकते हैं: एक exe फ़ाइल के लिए setup.exe/a, या MSI फ़ाइल के लिए msiexec/a YourMsiName.msi। –

4

एमएसआई फाइलें कस्टम एसक्यूएल इंजन के साथ छोटे बच्चे डेटाबेस हैं। आपको केवल क्वेरी चलाने की आवश्यकता है:

SELECT `File` FROM `File` 

और वापस आने वाली पंक्तियों की संख्या की गणना करें। एमएसबिल्ड टास्क में एकीकृत करने का सबसे आसान तरीका शायद वाईएक्स के डीटीएफ का उपयोग करना होगा जो सभी एमएसआई एपीआई के लिए प्रबंधित रैपर प्रदान करता है।

एक बार जब आप सभी टूल्स प्राप्त करते हैं तो समाधान वास्तव में सरल होगा।

+0

+1 मैं एक विक्स प्रशंसक हूं लेकिन "सी: \ प्रोग्राम फाइलों \ विंडोज इंस्टालर एक्सएमएल v3 \ sdk" फ़ोल्डर में भलाई कभी नहीं देखी, मैंने अपने उत्तर में एक नमूना जोड़ा है –

+0

धन्यवाद रोब! मैंने wcoenen का जवाब सही के रूप में चिह्नित किया है क्योंकि उसने कोड नमूना जोड़ा है। दोनों को सही के रूप में चिह्नित करना अच्छा होगा, लेकिन आपको मेरा +1 मिल गया। – si618

4

चूंकि अनेक तरीकों से आप यह लागू हो सकता है, मैं परिणाम मैं के साथ अपने ही सवाल का जवाब कर रहा हूँ मैं अब wcoenen और रोब से जवाब के लिए धन्यवाद का उपयोग कर रहा हूँ।

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

चीजों की युगल गौर करने योग्य:

  • मैं संक्षिप्तता के लिए दस्तावेज़ बाहर छोड़ दिया है

    यह कस्टम MSBuild कार्य है।

  • MSBuildHelper.Log ITask.BuildEngine.LogMessageEvent के लिए सिर्फ एक साधारण रैपर है NullReferenceException चलने वाले यूनिट परीक्षणों को पकड़ने के लिए।
  • अभी भी सुधार के लिए कमरा, उदा। गुणों के लिए स्ट्रिंग के बजाय ITaskItem का उपयोग करके, तुलना के लिए regex।
  • तुलना तर्क थोड़ा अजीब लग सकता है, लेकिन फ़ाइल तालिका निर्देशिका उपसर्ग के साथ कुछ फंकी चीजें करता है, और मैं किनारे के मामले से बचना चाहता हूं जहां एक फ़ाइल हटाई जा सकती है और एक नई फ़ाइल जोड़ दी गई है, इसलिए फ़ाइल गणना है सही लेकिन 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); 
     } 
    } 
} 
संबंधित मुद्दे