locked
Need a script to add RemoveFile table to the msi RRS feed

  • Question

  • Hi,

    I am not sure if this is a right place to ask the question. In case if it not the right forum, please guide me to the right forum.

    I am writing a vbscript to automate the process of mst creation for a msi. I actually need to add to write an entry in the RemoveFile table. But I am not able to do it, since the msi doesn't contain the Table. So I need a SQL query which would add the RemoveFile table to the database. I am not really sure what kind of data types the RemoveFile Table holds.

    Here's what I tried.

    Const ForAppending = 8
    Const ForReading = 1
    Const ForWriting = 2
    Const msiOpenDatabaseModeReadOnly = 0
    Const msiOpenDatabaseModeTransact = 1
    Const msiTransformErrorAddExistingRow = 1
    Const msiTransformErrorDeleteNonExistingRow = 2
    Const msiTransformErrorAddExistingTable = 4
    Const msiTransformErrorDeleteNonExistingTable = 8
    Const msiTransformErrorUpdateNonExistingRow = 16
    Const msiTransformErrorChangeCodePage = 32
    Const msiTransformErrorViewTransform = 256


    Dim i,sql, view, oMSI, oMST, transformWithChanges, temporaryMSI, objInstaller, database, objFSO, errorcondition, ALLUSERS, REINSTALLMODE, REBOOT, ROOTDRIVE, ProductName, Manufacturer, ProductVersion, Name, Value, Key, TypeLib, Tvalue, p1, p2, GUID
    errorCondition = msiTransformErrorChangeCodePage + msiTransformErrorUpdateNonExistingRow + msiTransformErrorDeleteNonExistingTable + msiTransformErrorAddExistingTable + msiTransformErrorDeleteNonExistingRow + msiTransformErrorAddExistingRow

    SET objFSO = CreateObject("Scripting.FileSystemObject")
    Set objInstaller = CreateObject("WindowsInstaller.Installer")
    Set wshShell = CreateObject("WScript.Shell")


    For i = 0 To Wscript.Arguments.Count - 1
     If Right(LCase(WScript.Arguments.Item(i)),3) = "msi" Then
      msiname = WScript.Arguments.Item(i)
     End If
    Next

    ' Creating a temporary msi to work on
    SET oMSI = objFSO.GetFile(msiname)
    TRANSFORM = Left(oMSI.Path, InStrRev(oMSI.Path, ".") - 1) & ".MST"
    TMSI = oMSI.ParentFolder & "\" & objFSO.GetTempName
    oMSI.COPY(TMSI)
    objFSO.GetFile(TMSI).Attributes = 0

    Path = oMSI.ParentFolder & "\"
    Set Database = objInstaller.OpenDatabase(TMSI , msiOpenDatabaseModeTransact)


    'Adding RemoveFile entry in the Component table, FeatureComponents table and RemoveFile table.

    sql = "Create Table `RemoveFile` (`FileKey` char(72) NOT NULL,`Component_` char(72) NOT NULL,`FileName` char(72),`DirProperty` char(72) NOT NULL, `InstallMode` Int(10) NOT NULL, LOCALIZABLE PRIMARY KEY `FileKey`)"
    MsgBox sql
    Set view = Database.OpenView(sql)
    view.Execute

    GUID = GetGUID()
    sql = "INSERT INTO `Component` (`Component`,`ComponentId`,`Directory_`,`Attributes`,`Condition`,`KeyPath`) VALUES ('RemoveFile','{" & GUID & "}','TARGETDIR',0,' ','')"
    Set view = Database.OpenView(sql)
    view.Execute

    sql = "SELECT `Feature` FROM `Feature`"
    Set view = Database.OpenView(sql)
    view.Execute
    Feature = view.fetch().Stringdata(1)
     
    sql = "INSERT INTO `FeatureComponents` (`Feature_`,`Component_`) VALUES ('" & Feature & "', 'RemoveFile')"
    Set view = Database.OpenView(sql)
    view.Execute

    sql = "INSERT INTO `RemoveFile` (`FileKey`,`Component_`,`FileName`,`DirProperty`,`InstallMode`) VALUES ('RemoveFile','RemoveFile','mms.cfg','FLASH',2)"
    MsgBox sql
    Set view = Database.OpenView(sql)
    view.Execute


    Function GetGUID()

      Set TypeLib = CreateObject("Scriptlet.TypeLib")
      GetGUID = mid(TypeLib.Guid,2,36)
     
    End Function

    Sub Fail(message)
        WScript.Echo message
        Wscript.Quit 2

    End Sub

    Set Record = Nothing
    Database.Commit


    Set Database1 = objInstaller.OpenDatabase(oMSI.Path , msiOpenDatabaseModeReadOnly)
    Database.GenerateTransform Database1, TRANSFORM
    Database.CreateTransformSummaryInfo Database1, TRANSFORM, 0, 0
    Database.Commit


    Set View  = Nothing
    Set Database = Nothing
    Set objInstaller = Nothing
    SET File= NOthing

    Set objFSO = Nothing
    WScript.echo("Created")

    The script gives me an MSI API error. So the error is exactly at the moment I am trying to Create the RemoveFile table. I think the SQL query I am using to create the table is wrong. https://msdn.microsoft.com/en-us/library/aa368562(v=vs.85).aspx -> I have used this article as my reference.

    Would somebody please let me know what the SQL query should be?

    PS: In case if this is the right forum for this question, please move this to the right forum.

    • Moved by Bill_Stewart Monday, November 23, 2015 10:56 PM This is not "teach me all details of Windows Installer object model" forum
    Thursday, September 24, 2015 8:58 AM

Answers

  • You are asking a question about the details of the Windows Installer object model and manipulation of Windows Installer objects. My recommendation would be to start with the documentation on the Windows Installer object model:

    http://msdn.microsoft.com/en-us/library/aa369432.aspx

    If you need assistance beyond that, you will probably need to open a ticket with Microsoft support to find someone who knows the details about how to do exactly what you're asking (if it's possible).


    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by Bill_Stewart Monday, November 23, 2015 10:55 PM
    Thursday, September 24, 2015 12:30 PM

All replies

  • Why do you think you need to do this with a script?  Use ORCA.

    \_(ツ)_/

    Thursday, September 24, 2015 9:16 AM
  • Yes, ORCA can be used. But it is also possible to add it via a script, so that no intervention is required from anyone other than running the script. Please let me know if you can help?
    Thursday, September 24, 2015 9:26 AM
  • You are asking a question about the details of the Windows Installer object model and manipulation of Windows Installer objects. My recommendation would be to start with the documentation on the Windows Installer object model:

    http://msdn.microsoft.com/en-us/library/aa369432.aspx

    If you need assistance beyond that, you will probably need to open a ticket with Microsoft support to find someone who knows the details about how to do exactly what you're asking (if it's possible).


    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by Bill_Stewart Monday, November 23, 2015 10:55 PM
    Thursday, September 24, 2015 12:30 PM
  • You have two major issues with your script.  One is that it is full of back-ticks.  They will not work.  This is a residue of blindly copying scripts from web pages.  It is also an issue caused by people trying to quote everything that they see.  Don't quote when not required.

    The second issue is guessing at how to declare an integer.  If you had looked up the SQL for the instaler you would see that you can only use INT or INTEGER with no length.

    I have fixed two lines as a demo of how this works.

    msifile = "c:\test\msi\test2.msi"
    Set objInstaller = CreateObject("WindowsInstaller.Installer")
    Set Database = objInstaller.OpenDatabase(msifile , 1)
    
    'Adding RemoveFile entry in the Component table, FeatureComponents table and RemoveFile table.
    sql = "Create Table RemoveFile(FileKey char(72) NOT NULL,Component_ char(72) NOT NULL,FileName char(72),DirProperty char(72) NOT NULL, InstallMode integer NOT NULL LOCALIZABLE PRIMARY KEY FileKey)"
    Set view = Database.OpenView(sql)
    view.Execute
    
    sql = "INSERT INTO RemoveFile(FileKey,Component_,FileName,DirProperty,InstallMode) VALUES('RemoveFile','RemoveFile','mms.cfg','FLASH',2)"
    Set view = Database.OpenView(sql)
    view.Execute 
    Database.Commit

    This works on an empty MSI file I created.  It may have object conflicts on a full MSI.

    The following will more correctly create a full GUID without removing the braces:

    Function GetGUID()
    	Set TypeLib = CreateObject("Scriptlet.TypeLib")
    	guid=TypeLib.Guid
    	GetGUID = Left(guid,Len(guid)-2)
    End Function


    \_(ツ)_/


    • Edited by jrv Saturday, September 26, 2015 6:33 PM
    Saturday, September 26, 2015 6:32 PM
  • Hi jrv,

    Thanks for your help. Yes, I have understood that the declaration of Interger could be an issue and I already tried declaring it as INT,INTEGER and SHORT. Nothing is woking. I need to check if this is working on an empty msi. Let me try this and get back to you.

    Thanks.

    Sunday, September 27, 2015 6:01 AM
  • Here is a full test using an in-memory database with no file.  You can just run this and it will work.  Add a file name and it will create a blank data base with the new table.  THis script can be run at anytimeonany system and it does not change the system in any way.

    Set objInstaller = CreateObject("WindowsInstaller.Installer")
    Set Database = objInstaller.OpenDatabase("" , 3)
    
    'Adding RemoveFile entry in the Component table, FeatureComponents table and RemoveFile table.
    sql = "Create Table RemoveFile(FileKey char(72) NOT NULL,Component_ char(72) NOT NULL,FileName char(72),DirProperty char(72) NOT NULL, InstallMode integer NOT NULL LOCALIZABLE PRIMARY KEY FileKey)"
    Set view = Database.OpenView(sql)
    view.Execute
    WScript.Echo "Created table"
    
    sql = "INSERT INTO RemoveFile(FileKey,Component_,FileName,DirProperty,InstallMode) VALUES('RemoveFile','RemoveFile','mms.cfg','FLASH',2)"
    Set view = Database.OpenView(sql)
    view.Execute 
    
    WScript.Echo "Inserted record"
    
    Database.Commit
    WScript.Echo "Successfully commited changes."
    
    


    \_(ツ)_/

    Sunday, September 27, 2015 3:34 PM
  • Here is the same script in PowerShell which is easier too test line by line.

    $msi=New-Object -ComObject WindowsInstaller.Installer
    $db=$msi.OpenDatabase("" , 3)
    
    #Adding RemoveFile entry in the Component table, FeatureComponents table and RemoveFile table.
    $sql = "Create Table RemoveFile(FileKey char(72) NOT NULL,Component_ char(72) NOT NULL,FileName char(72),DirProperty char(72) NOT NULL, InstallMode integer NOT NULL LOCALIZABLE PRIMARY KEY FileKey)"
    
    $view = $db.OpenView($sql)
    $view.Execute()
    $view.Close()
    Write-Host 'Created table' -ForegroundColor green
    
    $sql = "INSERT INTO RemoveFile(FileKey,Component_,FileName,DirProperty,InstallMode) VALUES('RemoveFile','RemoveFile','mms.cfg','FLASH',2)"
    $view = $db.OpenView($sql)
    $view.Execute()
    $view.Close()
    
    Write-Host 'Inserted record' -fore green
    
    $db.Commit()
    Write-Host 'Successfully commited changes.' -fore green
    
    # Installer SQl statements look like this (no backticks or quotes)
    <#
    	CREATE TABLE RemoveFile(
    		FileKey char(72) NOT NULL,
    		Component_ char(72) NOT NULL,
    		FileName char(72),
    		DirProperty char(72) NOT NULL,
    		InstallMode integer NOT NULL
    		LOCALIZABLE PRIMARY KEY FileKey
    	)
    #>
    
    <#
    	INSERT INTO RemoveFile(
    		FileKey,
    		Component_,
    		FileName,
    		DirProperty,
    		InstallMode
    	)
    	VALUES(
    		'RemoveFile',
    		'RemoveFile',
    		'mms.cfg',
    		'FLASH',
    		2
    	)
    #>
    
     

    I have included a more readable view of what the  SQL statements look like.


    \_(ツ)_/



    • Edited by jrv Sunday, September 27, 2015 4:00 PM
    Sunday, September 27, 2015 3:58 PM