public void MsiAddFile(string msiName, string absoluteFilename, string tmpCabName) { string tmpCabFullName = Environment.CurrentDirectory + "\\" + tmpCabName; //create a random cab key which will be used when referencing the cab internally Random r = new Random((int)DateTime.Now.Ticks); int rand = r.Next(65535); //valid cab ids are 0-65535 string cabId = rand.ToString(); uint retCode = 0; //Get some basic info on the requested file FileInfo f = new FileInfo(absoluteFilename); int filenameSize = (int)f.Length; string filenameExt = f.Extension; string basename = f.Name; // //*NOTE*: // //the overarching process to get the new file into our MSI is as follows: // // 1) generate a new CAB file using CABARC.EXE w/ file inside // 2) open the template MSI for editing // 3) add the necessary table entries for the new file // 4) add the binary data stream from CAB on disk to the internal MSI database // 5) close MSI and flush to disk // 6) cleanup any files // //see MSDN article: http://msdn.microsoft.com/en-us/library/aa369279(VS.85).aspx //and this one: http://www.symantec.com/community/tip/3024/add-file-msi-using-orca //******************************************************** // GENERATE A NEW CAB FILE //******************************************************** //this cab file will contain only our new file in compressed format. //it's necessary to create a new CAB, b/c only compressed cabs //can be added as an internal stream inside an MSI file. //launch a silent process to make the cab Process p = new Process(); p.StartInfo.FileName = "CABARC.EXE"; p.StartInfo.Arguments = " -i " + cabId + " n \"" + tmpCabName + "\" \"" + absoluteFilename+"\""; p.StartInfo.CreateNoWindow = true; p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; p.Start(); //wait for the process to finish while (!p.HasExited) { //chase tail... } //******************************************************** // EDIT MSI DATABASE //******************************************************** //we will use unmanaged win32 API's in msi.dll to open the the MSI database for editing //we will add only fields necessary to include this file as a necessary install file //---------------------------------------------------------------------- //1. open the msi for editing //---------------------------------------------------------------------- IntPtr hDatabase = (IntPtr)(0); retCode = CwMsiWin32.MsiOpenDatabaseW(msiName, CwMsiWin32.MSIDBOPEN_TRANSACT, out hDatabase); if (retCode != CwMsiWin32.ERROR_SUCCESS) { throw new Exception("Failed to open MSI database for file '" + msiName + "'. Error: " + CwMsiWin32.GetLastError32()); } //---------------------------------------------------------------------- //2. Initialize our data tables for the new record in MSI database //---------------------------------------------------------------------- Int16 maxDiskId = MsiGetMaxDiskIdFromMediaTable(hDatabase); Int16 lastSequenceNumber = MsiGetLastFileSequenceFromMediaTable(hDatabase, maxDiskId); //Component Table MsiComponentTable ComponentTable = new MsiComponentTable(); ComponentTable.Component = "C__" + basename; ComponentTable.ComponentId = ""; ComponentTable.Directory_ = "TARGETDIR"; ComponentTable.Attributes = 0; ComponentTable.Condition = ""; ComponentTable.KeyPath = basename; //Media Table MsiMediaTable MediaTable = new MsiMediaTable(); MediaTable.DiskId = maxDiskId; MediaTable.DiskId++; MediaTable.LastSequence = lastSequenceNumber; MediaTable.LastSequence++; MediaTable.DiskPrompt = basename; MediaTable.Cabinet = "#_"+cabId; MediaTable.VolumeLabel=""; MediaTable.Source=""; //File Table MsiFileTable FileTable = new MsiFileTable(); FileTable.File = basename; FileTable.Component_ = "C__" + basename; FileTable.FileName = basename.Substring(0, 6).ToUpper() + "~1." + filenameExt.ToUpper() + "|" + basename; FileTable.FileSize = filenameSize; FileTable.Version = "1.2.3.4"; FileTable.Language = "0"; FileTable.Attributes = 512; FileTable.Sequence = lastSequenceNumber; FileTable.Sequence++; //Feature Table MsiFeatureComponentsTable FeatureComponentsTable = new MsiFeatureComponentsTable(); FeatureComponentsTable.Feature_ = "DefaultFeature"; FeatureComponentsTable.Component_ = "C__" + basename; //MsiAssembly Table MsiAssemblyTable AssemblyTable = new MsiAssemblyTable(); AssemblyTable.Component_ = "C__" + basename; AssemblyTable.Feature_ = "DefaultFeature"; AssemblyTable.File_Manifest = basename; AssemblyTable.File_Application = basename; AssemblyTable.Attributes = 0; //MsiAssemblyName Table - 4 different tables MsiAssemblyNameTable AssemblyNameTable_Name = new MsiAssemblyNameTable(); AssemblyNameTable_Name.Component_ = "C__" + basename; AssemblyNameTable_Name.Name = "Name"; AssemblyNameTable_Name.Value = basename; MsiAssemblyNameTable AssemblyNameTable_Version = new MsiAssemblyNameTable(); AssemblyNameTable_Version.Component_ = "C__" + basename; AssemblyNameTable_Version.Name = "Version"; AssemblyNameTable_Version.Value = "1.2.3.4"; MsiAssemblyNameTable AssemblyNameTable_Culture = new MsiAssemblyNameTable(); AssemblyNameTable_Culture.Component_ = "C__" + basename; AssemblyNameTable_Culture.Name = "Culture"; AssemblyNameTable_Culture.Value = "neutral"; MsiAssemblyNameTable AssemblyNameTable_ProcessorArchitecture = new MsiAssemblyNameTable(); AssemblyNameTable_ProcessorArchitecture.Component_ = "C__" + basename; AssemblyNameTable_ProcessorArchitecture.Name = "ProcessorArchitecture"; AssemblyNameTable_ProcessorArchitecture.Value = "MSIL"; //---------------------------------------------------------------------- //3. create an entry in the Component table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase,"Component",ComponentTable,6); } catch (Exception ex) { throw new Exception("Failed to create record in the Component Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //4. create an entry in the File table //---------------------------------------------------------------------- //this entry is for the file INSIDE our CAB file try { MsiCreateRecordFromMsiTable(hDatabase, "File", FileTable,8); } catch (Exception ex) { throw new Exception("Failed to create record in the File Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //5. create an entry in the FeatureComponents table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "FeatureComponents", FeatureComponentsTable,2); } catch (Exception ex) { throw new Exception("Failed to create record in the FeatureComponents Table.\n\n" + ex.Message); } //---------------------------------------------------------------------------------------- //6. create an entry in the media table for the CAB file that will contain this new file //---------------------------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "Media", MediaTable,6); } catch (Exception ex) { throw new Exception("Failed to create record in the Media Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //7. load the binary data from the CAB file as a stream in MSI db //---------------------------------------------------------------------- try { MsiAddInternalBinaryStream(hDatabase, tmpCabName, "_" + cabId, tmpCabFullName); } catch (Exception ex) { throw new Exception("Failed to add binary stream from CAB to MSI database!\n\n" + ex.Message); } //---------------------------------------------------------------------- //8. create an entry in the MsiAssembly table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "MsiAssembly", AssemblyTable,5); } catch (Exception ex) { throw new Exception("Failed to create record in the MsiAssembly Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //9. create an entry in the MsiAssemblyName table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_Name,3); MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_Version,3); MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_Culture,3); MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_ProcessorArchitecture,3); } catch (Exception ex) { throw new Exception("Failed to create a record in the MsiAssemblyName Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //10. Finalize //---------------------------------------------------------------------- //dump the table values for debugging purposes MsiDumpAllTables(hDatabase); //commit all changes and cleanup CwMsiWin32.MsiDatabaseCommit(hDatabase); CwMsiWin32.MsiCloseHandle(hDatabase); //cleanup MsiCleanUp(tmpCabName); }
public void MsiAddFile(string msiName, string absoluteFilename, string tmpCabName) { string tmpCabFullName = Environment.CurrentDirectory + "\\" + tmpCabName; //create a random cab key which will be used when referencing the cab internally Random r = new Random((int)DateTime.Now.Ticks); int rand = r.Next(65535); //valid cab ids are 0-65535 string cabId = rand.ToString(); uint retCode = 0; //Get some basic info on the requested file FileInfo f = new FileInfo(absoluteFilename); int filenameSize = (int)f.Length; string filenameExt = f.Extension; string basename = f.Name; // //*NOTE*: // //the overarching process to get the new file into our MSI is as follows: // // 1) generate a new CAB file using CABARC.EXE w/ file inside // 2) open the template MSI for editing // 3) add the necessary table entries for the new file // 4) add the binary data stream from CAB on disk to the internal MSI database // 5) close MSI and flush to disk // 6) cleanup any files // //see MSDN article: http://msdn.microsoft.com/en-us/library/aa369279(VS.85).aspx //and this one: http://www.symantec.com/community/tip/3024/add-file-msi-using-orca //******************************************************** // GENERATE A NEW CAB FILE //******************************************************** //this cab file will contain only our new file in compressed format. //it's necessary to create a new CAB, b/c only compressed cabs //can be added as an internal stream inside an MSI file. //launch a silent process to make the cab Process p = new Process(); p.StartInfo.FileName = "CABARC.EXE"; p.StartInfo.Arguments = " -i " + cabId + " n \"" + tmpCabName + "\" \"" + absoluteFilename + "\""; p.StartInfo.CreateNoWindow = true; p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; p.Start(); //wait for the process to finish while (!p.HasExited) { //chase tail... } //******************************************************** // EDIT MSI DATABASE //******************************************************** //we will use unmanaged win32 API's in msi.dll to open the the MSI database for editing //we will add only fields necessary to include this file as a necessary install file //---------------------------------------------------------------------- //1. open the msi for editing //---------------------------------------------------------------------- IntPtr hDatabase = (IntPtr)(0); retCode = CwMsiWin32.MsiOpenDatabaseW(msiName, CwMsiWin32.MSIDBOPEN_TRANSACT, out hDatabase); if (retCode != CwMsiWin32.ERROR_SUCCESS) { throw new Exception("Failed to open MSI database for file '" + msiName + "'. Error: " + CwMsiWin32.GetLastError32()); } //---------------------------------------------------------------------- //2. Initialize our data tables for the new record in MSI database //---------------------------------------------------------------------- Int16 maxDiskId = MsiGetMaxDiskIdFromMediaTable(hDatabase); Int16 lastSequenceNumber = MsiGetLastFileSequenceFromMediaTable(hDatabase, maxDiskId); //Component Table MsiComponentTable ComponentTable = new MsiComponentTable(); ComponentTable.Component = "C__" + basename; ComponentTable.ComponentId = ""; ComponentTable.Directory_ = "TARGETDIR"; ComponentTable.Attributes = 0; ComponentTable.Condition = ""; ComponentTable.KeyPath = basename; //Media Table MsiMediaTable MediaTable = new MsiMediaTable(); MediaTable.DiskId = maxDiskId; MediaTable.DiskId++; MediaTable.LastSequence = lastSequenceNumber; MediaTable.LastSequence++; MediaTable.DiskPrompt = basename; MediaTable.Cabinet = "#_" + cabId; MediaTable.VolumeLabel = ""; MediaTable.Source = ""; //File Table MsiFileTable FileTable = new MsiFileTable(); FileTable.File = basename; FileTable.Component_ = "C__" + basename; FileTable.FileName = basename.Substring(0, 6).ToUpper() + "~1." + filenameExt.ToUpper() + "|" + basename; FileTable.FileSize = filenameSize; FileTable.Version = "1.2.3.4"; FileTable.Language = "0"; FileTable.Attributes = 512; FileTable.Sequence = lastSequenceNumber; FileTable.Sequence++; //Feature Table MsiFeatureComponentsTable FeatureComponentsTable = new MsiFeatureComponentsTable(); FeatureComponentsTable.Feature_ = "DefaultFeature"; FeatureComponentsTable.Component_ = "C__" + basename; //MsiAssembly Table MsiAssemblyTable AssemblyTable = new MsiAssemblyTable(); AssemblyTable.Component_ = "C__" + basename; AssemblyTable.Feature_ = "DefaultFeature"; AssemblyTable.File_Manifest = basename; AssemblyTable.File_Application = basename; AssemblyTable.Attributes = 0; //MsiAssemblyName Table - 4 different tables MsiAssemblyNameTable AssemblyNameTable_Name = new MsiAssemblyNameTable(); AssemblyNameTable_Name.Component_ = "C__" + basename; AssemblyNameTable_Name.Name = "Name"; AssemblyNameTable_Name.Value = basename; MsiAssemblyNameTable AssemblyNameTable_Version = new MsiAssemblyNameTable(); AssemblyNameTable_Version.Component_ = "C__" + basename; AssemblyNameTable_Version.Name = "Version"; AssemblyNameTable_Version.Value = "1.2.3.4"; MsiAssemblyNameTable AssemblyNameTable_Culture = new MsiAssemblyNameTable(); AssemblyNameTable_Culture.Component_ = "C__" + basename; AssemblyNameTable_Culture.Name = "Culture"; AssemblyNameTable_Culture.Value = "neutral"; MsiAssemblyNameTable AssemblyNameTable_ProcessorArchitecture = new MsiAssemblyNameTable(); AssemblyNameTable_ProcessorArchitecture.Component_ = "C__" + basename; AssemblyNameTable_ProcessorArchitecture.Name = "ProcessorArchitecture"; AssemblyNameTable_ProcessorArchitecture.Value = "MSIL"; //---------------------------------------------------------------------- //3. create an entry in the Component table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "Component", ComponentTable, 6); } catch (Exception ex) { throw new Exception("Failed to create record in the Component Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //4. create an entry in the File table //---------------------------------------------------------------------- //this entry is for the file INSIDE our CAB file try { MsiCreateRecordFromMsiTable(hDatabase, "File", FileTable, 8); } catch (Exception ex) { throw new Exception("Failed to create record in the File Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //5. create an entry in the FeatureComponents table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "FeatureComponents", FeatureComponentsTable, 2); } catch (Exception ex) { throw new Exception("Failed to create record in the FeatureComponents Table.\n\n" + ex.Message); } //---------------------------------------------------------------------------------------- //6. create an entry in the media table for the CAB file that will contain this new file //---------------------------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "Media", MediaTable, 6); } catch (Exception ex) { throw new Exception("Failed to create record in the Media Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //7. load the binary data from the CAB file as a stream in MSI db //---------------------------------------------------------------------- try { MsiAddInternalBinaryStream(hDatabase, tmpCabName, "_" + cabId, tmpCabFullName); } catch (Exception ex) { throw new Exception("Failed to add binary stream from CAB to MSI database!\n\n" + ex.Message); } //---------------------------------------------------------------------- //8. create an entry in the MsiAssembly table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "MsiAssembly", AssemblyTable, 5); } catch (Exception ex) { throw new Exception("Failed to create record in the MsiAssembly Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //9. create an entry in the MsiAssemblyName table //---------------------------------------------------------------------- try { MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_Name, 3); MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_Version, 3); MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_Culture, 3); MsiCreateRecordFromMsiTable(hDatabase, "MsiAssemblyName", AssemblyNameTable_ProcessorArchitecture, 3); } catch (Exception ex) { throw new Exception("Failed to create a record in the MsiAssemblyName Table.\n\n" + ex.Message); } //---------------------------------------------------------------------- //10. Finalize //---------------------------------------------------------------------- //dump the table values for debugging purposes MsiDumpAllTables(hDatabase); //commit all changes and cleanup CwMsiWin32.MsiDatabaseCommit(hDatabase); CwMsiWin32.MsiCloseHandle(hDatabase); //cleanup MsiCleanUp(tmpCabName); }