msidb: Add stub tool for manipulating MSI databases.

The "Windows SDK Components for Windows Installer Developers" has a
command line tool called msidb that is incredibly useful for creating,
editing, and exporting MSI installer databases, think of it as
winemsibuilder on steroids.  This patch series implements much of the
functionality of the msidb tool, maintains compatible CLI flags, and
the underlying MSI functionality necessary to support these features.
Jacek expressed an interest in having these patches resurrected for
use by the Gecko build scripts and Austin's VS builds of Valgrind.
With this patch series all the existing winemsibuilder functionality
is available, plus the ability to drop streams, export the
_SummaryInformation table, and export binary streams (Binary/Icon
tables).  A big feature of the implementation is that it allows you to
edit existing installer databases, rather than just creating new ones.

Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
stable
Erich E. Hoover 2015-09-15 17:44:33 -06:00 committed by Alexandre Julliard
parent 2f149571d5
commit 3d5b606dde
4 changed files with 163 additions and 0 deletions

3
configure vendored
View File

@ -1715,6 +1715,7 @@ enable_ipconfig
enable_lodctr
enable_mofcomp
enable_mshta
enable_msidb
enable_msiexec
enable_msinfo32
enable_net
@ -20292,6 +20293,7 @@ wine_fn_config_makefile programs/ipconfig enable_ipconfig
wine_fn_config_makefile programs/lodctr enable_lodctr
wine_fn_config_makefile programs/mofcomp enable_mofcomp
wine_fn_config_makefile programs/mshta enable_mshta
wine_fn_config_makefile programs/msidb enable_msidb
wine_fn_config_makefile programs/msiexec enable_msiexec
wine_fn_config_makefile programs/msinfo32 enable_msinfo32
wine_fn_config_makefile programs/net enable_net
@ -20443,6 +20445,7 @@ else
fonts \
loader/l_intl.nls \
loader/wine.inf \
programs/msidb/msidb \
programs/msiexec/msiexec \
programs/notepad/notepad \
programs/regedit/regedit \

View File

@ -3938,6 +3938,7 @@ WINE_CONFIG_MAKEFILE(programs/ipconfig)
WINE_CONFIG_MAKEFILE(programs/lodctr)
WINE_CONFIG_MAKEFILE(programs/mofcomp)
WINE_CONFIG_MAKEFILE(programs/mshta)
WINE_CONFIG_MAKEFILE(programs/msidb)
WINE_CONFIG_MAKEFILE(programs/msiexec)
WINE_CONFIG_MAKEFILE(programs/msinfo32)
WINE_CONFIG_MAKEFILE(programs/net)
@ -4106,6 +4107,7 @@ else
fonts \
loader/l_intl.nls \
loader/wine.inf \
programs/msidb/msidb \
programs/msiexec/msiexec \
programs/notepad/notepad \
programs/regedit/regedit \

View File

@ -0,0 +1,5 @@
MODULE = msidb.exe
APPMODE = -mconsole -municode
IMPORTS = msi
C_SRCS = main.c

View File

@ -0,0 +1,153 @@
/*
* msidb - command line tool for assembling MSI packages
*
* Copyright 2015 Erich E. Hoover
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define WIN32_LEAN_AND_MEAN
#include <stdlib.h>
#include <windows.h>
#include <msi.h>
#include <msiquery.h>
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msidb);
struct msidb_state
{
WCHAR *database_file;
WCHAR *table_folder;
MSIHANDLE database_handle;
BOOL create_database;
};
static void show_usage( void )
{
WINE_MESSAGE(
"Usage: msidb [OPTION]...[OPTION]...\n"
"Options:\n"
" -? Show this usage message and exit.\n"
" -c Create database file (instead of opening existing file).\n"
" -d package.msi Path to the database file.\n"
" -f folder Folder in which to open/save the tables.\n"
);
}
static int valid_state( struct msidb_state *state )
{
if (state->database_file == NULL)
{
FIXME( "GUI operation is not currently supported.\n" );
return 0;
}
if (state->table_folder == NULL)
{
ERR( "No table folder specified (-f option).\n" );
show_usage();
return 0;
}
return 1;
}
static int process_argument( struct msidb_state *state, int i, int argc, WCHAR *argv[] )
{
/* msidb accepts either "-" or "/" style flags */
if (strlenW(argv[i]) != 2 || (argv[i][0] != '-' && argv[i][0] != '/'))
{
WINE_FIXME( "Table names are not currently supported.\n" );
show_usage();
exit( 1 );
}
switch( argv[i][1] )
{
case '?':
show_usage();
exit( 0 );
case 'c':
state->create_database = TRUE;
return 1;
case 'd':
if (i + 1 >= argc) return 0;
state->database_file = argv[i + 1];
return 2;
case 'f':
if (i + 1 >= argc) return 0;
state->table_folder = argv[i + 1];
return 2;
default:
break;
}
show_usage();
exit( 1 );
}
static int open_database( struct msidb_state *state )
{
LPCWSTR db_mode = state->create_database ? MSIDBOPEN_CREATE : MSIDBOPEN_TRANSACT;
UINT ret;
ret = MsiOpenDatabaseW( state->database_file, db_mode, &state->database_handle );
if (ret != ERROR_SUCCESS)
{
ERR( "Failed to open database '%s', error %d\n", wine_dbgstr_w(state->database_file), ret );
return 0;
}
return 1;
}
static void close_database( struct msidb_state *state )
{
UINT ret;
ret = MsiDatabaseCommit( state->database_handle );
if (ret != ERROR_SUCCESS)
{
ERR( "Failed to commit changes to database.\n" );
return;
}
ret = MsiCloseHandle( state->database_handle );
if (ret != ERROR_SUCCESS)
{
WARN( "Failed to close database handle.\n" );
return;
}
}
int wmain( int argc, WCHAR *argv[] )
{
struct msidb_state state;
int i, n = 1;
memset( &state, 0x0, sizeof(state) );
/* process and validate all the command line flags */
for (i = 1; n && i < argc; i += n)
n = process_argument( &state, i, argc, argv );
if (!valid_state( &state ))
return 1;
/* perform the requested operations */
if (!open_database( &state ))
{
ERR( "Failed to open database '%s'.\n", wine_dbgstr_w(state.database_file) );
return 1;
}
close_database( &state );
return 0;
}