Jump to content
Sign in to follow this  
benners

Help needed with vbs to AutoIt conversion

Recommended Posts

benners

I have converted a vbs script to AutoIt for a program that I have written. I am having a problem where some users encounter an AutoIt error which I myself cannot reproduce. The code works for other users and myself but for some reason exits with the error "Line -1: Error: Error in expression" with a small percentage of users.

I with the logging function of the program I have tracked the problem to the line $session = $Installer.OpenPackage($DB). I would appreciate if someone could explain where I am going wrong or how to improve the code. With the vbs there is a call to an error checking function (: CheckError) after the command but I don't know how to replicate this. What I have done is add the line

Global $oMyError = ObjEvent('AutoIt.Error', 'ComErrorHandler')
which just sets $COM_Err = 1 and then a check is done for the value. I don't know if this is the best way (It obviously doesn't help with the current error) or if works for all of the code but as I have said previously I have no problems so I cannot track it down.

My function is below. It is not a complete conversion as some features are not required.

Func DI_UpdateCabs10($iembedCab = 0, $imakeCab = 1, $iupdateMsi = 1, $isequenceFile = 0, $scompressType = 'LZX')
    Local $Installer, $DB, $View, $Record, $updateMode, $SumInfo, $sequence, $lastSequence, $session, $shortNames, $sorderBy, $COM_Err = 0
    Local $fileKey, $fileName, $sequence, $folder, $delim, $attributes, $sourcePath
    Local $sOldFile, $sFile, $sNum, $iwCount, $oldPT, $msg

    Local $msiOpenDatabaseModeReadOnly = 0
    Local $msiOpenDatabaseModeTransact = 1
    Local $msiViewModifyInsert = 1
    Local $msiViewModifyUpdate = 2
    Local $msiViewModifyAssign = 3
    Local $msiUILevelNone = 2
    Local $msiRunModeSourceShortNames = 9
    Local $msidbFileAttributesNoncompressed = 8192

    $oldPT = $Proc_Title; Save the previous function process title.
    $Proc_Title = 'Rebuilding Cabinet File'; Set the process title.
    Log_Progress(0); Reset the progress bar.
    Log_Banner(0, $Proc_Title); Create a header for the process
    Log_Labels_Update('', 'Processing...', 'Retrieving cabinet info')
    $ssourceFolder = $Destination; Use the default destination.

    If DirGetSize($ssourceFolder) > 0 Then; If the Dir is not empty.
        If StringRight($ssourceFolder, 1) <> '\' Then $ssourceFolder &= '\'; Check for and add a '\'
        $session.Property("OriginalDatabase") = $ssourceFolder & '\'; Set the path to the MSI file.
    Else; Folder is empty.
        Log_Fatal('There are no files in the Source Folder: ' & $ssourceFolder, '', '', $ssourceFolder & ' is empty', $Proc_Title)
        Return 1
    EndIf

    $sdatabasePath = $ssourceFolder & $MSI; Set the location of the MSI.
    Local $cabFile = Msi_GetProperty($sdatabasePath, 'Cabinet', 'Media', 'DiskId', 1, 1); Get the cabinet name from the MSI.
    If $COM_Err Then Return 1; Error returned from getting cabfile.
    If StringLeft($cabFile, 1) = '#' Then $cabFile = StringReplace($cabFile, '#', ''); Trim the hash if cab file is embedded'
    Local $baseName = StringTrimRight($cabFile, 4)
    Local $DDF = $ssourceFolder & $baseName & '.ddf'
    Local $scabName = $cabFile
    If $iembedCab Then $scabName = '#' & $scabName; Add the # for embedded cabs.

    If $iupdateMsi Or $isequenceFile Then; If updating the MSI or re sequencing the file order.
        $iopenMode = $msiOpenDatabaseModeTransact; Open in write mode.
    Else
        $iopenMode = $msiOpenDatabaseModeReadOnly; Open in read mode.
    EndIf

    Log_Update('Database Path: ' & $sdatabasePath & '¦Source Folder: ' & $ssourceFolder & '¦Cabinet File : ' & $cabFile & '¦Base Name    : ' & $baseName & '¦DDF File name: ' & $baseName & '.ddf' & '¦¦Compress Type: ' & $scompressType & '¦MakeCab      : ' & _
            $imakeCab & '¦Embed Cab    : ' & $iembedCab & '¦Update MSI   : ' & $iupdateMsi & '¦Sequence File: ' & $isequenceFile & '¦¦Working Hard' & '...¦')

    $iwCount = MSI_SummaryInfo($sdatabasePath, 15); Get the compressed bit of the package (Expecting 2 (Compressed source files using long file name) or 3 (Compressed source files using short file names).
    Log_Update('Retrieving Compressed Bit: ' & $iwCount)
    Log_Update('Updating Compressed Bit  : ' & ($iwCount + 2))
    If MSI_SummaryInfo($sdatabasePath, 15, ($iwCount + 2), 1) = 1 Then Return; Change the compressed bit to the correct type of source file (Administrative image + long or short file names)
    Log_Update('Creating COM object   : 1')
    $Installer = ObjCreate("WindowsInstaller.Installer"); Create a reference to the Windows Installer COM.

    If Not IsObj($Installer) Then; Problem creating the COM object.
        $msg = 'Unable to create the WindowsInstaller COM object'
        $COM_Err = 1; Manually set the COM_Err.
    Else
        Log_Update('Opening database         : 1')
        $DB = $Installer.OpenDatabase($sdatabasePath, $iopenMode); Open database.

        If $COM_Err Then; Problem opening the MSI
            $msg = 'Unable to open file (' & $sdatabasePath & ') in Read mode'
        Else; Create an install session and execute actions in order to perform directory resolution
            Log_Update('Setting UILevel       : Silent')
            $Installer.UILevel = $msiUILevelNone; Set UI level of the installer to silent (msiUILevelNone).
            Log_Update('Opening installer package: 1')
            $session = $Installer.OpenPackage($DB)

            If $COM_Err Then; Problem starting a session.
                $msg = 'Database: ' & $sdatabasePath & ' Invalid installer package format'
            Else
                Log_Update('Setting Source files     : Short Names')
                $shortNames = $session.Mode($msiRunModeSourceShortNames); Set the mode property of the the session to Source files use only short file names (msiRunModeSourceShortNames).

                $session.Property("OriginalDatabase") = $ssourceFolder
                $stat = $session.DoAction("CostInitialize"); Run the CostInitialize action for the MSI.
                Log_Update('Running CostInitialize   : ' & $stat)

                If ($COM_Err) Or ($stat <> 1) Then
                    $msg = 'A problem occurred during CostInitialize'
                    $COM_Err = 1; Manually set the COM_Err.
                Else
                    $lastSequence = 0

                    If $isequenceFile Then; If re sequencing check for non-cabinet files to avoid sequence number collisions
                        $sorderBy = 'Directory_'; Order by Directory.
                        Log_Update('Resequencing by       : Directory')
                        $View = $DB.OpenView('SELECT Sequence, Attributes FROM File')

                        If $COM_Err Then; Error occured opening the view.
                            $msg = 'Unable to query the File Table'
                        Else
                            $View.Execute; Execute the query.

                            If $COM_Err Then; Error occured opening the view.
                                $msg = 'Unable to execute the view'
                            Else
                                While 1
                                    $Record = $View.fetch
                                    If Not IsObj($Record) Then ExitLoop; No more records.
                                    $sequence = $Record.IntegerData(1); Set the sequence number.
                                    If BitAND($Record.IntegerData(2), $msidbFileAttributesNoncompressed) <> 0 And $sequence > $lastSequence Then $lastSequence = $sequence
                                WEnd
                            EndIf
                        EndIf
                    Else; Not resequencing.
                        Log_Update('Resequencing by       : Sequence')
                        $sorderBy = 'Sequence'; Order by Sequence.
                    EndIf

                    $View.Close; Close the view.
                    $View = $DB.OpenView("SELECT File, FileName, Directory_, Sequence, File.Attributes FROM File, Component  WHERE Component_= Component ORDER BY " & $sorderBy)

                    If $COM_Err Then; Error occured opening the view.
                        $msg = 'Unable to query the File Table'
                    Else
                        $View.Execute; Execute the query.

                        If $COM_Err Then; Error occured opening the view.
                            $msg = 'Unable to execute the view'
                        Else
                            Log_Update('Creating DDF File       : ' & $DDF)
                            Log_Labels_Update('', 'Creating DDF file', $DDF); 'Direct Integration' & 'Creating DDF file'  | 'DDF file name'

                            If Not FileExists($DDF) Then; Create DDF file and write header properties
                                $file = FileOpen($DDF, 1); Open in write mode and append text.

                                If $file = -1 Then; Unable to create and open file.
                                    $COM_Err += 1
                                    $msg = 'Unable to open ' & $DDF & ' in write mode'
                                    Log_Fatal($msg, '', '', $msg, $Proc_Title); If COM error stop the process.
                                    Return $COM_Err
                                Else; Opened OK Write DDF header information.
                                    FileWriteLine($file, '; Generated from ' & $sdatabasePath & ' on ' & @YEAR & "/" & @MON & "/" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC)
                                    FileWriteLine($file, '.Set CabinetNameTemplate=' & $baseName & '*.CAB')
                                    FileWriteLine($file, '.Set CabinetName1=' & $cabFile)
                                    FileWriteLine($file, '.Set ReservePerCabinetSize=8')
                                    FileWriteLine($file, '.Set MaxDiskSize=CDROM')
                                    FileWriteLine($file, '.Set CompressionType=' & $scompressType)
                                    FileWriteLine($file, '.Set InfFileLineFormat=(*disk#*) *file#*: *file* = *Size*')
                                    FileWriteLine($file, '.Set InfFileName=' & $baseName & '.INF')
                                    FileWriteLine($file, '.Set RptFileName=' & $baseName & '.RPT')
                                    FileWriteLine($file, '.Set InfHeader=')
                                    FileWriteLine($file, '.Set InfFooter=')
                                    FileWriteLine($file, '.Set DiskDirectoryTemplate=.')
                                    FileWriteLine($file, '.Set Compress=ON')
                                    FileWriteLine($file, '.Set Cabinet=ON')
                                EndIf
                            Else
                                $file = FileOpen($DDF, 1); Open in write mode and append text.
                            EndIf

                            While 1; Fetch each file and request the source path, then verify the source path
                                $Record = $View.Fetch
                                If Not IsObj($Record) Then ExitLoop
                                $fileKey = $Record.StringData(1); Info from File Column.
                                $fileName = $Record.StringData(2); info from FileName column.
                                $folder = $Record.StringData(3); Folder location of the file.
                                $sequence = $Record.IntegerData(4); Sequence number of the file.
                                $attributes = $Record.IntegerData(5); Files attributes.

                                If BitAND($attributes, $msidbFileAttributesNoncompressed) = 0 Then
                                    If $sequence <= $lastSequence Then
                                        If Not $isequenceFile Then; Files need resequencing.
                                            Log_Fatal('Files exist with same sequence numbers. Select to sequence files', '', '', 'Files need resequencing but its not selected', $Proc_Title)
                                            Return 1
                                        Else; Files don't need resequencing.
                                            $sequence = $lastSequence + 1
                                            $Record.IntegerData(4) = $sequence
                                            $View.Modify($msiViewModifyUpdate, $Record)
                                        EndIf
                                    EndIf

                                    $lastSequence = $sequence; Set the last sequence.
                                    $delim = StringInStr($fileName, "|"); Check for pipe delimiter in filename.

                                    If $delim Then
                                        If $shortNames Then
                                            $fileName = StringLeft($fileName, $delim - 1)
                                        ;Log_update('shortnames ' & $fileName)
                                        Else
                                            $fileName = StringRight($fileName, StringLen($fileName) - $delim)
                                        ;Log_update('longnames ' & $fileName)
                                        EndIf
                                    EndIf

                                    $sourcePath = $session.SourcePath($folder) & $fileName; Get the session folder
                                ;Log_update('source path ' & $sourcePath)

                                    If Not FileExists($sourcePath) Then; Check for file.
                                        Log_Fatal($sourcePath & ' does not exist', '', '', $sourcePath & ' was not found')
                                        FileClose($file); Close the DDF file.
                                        Return 1
                                    EndIf

                                    FileWriteLine($file, """" & $sourcePath & """" & " " & $fileKey); Write the file to the ddf file.
                                EndIf
                            WEnd

                            FileClose($file); Close the DDF file.

                            If $imakeCab Then; If selected for recabbing.
                                Log_Update('Rebuilding cabinet file  : ' & $cabFile)
                                Log_Labels_Update('Rebuilding ' & $cabFile, '(?/?)', 'Current File:'); 'Direct Integration' & 'Updating Cabs'  | 'Recompressing Cabinet Files'
                                $Runstring = 'MakeCab.exe /f ' & $baseName & '.DDF'
                                Log_Update('Command Line             : ' & $Runstring & '¦')
                            ;$Run = Run(@ComSpec & ' /c ' & $Runstring, $ssourceFolder, @SW_HIDE, $STDOUT_CHILD); Generate compressed file cabinet.
                                $Run = Run($Runstring, $ssourceFolder, @SW_HIDE, $STDOUT_CHILD); Generate compressed file cabinet.
                                $sOldFile = ''

                                While ProcessExists($Run)
                                    $line1 = StdoutRead($Run); Read the output stream.

                                    If $line1 <> '' Then; If the output is not blank.
                                        $newLine = StringSplit($line1, @CR); Split the returned stream.
                                        $sStr = $newLine[(UBound($newLine) - 1) - 1]; Get the 2nd to last line.
                                        #cs
                                            If StringInStr($sStr, 'flushing current folder') Then
                                            GUICtrlSetData($Log_msg_lbl, 'Flushing current folder')
                                            GUICtrlSetData($Log_Action_lbl, 'Cleaning Up...')
                                            EndIf
                                        #ce
                                        $sFile = _StringBetween($sStr, '- ', ' ('); Split to get the file name.
                                        Log_Progress(StringMid($sStr, 1, (StringInStr($sStr, '%')) - 1)); Update the progress bar.

                                        If Not IsArray($sFile) Or ($sFile[0] = $sOldFile) Then; File name is the same.
                                        Else; Different file name.
                                            $sNum = _StringBetween($sStr, '(', ')'); Get the file count from the string.
                                            If IsArray($sNum) Then GUICtrlSetData($Log_Action_lbl, '(' & $sNum[0] & ')'); If found then update the actions label.
                                            GUICtrlSetData($Log_msg_lbl, 'Current File: ' & $sFile[0]); Update the log message label.
                                            $sOldFile = $sFile[0]; Set the old file name to the new one.
                                        EndIf
                                    EndIf
                                    Sleep(1)
                                WEnd

                                Log_Progress(100)
                            EndIf

                            If $iupdateMsi Then; Update MSI is selected.
                                $View = $DB.OpenView("SELECT DiskId, LastSequence, Cabinet FROM Media ORDER BY DiskId")

                                If $COM_Err Then
                                    $msg = 'Update MSI: Unable to query the database ' & $sdatabasePath
                                Else
                                    $View.Execute; Execute the SQL query.

                                    If $COM_Err Then
                                        $msg = 'Update MSI: Unable to execute the query on database ' & $sdatabasePath
                                    Else
                                        $updateMode = $msiViewModifyUpdate
                                        $Record = $View.Fetch

                                        If Not IsObj($Record) Then; Media table empty
                                            $Record = $Installer.CreateRecord(3); Create a new record with 3 fields.
                                            $Record.IntegerData(1) = 1; Set the number of cabinets.
                                            $updateMode = $msiViewModifyInsert
                                        EndIf

                                        $Record.IntegerData(2) = $lastSequence; Update the Media table LastSequence column with the cabinet name.
                                        $Record.StringData(3) = $scabName; Update the Media table Cabinet column with the cabinet name.
                                        $View.Modify($updateMode, $Record); Modify the media

                                        $SumInfo = $DB.SummaryInformation(3); Set the max number of proerties to be modified.
                                    ;$sumInfo.Property(11) = @YEAR & "/" & @MON & "/" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC
                                    ;$sumInfo.Property(13) = @YEAR & "/" & @MON & "/" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC
                                        $SumInfo.Property(15) = BitAND($shortNames, 1) + 2; Reset Word Count summary to "Compressed source files using short\long file names" setup will error if not.
                                        $SumInfo.Persist(); Formats and writes the previously stored properties into the standard summary information stream.
                                        $DB.Commit; Commit database in case updates performed
                                    EndIf
                                EndIf
                            EndIf

                            If $iembedCab Then; Embed the cabinet
                                $View = $DB.OpenView("SELECT `Name`,`Data` FROM _Streams")

                                If $COM_Err Then; Error occured openi
                                    $msg = 'Embed Cab: Unable to retrieve a view object from the database ' & $sdatabasePath
                                Else
                                    $Record = $Installer.CreateRecord(2)
                                    $Record.StringData(1) = $cabFile
                                    $Record.SetStream(2, $ssourceFolder & $cabFile); Copy the cab into the stream.

                                    If $COM_Err Then
                                        $msg = 'Embed Cab: Unable to copy the cab file into the record stream'
                                    Else
                                        $View.Modify($msiViewModifyAssign, $Record)
                                    EndIf
                                EndIf
                            EndIf

                            $DB.Commit; Commit database in case updates performed
                        EndIf

                        $View.Close; Close the view.
                    EndIf
                EndIf
            EndIf
        EndIf
    EndIf

    If $COM_Err Then
        Log_Fatal($msg, '', '', $msg); If COM error stop the process.
        Return $COM_Err; Return 1.
    EndIf

    Log_Update('Done: PHEW!!!, Having a Beer¦')
    Log_Banner(1, $Proc_Title)
    $Proc_Title = $oldPT; Set the process title as previous functions.
EndFunc  ;==>DI_UpdateCabs10

The original vbs is:

' Windows Installer utility to generate file cabinets from MSI database
' For use with Windows Scripting Host, CScript.exe or WScript.exe
' Copyright (c) 1999-2001, Microsoft Corporation
' Demonstrates the access to install engine and actions
'
Option Explicit

' FileSystemObject.CreateTextFile and FileSystemObject.OpenTextFile
Const OpenAsASCII   = 0 
Const OpenAsUnicode = -1

' FileSystemObject.CreateTextFile
Const OverwriteIfExist = -1
Const FailIfExist     = 0

' FileSystemObject.OpenTextFile
Const OpenAsDefault = -2
Const CreateIfNotExist = -1
Const FailIfNotExist   = 0
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

Const msiOpenDatabaseModeReadOnly = 0
Const msiOpenDatabaseModeTransact = 1

Const msiViewModifyInsert        = 1
Const msiViewModifyUpdate        = 2
Const msiViewModifyAssign        = 3
Const msiViewModifyReplace      = 4
Const msiViewModifyDelete        = 6

Const msiUILevelNone = 2

Const msiRunModeSourceShortNames = 9

Const msidbFileAttributesNoncompressed = &h00002000

Dim argCount:argCount = Wscript.Arguments.Count
Dim iArg:iArg = 0
If argCount > 0 Then If InStr(1, Wscript.Arguments(0), "?", vbTextCompare) > 0 Then argCount = 0
If (argCount < 2) Then
    Wscript.Echo "Windows Installer utility to generate compressed file cabinets from MSI database" &_
        vbNewLine & " The 1st argument is the path to MSI database, at the source file root" &_
        vbNewLine & " The 2nd argument is the base name used for the generated files (DDF, INF, RPT)" &_
        vbNewLine & " The 3rd argument can optionally specify separate source location from the MSI" &_
        vbNewLine & " The following options may be specified at any point on the command line" &_
        vbNewLine & "  /L to use LZX compression instead of MSZIP" &_
        vbNewLine & "  /F to limit cabinet size to 1.44 MB floppy size rather than CD" &_
        vbNewLine & "  /C to run compression, else only generates the .DDF file" &_
        vbNewLine & "  /U to update the MSI database to reference the generated cabinet" &_
        vbNewLine & "  /E to embed the cabinet file in the installer package as a stream" &_
        vbNewLine & "  /S to sequence number file table, ordered by directories" &_
        vbNewLine & "  /R to revert to non-cabinet install, removes cabinet if /E specified" &_
        vbNewLine & " Notes:" &_
        vbNewLine & "  In order to generate a cabinet, MAKECAB.EXE must be on the PATH" &_
        vbNewLine & "  base name used for files and cabinet stream is case-sensitive" &_
        vbNewLine & "  If source type set to compressed, all files will be opened at the root" &_
        vbNewLine & "  (The /R option removes the compressed bit - SummaryInfo property 15 & 2)" &_
        vbNewLine & "  To replace an embedded cabinet, include the options: /R /C /U /E" &_
        vbNewLine & "  Does not handle updating of Media table to handle multiple cabinets" &_
        vbNewLine &_
        vbNewLine & "Copyright (C) Microsoft Corporation, 1999-2001.  All rights reserved."
    Wscript.Quit 1
End If

' Get argument values, processing any option flags
Dim compressType : compressType = "MSZIP"
Dim cabSize   : cabSize   = "CDROM"
Dim makeCab   : makeCab   = False
Dim embedCab     : embedCab  = False
Dim updateMsi   : updateMsi = False
Dim sequenceFile : sequenceFile = False
Dim removeCab   : removeCab = False
Dim databasePath : databasePath = NextArgument
Dim baseName     : baseName  = NextArgument
Dim sourceFolder : sourceFolder = NextArgument
If Not IsEmpty(NextArgument) Then Fail "More than 3 arguments supplied" ' process any trailing options
If Len(baseName) < 1 Or Len(baseName) > 8 Then Fail "Base file name must be from 1 to 8 characters"
If Not IsEmpty(sourceFolder) And Right(sourceFolder, 1) <> "\" Then sourceFolder = sourceFolder & "\"
Dim cabFile : cabFile = baseName & ".CAB"
Dim cabName : cabName = cabFile : If embedCab Then cabName = "#" & cabName

' Connect to Windows Installer object
On Error Resume Next
Dim installer : Set installer = Nothing
Set installer = Wscript.CreateObject("WindowsInstaller.Installer") : CheckError

' Open database
Dim database, openMode, view, record, updateMode, sumInfo, sequence, lastSequence
If updateMsi Or sequenceFile Or removeCab Then openMode = msiOpenDatabaseModeTransact Else openMode = msiOpenDatabaseModeReadOnly
Set database = installer.OpenDatabase(databasePath, openMode) : CheckError

' Remove existing cabinet(s) and revert to source tree install if options specified
If removeCab Then
    Set view = database.OpenView("SELECT DiskId, LastSequence, Cabinet FROM Media ORDER BY DiskId") : CheckError
    view.Execute : CheckError
    updateMode = msiViewModifyUpdate
    Set record = view.Fetch : CheckError
    If Not record Is Nothing Then ' Media table not empty
        If Not record.IsNull(3) Then
            If record.StringData(3) <> cabName Then Wscript.Echo "Warning, cabinet name in media table, " & record.StringData(3) & " does not match " & cabName
            record.StringData(3) = Empty
        End If
        record.IntegerData(2) = 9999 ' in case of multiple cabinets, force all files from 1st media
        view.Modify msiViewModifyUpdate, record : CheckError
        Do
            Set record = view.Fetch : CheckError
            If record Is Nothing Then Exit Do
            view.Modify msiViewModifyDelete, record : CheckError 'remove other cabinet records
        Loop
    End If
    Set sumInfo = database.SummaryInformation(3) : CheckError
    sumInfo.Property(11) = Now
    sumInfo.Property(13) = Now
    sumInfo.Property(15) = sumInfo.Property(15) And Not 2
    sumInfo.Persist
    Set view = database.OpenView("SELECT `Name`,`Data` FROM _Streams WHERE `Name`= '" & cabFile & "'") : CheckError
    view.Execute : CheckError
    Set record = view.Fetch
    If record Is Nothing Then
        Wscript.Echo "Warning, cabinet stream not found in package: " & cabFile
    Else
        view.Modify msiViewModifyDelete, record : CheckError
    End If
    Set sumInfo = Nothing ' must release stream
    database.Commit : CheckError
    If Not updateMsi Then Wscript.Quit 0
End If

' Create an install session and execute actions in order to perform directory resolution
installer.UILevel = msiUILevelNone
Dim session : Set session = installer.OpenPackage(database) : If Err <> 0 Then Fail "Database: " & databasePath & ". Invalid installer package format"
Dim shortNames : shortNames = session.Mode(msiRunModeSourceShortNames) : CheckError
If Not IsEmpty(sourceFolder) Then session.Property("OriginalDatabase") = sourceFolder : CheckError
Dim stat : stat = session.DoAction("CostInitialize") : CheckError
If stat <> 1 Then Fail "CostInitialize failed, returned " & stat

' Check for non-cabinet files to avoid sequence number collisions
lastSequence = 0
If sequenceFile Then
    Set view = database.OpenView("SELECT Sequence,Attributes FROM File") : CheckError
    view.Execute : CheckError
    Do
        Set record = view.Fetch : CheckError
        If record Is Nothing Then Exit Do
        sequence = record.IntegerData(1)
        If (record.IntegerData(2) And msidbFileAttributesNoncompressed) <> 0 And sequence > lastSequence Then lastSequence = sequence
    Loop    
End If

' Join File table to Component table in order to find directories
Dim orderBy : If sequenceFile Then orderBy = "Directory_" Else orderBy = "Sequence"
Set view = database.OpenView("SELECT File,FileName,Directory_,Sequence,File.Attributes FROM File,Component WHERE Component_=Component ORDER BY " & orderBy) : CheckError
view.Execute : CheckError

' Create DDF file and write header properties
Dim FileSys : Set FileSys = CreateObject("Scripting.FileSystemObject") : CheckError
Dim outStream : Set outStream = FileSys.CreateTextFile(baseName & ".DDF", OverwriteIfExist, OpenAsASCII) : CheckError
outStream.WriteLine "; Generated from " & databasePath & " on " & Now
outStream.WriteLine ".Set CabinetNameTemplate=" & baseName & "*.CAB"
outStream.WriteLine ".Set CabinetName1=" & cabFile
outStream.WriteLine ".Set ReservePerCabinetSize=8"
outStream.WriteLine ".Set MaxDiskSize=" & cabSize
outStream.WriteLine ".Set CompressionType=" & compressType
outStream.WriteLine ".Set InfFileLineFormat=(*disk#*) *file#*: *file* = *Size*"
outStream.WriteLine ".Set InfFileName=" & baseName & ".INF"
outStream.WriteLine ".Set RptFileName=" & baseName & ".RPT"
outStream.WriteLine ".Set InfHeader="
outStream.WriteLine ".Set InfFooter="
outStream.WriteLine ".Set DiskDirectoryTemplate=."
outStream.WriteLine ".Set Compress=ON"
outStream.WriteLine ".Set Cabinet=ON"

' Fetch each file and request the source path, then verify the source path
Dim fileKey, fileName, folder, sourcePath, delim, message, attributes
Do
    Set record = view.Fetch : CheckError
    If record Is Nothing Then Exit Do
    fileKey = record.StringData(1)
    fileName   = record.StringData(2)
    folder   = record.StringData(3)
    sequence   = record.IntegerData(4)
    attributes = record.IntegerData(5)
    If (attributes And msidbFileAttributesNoncompressed) = 0 Then
        If sequence <= lastSequence Then
            If Not sequenceFile Then Fail "Duplicate sequence numbers in File table, use /S option"
            sequence = lastSequence + 1
            record.IntegerData(4) = sequence
            view.Modify msiViewModifyUpdate, record
        End If
        lastSequence = sequence
        delim = InStr(1, fileName, "|", vbTextCompare)
        If delim <> 0 Then
            If shortNames Then fileName = Left(fileName, delim-1) Else fileName = Right(fileName, Len(fileName) - delim)
        End If
        sourcePath = session.SourcePath(folder) & fileName
        outStream.WriteLine """" & sourcePath & """" & " " & fileKey
        If installer.FileAttributes(sourcePath) = -1 Then message = message & vbNewLine & sourcePath
    End If
Loop
outStream.Close
REM Wscript.Echo "SourceDir = " & session.Property("SourceDir")
If Not IsEmpty(message) Then Fail "The following files were not available:" & message

' Generate compressed file cabinet
If makeCab Then
    Dim WshShell : Set WshShell = Wscript.CreateObject("Wscript.Shell") : CheckError
    Dim cabStat : cabStat = WshShell.Run("MakeCab.exe /f " & baseName & ".DDF", 1, True) : CheckError
    If cabStat <> 0 Then Fail "MAKECAB.EXE failed, possibly could not find source files, or invalid DDF format"
End If

' Update Media table and SummaryInformation if requested
If updateMsi Then
    Set view = database.OpenView("SELECT DiskId, LastSequence, Cabinet FROM Media ORDER BY DiskId") : CheckError
    view.Execute : CheckError
    updateMode = msiViewModifyUpdate
    Set record = view.Fetch : CheckError
    If record Is Nothing Then ' Media table empty
        Set record = Installer.CreateRecord(3)
        record.IntegerData(1) = 1
        updateMode = msiViewModifyInsert
    End If
    record.IntegerData(2) = lastSequence
    record.StringData(3) = cabName
    view.Modify updateMode, record
    Set sumInfo = database.SummaryInformation(3) : CheckError
    sumInfo.Property(11) = Now
    sumInfo.Property(13) = Now
    sumInfo.Property(15) = (shortNames And 1) + 2
    sumInfo.Persist
End If

' Embed cabinet if requested
If embedCab Then
    Set view = database.OpenView("SELECT `Name`,`Data` FROM _Streams") : CheckError
    view.Execute : CheckError
    Set record = Installer.CreateRecord(2)
    record.StringData(1) = cabFile
    record.SetStream 2, cabFile : CheckError
    view.Modify msiViewModifyAssign, record : CheckError 'replace any existing stream of that name
End If

' Commit database in case updates performed
database.Commit : CheckError
Wscript.Quit 0

' Extract argument value from command line, processing any option flags
Function NextArgument
    Dim arg
    Do  ' loop to pull in option flags until an argument value is found
        If iArg >= argCount Then Exit Function
        arg = Wscript.Arguments(iArg)
        iArg = iArg + 1
        If (AscW(arg) <> AscW("/")) And (AscW(arg) <> AscW("-")) Then Exit Do
        Select Case UCase(Right(arg, Len(arg)-1))
            Case "C" : makeCab    = True
            Case "E" : embedCab  = True
            Case "F" : cabSize    = "1.44M"
            Case "L" : compressType = "LZX"
            Case "R" : removeCab    = True
            Case "S" : sequenceFile = True
            Case "U" : updateMsi    = True
            Case Else: Wscript.Echo "Invalid option flag:", arg : Wscript.Quit 1
        End Select
    Loop
    NextArgument = arg
End Function

Sub CheckError
    Dim message, errRec
    If Err = 0 Then Exit Sub
    message = Err.Source & " " & Hex(Err) & ": " & Err.Description
    If Not installer Is Nothing Then
        Set errRec = installer.LastErrorRecord
        If Not errRec Is Nothing Then message = message & vbNewLine & errRec.FormatText
    End If
    Fail message
End Sub

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

Share this post


Link to post
Share on other sites
John117

have you tried adding something like this to debug?

Func ComErrorHandler()
    Local $sHexNumber = Hex($oMyError.number,8)
    Local $sDesc = StringStripWS($oMyError.windescription, 2) 
    Msgbox(0,"AutoItCOM Test","We intercepted a COM Error !"        & @CRLF & @CRLF & _
             "err.description is: "    & @TAB & $oMyError.description       & @CRLF & _
             "err.windescription:"     & @TAB & $oMyError.windescription    & @CRLF & _
             "err.number is: "         & @TAB & $sHexNumber                & @CRLF & _
             "err.scriptline is: "     & @TAB & $oMyError.scriptline        & @CRLF)
    $g_eventerror = 1 ; something to check for when this function returns
    $oMyError.clear
Endfunc

Edit: If they run it from sciTE it should help . . . Read the console for error info . . .

Edited by John117

Share this post


Link to post
Share on other sites
benners

have you tried adding something like this to debug?

Func ComErrorHandler()
    Local $sHexNumber = Hex($oMyError.number,8)
    Local $sDesc = StringStripWS($oMyError.windescription, 2) 
    Msgbox(0,"AutoItCOM Test","We intercepted a COM Error !"        & @CRLF & @CRLF & _
             "err.description is: "    & @TAB & $oMyError.description       & @CRLF & _
             "err.windescription:"     & @TAB & $oMyError.windescription    & @CRLF & _
             "err.number is: "         & @TAB & $sHexNumber                & @CRLF & _
             "err.scriptline is: "     & @TAB & $oMyError.scriptline        & @CRLF)
    $g_eventerror = 1 ; something to check for when this function returns
    $oMyError.clear
Endfunc

Edit: If they run it from sciTE it should help . . . Read the console for error info . . .

Yes I have the same function for the error handler but I have commented out the code and just set the $COM_Err to 1. I don't always want the program to stop on an error and prefer to have error messages relevant to what action is being performed. My next action is to log the error information as well as setting the $COM_Err to see if anything obvious is happening. I thought I would post to try and find out why some users have no problem and for others the "error in expression problem", surely the code should work for all and just not return a value if an action cannot be performed.

The code is compiled so running from sciTe is not an option.

Thanks

Edited by benners

Share this post


Link to post
Share on other sites
John117

Yes I have the same function for the error handler but I have commented out the code and just set the $COM_Err to 1. I don't always want the program to stop on an error and prefer to have error messages relevant to what action is being performed. My next action is to log the error information as well as setting the $COM_Err to see if anything obvious is happening. I thought I would post to try and find out why some users have no problem and for others the "error in expression problem", surely the code should work for all and just not return a value if an action cannot be performed.

The code is compiled so running from sciTe is not an option.

Thanks

Were I on my home pc I could give you a better one.

Try keeping the entire code given before. convert it to Console write. Then do a console read and dump everything to a txt file in the @ScriptDir.

May help. -They could then email you that . . .

Share this post


Link to post
Share on other sites
benners

Were I on my home pc I could give you a better one.

Try keeping the entire code given before. convert it to Console write. Then do a console read and dump everything to a txt file in the @ScriptDir.

May help. -They could then email you that . . .

With the program, I have a logging function so I can just write the "$oMyError.description" etc to the log file. The problem is the error function is not triggered (if an error occurs) because the command is not run due to the "error with the expression" problem. I just don't know why the code is wrong for some people and not always wrong.

Share this post


Link to post
Share on other sites
Quual

I'm clueless when it comes to the installer object, might be a dumb suggestion.

Have you tried changing the line to $session = $Installer.OpenPackage($DB,1)

'When options is 1, the OpenPackage Method ignores the current computer state when opening the package.

Edited by Quual

Share this post


Link to post
Share on other sites
benners

I'm clueless when it comes to the installer object, might be a dumb suggestion.

Have you tried changing the line to $session = $Installer.OpenPackage($DB,1)

'When options is 1, the OpenPackage Method ignores the current computer state when opening the package.

Hi Quual

Thanks for the reply. I had tried the 1 option in other parts of the code and it didn't seem to have any effect. I altered the code to use the vbs and the users had the same problem. The script reported an "invalid installer package". I added the 1 as you suggested and have received feedback that the solution worked. I don't understand what could cause the problem if the same package is already installed, maybe it looks for the installer in a cached folder? but it seems to work.

Thanks

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.