jsMSI.dll Help File   -   v. 2.11.104 - last update: Jan. 4, 2011




General Info

Introduction
   jsMSI.dll is a COM DLL with two objects. Ops and OpsV. The two objects are identical except for one thing: The methods of Ops are strongly typed, designed for use with compiled software. OpsV methods (V is for Variant) use only variants for parameters and return values. OpsV is for use in scripting.

   The documentation of methods and properties provided in this file is for the Ops object, with data types indicated. For the OpsV object just ignore the datatype details. All parameters and function returns will be variants with the OpsV object. A returned array of strings or longs will be an array of variants. Etc. Everything else -- parameter definitions, error codes, etc. -- is the same for both Ops and OpsV

   jsMSI.dll is a "wrapper" around the Windows Installer API. It is similar to the VBScript code available at JSWare for working with MSI files, but it uses the Windows Installer API functions rather than the Dispatch scripting interface. jsMSI provides a wide range of functions for working with MSI database files, without the awkward functions and tedious SQL normally required. For instance, you can use the GetValue and SetValue functions to read and write column values in MSI tables without being concerned with whether the value is string or numeric, and without having to construct error-prone SQL queries. jsMSI also incorporate CAB extraction and creation functions, since those are needed in dealing with MSI files.

   jsMSI does not really achieve anything that cannot be achieved with VBScript. It is being provided in hopes that it may be useful to some people who may be put off by the idea of using script, or who may want to incorporate MSI functions into compiled software.
   The Windows Installer API is COM-oriented. It comes in two forms: a set of standard programming functions and a Dispatch (late-bound) object model for scripting. (There is no vTable or early-bound version of the object model.) Both API systems are awkward to use. The structure of MSI files is a horrendous mess of unnecessary complexity. The SQL required to deal with that structure is convoluted and tedious. jsMSI.DLL is an attempt to "tame" the poor design of Windows Installer, wrapping the more efficient programming API and presenting, hopefully, a more clear and usable API for use with either scripting or compiled software.


Email: Feel free to write with comments or requests for help if necessary. If you send email please send it to jsware@jsware.net. "Webmail" from yahoo, gmail, facebook, hotmail, etc. is not accepted. It is auto-deleted on the server. Please use a real email address when writing. For full explanation of that requirement see http://www.jsware.net/jsware/contact.php5.

   jsMSI is mainly designed for unpacking and editing existing MSI installer files. It does not incorporate all of the functionality that one might want for writing MSI installers from scratch. Part of the reason for that is because we do not regard Windows Installer as a usable system for software installation, so jsMSI is not designed to be a tool for building MSI installer files. But a bigger reason for the design of jsMSI is the inherent limitations presented by the design of MSI databases. For instance, if jsMSI provided a method to create new tables that would require a complex and awkward function to accomodate all the possible variations in the format of that table. The resulting function would have no advantage over the native Windows Installer API method using an SQL query string. So jsMSI does not have a "CreateTable" method. Instead it provides ImportTable as a way that new tables can be created easily without resorting to convoluted SQL queries.

Using the object
First, jsMSI.dll is a COM DLL. It must be registered on the system. Put it in a safe place, like the system folder, then drop it onto RegSvr.vbs.

• Scripting usage:
Set DB = CreateObject("jsMSI.OpsV")

• Usage in VB:
Dim DB as jsMSI.Ops
Set DB = New jsMSI.Ops


   jsMSI.dll can be used by any COM-compatible programming language. In most cases you will want to use the Ops object. OpsV is specifically designed for VBScript.

Update Information for jsMSI v. 2 (9/28/2010)

   jsMSI.dll v. 1 required an external DLL named jcabio.dll. jsMSI.dll v. 2 no longer requires that extra file. Also, the returned error codes from BuildCAB have been changed. Those are the only major change in v. 2. Differences between v. 1 and v. 2 are mainly internal.

Things to know about MSI databases

   An MSI file is actually an SQL database that uses a subset of SQL functions. In most cases the names of tables and columns are case-sensitive. In other words, if you want to set a value in the Color column of a table named Fruit, you must use those exact strings. If you instead try to set a value in the "fruit" table the function will fail. That is just part of how the database works. It is not related to whether or not you use jsMSI.dll to access the MSI file.

   Also, when importing tables from text files, be careful with the file format. It must be constructed just-so, with tab characters between items in rows. And there should be 1 carriage return at the end of the last record. If you have two carriage returns the import will fail!

Back to Index
About error codes and error information
   jsMSI has 3 properties associated with error information: ErrorNumber, ErrorDesc and ErrorNumberInfo. Many of the jsMSI functions are marked "sets error values". That means that when the function is called, ErrorNumber and ErrorDesc are cleared. When the function returns those values will be relevant. 0 indicates success. Other error codes/descriptions may be jsMSI errors caused by mistakes with function calls. For example, if you try to extract a CAB to an invalid folder path an error will result. jsMSI errors are -1 (failed) or 1-5. Error codes can also come from Windows Installer. Those errors are generally numbered 1600+. Also, Windows Installer sometimes returns standard Windows error codes rather than MSI error codes. Those numbers will typically be greater than 5 and less than 1600. For example the Windows error 87 indicates an invalid parameter in a function call. (For anyone who is curious, Windows errors are documented in winerror.h, part of the Win32 API documentation.)

ErrorNumber - Returns the error number returned by last function, if that function sets error values.

ErrorDesc - Returns a descriptive string about the error number returned by last function, if that function sets error values. ErrorDesc returns descriptions of errors raised in jsMSI functions.

ErrorNumberInfo(ErrNo) - In many cases this will return further information beyond ErrDesc. ErrDesc attempts to provide a specific problem report when a function fails. It helps with debugging by telling you what part of the function failed. Often ErrDesc can tell you which of several calls to the Windows Installer API failed. By looking up those functions in msi.chm you may be able to figure out the cause of the error. But ErrDesc is specific to jsMSI, ErrorNumberInfo returns a standard error description string for specific error numbers. (If the number is not listed it returns "".)

Example 1: You call a jsMSI function that sets error values. In the call you provide an invalid file path. jsMSI will catch that and set both ErrorNumber and ErrorDesc. Example 2: You call a jsMSI function which fails due to problems with a call to Windows Installer functions. In that case ErrorNumber will return the 1600+ error number from Windows Installer. If possible, jsMSI will also provide information in the ErrorDesc property. But since this is an MSI error there may be no info. In that case ErrorNumberInfo can be useful because it will return the official error description. For instance, a common error code when using SQL database queries is error 1615. The official description for that error, which ErrorNumberInfo will return, is "SQL query syntax invalid or unsupported."
   So ErrorNumberInfo can be thought of as a generic error number reference table.

Back to Index
Notes about unpacking MSI installers
   The typical MSI installer is an MSI database file with a CAB file embedded in it. With very large installs the CAB may be stored externally. And in some rare cases there may be more than one CAB.

   jsMSI's UnpackMSI function performs a number of operations to unpack an MSI installer. It reads a large part of the database to get file names, folder structure, Registry settings, etc. It then extracts the CAB, if necessary, extracts the files stored in the CAB, and copies those with their correct names into a folder tree representing the default program install. In the course of reading the MSI database and unpacking files, UnpackMSI writes a log file that describes those actions, lists "Features" and "Components" in the MSI, and lists Registry settings made during install.

   Occasionally the job is not so simple. Some people like to create an EXE that contains the MSI installer. In those cases, typically, one has to run the EXE while the TEMP folder is open, fish out a copy of the MSI that gets copied there, and then cancel the install. (When an install is cancelled any setup files are typically deleted. Thus the step of copying the MSI file.)

   You may also run into problems with "overproduced" installers made using Wise, InstallShield, etc. Those installers are often MSI files inside EXEs, using custom compression methods, and adding custom-action installer executables embedded in the MSI database. Again, you should be able to fish the MSI out of the TEMP folder by starting the install. Then cancel once you have a copy of the MSI.

   With later versions of Windows Installer it may be even more difficult to unpack the program. With Windows Installer 4.5, for example, it is not unusual to find an installer that is an EXE but contains an MSI, as detailed above. But in some of these more recent vintage MSIs there is rather bizarre behavior: The EXE wrapper may do the work of extracting the actual program files, creating a temporary program folder tree in the TEMP folder! In other words, the installer EXE is using the Windows Installer API to carry out at least part of the job that Windows Installer would normally perform. In those cases jsMSI cannot unpack the files because there is no CAB. (And the EXE does its own unpacking, anyway.) But jsMSI can still document the file paths, Registry settings, etc. in its log file. So you have to go to the TEMP folder to get the MSI for documentation purposes, then you have to fish around further in the TEMP folder to retrieve the unpacked files.

   SQL Server 2008 Express is an example of a v. 4.5 installer that only partially uses Windows Installer. The installer file is an EXE. It is actually a self-executing CAB file! Inside are an MSI file and a large number of installation files. When the installer is run it dumps the files into a folder tree, renaming them in the process from gibberish code names to their final destination names. In other words, the installer creates a folder/file hierarchy in TEMP that represents the installed program. Apparently those files are then copied to a matching folder tree in Program Files. With an installer that exhibits this odd behavior you will need to copy the folder tree(s) from the TEMP folder if you want to inspect the files. Then also process the accompanying MSI file with jsMSI and use the log file written by jsMSI to make sense of the files.

   So the long and the short of it is that you can document the details of an install with jsMSI if there is an MSI file involved. But the means of getting at that MSI file can vary. And one should never expect Microsoft to follow their own prescribed standards. More often than not, they don't.

   If you find yourself repeatedly running into these "semi-Windows-Installer" installs you may want to use jsMSI to write a custom script/EXE to deal with them. For instance, the function GetFileNameFromID provides a way to translate file names in an MSI CAB to their real names. The function EXEtoCAB provides a way to convert a self-executing CAB file to a normal CAB file. Etc.

Back to Index

UnpackMSI

Function UnpackMSI(sMSIFile As String, sDestFolder As String, ClearLogBeforeUnpack As Boolean, IncludeRegSettings As Boolean) As Boolean
  Unpack an MSI file. This function stands alone. All other functions relate to managing MSI file databases. One can use jsMSI functions to open an MSI, do various operations, then closes the MSI. But UnpackMSI is an all-in-one function that opens an MSI installer file, unpacks the contents to a folder tree that mimics the install locations for the various files, details Registry settings that are created with installation, and writes a log file to provide a detailed report of the operation. This function does not set error values. Any error reports are written to the unpacking log, which is named "Program Description.log".

sMSIFile - Full path of MSI file to unpack.
sDestFolder - Full path of folder to unpack into.
ClearLogBeforeUnpack - Clear log entries before starting unpack?
IncludeRegSettings - Do you want all Registry settings listed as part of the log file?

Notes: Function returns True/False. If any part of the unpacking was unsuccessful that is detailed in the log file. (Function could return True for partial success.) IncludeRegSettings is included as an option because some installations add hundreds or even thousands of Registry entries. If that data is not relevant it can be left out of the log for the sake of brevity. UnpackMSI does not set error number or description because the log details all error data. The error functions and logging functions are all irrelevant with this function.

Back to Index

General MSI Functions

Function OpenMSI(sPath As String, IfReadOnly As Boolean) As Boolean
   Opens an MSI file. Opening is required before any methods that access the database can be used. (With the exception of UnpackMSI.) Sets error values.

sPath - Full path of MSI file.
IfReadOnly - Whether to open the file read-only or read/write. When IfReadOnly is set to True the various functions to set values, delete records, etc. cannot be used.

Notes: An MSI cannot be opened by two processes at once. If you do not properly close an MSI you may not be able to re-open it until reboot, or at least until your script/software stops running. In some cases an MSI file may be set to read-only mode in the SummaryInformation Security value. There is actually an option to do that. That setting seems to be a recommendation for MSI editors rather than an enforced rule. But for the sake of completeness there are two functions SetReadOnlyStatus and GetReadOnlyStatus included to ascertain/change the read-only status of an MSI database.

Back to Index
Sub CloseMSI()
   Whenever an MSI is opened this method should be called after all operations are finished. It causes jsMSI to release stored data and it causes Windows Installer to close the MSI database. jsMSI will call CloseMSI when it itself is unloaded, but it's best to just always call CloseMSI anyway. That is especially true in cases where you may open an MSI more than once during the course of a script or program. If the MSI has not been properly closed it cannot be reopened because Windows Installer will view it as already being open.

Back to Index
Function CreateMSI(sPath As String) As Boolean
   Creates a basic MSI database file. This is a "bare" file with no tables. The function creates the MSI and then closes it. OpenMSI must then be called if you want to work with the new file. Sets error values.

sPath - Full path of file to create.

Notes: This function has limited usefulness. jsMSI is not designed for designing MSI databases. The SQL queries to do that get very involved, with many different data type options for columns. If you want to create an MSI from scratch using jsMSI, the best approach would be to use CreateMSI, then write table text files and import them. Export tables from any MSI installer to see how that is done. A typical MSI installer database has over 80 tables.
   The layout of an MSI table in text-file form is actually very simple. The table name, column names, and column data types are listed in tab-delimited lines at the top of the file. Any records are then listed in lines below that. So a complex table, with many columns, can be easily designed and created in Notepad, without needing to deal with the awkwardness of SQL. That file can then be imported using ImportTable to create a new database table.

Back to Index
Function GetSummaryInfo(LNumRet As Long) As String()
   Returns an array(4) of common SummaryInformation values, if they exist in the MSI. LNumRet returns the number of values retrieved. Those are not necessarily in order. For example, the array may hold values in array(1) and array(4). In that case LNumRet would return 2. Example: A4 = DB.GetSummaryInfo(NumberVals) The values that may be returned are as follows:

Array index-Value:
0-Title
1-Subject
2-Author
3-Comments
4-Program Name


Back to Index
Function GetSummaryInfoName(LIndex As Long) As String
   Returns a string that describes SummaryInformation data. See GetSummaryInfo (above) for the list.
Example: GetSummaryInfoName(2) will return "Author". This function is just a simple convenience for use in formatting returned SummaryInformation data.

Back to Index
Function SetSummaryInfoString(sValue As String, sData As String) As Long
   Sets the value for any of the 5 properties listed above. Returns 0 on success, -1 if the property name is invalid, or an MSI error code if the call fails. sValue is not case-sensitive.
Ex.: LRet = DB.SetSummaryInfoString("Author", "Ace and Acme Software")

Back to Index
Function GetReadOnlyStatus() As Long
   Returns a value that indicates the read-only status of an MSI database. Possible return values: -1 - failed to find read-only status. 0 - no restrictions set. 2 - restrictions "recommended" but not set. 4 - database is read-only.
Does not set error values. Note that the read-only setting seems to be only a recommendation for MSI editors, not directly enforced by Windows Installer.

Back to Index
Function SetReadOnlyStatus(ROVal As Long) As Long
   Sets the read-only status of an MSI database. ROVal should be 0 (RW), 2 (read-only recommended), or 4 (read-only enforced).

Return: Does not set error values. Return is the MSI error code returned from the MSI function MsiSummaryInfoSetProperty. For more information try the ErrorNumberInfo property, which may return an error description for both jsMSI and Windows/MSI error codes. Note that the read-only setting seems to be only a recommendation for MSI editors, not directly enforced by Windows Installer.

Table

Back to Index
Property Get TableNames() As String()
   Returns a string array of table names for the tables in an MSI.

Back to Index
Property Get TableCount() As Long
   Returns the number of tables in an MSI.

Back to Index
Function TableExists(sTableName As String) As Boolean
   Does this table exist in the MSI?
Ex.:If DB.TableExists("Classes") = True Then....

Back to Index
Function ImportTable(sFolPath As String, sFileName As String) As Boolean
   Imports a table into an MSI from text file. jsMSI does not have functions to create tables because the possible variations of SQL are so numerous. But tables can easily be created from text files. To see how it works, use ExportAllTables (below) to export all of the tables in an MSI to text files. You can copy the syntax from those sample tables.

Ex.: Boo = DB.ImportTable("C:\tables", "sometable.txt")

Return: Returns boolean. If return is False then error values are set.

Notes on writing tables: If you look at a few exported tables you can see the pattern. Windows Installer is finicky about table layout. It must be done just-so. The top line contains column names, separated by Tabs (Chr(9)). The next line specifies the data type of the column. For example "i4" indicates a 4-byte integer. "s72" indicates a string of max. 72 characters. v0 indicates a binary stream (embedded files). (A numeric value of zero indicates no size limit. An alphabetic value uppercased indicates the value can be blank. For instance, S72 is a string value of max. 72 characters that is "nullable". s24 is a string value, of max. 24 characters, that is not nullable.) After the column names and types comes a line containig the table name and key column name. The key column values cannot be changed or deleted unless the record is deleted. Typically the key column is the first column, but that is not required.
   So there is a line with column names, a 2nd line with column types, and a 3rd line naming the table and key column. All listed items in these lines are separated by tabs. Directly after those 3 lines the records begin. Records are formatted like the column names line, with the value of each column separated by a tab. Each record is on a separate line. The file must end with a single carriage return (vbCrLf) after the last record. If there is no return, or if there are two, the table will probably fail to import.

   By writing a table as a text file and importing it the tedious work of SQL queries can be avoided.

Back to Index
Function ExportTable(sTableName As String, sDestFolder As String, sFileName As String) As Boolean
   Creates a text file on disk that represents an MSI database table. Such tables can be edited and then re-imported.

Ex.: Boo = DB.ExportTable("Files", "C:\tables", "FilesTable.txt")

sTableName - Name of database table.
sDestFolder - Location for exporting.
sFileName - Name of exported table file.

Return: Returns boolean. If return is False then error values are set.

Back to Index
Function ExportAllTables(sDestFolder As String) As Boolean
   Exports all tables in database to text files. sDestFolder is the location to write the files. Each table in the database is represented by a text file named for the table. For example, the "Directory" table will be exported as Directory.txt.

Return: Returns boolean. If return is False then error values are set.

Back to Index
Function RemoveTable(sTableName As String) As Boolean
   Removes specified table from database.
Return: Returns boolean. If return is False then error values are set.

Back to Index

Columns

Function ColumnExists(sTableName As String, sColumnName As String) As Boolean
   Does the column exist? Returns boolean.

Back to Index
Function FirstColumnName(sTableName As String) As String
   Returns the name of the first column in a table. The first column is usually -- but does not have to be -- the key column, meaning that the value cannot be changed without removing the record and then adding it back.

Back to Index
Function GetColumnInfo(sTable As String, AColNames As Variant, AColTypes As Variant) As Long
   Returns 2 arrays that describe a table's columns.
sTable - Name of table.
AColNames - Array of column names, starting from left.
AColTypes - an array of numeric values that indicate the column's data type. Possible values are 1-short integer. 2-long integer. 3-binary. 4-string. 5-localizable string. (0-error)

The function return is the number of columns, which would be 1 more than UBound of the arrrays. If the function failes, due to something like an invalid table name, the function return is 0.

Back to Index
Function GetColumnType(sTableName As String, sColumnName As String) As Long
   Returns data type of column. Possible values are 1-short integer. 2-long integer. 3-binary. 4-string. 5-localizable string. (0-error)

Back to Index
Function GetColumnTypeValue(sTableName As String, sColumnName As String) As Long
   Returns the actual numeric flag value for the column type. This is the hidden Type column value of the _Columns table. For most purposes this function is irrelevant. The other column functions and properties provide the same information in a more usable form.

Back to Index
Function ColumnIsString(sTableName As String, sColumnName As String) As Boolean
   Does column hold string data?

Back to Index
Function ColumnIsNumeric(sTableName As String, sColumnName As String) As Boolean
   Does column hold numeric values?

Back to Index
Function ColumnIsNullable(sTableName As String, sColumnName As String) As Boolean
   Can the column data be blank?

Back to Index
Function ColumnIsPrimary(sTableName As String, sColumnName As String) As Boolean
   Is this the primary key column? (The MSI docs refer to "primary" columns and "key" columns. They also refer to "primary key" columns. The terms seem to be interchangeable. This is the identifying column in a record. It cannot be changed except by deleting the record and adding a new one. It is possible to have more than one primary column in a table, but it seems to only occur in the "artificial" tables such as _Tables and _Columns. These tables are described as "read only system tables". They seem to actually be tables in name only, really existing as just some sort of inherent lists in the .msi file.)

Back to Index
Function GetColumnDataMaxLength(sTableName As String, sColumnName As String) As Long
   Returns the maximum string length for string values. Irrelevant for other values.

Back to Index
Function GetValue(sTableName As String, sColumnToGet As String, sColumntoCompare As String, CompareValIfString As String, CompareValIfNumeric As Long, NumberValuesReturned As Long, ReturnIsNumeric As Boolean) As Variant
   Returns a value from a specific column, based on a value match in another column. The value is returned as an array. In most cases you will probably be using this function to return a single value: "what is the value of Color in the record where Name is Tangerine?". But by the nature of the database, some queries could return more than one match. Returning an array accomodates that. The value sought can be string or numeric. (This function will not work with binary values. Use ExtractFileFromMSI for that.)
sTableName - Name of table.
sColumnToGet - Name of column for which value is sought.
sColumnToCompare - Name of column to match.
CompareValIfString, CompareValIfNumeric - Value to match. If value is string then CompareValIfNumeric must be 0. If value is numeric then CompareValIfString must be "".
NumberValuesReturned - Returns the number of matches found.
ReturnIsNumeric - Returns whether the value sought is numeric.

Notes: Think of this function like so: "Get all values in sColumnToGet where sColumnToCompare is [CompareValIfString / CompareValIfNumeric].
Ex.: A1 = DB.GetValue("Fruit", "Color", "SeedNumber", "", 20, NumRet, BooNumeric) '-- Get values in Color column where SeedNumber is 20
A1 = DB.GetValue("Fruit", "SeedNumber", "Color", "orange", 0, NumRet, BooNumeric) '-- Get values in SeedNumber where Color is "orange"

Return: Check NumberValuesReturned first. If that value is 0 no either matches were found or there was an error, in which case error values are set. If NumberValuesReturned is greater than 0 then function return is an array of values.

Back to Index
Function SetValue(sTableName As String, sColumnToSet As String, ValToSetIfString As String, ValToSetIfNumeric As Long, sColumntoCompare As String, ValCompareIfString As String, ValCompareIfLong As Long) As Boolean
   Sets value(s) based on a match to specified column.
sColumnToSet - Name of column in which to set value(s).
ValToSetIfString, ValToSetIfNumeric - Value to set. If string then ValToSetIfNumeric must be 0. If numeric then ValToSetIfString must be "".
sColumntoCompare - Name of column to match.
ValCompareIfString, ValCompareIfLong - Value to match for setting value.

Notes: Think of this function like so: "Set the value in sColumnToSet to ValToSet in any records where the value in sColumntoCompare is ValCompare"

Ex.: Boo = DB.SetValue("Fruit", "Color", "Orange", 0, "Fruit", "Tangerine", 0) '-- In the Fruit table, set Color column value to "Orange" where Fruit column contains "Tangerine".
Boo = DB.SetValue("Fruit", "SeedNumber", "", 12, "Fruit", "Apple", 0) '-- In the Fruit table, set SeedNumber column value to 12 where Fruit column contains "Apple".


Return: Returns boolean. If return is False then error values are set.

Back to Index
Function GetAllStringValues(sTableName As String, sColumnName As String, ARet() As String) As Long
   Returns an array of all values in column sColumnName. Column must hold string values.

Return: Returns number of values found. (Which will be UBound(ARet) + 1). If return is 0 there are no values or there could be an error. If there was an error then error values are set. ErrorNumber will return non-zero.

Back to Index
Function GetAllNumericValues(sTableName As String, sColumnName As String, ARet() As Long) As Long
   Returns an array of all values in column sColumnName. Column must hold numeric values.

Return: Returns number of values found. (Which will be UBound(ARet) + 1). If return is 0 there are no values or there could be an error. If there was an error then error values are set. ErrorNumber will return non-zero.

Back to Index

Records

Function AddRecord(sTableName As String, sColumnValues As String) As Boolean
   Adds a record to a table.
Ex.: Boo = DB.AddRecord("Fruit", "Tangerine, orange, 20")

   The column values are sent as a comma-delimited list. No special syntax is needed for strings vs numeric values. Just put them all in a string and jsMSI will sort it out. However, the values must accurately match up with existing columns. For instance, if you send the string "Tangerine, orange, 20" and the 2nd column is numeric then the function will fail.

   If blank entries are desired, send "-" for those values. For example "Tangerine, -, 20" would leave the second column blank when adding the new record.

Return: Returns boolean. If return is False then error values are set.

Notes: 1) When leaving a numeric value blank, Windows Installer will generally write a negative number in its place. 2) If "-" is sent to leave a column blank, and that column is not nullable, the method will fail, probably with error 1627. Some columns are not allowed to be left blank. (In an exported table text file nullable columns are marked by capitaization. That is, a column of datatype S72 takes a string value of up to 72 characters and may be left blank. A datatype of s72 may not be left blank.)

Back to Index
Function RemoveRecord(sTableName As String, sColumnName As String, ValToMatchStr As String, ValToMatchLong As Long) As Boolean
   Removes a record from a table.
sTableName - Name of table.
sColumnName - Column by which to identify record. ValToMatchStr, ValToMatchLong - Value to match. If string then long value must be 0. If long then string value must be "".

Notes: Read this function as "Delete any record where the value in sColumnName column matches [ValToMatchStr / ValToMatchLong].
Ex.: Boo = DB.RemoveRecord("Fruit", "Color", "orange", 0) '-- Delete all records where Color is "orange"
Boo = DB.RemoveRecord("Fruit", "SeedNumber", "", 20) '-- Delete all records where SeedNumber is 20

Return: Returns boolean. If return is False then error values are set.

Back to Index

Binary I/O

Function ExtractFileFromMSI(sTableName As String, sFileID As String, sFilePath As String) As Boolean
   This is the reverse of InsertFile. It extracts a file stored in the Binary table, normally. But the sTableName parameter allows you to specify a different, custom table where binary files are stored. sFileID is the value from the Name column of the Binary table. (If another table is referenced it must still have the same structure as the Binary table, with two columns named Name and Data that hold string and binary data, respectively.) sFilePath is the full path for the extracted file to be written to.

Return: Returns boolean. If return is False then error values are set.

Back to Index
Function ExtractCAB(sFileID As String, sFilePath As String) As Boolean
   Extracts a CAB file from an MSI database. sFileID must be a value from the Cabinet column of the Media table. sFilePath is destination path for extracted file. sFilePath is an in/out value. It must be sent as a variable. When the function is called it contains the requested path of the extracted CAB. When the function returns, sFilePath contains the actual path. The reason for that is that CAB files may not always be stored with valid file names. Example:
sPath = "C:\CABFolder\" & sCABID
Boo = DB.ExtractCAB(sCABID, sPath)

If sCABID, taken from the Media table, were "_abcdefghi" then sPath would be "C:\CABFolder\_abcdefghi". That would not be a valid CAB file path, so the function would write the file to "C:\CABFolder\_abcdefghi.cab" and when the function returns, sPath will hold the string "C:\CABFolder\_abcdefghi.cab".

Return: Returns boolean. If return is False then error values are set.

Back to Index
Function ExtractAllFilesFromCAB(sCABPath As String, sDestFolder As String) As Boolean
   Extracts all files in a CAB to sDestFolder.

Return: Returns boolean. If return is False then error values are set. ErrorNumber will return an error code related to a basic file error or a cabinet.dll error. ErrorDesc will return the explanation of that error number. (ErrorNumberInfo is not relevant with CAB errors.)

Back to Index
Function ExtractFileFromCAB(sCABPath As String, sFileName As String, sDestFolder As String)) As Boolean
   Extracts one file from a CAB. sFileName must match the name of a file contained within the CAB. sDestFolder is the location to write the extracted file to.

Return: Returns boolean. If return is False then error values are set. ErrorNumber will return an error code related to a basic file error or a cabinet.dll error. ErrorDesc will return the explanation of that error number. (ErrorNumberInfo is not relevant with CAB errors.)

Back to Index
Function BuildCAB(sCABFilePath As String, OverWrite As Boolean, AFileList As Variant) As Long
   Creates a new CAB file.

sCABFilePath is the full path of the new CAB file to be created.
OverWrite - Overwrite an existing CAB file of the same name? If this parameter is false and the file exists, BuildCAB will fail.
AFileList - An array of file paths. These are the files to be included in the CAB.

Dim sPath as String
Dim AList()
'-- May be variant or string array.
sPath = "C:\CABFolder\NewCAB.cab"
ReDim AList(2)
AList(0) = "C:\Files\File1.txt"
AList(1) = "C:\Files\OtherFile.dll"
AList2) = "C:\Files\ThirdFile.exe"
Ret = DB.BuildCAB(sPath, True, AList)


Return: The return value is the error number. Returns 0 on success. ErrorNumber will return an error code related to a basic file error or a cabinet.dll error. ErrorDesc will return the explanation of that error number. (ErrorNumberInfo is not relevant with CAB errors.) Note that the errors returned work slightly differently from the jsMSI v. 1 function. They are simplified and ErrorDesc can now be used to get a description of the error.

Back to Index
Function ReplaceCAB(sCABName As String, sCABPath As String) As Boolean
   Replaces a CAB embedded in an MSI file with another CAB.sCABName is the string from the Cabinet column of the Media table. sCABPath is the full path of the new CAB file.

Return: Returns boolean. If return is False then error values are set.

Back to Index
Function InsertFile(sFileID As String, sFilePath As String) As Boolean
   Stores a file in the Binary table of an MSI database. sFileID is the value for the Name column of the Binary table. sFilePath is the full path of the file to be stored.

Return: Returns boolean. If return is False then error values are set.

Back to Index
Function GetFileNameFromID(sFileID As String) As String
   Returns a file name given a file ID. Files in an MSI are stored with unique IDs. The ID name can be anything as long as all files have unique names. The file ID is usually not the same as the real file name. This function provides a way to get the match when unpacking CAB files.

Back to Index
Function EXEtoCAB(sEXEPath As String, sCABPath As String) As Boolean
   Converts a self-executing CAB file to a normal CAB file, which can then be accessed for extraction.

Return: Returns boolean. If return is False then error values are set.

Back to Index

Error Codes

   The 3 error properties apply to a large number of the jsMSI methods. They especially apply to methods where jsMSI has to call Windows Installer methods. For example, TableCount does not set error values because it simply returns a stored number. On the other hand, GetValue must make a number of internal function calls to Windows Installer functions. Detailed error information could be useful in that case. The following methods clear error info. when called and return valid error info. on return:

UnpackMSI (Up to opening of database. after that errors go to log file.)
OpenMSI
CreateMSI
ImportTable
ExportTable
GetValue
SetValue
AddRecord
RemoveRecord
RemoveTable
ExportAllTables
GetAllStringValues
GetAllNumericValues
GetSummaryInfo
InsertFile
ExtractFileFromMSI
ExtractFileFromCAB
ExtractCAB
ExtractAllFilesFromCAB
ReplaceCAB

Property ErrorDesc() As String
   May return detailed information about the function where an error originated. A particular jsMSI function might call to 5 Windows Installer functions, for instance. ErrorDesc can often provide details about exacly which internal call failed, providing help in debugging.

Back to Index
Property ErrorNumber() As Long
   Returns error number when a function fails

Back to Index
Property ErrorNumberInfo(LErrNo As Long) As String
   Returns information for specific error numbers. ErrorDesc will often return error information about a specific error that occurs. ErrorNumberInfo returns a generic definition for a particular error number. Note that ErrorNumberInfo mainly relates to MSI errors. Errors with CAB extraction or building are separate CAB errors retrieved with ErrorDesc.

Back to Index

Logging

Sub WriteLogEntry(sEntry As String)
   Adds a string to the currently saved log content.

Back to Index
Property LogText() As String
      Returns any text currently saved in Log.

Back to Index
Sub ClearLog()
   Clears any text currently saved in Log.

Back to Index
Sub WriteLogToFile(sPath As String)
   Writes current log to disk. sPath is full path of new log file. This function does not clear the log.
Contact
   If you have trouble with jsMSI, or find an MSI/MSM file that it cannot unpack, please feel free to write. Occasionally a faulty MSI file shows up that does not unpack properly. If we have a sample of such an MSI to work from jsMSI can probably be updated to unpack such faulty installers.

Email: jsware@jsware.net

Note: We respect your privacy and expect the same from others. Purveyors of free webmail do not respect the privacy of email correspondence. Email from free corporate webmail accounts (hotmail, live.com, yahoo, facebook, gmail) is auto-deleted and will not be received. When writing please use a real email account.

License
   You use all script code and components from JSWare at your own risk.

   The components (compiled DLL and EXE files) may be used for personal or commercial purposes. No payment or attribution is required for either use. The components may be redistributed if they are required as support files for scripts or software that you have written.
   Also, the script code may be used freely, in part or as whole scripts, for any purpose, personal or commercial, without payment or attribution.

   I ask only that you not redistribute these scripts and components, except as required for your direct use. Instead, please direct others to obtain copies of JSWare scripts and components directly from www.jsware.net.

   Also, none of the code here may be redistributed under another license. If a work using code from JSWare is distributed with restrictions of any kind the code from JSWare must be kept exempt from those restrictions. This includes, but is not limited to, code sold for profit, code with usage restrictions and code distributed as so-called "Open Source" with redistribution restrictions.

    Joe Priestley


jsMSI.dll  •  JSWare  •  www.jsware.net  •  jsware@jsware.net