📄 table.c
字号:
{
MSITABLE *t;
LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
if( !lstrcmpW( name, t->name ) )
return t;
return NULL;
}
static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
{
UINT r, column_count = 0;
MSICOLUMNINFO *columns;
/* 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 %s found\n", debugstr_w(name) );
columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO) );
if( !columns )
return ERROR_FUNCTION_FAILED;
r = get_tablecolumns( db, name, columns, &column_count );
if( r != ERROR_SUCCESS )
{
msi_free( columns );
return ERROR_FUNCTION_FAILED;
}
*pcols = columns;
*pcount = column_count;
return r;
}
static MSITABLE *get_table( MSIDATABASE *db, LPCWSTR name,
const MSICOLUMNINFO *cols, UINT num_cols )
{
MSITABLE *table;
/* first, see if the table is cached */
table = find_cached_table( db, name );
if( table )
return table;
table = read_table_from_storage( db->storage, name, cols, num_cols );
if( table )
list_add_head( &db->tables, &table->entry );
return table;
}
static UINT save_table( MSIDATABASE *db, MSITABLE *t )
{
USHORT *rawdata = NULL, *p;
UINT rawsize, r, i, j, row_size, num_cols = 0;
MSICOLUMNINFO *cols = NULL;
TRACE("Saving %s\n", debugstr_w( t->name ) );
r = table_get_column_info( db, t->name, &cols, &num_cols );
if( r != ERROR_SUCCESS )
return r;
row_size = msi_table_get_row_size( cols, num_cols );
rawsize = t->row_count * row_size;
rawdata = msi_alloc_zero( rawsize );
if( !rawdata )
{
r = ERROR_NOT_ENOUGH_MEMORY;
goto err;
}
p = rawdata;
for( i=0; i<num_cols; i++ )
{
for( j=0; j<t->row_count; j++ )
{
UINT offset = cols[i].offset;
*p++ = t->data[j][offset/2];
if( 4 == bytes_per_column( &cols[i] ) )
*p++ = t->data[j][offset/2+1];
}
}
TRACE("writing %d bytes\n", rawsize);
r = write_stream_data( db->storage, t->name, rawdata, rawsize );
err:
msi_free_colinfo( cols, num_cols );
msi_free( cols );
msi_free( rawdata );
return r;
}
HRESULT init_string_table( IStorage *stg )
{
HRESULT r;
USHORT zero[2] = { 0, 0 };
ULONG count = 0;
IStream *stm = NULL;
LPWSTR encname;
encname = encode_streamname(TRUE, szStringPool );
/* create the StringPool stream... add the zero string to it*/
r = IStorage_CreateStream( stg, encname,
STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
msi_free( encname );
if( r )
{
TRACE("Failed\n");
return r;
}
r = IStream_Write(stm, zero, sizeof zero, &count );
IStream_Release( stm );
if( FAILED( r ) || ( count != sizeof zero ) )
{
TRACE("Failed\n");
return E_FAIL;
}
/* create the StringData stream... make it zero length */
encname = encode_streamname(TRUE, szStringData );
r = IStorage_CreateStream( stg, encname,
STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
msi_free( encname );
if( r )
{
TRACE("Failed\n");
return E_FAIL;
}
IStream_Release( stm );
return r;
}
string_table *load_string_table( IStorage *stg )
{
string_table *st = NULL;
CHAR *data = NULL;
USHORT *pool = NULL;
UINT r, datasize = 0, poolsize = 0, codepage;
DWORD i, count, offset, len, n, refs;
r = read_stream_data( stg, szStringPool, &pool, &poolsize );
if( r != ERROR_SUCCESS)
goto end;
r = read_stream_data( stg, szStringData, (USHORT**)&data, &datasize );
if( r != ERROR_SUCCESS)
goto end;
count = poolsize/4;
if( poolsize > 4 )
codepage = pool[0] | ( pool[1] << 16 );
else
codepage = CP_ACP;
st = msi_init_stringtable( count, codepage );
offset = 0;
n = 1;
i = 1;
while( i<count )
{
/* the string reference count is always the second word */
refs = pool[i*2+1];
/* empty entries have two zeros, still have a string id */
if (pool[i*2] == 0 && refs == 0)
{
i++;
n++;
continue;
}
/*
* If a string is over 64k, the previous string entry is made null
* and its the high word of the length is inserted in the null string's
* reference count field.
*/
if( pool[i*2] == 0)
{
len = (pool[i*2+3] << 16) + pool[i*2+2];
i += 2;
}
else
{
len = pool[i*2];
i += 1;
}
if ( (offset + len) > datasize )
{
ERR("string table corrupt?\n");
break;
}
r = msi_addstring( st, n, data+offset, len, refs );
if( r != n )
ERR("Failed to add string %d\n", n );
n++;
offset += len;
}
if ( datasize != offset )
ERR("string table load failed! (%08x != %08x), please report\n", datasize, offset );
TRACE("Loaded %d strings\n", count);
end:
msi_free( pool );
msi_free( data );
return st;
}
static UINT save_string_table( MSIDATABASE *db )
{
UINT i, count, datasize = 0, poolsize = 0, sz, used, r, codepage, n;
UINT ret = ERROR_FUNCTION_FAILED;
CHAR *data = NULL;
USHORT *pool = NULL;
TRACE("\n");
/* construct the new table in memory first */
count = msi_string_totalsize( db->strings, &datasize, &poolsize );
TRACE("%u %u %u\n", count, datasize, poolsize );
pool = msi_alloc( poolsize );
if( ! pool )
{
WARN("Failed to alloc pool %d bytes\n", poolsize );
goto err;
}
data = msi_alloc( datasize );
if( ! data )
{
WARN("Failed to alloc data %d bytes\n", poolsize );
goto err;
}
used = 0;
codepage = msi_string_get_codepage( db->strings );
pool[0]=codepage&0xffff;
pool[1]=(codepage>>16);
n = 1;
for( i=1; i<count; i++ )
{
sz = datasize - used;
r = msi_id2stringA( db->strings, i, data+used, &sz );
if( r != ERROR_SUCCESS )
{
ERR("failed to fetch string\n");
sz = 0;
}
if( sz && (sz < (datasize - used ) ) )
sz--;
if (sz)
pool[ n*2 + 1 ] = msi_id_refcount( db->strings, i );
else
pool[ n*2 + 1 ] = 0;
if (sz < 0x10000)
{
pool[ n*2 ] = sz;
n++;
}
else
{
pool[ n*2 ] = 0;
pool[ n*2 + 2 ] = sz&0xffff;
pool[ n*2 + 3 ] = (sz>>16);
n += 2;
}
used += sz;
if( used > datasize )
{
ERR("oops overran %d >= %d\n", used, datasize);
goto err;
}
}
if( used != datasize )
{
ERR("oops used %d != datasize %d\n", used, datasize);
goto err;
}
/* write the streams */
r = write_stream_data( db->storage, szStringData, data, datasize );
TRACE("Wrote StringData r=%08x\n", r);
if( r )
goto err;
r = write_stream_data( db->storage, szStringPool, pool, poolsize );
TRACE("Wrote StringPool r=%08x\n", r);
if( r )
goto err;
ret = ERROR_SUCCESS;
err:
msi_free( data );
msi_free( pool );
return ret;
}
/* information for default tables */
static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
static const WCHAR szTable[] = { 'T','a','b','l','e',0 };
static const WCHAR szName[] = { 'N','a','m','e',0 };
static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
static const WCHAR szColumn[] = { 'C','o','l','u','m','n',0 };
static const WCHAR szNumber[] = { 'N','u','m','b','e','r',0 };
static const WCHAR szType[] = { 'T','y','p','e',0 };
static const MSICOLUMNINFO _Columns_cols[4] = {
{ szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
{ szColumns, 2, szNumber, MSITYPE_VALID | 2, 2 },
{ szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4 },
{ szColumns, 4, szType, MSITYPE_VALID | 2, 6 },
};
static const MSICOLUMNINFO _Tables_cols[1] = {
{ szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
};
static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
{
const MSICOLUMNINFO *p;
DWORD i, n;
TRACE("%s\n", debugstr_w(name));
if (!lstrcmpW( name, szTables ))
{
p = _Tables_cols;
n = 1;
}
else if (!lstrcmpW( name, szColumns ))
{
p = _Columns_cols;
n = 4;
}
else
return ERROR_FUNCTION_FAILED;
/* duplicate the string data so we can free it in msi_free_colinfo */
for (i=0; i<n; i++)
{
if (colinfo && (i < *sz) )
{
memcpy( &colinfo[i], &p[i], sizeof(MSICOLUMNINFO) );
colinfo[i].tablename = strdupW( p[i].tablename );
colinfo[i].colname = strdupW( p[i].colname );
}
if( colinfo && (i >= *sz) )
break;
}
*sz = n;
return ERROR_SUCCESS;
}
static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
{
UINT i;
for( i=0; i<count; i++ )
{
msi_free( (LPWSTR) colinfo[i].tablename );
msi_free( (LPWSTR) colinfo[i].colname );
msi_free( colinfo[i].hash_table );
}
}
static LPWSTR msi_makestring( MSIDATABASE *db, UINT stringid)
{
return strdupW(msi_string_lookup_id( db->strings, stringid ));
}
static UINT get_tablecolumns( MSIDATABASE *db,
LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
{
UINT r, i, n=0, table_id, count, maxcount = *sz;
MSITABLE *table = NULL;
TRACE("%s\n", debugstr_w(szTableName));
/* first check if there is a default table with that name */
r = get_defaulttablecolumns( szTableName, colinfo, sz );
if( ( r == ERROR_SUCCESS ) && *sz )
return r;
table = get_table( db, szColumns, _Columns_cols, 4 );
if( !table )
{
ERR("couldn't load _Columns table\n");
return ERROR_FUNCTION_FAILED;
}
/* convert table and column names to IDs from the string table */
r = msi_string2idW( db->strings, szTableName, &table_id );
if( r != ERROR_SUCCESS )
{
WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
return r;
}
TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
/* if maxcount is non-zero, assume it's exactly right for this table */
memset( colinfo, 0, maxcount*sizeof(*colinfo) );
count = table->row_count;
for( i=0; i<count; i++ )
{
if( table->data[ i ][ 0 ] != table_id )
continue;
if( colinfo )
{
UINT id = table->data[ i ] [ 2 ];
UINT col = table->data[ i ][ 1 ] - (1<<15);
/* check the column number is in range */
if (col<1 || col>maxcount)
{
ERR("column %d out of range\n", col);
continue;
}
/* check if this column was already set */
if (colinfo[ col - 1 ].number)
{
ERR("duplicate column %d\n", col);
continue;
}
colinfo[ col - 1 ].tablename = msi_makestring( db, table_id );
colinfo[ col - 1 ].number = col;
colinfo[ col - 1 ].colname = msi_makestring( db, id );
colinfo[ col - 1 ].type = table->data[ i ] [ 3 ] - (1<<15);
colinfo[ col - 1 ].offset = 0;
colinfo[ col - 1 ].hash_table = NULL;
}
n++;
}
TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
if (maxcount && n != maxcount)
{
ERR("missing column in table %s\n", debugstr_w(szTableName));
msi_free_colinfo(colinfo, maxcount );
return ERROR_FUNCTION_FAILED;
}
/* calculate the offsets */
for( i=0; maxcount && (i<maxcount); i++ )
{
assert( (i+1) == colinfo[ i ].number );
if (i)
colinfo[i].offset = colinfo[ i - 1 ].offset
+ bytes_per_column( &colinfo[ i - 1 ] );
else
colinfo[i].offset = 0;
TRACE("column %d is [%s] with type %08x ofs %d\n",
colinfo[i].number, debugstr_w(colinfo[i].colname),
colinfo[i].type, colinfo[i].offset);
}
*sz = n;
return ERROR_SUCCESS;
}
/* try to find the table name in the _Tables table */
BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
{
UINT r, table_id = 0, i, count;
MSITABLE *table = NULL;
if( !lstrcmpW( name, szTables ) )
return TRUE;
if( !lstrcmpW( name, szColumns ) )
return TRUE;
r = msi_string2idW( db->strings, name, &table_id );
if( r != ERROR_SUCCESS )
{
TRACE("Couldn't find id for %s\n", debugstr_w(name));
return FALSE;
}
table = get_table( db, szTables, _Tables_cols, 1 );
if( !table )
{
TRACE("table %s not available\n", debugstr_w(szTables));
return FALSE;
}
/* count = table->size/2; */
count = table->row_count;
for( i=0; i<count; i++ )
if( table->data[ i ][ 0 ] == table_id )
break;
if (i!=count)
return TRUE;
TRACE("Searched %d tables, but %d was not found\n", count, table_id );
return FALSE;
}
/* below is the query interface to a table */
typedef struct tagMSITABLEVIEW
{
MSIVIEW view;
MSIDATABASE *db;
MSITABLE *table;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -