⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 table.c

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 C
📖 第 1 页 / 共 4 页
字号:

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 + -