From f6dd90de5ec08218cc324a847d81021b0128d2a6 Mon Sep 17 00:00:00 2001 From: Hib Eris Date: Sun, 24 May 2009 18:51:53 +0200 Subject: [PATCH] msi: Add support for binary OBJECTS. --- dlls/msi/record.c | 1 + dlls/msi/table.c | 199 ++++++++++++++++++++++++++++++++++---------- dlls/msi/tests/db.c | 23 +++-- 3 files changed, 165 insertions(+), 58 deletions(-) diff --git a/dlls/msi/record.c b/dlls/msi/record.c index db54cdc3e0..283c41849f 100644 --- a/dlls/msi/record.c +++ b/dlls/msi/record.c @@ -930,6 +930,7 @@ MSIRECORD *MSI_CloneRecord(MSIRECORD *rec) msiobj_release(&clone->hdr); return NULL; } + clone->fields[i].type = MSIFIELD_STREAM; } else { diff --git a/dlls/msi/table.c b/dlls/msi/table.c index e7e2e68c38..8bb607cbc2 100644 --- a/dlls/msi/table.c +++ b/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 ) diff --git a/dlls/msi/tests/db.c b/dlls/msi/tests/db.c index 249b990057..a9bd475e99 100644 --- a/dlls/msi/tests/db.c +++ b/dlls/msi/tests/db.c @@ -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);