簡體   English   中英

使用DTF(wix)以編程方式將Cabinet文件添加到MSI

[英]Adding cabinet file to msi programatically with DTF (wix)

即將進行的任務介紹: 如果不耐煩可以跳過

我工作的公司不是軟件公司,而是專注於機械和熱力學工程問題。 為了幫助解決他們的系統設計難題,他們開發了一種軟件來計算更換單個組件對系統的影響。 該軟件已經很老了,是用FORTRAN編寫的,並且已經發展了30年,這意味着我們無法快速重寫或更新它。

您可能會想到,該軟件的安裝方式也有所發展,但比系統的其余部分慢得多,這意味着打包工作是通過批處理腳本完成的,該批處理腳本從不同位置收集文件,然后將其放在文件夾中,然后該文件夾編譯成ISO,刻錄到CD,並隨郵件一起寄出。

你們年輕的程序員(我30歲)可能期望程序加載dll,但在鏈接后還是相當獨立的。 即使代碼是由幾個類組成的,它們來自不同的名稱空間等。

但是在FORTRAN 70中。 這意味着它本身的軟件包括對預制模塊的驚人數量的調用(閱讀: 單獨的程序 )。

我們需要能夠通過互聯網進行分發,就像任何其他現代公司已經能夠這樣做一樣。 為此,我們可以使* .iso可下載對嗎?

好吧,不幸的是,iso包含幾個特定於用戶的文件。 就像您想象的那樣,對於成千上萬的用戶,這將是成千上萬的isos,幾乎是相同的。

同樣,我們也不會將基於FORTRAN的舊安裝軟件轉換為真實的安裝程序包,而我們所有其他(以及更現代的)程序都是C#程序,打包為MSI。.但是,使用此舊軟件的單個msi的編譯時間在我們的服務器上,該時間接近10秒,因此,當用戶要求時,我們根本無法選擇構建msi。 (如果多個用戶同時請求,則服務器將無法在請求超時之前完成。。)我們也無法預先構建用戶特定的msi並將其緩存,因為我們將耗盡服務器上的內存。(每個發布版本總計約15千兆字節)

任務描述 tl:dr;

這是我雖然會做的事情:(受Christopher Painter的評論啟發)

  • 創建基本的MSI,使用虛擬文件而不是用戶特定的文件
  • 為每個用戶創建cab文件,以及用戶特定的文件
  • 在請求時,使用“ _Stream” 將用戶特定的cab文件注入到基本msi的臨時副本中。
  • 將帶有新“ DiskID”和“ LastSequence”(與額外文件相對應)以及注入的cabfile的名稱的引用插入媒體表。
  • 使用新cab文件中用戶特定文件的名稱,新序號(在新cab文件序列范圍內)和文件大小更新Filetable。

我的代碼無法完成剛剛描述的任務。 我可以從msi讀取,但絕對不會插入cabinet文件。

也:

如果以DIRECT模式打開msi,它會損壞媒體表, 而如果以TRANSACTION模式打開它,則根本無法更改任何內容。

在直接模式下,Media表中的現有行替換為:

DiskId: 1
LastSequence: -2145157118
Cabinet: "Name of action to invoke, either in the engine or the handler DLL."

我究竟做錯了什么 ?

下面,我提供了有關注入新cab文件的代碼段。

片段1

public string createCabinetFileForMSI(string workdir, List<string> filesToArchive)
    {
        //create temporary cabinet file at this path:
        string GUID = Guid.NewGuid().ToString();
        string cabFile = GUID + ".cab";
        string cabFilePath = Path.Combine(workdir, cabFile);

        //create a instance of Microsoft.Deployment.Compression.Cab.CabInfo
        //which provides file-based operations on the cabinet file
        CabInfo cab = new CabInfo(cabFilePath);

        //create a list with files and add them to a cab file
        //now an argument, but previously this was used as test:
        //List<string> filesToArchive = new List<string>() { @"C:\file1", @"C:\file2" };
        cab.PackFiles(workdir, filesToArchive, filesToArchive);

        //we will ned the path for this file, when adding it to an msi..
        return cabFile;
    }

片段2

    public int insertCabFileAsNewMediaInMSI(string cabFilePath, string pathToMSIFile, int numberOfFilesInCabinet = -1)
    {
        //open the MSI package for editing
        pkg = new InstallPackage(pathToMSIFile, DatabaseOpenMode.Direct); //have also tried direct, while database was corrupted when writing.
        return insertCabFileAsNewMediaInMSI(cabFilePath, numberOfFilesInCabinet);
    }

片段3

 public int insertCabFileAsNewMediaInMSI(string cabFilePath, int numberOfFilesInCabinet = -1)
    {
        if (pkg == null)
        {
            throw new Exception("Cannot insert cabinet file into non-existing MSI package. Please Supply a path to the MSI package");
        }

        int numberOfFilesToAdd = numberOfFilesInCabinet;
        if (numberOfFilesInCabinet < 0)
        {
            CabInfo cab = new CabInfo(cabFilePath);
            numberOfFilesToAdd = cab.GetFiles().Count;
        }

        //create a cab file record as a stream (embeddable into an MSI)
        Record cabRec = new Record(1);
        cabRec.SetStream(1, cabFilePath);

        /*The Media table describes the set of disks that make up the source media for the installation.
          we want to add one, after all the others
          DiskId - Determines the sort order for the table. This number must be equal to or greater than 1,
          for out new cab file, it must be > than the existing ones...
        */
        //the baby SQL service in the MSI does not support "ORDER BY `` DESC" but does support order by..
        IList<int> mediaIDs = pkg.ExecuteIntegerQuery("SELECT `DiskId` FROM `Media` ORDER BY `DiskId`");
        int lastIndex = mediaIDs.Count - 1;
        int DiskId = mediaIDs.ElementAt(lastIndex) + 1;

        //wix name conventions of embedded cab files is "#cab" + DiskId + ".cab"
        string mediaCabinet = "cab" + DiskId.ToString() + ".cab";

        //The _Streams table lists embedded OLE data streams.
        //This is a temporary table, created only when referenced by a SQL statement.
        string query = "INSERT INTO `_Streams` (`Name`, `Data`) VALUES ('" + mediaCabinet + "', ?)";
        pkg.Execute(query, cabRec);
        Console.WriteLine(query);

        /*LastSequence - File sequence number for the last file for this new media.
          The numbers in the LastSequence column specify which of the files in the File table
          are found on a particular source disk.

          Each source disk contains all files with sequence numbers (as shown in the Sequence column of the File table)
          less than or equal to the value in the LastSequence column, and greater than the LastSequence value of the previous disk
          (or greater than 0, for the first entry in the Media table).
          This number must be non-negative; the maximum limit is 32767 files.
          /MSDN
         */
        IList<int> sequences = pkg.ExecuteIntegerQuery("SELECT `LastSequence` FROM `Media` ORDER BY `LastSequence`");
        lastIndex = sequences.Count - 1;
        int LastSequence = sequences.ElementAt(lastIndex) + numberOfFilesToAdd;

        query = "INSERT INTO `Media` (`DiskId`, `LastSequence`, `Cabinet`) VALUES (" + DiskId.ToString() + "," + LastSequence.ToString() + ",'#" + mediaCabinet + "')";
        Console.WriteLine(query);
        pkg.Execute(query);

        return DiskId;

    }

更新:愚蠢的我,忘記了在事務模式下的“提交”-但是現在它與直接模式下的一樣,因此對該問題沒有真正的改變。

我會自我回答,因為我剛剛學到了一些以前不知道的有關DIRECT模式的知識,並且不想將其保留在這里,以便最終重新使用Google。

顯然,如果我們在程序最終崩潰之前關閉了數據庫句柄,就只能成功更新MSI。

為了回答問題,此析構函數應該這樣做。

~className()
{
        if (pkg != null)
        {
            try
            {
                pkg.Close();
            }
            catch (Exception ex)
            {
                //rollback not included as we edit directly?

                //do nothing.. 
                //atm. we just don't want to break anything if database was already closed, without dereferencing
            }
        }
}

添加正確的close語句后,MSI的大小增加了(並且在介質表中添加了介質行:))

當完成並測試了該任務后,我將發布整個課程來解決該任務,但是我將在SO的相關問題中進行討論。 有關SO的相關問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM