📄 table.c
字号:
static const MSIVIEWOPS table_ops =
{
TABLE_fetch_int,
TABLE_fetch_stream,
TABLE_set_row,
TABLE_insert_row,
TABLE_execute,
TABLE_close,
TABLE_get_dimensions,
TABLE_get_column_info,
TABLE_modify,
TABLE_delete,
TABLE_find_matching_rows
};
UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
{
MSITABLEVIEW *tv ;
UINT r, sz, column_count;
MSICOLUMNINFO *columns;
TRACE("%p %s %p\n", db, debugstr_w(name), view );
/* get the number of columns in this table */
column_count = 0;
r = get_tablecolumns( db, name, NULL, &column_count );
if( r != ERROR_SUCCESS )
return r;
/* if there's no columns, there's no table */
if( column_count == 0 )
return ERROR_INVALID_PARAMETER;
TRACE("Table found\n");
sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
tv = msi_alloc_zero( sz );
if( !tv )
return ERROR_FUNCTION_FAILED;
columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO));
if( !columns )
{
msi_free( tv );
return ERROR_FUNCTION_FAILED;
}
r = get_tablecolumns( db, name, columns, &column_count );
if( r != ERROR_SUCCESS )
{
msi_free( columns );
msi_free( tv );
return ERROR_FUNCTION_FAILED;
}
TRACE("Table has %d columns\n", column_count);
/* fill the structure */
tv->view.ops = &table_ops;
tv->db = db;
tv->columns = columns;
tv->num_cols = column_count;
tv->table = NULL;
tv->row_size = msi_table_get_row_size( columns, column_count );
TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
*view = (MSIVIEW*) tv;
lstrcpyW( tv->name, name );
return ERROR_SUCCESS;
}
UINT MSI_CommitTables( MSIDATABASE *db )
{
UINT r;
MSITABLE *table = NULL;
TRACE("%p\n",db);
r = save_string_table( db );
if( r != ERROR_SUCCESS )
{
WARN("failed to save string table r=%08x\n",r);
return r;
}
LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
{
r = save_table( db, table );
if( r != ERROR_SUCCESS )
{
WARN("failed to save table %s (r=%08x)\n",
debugstr_w(table->name), r);
return r;
}
}
/* force everything to reload next time */
free_cached_tables( db );
return ERROR_SUCCESS;
}
MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
{
if (!table)
return MSICONDITION_ERROR;
return MSICONDITION_FALSE;
}
static MSIRECORD *msi_get_transform_record( MSITABLEVIEW *tv, string_table *st, USHORT *rawdata )
{
UINT i, val, ofs = 0;
USHORT mask = *rawdata++;
MSICOLUMNINFO *columns = tv->columns;
MSIRECORD *rec;
rec = MSI_CreateRecord( tv->num_cols );
if( !rec )
return rec;
TRACE("row ->\n");
for( i=0; i<tv->num_cols; i++ )
{
UINT n = bytes_per_column( &columns[i] );
if ( (mask&1) && (i>=(mask>>8)) )
break;
/* all keys must be present */
if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
continue;
switch( n )
{
case 2:
val = rawdata[ofs];
if( (columns[i].type & MSITYPE_STRING) &&
! MSITYPE_IS_BINARY(tv->columns[i].type) )
{
LPCWSTR sval = msi_string_lookup_id( st, val );
MSI_RecordSetStringW( rec, i+1, sval );
TRACE(" field %d [%s]\n", i+1, debugstr_w(sval));
}
else
{
if (val)
MSI_RecordSetInteger( rec, i+1, val^0x8000 );
TRACE(" field %d [0x%04x]\n", i+1, val );
}
break;
case 4:
val = (rawdata[ofs] + (rawdata[ofs + 1]<<16));
if (val)
MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
TRACE(" field %d [0x%08x]\n", i+1, val );
break;
default:
ERR("oops - unknown column width %d\n", n);
break;
}
ofs += n/2;
}
return rec;
}
static void dump_record( MSIRECORD *rec )
{
UINT i, n;
n = MSI_RecordGetFieldCount( rec );
for( i=1; i<=n; i++ )
{
LPCWSTR sval = MSI_RecordGetString( rec, i );
if( MSI_RecordIsNull( rec, i ) )
TRACE("row -> []\n");
else if( (sval = MSI_RecordGetString( rec, i )) )
TRACE("row -> [%s]\n", debugstr_w(sval));
else
TRACE("row -> [0x%08x]\n", MSI_RecordGetInteger( rec, i ) );
}
}
static void dump_table( string_table *st, USHORT *rawdata, UINT rawsize )
{
LPCWSTR sval;
UINT i;
for( i=0; i<(rawsize/2); i++ )
{
sval = msi_string_lookup_id( st, rawdata[i] );
MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
}
}
static UINT* msi_record_to_row( MSITABLEVIEW *tv, MSIRECORD *rec )
{
LPCWSTR str;
UINT i, r, *data;
data = msi_alloc( tv->num_cols *sizeof (UINT) );
for( i=0; i<tv->num_cols; i++ )
{
data[i] = 0;
if ( ~tv->columns[i].type & MSITYPE_KEY )
continue;
/* turn the transform column value into a row value */
if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
! MSITYPE_IS_BINARY(tv->columns[i].type) )
{
str = MSI_RecordGetString( rec, i+1 );
r = msi_string2idW( tv->db->strings, str, &data[i] );
/* if there's no matching string in the string table,
these keys can't match any record, so fail now. */
if( ERROR_SUCCESS != r )
{
msi_free( data );
return NULL;
}
}
else
{
data[i] = MSI_RecordGetInteger( rec, i+1 );
if ((tv->columns[i].type&0xff) == 2)
data[i] += 0x8000;
else
data[i] += 0x80000000;
}
}
return data;
}
static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, UINT *data )
{
UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
for( i=0; i<tv->num_cols; i++ )
{
if ( ~tv->columns[i].type & MSITYPE_KEY )
continue;
/* turn the transform column value into a row value */
r = TABLE_fetch_int( &tv->view, row, i+1, &x );
if ( r != ERROR_SUCCESS )
{
ERR("TABLE_fetch_int shouldn't fail here\n");
break;
}
/* if this key matches, move to the next column */
if ( x != data[i] )
{
ret = ERROR_FUNCTION_FAILED;
break;
}
ret = ERROR_SUCCESS;
}
return ret;
}
static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
{
UINT i, r = ERROR_FUNCTION_FAILED, *data;
data = msi_record_to_row( tv, rec );
if( !data )
return r;
for( i=0; i<tv->table->row_count; i++ )
{
r = msi_row_matches( tv, i, data );
if( r == ERROR_SUCCESS )
{
*row = i;
break;
}
}
msi_free( data );
return r;
}
static UINT msi_delete_row( MSITABLEVIEW *tv, UINT row )
{
UINT i;
for( i=1; i<=tv->num_cols; i++ )
TABLE_set_int( tv, row, i, 0 );
return ERROR_SUCCESS;
}
static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
string_table *st, LPCWSTR name )
{
UINT rawsize = 0;
USHORT *rawdata = NULL;
MSITABLEVIEW *tv = NULL;
UINT r, n, sz, i, mask;
MSIRECORD *rec = NULL;
UINT colcol = 0;
WCHAR coltable[32];
coltable[0] = 0;
TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
/* read the transform data */
read_stream_data( stg, name, &rawdata, &rawsize );
if ( !rawdata )
{
TRACE("table %s empty\n", debugstr_w(name) );
return ERROR_INVALID_TABLE;
}
/* create a table view */
r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
if( r != ERROR_SUCCESS )
goto err;
r = tv->view.ops->execute( &tv->view, NULL );
if( r != ERROR_SUCCESS )
goto err;
TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
/* interpret the data */
r = ERROR_SUCCESS;
for( n=0; n < (rawsize/2); )
{
mask = rawdata[n];
if (mask&1)
{
/*
* if the low bit is set, columns are continuous and
* the number of columns is specified in the high byte
*/
sz = 2 + tv->row_size;
}
else
{
/*
* If the low bit is not set, rowdata[n] is a bitmask.
* Excepting for key fields, which are always present,
* each bit indicates that a field is present in the transform record.
*
* rawdata[n] == 0 is a special case ... only the keys will be present
* and it means that this row should be deleted.
*/
sz = 2;
for( i=0; i<tv->num_cols; i++ )
{
if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
sz += bytes_per_column( &tv->columns[i] );
}
}
/* check we didn't run of the end of the table */
if ( (n+sz) > rawsize )
{
ERR("borked.\n");
dump_table( st, rawdata, rawsize );
break;
}
rec = msi_get_transform_record( tv, st, &rawdata[n] );
if (rec)
{
if ( mask & 1 )
{
TRACE("inserting record\n");
/*
* Native msi seems writes nul into the
* Number (2nd) column of the _Columns table.
* Not sure that it's deliberate...
*/
if (!lstrcmpW(name, szColumns))
{
WCHAR table[32];
DWORD sz = 32;
MSI_RecordGetStringW( rec, 1, table, &sz );
/* reset the column number on a new table */
if ( lstrcmpW(coltable, table) )
{
colcol = 0;
lstrcpyW( coltable, table );
}
/* fix nul column numbers */
MSI_RecordSetInteger( rec, 2, ++colcol );
}
r = TABLE_insert_row( &tv->view, rec );
if (r != ERROR_SUCCESS)
ERR("insert row failed\n");
}
else
{
UINT row = 0;
r = msi_table_find_row( tv, rec, &row );
if (r != ERROR_SUCCESS)
ERR("no matching row to transform\n");
else if ( mask )
{
TRACE("modifying row [%d]:\n", row);
TABLE_set_row( &tv->view, row, rec, mask );
}
else
{
TRACE("deleting row [%d]:\n", row);
msi_delete_row( tv, row );
}
}
if( TRACE_ON(msidb) ) dump_record( rec );
msiobj_release( &rec->hdr );
}
n += sz/2;
}
err:
/* no need to free the table, it's associated with the database */
msi_free( rawdata );
if( tv )
tv->view.ops->delete( &tv->view );
return ERROR_SUCCESS;
}
/*
* msi_table_apply_transform
*
* Enumerate the table transforms in a transform storage and apply each one.
*/
UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
{
IEnumSTATSTG *stgenum = NULL;
HRESULT r;
STATSTG stat;
ULONG count;
WCHAR name[0x40];
string_table *strings;
UINT ret = ERROR_FUNCTION_FAILED;
TRACE("%p %p\n", db, stg );
strings = load_string_table( stg );
if( !strings )
goto end;
r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
if( FAILED( r ) )
goto end;
/*
* Apply _Tables and _Columns transforms first so that
* the table metadata is correct, and empty tables exist.
*/
ret = msi_table_load_transform( db, stg, strings, szTables );
if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
goto end;
ret = msi_table_load_transform( db, stg, strings, szColumns );
if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
goto end;
ret = ERROR_SUCCESS;
while( r == ERROR_SUCCESS )
{
count = 0;
r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
if( FAILED( r ) || !count )
break;
decode_streamname( stat.pwcsName, name );
if ( name[0] != 0x4840 )
continue;
TRACE("transform contains stream %s\n", debugstr_w(name));
if ( !lstrcmpW( name+1, szStringPool ) ||
!lstrcmpW( name+1, szStringData ) ||
!lstrcmpW( name+1, szColumns ) ||
!lstrcmpW( name+1, szTables ) )
continue;
ret = msi_table_load_transform( db, stg, strings, name+1 );
}
if ( ret == ERROR_SUCCESS )
append_storage_to_db( db, stg );
end:
if ( stgenum )
IEnumSTATSTG_Release( stgenum );
if ( strings )
msi_destroy_stringtable( strings );
return ret;
}
void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
{
MSITRANSFORM *t;
t = msi_alloc( sizeof *t );
t->stg = stg;
IStorage_AddRef( stg );
list_add_tail( &db->transforms, &t->entry );
}
void msi_free_transforms( MSIDATABASE *db )
{
while( !list_empty( &db->transforms ) )
{
MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
MSITRANSFORM, entry );
list_remove( &t->entry );
IStorage_Release( t->stg );
msi_free( t );
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -