public bool MsiAddInternalBinaryStream(IntPtr hDatabase, string cabname, string cabId, string cabfullpath) { //for info on the _Streams table, see MSDN: // http://msdn.microsoft.com/en-us/library/aa372919(VS.85).aspx Dictionary <int, Dictionary <string, string> > tableRecords = new Dictionary <int, Dictionary <string, string> >(); Dictionary <string, string> record = new Dictionary <string, string>(); uint retVal = CwMsiWin32.ERROR_SUCCESS; string selectQuery = "SELECT * FROM `_Streams`"; IntPtr hView = IntPtr.Zero; IntPtr hRecord = IntPtr.Zero; //open the database view retVal = CwMsiWin32.MsiDatabaseOpenViewW(hDatabase, selectQuery, out hView); MsiThrowOnFailure(hDatabase, retVal, "MsiDatabaseOpenViewW()"); //create a new record hRecord = CwMsiWin32.MsiCreateRecord(2); //2 fields: Name and Data //set the Name column of the _Streams table to the Cabinet name (file name on disk) retVal = CwMsiWin32.MsiRecordSetStringW(hRecord, 1, cabId); MsiThrowOnFailure(hDatabase, retVal, "MsiRecordSetStringW: Failed to set the Name column in the _Streams table to '" + cabname + "'"); //set the Data column of the _Streams table to the Cabinet name (full file path location on disk) retVal = CwMsiWin32.MsiRecordSetStreamW(hRecord, 2, cabfullpath); MsiThrowOnFailure(hDatabase, retVal, "MsiRecordSetStream: Failed to set the Data column in the _Streams table to '" + cabfullpath + "'"); //apply the update retVal = CwMsiWin32.MsiViewModify(hView, CwMsiWin32.MSIMODIFY_INSERT, hRecord); MsiThrowOnFailure(hDatabase, retVal, "MsiViewModify: Failed to apply update to CAB '" + cabId + "'"); //commit database retVal = CwMsiWin32.MsiDatabaseCommit(hDatabase); MsiThrowOnFailure(hDatabase, retVal, "MsiDatabaseCommit: Could not commit msi db."); CwMsiWin32.MsiCloseHandle(hView); CwMsiWin32.MsiCloseHandle(hRecord); return(true); }
public void MsiCreateEditRecordInTable(IntPtr hDatabase, string tableName, string action, int recordnumber, int[] fieldnumbers, ArrayList fieldvalues) { uint retVal = CwMsiWin32.ERROR_SUCCESS; int numfields = fieldvalues.Count; int msiAction = -1; //create a view to be used later for the operation IntPtr hView = IntPtr.Zero; string q = "SELECT * FROM `" + tableName + "`"; retVal = CwMsiWin32.MsiDatabaseOpenViewW(hDatabase, q, out hView); MsiThrowOnFailure(hDatabase, retVal, "MsiDatabaseOpenViewW('" + q + "')"); //execute the view retVal = CwMsiWin32.MsiViewExecute(hView, IntPtr.Zero); MsiThrowOnFailure(hDatabase, retVal, "MsiViewExecute()"); IntPtr hRecord = (IntPtr)(0); //create a record ptr with numfields if (action == "CreateRecord") { hRecord = CwMsiWin32.MsiCreateRecord((uint)numfields); msiAction = CwMsiWin32.MSIMODIFY_INSERT; if (hRecord == IntPtr.Zero) { throw new Exception("MsiCreateRecord"); } int fieldnum = 1; //note: not a zero-based index! //loop through each field in this record and set it foreach (object val in fieldvalues) { if (val.GetType() == typeof(string)) { retVal = CwMsiWin32.MsiRecordSetStringW(hRecord, (uint)fieldnum, (string)val); } else if (val.GetType() == typeof(Int32)) { retVal = CwMsiWin32.MsiRecordSetInteger(hRecord, (uint)fieldnum, (Int32)val); } else if (val.GetType() == typeof(Int16)) { retVal = CwMsiWin32.MsiRecordSetInteger(hRecord, (uint)fieldnum, (Int16)val); } MsiThrowOnFailure(hDatabase, retVal, "MsiRecordSetStringW(" + fieldnum.ToString() + ", '" + val + "')"); fieldnum++; } } else if (action == "ChangeFieldsInRecord") { //make sure the fieldnumbers size and fieldvalues size match if (fieldnumbers.Length != fieldvalues.Count) { retVal = CwMsiWin32.MsiCloseHandle(hView); MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle(hView)"); throw new Exception("fieldnumbers and fieldvalues are not equal."); } hRecord = MsiGetRecordHandle(hDatabase, hView, recordnumber); int i = 0; //loop through all field numbers we want to change in this record //and alter those fields in the record we just retrieved foreach (int fieldnum in fieldnumbers) { retVal = CwMsiWin32.MsiRecordSetStringW(hRecord, (uint)fieldnum, fieldvalues[i].ToString()); MsiThrowOnFailure(hDatabase, retVal, "MsiRecordSetStringW(hRecord," + fieldnum.ToString() + "," + fieldvalues[i] + ")"); i++; } msiAction = CwMsiWin32.MSIMODIFY_UPDATE; } else { retVal = CwMsiWin32.MsiCloseHandle(hView); MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle(hView)"); throw new Exception("Error: Invalid action specified '" + msiAction + "'!"); } if (hRecord == (IntPtr)(-1)) { throw new Exception("Failed to retrieve hRecord!"); } //perform the operation on the MSI database retVal = CwMsiWin32.MsiViewModify(hView, msiAction, hRecord); MsiThrowOnFailure(hDatabase, retVal, "MsiViewModify - failed to alter/create record"); //cleanup retVal = CwMsiWin32.MsiCloseHandle(hRecord); MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle(hRecord)"); retVal = CwMsiWin32.MsiCloseHandle(hView); MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle(hView)"); retVal = CwMsiWin32.MsiDatabaseCommit(hDatabase); MsiThrowOnFailure(hDatabase, retVal, "MsiDatabaseCommit(hDatabase)"); }
public void MsiCreateRecordFromMsiTable(IntPtr hDatabase, string tableName, object tableStruct, int numfields) { uint retVal = CwMsiWin32.ERROR_SUCCESS; IntPtr hView = IntPtr.Zero; string q = "SELECT * FROM `" + tableName + "`"; IntPtr hRecord = (IntPtr)(0); int fieldnum = 1; //create a view to be used later for the operation retVal = CwMsiWin32.MsiDatabaseOpenViewW(hDatabase, q, out hView); MsiThrowOnFailure(hDatabase, retVal, "MsiDatabaseOpenViewW('" + q + "')"); //execute the view retVal = CwMsiWin32.MsiViewExecute(hView, IntPtr.Zero); MsiThrowOnFailure(hDatabase, retVal, "MsiViewExecute()"); //create the record hRecord = CwMsiWin32.MsiCreateRecord((uint)numfields); if (hRecord == IntPtr.Zero) { throw new Exception("MsiCreateRecord"); } //get the fields for this table - note we can't use [type].GetType().GetFields() //because this function returns the fields in an unpredictable order! string[] fields = MsiTableGetFieldNames(tableName); if (fields == null) { throw new Exception("No fields for table '" + tableName + "'."); } if (fields.Length != numfields) { throw new Exception("Field count mismatch: fields.Length=" + fields.Length.ToString() + " / numfields=" + numfields.ToString()); } foreach (string thisfield in fields) { FieldInfo fi = tableStruct.GetType().GetField(thisfield); object value = fi.GetValue(tableStruct); if (value.GetType() == typeof(string)) { retVal = CwMsiWin32.MsiRecordSetStringW(hRecord, (uint)fieldnum, (string)value); } else if (value.GetType() == typeof(int)) { retVal = CwMsiWin32.MsiRecordSetInteger(hRecord, (uint)fieldnum, (int)value); } else if (value.GetType() == typeof(Int16)) { retVal = CwMsiWin32.MsiRecordSetInteger(hRecord, (uint)fieldnum, (Int16)value); } MsiThrowOnFailure(hDatabase, retVal, "MsiRecordSetStringW(" + fieldnum.ToString() + ", '" + value.ToString() + "')"); fieldnum++; } //perform the operation on the MSI database retVal = CwMsiWin32.MsiViewModify(hView, CwMsiWin32.MSIMODIFY_INSERT, hRecord); MsiThrowOnFailure(hDatabase, retVal, "MsiViewModify - failed to alter/create record"); //cleanup retVal = CwMsiWin32.MsiCloseHandle(hRecord); MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle(hRecord)"); retVal = CwMsiWin32.MsiCloseHandle(hView); MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle(hView)"); retVal = CwMsiWin32.MsiDatabaseCommit(hDatabase); MsiThrowOnFailure(hDatabase, retVal, "MsiDatabaseCommit(hDatabase)"); }
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); }