The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Win32::MSI::DB - a Perl package to modify MSI databases

SYNOPSIS

  use Win32::MSI::DB;
  
  $database=Win32::MSI::DB::new('filename', $flags);

  $database->transform('filename', $flags);

  $table=$database->table("table");
  $view=$database->view("SELECT * FROM File 
			WHERE FileSize < ?",100000);
  
  @rec=$table->records();
  $rec4=$table->record(4);
  
  $rec->set("field","value"); # string
  $rec->set("field",4);       # int
  $rec->set("field","file");  # streams
  
  $rec->get("field");
  $rec->getintofile("field","file");
  
  $field=$rec->field("field");
  $field->set(2);
  $data=$field->get();
  $field->fromfile("autoexec.bat");
  $field->intofile("tmp.aa");
  
  $db->error();
  $view->error();
  $rec->error();

DESCRIPTION

Obtaining a database object

This is currently done by using MSI::DB::new. It takes filename as first parameter and one of the following constants as optional second.

$Win32::MSI::MSIDBOPEN_READONLY

This opens the file not read-only, but changes will not be written to disk.

$Win32::MSI::MSIDBOPEN_TRANSACT

This allows transactional functionality, ie. changes are written on commit only. This is the default.

$Win32::MSI::MSIDBOPEN_DIRECT

Opens read/write without transactional behaviour.

$Win32::MSI::MSIDBOPEN_CREATE

This creates a new database in transactional mode.

This database object allows creation of table or views, depending on your needs. If you simply need access to a table you can use the table method; for a subset of records or even a SQL-query you can use the view method.

Using transforms

When you have got a handle to a database, you can successivly apply transforms to it. You do this by using transform, which needs the filename of the transform file (normally with extension .mst) and optionally a flag specification.

Most of the possible flag values specify which merge errors are to be suppressed.

$Win32::MSI::MSITR_IGNORE_ADDEXISTINGROW

Ignores adding a row that already exists.

$Win32::MSI::MSITR_IGNORE_ADDEXISTINGTABLE

Ignores adding a table that already exists.

$Win32::MSI::MSITR_IGNORE_DELMISSINGROW

Ignores deleting a row that doesn't exist.

$Win32::MSI::MSITR_IGNORE_DELMISSINGTABLE

Ignores deleting a table that doesn't exist.

$Win32::MSI::MSITR_IGNORE_UPDATEMISSINGROW

Ignores updating a row that doesn't exist.

$Win32::MSI::MSITR_IGNORE_CHANGECODEPAGE

Ignores that the code pages in the MSI database and the transform file do not match and neither has a neutral code page.

$Win32::MSI::MSITR_IGNORE_ALL

This flag combines all of the above mentioned flags. This is the default.

$Win32::MSI::MSITR_VIEWTRANSFORM

This flag should not be used together with the other flags. It specifies that instead of merging the data a table named _TransformView is created in memory, which has the columns Table, Column, Row, Data and Current.

This way the data in a transform file can be directly queried.

For more information please see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/_transformview_table.asp.

This opens the file not read-only, but changes will not be written to disk.

A transform is a specification of changed values. So you get a MSI database from your favorite vendor, make a transform to <overlay> your own settings (the target installation directory, the features to be installed, etc.) and upon installation you can use these settings via a commandline similar to

msiexec /i TRANSFORMS=F<your transform file> F<the msi database> /qb

The changes in a transform are stored by a (table, row, cell, old value, new value) tuple.

From a table or view to records

When you have obtained a table or view object, you can use the record method to access individual records. It takes a number as parameter. Here the records are fetched as needed; using undef as parameter fetches all records and returns the first (index 0).

Another possibility is to use the method records, which returns an array of all records in this table or view.

A record has fields

And this fields can be queried or changed using the record object, as in

$rec->set("field","value"); # string
$rec->set("field",4);       # int
$rec->set("field","file");  # streams

$rec->get("field");
$rec->getintofile("field","file");

or you can have separate field objects:

$field=$rec->field("field");

$data=$field->get();
$field->set(2);

Remark: the access to files (streams) is currently not finished.

Errors

Each object may access an error method, which gives a string or an array (depending on context) containing the error information.

Help wanted: Is there a way to get a error string from the number which does not depend on the current MSI database?

Especially the developer-errors (2000 and above) are not listed.

REMARKS

This module depends on Win32::API, which is used to import the functions out of the msi.dll.

Currently the Exporter is not used - patches are welcome.

AUTHOR

Please contact pmarek@cpan.org for questions, suggestions, and patches (diff -wu2 please).

Further plans

A Win32::MSI::Tools package is planned - which will allow to compare databases and give a diff, and similar tools.

I have started to write a simple Tk visualization.

SEE ALSO

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installer_database_reference.asp