2015-04-10 2 views
0

Введение в задачи под рукой:может быть пропущен, если нетерпеливДобавление картотеке к MSI программно с DTF (WIX)

Компания Я работаю не программное обеспечение компании, но сосредоточиться на механические и термодинамические инженерные проблемы. Чтобы решить проблемы с их системным дизайном, они разработали программное обеспечение для расчета влияния системы на замену отдельных компонентов. Программное обеспечение довольно старое, написанное в FORTRAN и развивающееся в течение 30 лет, что означает, что мы не можем быстро его переписать или обновить.

Как вы можете себе представить, как это программное обеспечение было установлено, также развилось, но значительно медленнее, чем остальная система, что означает, что упаковка выполняется пакетным скриптом, который собирает файлы из разных мест и помещает их в папку , который затем скомпилируется в iso, записывается на компакт-диск и отправляется почтой.

Вам, молодым программистам (мне 30 лет), может ожидать, что программа загрузит dll, но в противном случае будет достаточно автономной после связывания. Даже если код состоит из нескольких классов, из разных пространств имен и т. Д.

В FORTRAN 70 однако .. Не так много. Это означает, что программное обеспечение, оно само состоит из тревожного числа вызовов прекомпилированных модулей (читайте: раздельных программы) ..

Мы должны иметь возможность распространять через Интернет, как и любую другую современную компания удались на какое-то время. Чтобы сделать это, мы могли бы просто сделать * .iso загружаемым правильно?

К сожалению, нет, iso содержит несколько файлов, которые являются специфичными для пользователя. Как вы можете представить себе тысячи пользователей, это были бы тысячи isos, которые почти идентичны.

Также мы не хотим конвертировать старое программное обеспечение для установки на FORTRAN в настоящий пакет установки, а все наши другие (и более современные) программы - это программы на C#, упакованные как MSI .. Но время компиляции для одного msi с этим старым программным обеспечением на нашем сервере, приближается к 10 секундам, поэтому нам просто не нужно строить msi по запросу пользователя. (если одновременно запрашивается несколько пользователей, сервер не сможет выполнить запрос до истечения времени ожидания запросов). Мы также не можем предварительно создать пользовательские msi и кэшировать их, так как на сервере не хватит памяти. (всего на ~ 15 гигабайт на одну выпущенную версию)

Задача Описаниеtl: dr;

Вот что я, хотя я хотел бы сделать: (вдохновленный комментарии от Christopher Painter)

  • Создать базу MSI, фиктивными файлов вместо конкретных файлов пользователя
  • Создание кабины файл для каждого пользователь, с конкретными файлами пользователя
  • По запросу время вводите файл кабины пользователя в временную копию базы msi, используя таблицу «_Stream» .
  • Вставьте ссылку в таблицу мультимедиа с новым «DiskID» и «LastSequence», соответствующим дополнительным файлам, и именем введенного файла cab.
  • Обновите файловую таблицу именем конкретного файла в новом файле кабины, новым порядковым номером (в диапазоне нового диапазона последовательности файлов кабины) и размером файла.

Вопрос

Мой код не делает задачу только что описанной. Я могу читать из msi просто отлично, но файл в шкафу никогда не вставлен.

также:

Если я открываю с MSI DIRECT режиме, он разлагает медиа таблицу, и если я открыть его в режиме TRANSACTION, он не может изменить что-либо вообще ..

В прямом режиме существующей строки в таблице СМИ заменяется:

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

Что я делаю неправильно?

Ниже я предоставил фрагменты, связанные с вводом нового файла кабины.

фрагмент 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; 

    } 

обновление: глупо мне, забыл о "совершении" в режиме транзакций - но теперь это делает так же, как в прямом режиме, поэтому никаких реальных изменений в вопросе не возникает.

ответ

0

Отвечу это мой сам, так как я только что узнал кое-что о прямом режиме, что я не знал раньше, и wan't держать его здесь, чтобы в конечном итоге повторного гугле ..

Видимо мы только успешно обновляем 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 
      } 
     } 
} 

после добавления правильного близко заявления, то MSI выросла в размерах (и медиа строка была добавлена ​​к медиа-столу :))

Я вывешу весь класс для решения этой задачи, когда его сделано и протестировано, , но я сделаю это в соответствующем вопросе о SO. the related question on SO

+0

Я настоятельно рекомендую использовать() операторы в вашем коде для вызова IDispose, когда объекты выходят из космоса. DTF закроет и очистит все ваши ресурсы, чтобы вы избежали этих проблем. –

+0

Отмечено, и спасибо за головы. Как встроенный программист по профессии, причудливые новые трюки на C# являются немощными, но не непримиримыми – Henrik

+0

BTW Я имел в виду сферу действия не пространство. Я понимаю ... Я специалист по сборке/выпуску, который в течение последних двадцати лет занимался десятком языков, но никогда не стал экспертом ни в одном из них.Я только учился достаточно, чтобы понять технологии и потребности разработчиков в создании/выпуске. –