msi: Add support for binary OBJECTS.
This commit is contained in:
parent
8356484c9f
commit
f6dd90de5e
|
@ -930,6 +930,7 @@ MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
|
|||
msiobj_release(&clone->hdr);
|
||||
return NULL;
|
||||
}
|
||||
clone->fields[i].type = MSIFIELD_STREAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
199
dlls/msi/table.c
199
dlls/msi/table.c
|
@ -1158,6 +1158,93 @@ static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *
|
|||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static UINT msi_stream_name( const MSITABLEVIEW *tv, UINT row, LPWSTR *pstname )
|
||||
{
|
||||
LPWSTR p, stname = NULL;
|
||||
UINT i, r, type, ival;
|
||||
DWORD len;
|
||||
LPCWSTR sval;
|
||||
MSIVIEW *view = (MSIVIEW *) tv;
|
||||
|
||||
TRACE("%p %d\n", tv, row);
|
||||
|
||||
len = lstrlenW( tv->name ) + 1;
|
||||
stname = msi_alloc( len*sizeof(WCHAR) );
|
||||
if ( !stname )
|
||||
{
|
||||
r = ERROR_OUTOFMEMORY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
lstrcpyW( stname, tv->name );
|
||||
|
||||
for ( i = 0; i < tv->num_cols; i++ )
|
||||
{
|
||||
type = tv->columns[i].type;
|
||||
if ( type & MSITYPE_KEY )
|
||||
{
|
||||
static const WCHAR szDot[] = { '.', 0 };
|
||||
|
||||
r = TABLE_fetch_int( view, row, i+1, &ival );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
goto err;
|
||||
|
||||
if ( tv->columns[i].type & MSITYPE_STRING )
|
||||
{
|
||||
sval = msi_string_lookup_id( tv->db->strings, ival );
|
||||
if ( !sval )
|
||||
{
|
||||
r = ERROR_INVALID_PARAMETER;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static const WCHAR fmt[] = { '%','d',0 };
|
||||
WCHAR number[0x20];
|
||||
UINT n = bytes_per_column( tv->db, &tv->columns[i] );
|
||||
|
||||
switch( n )
|
||||
{
|
||||
case 2:
|
||||
sprintfW( number, fmt, ival^0x8000 );
|
||||
break;
|
||||
case 4:
|
||||
sprintfW( number, fmt, ival^0x80000000 );
|
||||
break;
|
||||
default:
|
||||
ERR( "oops - unknown column width %d\n", n );
|
||||
r = ERROR_FUNCTION_FAILED;
|
||||
goto err;
|
||||
}
|
||||
sval = number;
|
||||
}
|
||||
|
||||
len += lstrlenW( szDot ) + lstrlenW( sval );
|
||||
p = msi_realloc ( stname, len*sizeof(WCHAR) );
|
||||
if ( !p )
|
||||
{
|
||||
r = ERROR_OUTOFMEMORY;
|
||||
goto err;
|
||||
}
|
||||
stname = p;
|
||||
|
||||
lstrcatW( stname, szDot );
|
||||
lstrcatW( stname, sval );
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
*pstname = stname;
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
err:
|
||||
msi_free( stname );
|
||||
*pstname = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need a special case for streams, as we need to reference column with
|
||||
* the name of the stream in the same table, and the table name
|
||||
|
@ -1166,58 +1253,19 @@ static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *
|
|||
static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
|
||||
{
|
||||
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
|
||||
UINT ival = 0, refcol = 0, r;
|
||||
LPCWSTR sval;
|
||||
LPWSTR full_name;
|
||||
DWORD len;
|
||||
static const WCHAR szDot[] = { '.', 0 };
|
||||
WCHAR number[0x20];
|
||||
UINT r;
|
||||
LPWSTR full_name = NULL;
|
||||
|
||||
if( !view->ops->fetch_int )
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
/*
|
||||
* The column marked with the type stream data seems to have a single number
|
||||
* which references the column containing the name of the stream data
|
||||
*
|
||||
* Fetch the column to reference first.
|
||||
*/
|
||||
r = view->ops->fetch_int( view, row, col, &ival );
|
||||
if( r != ERROR_SUCCESS )
|
||||
r = msi_stream_name( tv, row, &full_name );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
{
|
||||
ERR("fetching stream, error = %d\n", r);
|
||||
return r;
|
||||
|
||||
/* check the column value is in range */
|
||||
if (ival > tv->num_cols || ival == col)
|
||||
{
|
||||
ERR("bad column ref (%u) for stream\n", ival);
|
||||
return ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
if ( tv->columns[ival - 1].type & MSITYPE_STRING )
|
||||
{
|
||||
/* now get the column with the name of the stream */
|
||||
r = view->ops->fetch_int( view, row, ival, &refcol );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
return r;
|
||||
|
||||
/* lookup the string value from the string table */
|
||||
sval = msi_string_lookup_id( tv->db->strings, refcol );
|
||||
if ( !sval )
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const WCHAR fmt[] = { '%','d',0 };
|
||||
sprintfW( number, fmt, ival );
|
||||
sval = number;
|
||||
}
|
||||
|
||||
len = lstrlenW( tv->name ) + 2 + lstrlenW( sval );
|
||||
full_name = msi_alloc( len*sizeof(WCHAR) );
|
||||
lstrcpyW( full_name, tv->name );
|
||||
lstrcatW( full_name, szDot );
|
||||
lstrcatW( full_name, sval );
|
||||
|
||||
r = db_get_raw_stream( tv->db, full_name, stm );
|
||||
if( r )
|
||||
ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
|
||||
|
@ -1285,6 +1333,46 @@ static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
|
|||
return msi_view_get_row(tv->db, view, row, rec);
|
||||
}
|
||||
|
||||
static UINT msi_addstreamW( MSIDATABASE *db, LPCWSTR name, IStream *data )
|
||||
{
|
||||
UINT r;
|
||||
MSIQUERY *query = NULL;
|
||||
MSIRECORD *rec = NULL;
|
||||
|
||||
static const WCHAR insert[] = {
|
||||
'I','N','S','E','R','T',' ','I','N','T','O',' ',
|
||||
'`','_','S','t','r','e','a','m','s','`',' ',
|
||||
'(','`','N','a','m','e','`',',',
|
||||
'`','D','a','t','a','`',')',' ',
|
||||
'V','A','L','U','E','S',' ','(','?',',','?',')',0};
|
||||
|
||||
TRACE("%p %s %p\n", db, debugstr_w(name), data);
|
||||
|
||||
rec = MSI_CreateRecord( 2 );
|
||||
if ( !rec )
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
r = MSI_RecordSetStringW( rec, 1, name );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
goto err;
|
||||
|
||||
r = MSI_RecordSetIStream( rec, 2, data );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
goto err;
|
||||
|
||||
r = MSI_DatabaseOpenViewW( db, insert, &query );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
goto err;
|
||||
|
||||
r = MSI_ViewExecute( query, rec );
|
||||
|
||||
err:
|
||||
msiobj_release( &query->hdr );
|
||||
msiobj_release( &rec->hdr );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
|
||||
{
|
||||
MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
|
||||
|
@ -1315,6 +1403,27 @@ static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UI
|
|||
{
|
||||
if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
|
||||
{
|
||||
IStream *stm;
|
||||
LPWSTR stname;
|
||||
|
||||
r = MSI_RecordGetIStream( rec, i + 1, &stm );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
return r;
|
||||
|
||||
r = msi_stream_name( tv, row, &stname );
|
||||
if ( r != ERROR_SUCCESS )
|
||||
{
|
||||
IStream_Release( stm );
|
||||
return r;
|
||||
}
|
||||
|
||||
r = msi_addstreamW( tv->db, stname, stm );
|
||||
IStream_Release( stm );
|
||||
msi_free ( stname );
|
||||
|
||||
if ( r != ERROR_SUCCESS )
|
||||
return r;
|
||||
|
||||
val = 1; /* refers to the first key column */
|
||||
}
|
||||
else if ( tv->columns[i].type & MSITYPE_STRING )
|
||||
|
|
|
@ -1524,18 +1524,18 @@ static void test_binary(void)
|
|||
|
||||
query = "SELECT * FROM `_Streams`";
|
||||
r = do_query( hdb, query, &rec );
|
||||
todo_wine ok( r == ERROR_SUCCESS, "SELECT query failed: %d\n", r );
|
||||
ok( r == ERROR_SUCCESS, "SELECT query failed: %d\n", r );
|
||||
|
||||
size = MAX_PATH;
|
||||
r = MsiRecordGetString( rec, 1, file, &size );
|
||||
todo_wine ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r );
|
||||
todo_wine ok( !lstrcmp(file, "Binary.filename1.1"), "Expected 'Binary.filename1.1', got %s\n", file );
|
||||
ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r );
|
||||
ok( !lstrcmp(file, "Binary.filename1.1"), "Expected 'Binary.filename1.1', got %s\n", file );
|
||||
|
||||
size = MAX_PATH;
|
||||
memset( buf, 0, MAX_PATH );
|
||||
r = MsiRecordReadStream( rec, 2, buf, &size );
|
||||
todo_wine ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r );
|
||||
todo_wine ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf );
|
||||
ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r );
|
||||
ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf );
|
||||
|
||||
r = MsiCloseHandle( rec );
|
||||
ok( r == ERROR_SUCCESS , "Failed to close record handle\n" );
|
||||
|
@ -1553,8 +1553,8 @@ static void test_binary(void)
|
|||
size = MAX_PATH;
|
||||
memset( buf, 0, MAX_PATH );
|
||||
r = MsiRecordReadStream( rec, 3, buf, &size );
|
||||
todo_wine ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r );
|
||||
todo_wine ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf );
|
||||
ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r );
|
||||
ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf );
|
||||
|
||||
r = MsiCloseHandle( rec );
|
||||
ok( r == ERROR_SUCCESS , "Failed to close record handle\n" );
|
||||
|
@ -6982,12 +6982,9 @@ static void test_dbmerge(void)
|
|||
size = MAX_PATH;
|
||||
ZeroMemory(buf, MAX_PATH);
|
||||
r = MsiRecordReadStream(hrec, 2, buf, &size);
|
||||
todo_wine
|
||||
{
|
||||
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
||||
ok(!lstrcmpA(buf, "binary.dat\n"),
|
||||
"Expected \"binary.dat\\n\", got \"%s\"\n", buf);
|
||||
}
|
||||
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
||||
ok(!lstrcmpA(buf, "binary.dat\n"),
|
||||
"Expected \"binary.dat\\n\", got \"%s\"\n", buf);
|
||||
|
||||
MsiCloseHandle(hrec);
|
||||
|
||||
|
|
Loading…
Reference in New Issue