Example #1
0
        public void MsiThrowOnFailure(IntPtr hDatabase, uint retVal, string message)
        {
            if (retVal != CwMsiWin32.ERROR_SUCCESS)
            {
                IntPtr hView   = IntPtr.Zero;
                IntPtr hRecord = IntPtr.Zero;
                hRecord = CwMsiWin32.MsiGetLastErrorRecord();  //try to get a handle to the error record
                string retString = message + "( SYSTEM ERROR CODE " + retVal + ").";

                if (hRecord != IntPtr.Zero)
                {
                    uint errCode = CwMsiWin32.MsiRecordGetInteger(hRecord, 1); //MSDN says field 1 is the err code
                    retString += "\n\nError Table data (code=" + errCode + ")";
                    IntPtr lpBuffer      = Marshal.AllocHGlobal(4096);
                    uint   pcchResultBuf = 4096;

                    //format the record string
                    CwMsiWin32.MsiFormatRecord(IntPtr.Zero, hRecord, lpBuffer, ref pcchResultBuf);

                    //add it to our main return string
                    retString += "'" + Marshal.PtrToStringAnsi(lpBuffer) + "'";

                    //cleanup duty
                    Marshal.FreeHGlobal(lpBuffer);
                    CwMsiWin32.MsiCloseHandle(hRecord); //discard the error record handle now that we have the code
                    hRecord = IntPtr.Zero;
                }

                throw new Exception(retString);
            }
        }
Example #2
0
        internal uint MsiRecordGetStringW(IntPtr hRecord, uint iField, out string szValueBuf)
        {
            uint   len    = 256;
            string sz     = new string(' ', (int)len);
            IntPtr bstr   = Marshal.StringToBSTR(sz);
            uint   retVal = CwMsiWin32.MsiRecordGetStringW(hRecord, iField, bstr, ref len);

            if (retVal == CwMsiWin32.ERROR_SUCCESS)
            {
                szValueBuf = Marshal.PtrToStringUni(bstr);
            }
            else
            {
                szValueBuf = null;
            }

            Marshal.FreeBSTR(bstr);
            bstr = IntPtr.Zero;

            return(retVal);
        }
Example #3
0
        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);
        }
Example #4
0
        public IntPtr MsiGetRecordHandle(IntPtr hDatabase, IntPtr hView, int desiredRecordNumber)
        {
            int    index, rowcount = 0;
            IntPtr hRecord = IntPtr.Zero;
            string propertyFieldValue;
            uint   retVal = CwMsiWin32.ERROR_SUCCESS;

            //string colname;
            //IntPtr hColNames;

            //get first record
            retVal = CwMsiWin32.MsiViewFetch(hView, out hRecord);
            MsiThrowOnFailure(hDatabase, retVal, "MsiViewFetch");

            //loop through all records in this view
            do
            {
                index = 0;
                Dictionary <int, string> values = new Dictionary <int, string>();

                //get all fields in this record
                while (MsiRecordGetStringW(hRecord, (uint)index, out propertyFieldValue) == CwMsiWin32.ERROR_SUCCESS && propertyFieldValue != "")
                {
                    //string colname = MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, hColNames);
                    values[(int)index] = propertyFieldValue;
                    index++;
                }

                if (rowcount == desiredRecordNumber)
                {
                    break;
                }

                retVal = CwMsiWin32.MsiViewFetch(hView, out hRecord);
                rowcount++;
            }while (retVal != CwMsiWin32.ERROR_NO_MORE_ITEMS);

            return(hRecord);
        }
Example #5
0
        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)");
        }
Example #6
0
        public Dictionary <int, Dictionary <string, string> > MsiGetTableRows(IntPtr hDatabase, string tableName)
        {
            Dictionary <int, Dictionary <string, string> > tableRecords = new Dictionary <int, Dictionary <string, string> >();
            Dictionary <string, string> record = new Dictionary <string, string>();
            int    fieldcount = 0;
            uint   retVal     = CwMsiWin32.ERROR_SUCCESS;
            string propertyFieldValue;
            string selectQuery = "SELECT * FROM `" + tableName + "`";
            int    recordCount = 0;
            IntPtr hView       = IntPtr.Zero;
            IntPtr hRecord     = IntPtr.Zero;
            IntPtr hColnames   = IntPtr.Zero;

            retVal = CwMsiWin32.MsiDatabaseOpenViewW(hDatabase, selectQuery, out hView);
            //if we get bad syntax here, just keep going b/c it could just be an invalid table name..?
            if (retVal != CwMsiWin32.ERROR_BAD_QUERY_SYNTAX && retVal != CwMsiWin32.ERROR_SUCCESS)
            {
                MsiThrowOnFailure(hDatabase, retVal, "MsiDatabaseOpenViewW()");
            }
            if (retVal == CwMsiWin32.ERROR_BAD_QUERY_SYNTAX)
            {
                return(null);
            }

            retVal = CwMsiWin32.MsiViewExecute(hView, IntPtr.Zero);
            MsiThrowOnFailure(hDatabase, retVal, "MsiViewExecute()");

            // Loop through the properties and copy the ones passed in to this function
            do
            {
                //clear the record var
                record = new Dictionary <string, string>();

                //fetch the view
                retVal = CwMsiWin32.MsiViewFetch(hView, out hRecord);
                if (retVal != CwMsiWin32.ERROR_SUCCESS && retVal != CwMsiWin32.ERROR_NO_MORE_ITEMS)
                {
                    MsiThrowOnFailure(hDatabase, retVal, "MsiViewFetch");
                }
                if (retVal == CwMsiWin32.ERROR_NO_MORE_ITEMS)
                {
                    break;
                }

                //get the field count
                fieldcount = (int)CwMsiWin32.MsiRecordGetFieldCount(hRecord) + 1; //must add 1 b/c it ignores column 0..?!

                //get the column names
                retVal = CwMsiWin32.MsiViewGetColumnInfo(hView, CwMsiWin32.MSICOLINFO_NAMES, out hColnames);
                MsiThrowOnFailure(hDatabase, retVal, "MsiViewGetColumnInfo");
                string [] colnames = new string[fieldcount];

                //loop the number of times there are fields and get a column name for each
                for (int i = 0; i < fieldcount; i++)
                {
                    string thisColName = "";
                    MsiRecordGetStringW(hColnames, (uint)i, out thisColName);
                    if (thisColName == "" && i == 0) //primary key is 0
                    {
                        thisColName = "Primary Key";
                    }
                    else if (thisColName == "")
                    {
                        thisColName = "[unknown]";
                    }
                    colnames[i] = thisColName;
                }

                //loop through all fields, get the column name, and store in our dictionary
                for (int i = 0; i < fieldcount; i++)
                {
                    retVal = MsiRecordGetStringW(hRecord, (uint)i, out propertyFieldValue);
                    MsiThrowOnFailure(hDatabase, retVal, "MsiRecordGetStringW(hRecord," + i.ToString() + "," + propertyFieldValue + ")");
                    record[colnames[i]] = propertyFieldValue;
                }

                //save the record in our table of records
                tableRecords[recordCount] = record;

                retVal = CwMsiWin32.MsiCloseHandle(hRecord);
                MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle(hRecord)");
                recordCount++;
            }while (retVal != CwMsiWin32.ERROR_NO_MORE_ITEMS);

            retVal = CwMsiWin32.MsiCloseHandle(hView);
            MsiThrowOnFailure(hDatabase, retVal, "MsiCloseHandle");

            return(tableRecords);
        }
Example #7
0
        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)");
        }
Example #8
0
        ////////////////////////////////////////////////////////////////////////////////////////
        //
        //                      MSICLASS FUNCTION DEFINITIONS
        //
        //
        ////////////////////////////////////////////////////////////////////////////////////////

        public void Start(BackgroundWorker workerThread, int pStep, DoWorkEventArgs workEventArgs)
        {
            string    msiName         = "Codeword_Installer.msi";
            int       progress        = 0;
            ArrayList FilesToAddToMsi = new ArrayList();
            string    tmpCabName      = "tmp.cab";

            //arguments passed in from GUI
            ArrayList args = (ArrayList)workEventArgs.Argument;
            bool      skipDotNetFxAssembly = (bool)args[0];
            ArrayList x509certs            = (ArrayList)args[1];
            ArrayList thirdPartyApps       = (ArrayList)args[2];

            //add any x509 certificates now
            if (x509certs.Count > 0)
            {
                foreach (string x in x509certs)
                {
                    if (x != "")
                    {
                        FilesToAddToMsi.Add(x);
                    }
                }
            }

            //add any third party apps we should run post-scan
            if (thirdPartyApps.Count > 0)
            {
                foreach (string filename in thirdPartyApps)
                {
                    if (filename != "")
                    {
                        FilesToAddToMsi.Add(filename);
                    }
                }
            }

            //********************************************************
            //              EXTRACT DOTNETFX
            //********************************************************
            //while dotnetfx.exe (21mb) .NET 2.0 installer is included
            //in the Handler assembly itself, we do not always necessarily
            //write it to the generated MSI installer -- the only time
            //we DONT is when the user has checked the appropriate checkbox
            if (!skipDotNetFxAssembly)
            {
                try
                {
                    CwUtilitiesHelperClass.ExtractInternalResource("CwHandler.Resources.dotnetfx.exe", "dotnetfx.exe");
                }
                catch (Exception ex)
                {
                    workEventArgs.Result = false;
                    throw new Exception("The .NET 2.0 Framework installer file (dotnetfx.exe) was not found.  If you do not want to deploy .NET, please select the appropriate checkbox on the Options tab.\n\nError text:  '" + ex.Message);
                }

                FilesToAddToMsi.Add("dotnetfx.exe");
                workerThread.ReportProgress(25);
            }

            //********************************************************
            //              EXTRACT TEMPLATE MSI
            //********************************************************
            //rather than create an MSI from scratch, we will use and build
            //upon the MSI created in Visual Studio for our tool (CwInstaller.msi).
            //this MSI is stored in our own internal assembly (in memory)
            //It initially contains:
            //      (1) CwAgent.exe - the actual program binary
            //      (2) Ionic.Utils.Zip.dll - zipping library needed
            //We will add to it:
            //      (1) CwAgentConfiguration.xml - the config file the admin just generated
            //      (2) dotnetfx.exe - .NET installer that setup.exe expects to be in same folder
            //      (3) any other files specified by the admin
            //  this MSI will wrap all these files into an installer database msi file
            //  which can be pushed out and executed on host systems using SMS or other distro system
            try
            {
                CwUtilitiesHelperClass.ExtractInternalResource("CwHandler.Resources.CwInstaller.msi", msiName);
            }
            catch (Exception ex)
            {
                workEventArgs.Result = false;
                throw new Exception(ex.Message);
            }

            workerThread.ReportProgress(25);

            //********************************************************
            //              SET MSI PROPERTIES
            //********************************************************
            //setup projects are retarded.  one example of this retardedness
            //is that the setup project will not install over older versions
            //unless the version number has changed (this is set in the Setup Project's properties in VS)
            //a hack , as described here http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework/2009-01/msg00161.html
            //is to set the REINSTALLMODE to "amus" ...?
            IntPtr hProduct = IntPtr.Zero;

            CwMsiWin32.MsiOpenPackage(msiName, hProduct);
            CwMsiWin32.MsiSetProperty(hProduct, "REINSTALLMODE", "amus");
            //also, set the product code to a new version, so taht when the MSI is installed,
            //it removes the other version.  See:  http://msdn.microsoft.com/en-us/library/aafz9hx4(VS.80).aspx
            CwMsiWin32.MsiSetProperty(hProduct, "PRODUCTCODE", Guid.NewGuid().ToString().ToUpper());
            CwMsiWin32.MsiCloseHandle(hProduct);

            //********************************************************
            //              EXTRACT CABARC UTILITY
            //********************************************************
            try
            {
                CwUtilitiesHelperClass.ExtractInternalResource("CwHandler.Resources.CABARC.EXE", "CABARC.EXE");
            }
            catch (Exception ex)
            {
                workEventArgs.Result = false;
                throw new Exception(ex.Message);
            }

            workerThread.ReportProgress(50);

            //manually add two files we know will exist at this point-
            //  -CwAgentConfiguration.xml - agent config
            //  -CwAgentSignatures.xml - signatures
            FilesToAddToMsi.Add("CwAgentConfiguration.xml");
            FilesToAddToMsi.Add("CwAgentSignatures.xml");

            progress = 50;
            int progressPerFile = progress / FilesToAddToMsi.Count;

            foreach (string filename in FilesToAddToMsi)
            {
                //process cancelation request?
                if (workerThread.CancellationPending)
                {
                    workEventArgs.Cancel = true;
                }
                else
                {
                    try
                    {
                        MsiAddFile(msiName, filename, tmpCabName);
                        progress += progressPerFile;
                        workerThread.ReportProgress(progress);
                    }
                    catch (Exception e)
                    {
                        MsiCleanUpOnFailure(tmpCabName);
                        workEventArgs.Result = false;
                        throw new Exception("Failed to add '" + filename + "' to the MSI database in '" + msiName + "'.  This MSI is most likely corrupt now.\n\n" + e.Message);
                    }
                }
            }

            return;
        }
Example #9
0
        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);
        }