2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002 Mike McCormack for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
38 typedef struct tagMSICOLUMNINFO
52 /* MSICOLUMNINFO *columns; */
54 struct tagMSITABLE
*next
;
55 struct tagMSITABLE
*prev
;
59 #define MAX_STREAM_NAME 0x1f
61 static int utf2mime(int x
)
63 if( (x
>='0') && (x
<='9') )
65 if( (x
>='A') && (x
<='Z') )
67 if( (x
>='a') && (x
<='z') )
76 static BOOL
encode_streamname(BOOL bTable
, LPCWSTR in
, LPWSTR out
)
78 DWORD count
= MAX_STREAM_NAME
;
94 if( ( ch
< 0x80 ) && ( utf2mime(ch
) >= 0 ) )
96 ch
= utf2mime(ch
) + 0x4800;
98 if( next
&& (next
<0x80) )
100 next
= utf2mime(next
);
115 static int mime2utf(int x
)
122 return x
- 10 - 26 + 'a';
123 if( x
== (10+26+26) )
128 static BOOL
decode_streamname(LPWSTR in
, LPWSTR out
)
133 while ( (ch
= *in
++) )
135 if( (ch
>= 0x3800 ) && (ch
< 0x4840 ) )
138 ch
= mime2utf(ch
-0x4800);
142 *out
++ = mime2utf(ch
&0x3f);
144 ch
= mime2utf((ch
>>6)&0x3f);
155 static BOOL
read_stream_data( IStorage
*stg
, LPWSTR stname
,
156 USHORT
**pdata
, UINT
*psz
)
159 UINT ret
= ERROR_FUNCTION_FAILED
;
165 r
= IStorage_OpenStream(stg
, stname
, NULL
,
166 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
169 WARN("open stream failed r = %08lx - empty table?\n",r
);
173 r
= IStream_Stat(stm
, &stat
, STATFLAG_NONAME
);
176 ERR("open stream failed r = %08lx!\n",r
);
180 if( stat
.cbSize
.QuadPart
>> 32 )
186 sz
= stat
.cbSize
.QuadPart
;
187 data
= HeapAlloc( GetProcessHeap(), 0, sz
);
190 ERR("couldn't allocate memory r=%08lx!\n",r
);
191 ret
= ERROR_NOT_ENOUGH_MEMORY
;
195 r
= IStream_Read(stm
, data
, sz
, &count
);
196 if( FAILED( r
) || ( count
!= sz
) )
198 HeapFree( GetProcessHeap(), 0, data
);
199 ERR("read stream failed r = %08lx!\n",r
);
208 IStream_Release( stm
);
213 UINT
read_table_from_storage(IStorage
*stg
, LPCWSTR name
, MSITABLE
**ptable
)
218 TRACE("%s -> %s\n",debugstr_w(name
),debugstr_w(buffer
));
220 /* non-existing tables should be interpretted as empty tables */
221 t
= HeapAlloc( GetProcessHeap(), 0,
222 sizeof (MSITABLE
) + lstrlenW(name
)*sizeof (WCHAR
) );
224 return ERROR_NOT_ENOUGH_MEMORY
;
228 lstrcpyW( t
->name
, name
);
232 /* if we can't read the table, just assume that it's empty */
233 encode_streamname(TRUE
, name
, buffer
);
234 read_stream_data( stg
, buffer
, &t
->data
, &t
->size
);
236 return ERROR_SUCCESS
;
239 /* add this table to the list of cached tables in the database */
240 void add_table(MSIDATABASE
*db
, MSITABLE
*table
)
242 table
->next
= db
->first_table
;
244 if( db
->first_table
)
245 db
->first_table
->prev
= table
;
247 db
->last_table
= table
;
248 db
->first_table
= table
;
251 /* remove from the list of cached tables */
252 void remove_table( MSIDATABASE
*db
, MSITABLE
*table
)
255 table
->next
->prev
= table
->prev
;
257 db
->last_table
= table
->prev
;
259 table
->prev
->next
= table
->next
;
261 db
->first_table
= table
->next
;
266 void release_table( MSIDATABASE
*db
, MSITABLE
*table
)
268 if( !table
->ref_count
)
269 ERR("Trying to destroy table with refcount 0\n");
271 if( !table
->ref_count
)
273 remove_table( db
, table
);
274 HeapFree( GetProcessHeap(), 0, table
->data
);
275 HeapFree( GetProcessHeap(), 0, table
);
276 TRACE("Destroyed table %s\n", debugstr_w(table
->name
));
280 void free_cached_tables( MSIDATABASE
*db
)
282 while( db
->first_table
)
284 MSITABLE
*t
= db
->first_table
;
286 if ( --t
->ref_count
)
287 ERR("table ref count not zero for %s\n", debugstr_w(t
->name
));
288 remove_table( db
, t
);
289 HeapFree( GetProcessHeap(), 0, t
->data
);
290 HeapFree( GetProcessHeap(), 0, t
);
294 UINT
find_cached_table(MSIDATABASE
*db
, LPCWSTR name
, MSITABLE
**ptable
)
298 for( t
= db
->first_table
; t
; t
=t
->next
)
300 if( !lstrcmpW( name
, t
->name
) )
303 return ERROR_SUCCESS
;
307 return ERROR_FUNCTION_FAILED
;
310 UINT
get_table(MSIDATABASE
*db
, LPCWSTR name
, MSITABLE
**ptable
)
316 /* first, see if the table is cached */
317 r
= find_cached_table( db
, name
, ptable
);
318 if( r
== ERROR_SUCCESS
)
320 (*ptable
)->ref_count
++;
324 r
= read_table_from_storage( db
->storage
, name
, ptable
);
325 if( r
!= ERROR_SUCCESS
)
328 /* add the table to the list */
329 add_table( db
, *ptable
);
330 (*ptable
)->ref_count
++;
332 return ERROR_SUCCESS
;
335 UINT
dump_string_table(MSIDATABASE
*db
)
337 DWORD i
, count
, offset
, len
;
338 string_table
*st
= &db
->strings
;
340 MESSAGE("%d,%d bytes\n",st
->pool
.size
,st
->info
.size
);
342 count
= st
->pool
.size
/4;
345 for(i
=0; i
<count
; i
++)
347 len
= st
->pool
.data
[i
*2];
348 MESSAGE("[%2ld] = %s\n",i
, debugstr_an(st
->info
.data
+offset
,len
));
352 return ERROR_SUCCESS
;
355 UINT
load_string_table( MSIDATABASE
*db
, string_table
*pst
)
357 MSITABLE
*pool
= NULL
, *info
= NULL
;
358 UINT r
, ret
= ERROR_FUNCTION_FAILED
;
359 const WCHAR szStringData
[] = {
360 '_','S','t','r','i','n','g','D','a','t','a',0 };
361 const WCHAR szStringPool
[] = {
362 '_','S','t','r','i','n','g','P','o','o','l',0 };
364 r
= get_table( db
, szStringPool
, &pool
);
365 if( r
!= ERROR_SUCCESS
)
367 r
= get_table( db
, szStringData
, &info
);
368 if( r
!= ERROR_SUCCESS
)
371 pst
->pool
.size
= pool
->size
;
372 pst
->pool
.data
= pool
->data
;
373 pst
->info
.size
= info
->size
;
374 pst
->info
.data
= (CHAR
*)info
->data
;
376 TRACE("Loaded %d,%d bytes\n",pst
->pool
.size
,pst
->info
.size
);
382 release_table( db
, info
);
384 release_table( db
, pool
);
389 UINT
msi_id2string( string_table
*st
, UINT string_no
, LPWSTR buffer
, UINT
*sz
)
391 DWORD i
, count
, offset
, len
;
393 count
= st
->pool
.size
/4;
394 TRACE("Finding string %d of %ld\n", string_no
, count
);
395 if(string_no
>= count
)
396 return ERROR_FUNCTION_FAILED
;
399 for(i
=0; i
<string_no
; i
++)
401 len
= st
->pool
.data
[i
*2];
405 len
= st
->pool
.data
[i
*2];
410 return ERROR_SUCCESS
;
413 if( (offset
+len
) > st
->info
.size
)
414 return ERROR_FUNCTION_FAILED
;
416 len
= MultiByteToWideChar(CP_ACP
,0,&st
->info
.data
[offset
],len
,buffer
,*sz
-1);
420 return ERROR_SUCCESS
;
423 static UINT
msi_string2id( string_table
*st
, LPCWSTR buffer
, UINT
*id
)
425 DWORD i
, count
, offset
, len
, sz
;
426 UINT r
= ERROR_INVALID_PARAMETER
;
429 count
= st
->pool
.size
/4;
430 TRACE("Finding string %s in %ld strings\n", debugstr_w(buffer
), count
);
432 sz
= WideCharToMultiByte( CP_ACP
, 0, buffer
, -1, NULL
, 0, NULL
, NULL
);
435 str
= HeapAlloc( GetProcessHeap(), 0, sz
);
437 return ERROR_NOT_ENOUGH_MEMORY
;
438 WideCharToMultiByte( CP_ACP
, 0, buffer
, -1, str
, sz
, NULL
, NULL
);
441 sz
--; /* nul terminated */
442 for(i
=0; i
<count
; i
++)
444 len
= st
->pool
.data
[i
*2];
445 if ( ( sz
== len
) && !memcmp( str
, st
->info
.data
+offset
, sz
) )
447 TRACE("%ld <- %s\n",i
,debugstr_an(st
->info
.data
+offset
, len
) );
456 HeapFree( GetProcessHeap(), 0, str
);
461 static LPWSTR
strdupW( LPCWSTR str
)
463 UINT len
= lstrlenW( str
) + 1;
464 LPWSTR ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof (WCHAR
) );
466 lstrcpyW( ret
, str
);
470 static inline UINT
bytes_per_column( MSICOLUMNINFO
*col
)
472 if( col
->type
& MSITYPE_STRING
)
474 if( (col
->type
& 0xff) > 4 )
475 ERR("Invalid column size!\n");
476 return col
->type
& 0xff;
479 /* information for default tables */
480 const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
481 const WCHAR szTable
[] = { 'T','a','b','l','e',0 };
482 const WCHAR szName
[] = { 'N','a','m','e',0 };
483 const WCHAR szColumns
[] = { '_','C','o','l','u','m','n','s',0 };
484 const WCHAR szColumn
[] = { 'C','o','l','u','m','n',0 };
485 const WCHAR szNumber
[] = { 'N','u','m','b','e','r',0 };
486 const WCHAR szType
[] = { 'T','y','p','e',0 };
488 struct standard_table
{
493 } MSI_standard_tables
[] =
495 { szTables
, szName
, 1, MSITYPE_VALID
| MSITYPE_STRING
| 32},
496 { szColumns
, szTable
, 1, MSITYPE_VALID
| MSITYPE_STRING
| 32},
497 { szColumns
, szNumber
, 2, MSITYPE_VALID
| 2},
498 { szColumns
, szName
, 3, MSITYPE_VALID
| MSITYPE_STRING
| 32},
499 { szColumns
, szType
, 4, MSITYPE_VALID
| 2},
502 #define STANDARD_TABLE_COUNT \
503 (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
505 UINT
get_defaulttablecolumns( LPCWSTR szTable
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
509 for(i
=0; i
<STANDARD_TABLE_COUNT
; i
++)
511 if( lstrcmpW( szTable
, MSI_standard_tables
[i
].tablename
) )
513 if(colinfo
&& (n
< *sz
) )
515 colinfo
[n
].tablename
= strdupW(MSI_standard_tables
[i
].tablename
);
516 colinfo
[n
].colname
= strdupW(MSI_standard_tables
[i
].columnname
);
517 colinfo
[n
].number
= MSI_standard_tables
[i
].number
;
518 colinfo
[n
].type
= MSI_standard_tables
[i
].type
;
519 /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
520 debugstr_w(colinfo[n].colname)); */
522 colinfo
[n
].offset
= colinfo
[n
-1].offset
523 + bytes_per_column( &colinfo
[n
-1] );
525 colinfo
[n
].offset
= 0;
528 if( colinfo
&& (n
>= *sz
) )
532 return ERROR_SUCCESS
;
535 LPWSTR
MSI_makestring( MSIDATABASE
*db
, UINT stringid
)
540 r
= msi_id2string( &db
->strings
, stringid
, NULL
, &sz
);
541 if( r
!= ERROR_SUCCESS
)
543 sz
++; /* space for NUL char */
544 str
= HeapAlloc( GetProcessHeap(), 0, sz
*sizeof (WCHAR
));
547 r
= msi_id2string( &db
->strings
, stringid
, str
, &sz
);
548 if( r
== ERROR_SUCCESS
)
550 HeapFree( GetProcessHeap(), 0, str
);
554 UINT
get_tablecolumns( MSIDATABASE
*db
,
555 LPCWSTR szTableName
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
557 UINT r
, i
, n
=0, table_id
, count
, maxcount
= *sz
;
558 MSITABLE
*table
= NULL
;
559 const WCHAR szColumns
[] = { '_','C','o','l','u','m','n','s',0 };
561 /* first check if there is a default table with that name */
562 r
= get_defaulttablecolumns( szTableName
, colinfo
, sz
);
563 if( ( r
== ERROR_SUCCESS
) && *sz
)
566 r
= get_table( db
, szColumns
, &table
);
567 if( r
!= ERROR_SUCCESS
)
569 ERR("table %s not available\n", debugstr_w(szColumns
));
573 /* convert table and column names to IDs from the string table */
574 r
= msi_string2id( &db
->strings
, szTableName
, &table_id
);
575 if( r
!= ERROR_SUCCESS
)
577 release_table( db
, table
);
578 ERR("Couldn't find id for %s\n", debugstr_w(szTableName
));
582 TRACE("Table id is %d\n", table_id
);
584 count
= table
->size
/8;
585 for( i
=0; i
<count
; i
++ )
587 if( table
->data
[ i
] != table_id
)
591 UINT id
= table
->data
[ i
+ count
*2 ];
592 colinfo
[n
].tablename
= MSI_makestring( db
, table_id
);
593 colinfo
[n
].number
= table
->data
[ i
+ count
] - (1<<15);
594 colinfo
[n
].colname
= MSI_makestring( db
, id
);
595 colinfo
[n
].type
= table
->data
[ i
+ count
*3 ];
596 /* this assumes that columns are in order in the table */
598 colinfo
[n
].offset
= colinfo
[n
-1].offset
599 + bytes_per_column( &colinfo
[n
-1] );
601 colinfo
[n
].offset
= 0;
602 TRACE("table %s column %d is [%s] (%d) with type %08x "
603 "offset %d at row %d\n", debugstr_w(szTableName
),
604 colinfo
[n
].number
, debugstr_w(colinfo
[n
].colname
),
605 id
, colinfo
[n
].type
, colinfo
[n
].offset
, i
);
606 if( n
!= (colinfo
[n
].number
-1) )
608 ERR("oops. data in the _Columns table isn't in the right "
609 "order for table %s\n", debugstr_w(szTableName
));
610 return ERROR_FUNCTION_FAILED
;
614 if( colinfo
&& ( n
>= maxcount
) )
619 release_table( db
, table
);
621 return ERROR_SUCCESS
;
624 /* try to find the table name in the _Tables table */
625 BOOL
TABLE_Exists( MSIDATABASE
*db
, LPWSTR name
)
627 const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
628 const WCHAR szColumns
[] = { '_','C','o','l','u','m','n','s',0 };
629 UINT r
, table_id
= 0, i
, count
;
630 MSITABLE
*table
= NULL
;
632 if( !lstrcmpW( name
, szTables
) )
634 if( !lstrcmpW( name
, szColumns
) )
637 r
= msi_string2id( &db
->strings
, name
, &table_id
);
638 if( r
!= ERROR_SUCCESS
)
640 ERR("Couldn't find id for %s\n", debugstr_w(name
));
644 r
= get_table( db
, szTables
, &table
);
645 if( r
!= ERROR_SUCCESS
)
647 ERR("table %s not available\n", debugstr_w(szTables
));
651 count
= table
->size
/2;
652 for( i
=0; i
<count
; i
++ )
653 if( table
->data
[ i
] == table_id
)
656 release_table( db
, table
);
664 /* below is the query interface to a table */
666 typedef struct tagMSITABLEVIEW
671 MSICOLUMNINFO
*columns
;
678 static UINT
TABLE_fetch_int( struct tagMSIVIEW
*view
, UINT row
, UINT col
, UINT
*val
)
680 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
681 UINT offset
, num_rows
, n
;
684 return ERROR_INVALID_PARAMETER
;
686 if( (col
==0) || (col
>tv
->num_cols
) )
687 return ERROR_INVALID_PARAMETER
;
689 /* how many rows are there ? */
690 num_rows
= tv
->table
->size
/ tv
->row_size
;
691 if( row
>= num_rows
)
692 return ERROR_NO_MORE_ITEMS
;
694 if( tv
->columns
[col
-1].offset
>= tv
->row_size
)
696 ERR("Stuffed up %d >= %d\n", tv
->columns
[col
-1].offset
, tv
->row_size
);
697 ERR("%p %p\n", tv
, tv
->columns
);
698 return ERROR_FUNCTION_FAILED
;
701 offset
= row
+ (tv
->columns
[col
-1].offset
/2) * num_rows
;
702 n
= bytes_per_column( &tv
->columns
[col
-1] );
706 offset
= row
*2 + (tv
->columns
[col
-1].offset
/2) * num_rows
;
707 *val
= tv
->table
->data
[offset
] + (tv
->table
->data
[offset
+ 1] << 16);
710 offset
= row
+ (tv
->columns
[col
-1].offset
/2) * num_rows
;
711 *val
= tv
->table
->data
[offset
];
714 ERR("oops! what is %d bytes per column?\n", n
);
715 return ERROR_FUNCTION_FAILED
;
718 TRACE("Data [%d][%d] = %d \n", row
, col
, *val
);
720 return ERROR_SUCCESS
;
723 static UINT
TABLE_execute( struct tagMSIVIEW
*view
, MSIHANDLE record
)
725 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
728 TRACE("%p %ld\n", tv
, record
);
731 return ERROR_FUNCTION_FAILED
;
733 r
= get_table( tv
->db
, tv
->name
, &tv
->table
);
734 if( r
!= ERROR_SUCCESS
)
737 return ERROR_SUCCESS
;
740 static UINT
TABLE_close( struct tagMSIVIEW
*view
)
742 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
744 TRACE("%p\n", view
);
747 return ERROR_FUNCTION_FAILED
;
749 release_table( tv
->db
, tv
->table
);
752 return ERROR_SUCCESS
;
755 static UINT
TABLE_get_dimensions( struct tagMSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
757 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
759 TRACE("%p %p %p\n", view
, rows
, cols
);
762 *cols
= tv
->num_cols
;
766 return ERROR_INVALID_PARAMETER
;
767 *rows
= tv
->table
->size
/ tv
->row_size
;
770 return ERROR_SUCCESS
;
773 static UINT
TABLE_get_column_info( struct tagMSIVIEW
*view
,
774 UINT n
, LPWSTR
*name
, UINT
*type
)
776 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
778 TRACE("%p %d %p %p\n", tv
, n
, name
, type
);
780 if( ( n
== 0 ) || ( n
> tv
->num_cols
) )
781 return ERROR_INVALID_PARAMETER
;
785 *name
= strdupW( tv
->columns
[n
-1].colname
);
787 return ERROR_FUNCTION_FAILED
;
790 *type
= tv
->columns
[n
-1].type
;
792 return ERROR_SUCCESS
;
795 static UINT
TABLE_modify( struct tagMSIVIEW
*view
, MSIMODIFY eModifyMode
, MSIHANDLE hrec
)
797 FIXME("%p %d %ld\n", view
, eModifyMode
, hrec
);
798 return ERROR_CALL_NOT_IMPLEMENTED
;
801 static UINT
TABLE_delete( struct tagMSIVIEW
*view
)
803 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
805 TRACE("%p\n", view
);
808 release_table( tv
->db
, tv
->table
);
814 for( i
=0; i
<tv
->num_cols
; i
++)
816 HeapFree( GetProcessHeap(), 0, tv
->columns
[i
].colname
);
817 HeapFree( GetProcessHeap(), 0, tv
->columns
[i
].tablename
);
819 HeapFree( GetProcessHeap(), 0, tv
->columns
);
823 HeapFree( GetProcessHeap(), 0, tv
);
825 return ERROR_SUCCESS
;
829 MSIVIEWOPS table_ops
=
834 TABLE_get_dimensions
,
835 TABLE_get_column_info
,
840 UINT
TABLE_CreateView( MSIDATABASE
*db
, LPWSTR name
, MSIVIEW
**view
)
843 UINT r
, sz
, column_count
;
844 MSICOLUMNINFO
*columns
, *last_col
;
846 TRACE("%p %s %p\n", db
, debugstr_w(name
), view
);
848 /* get the number of columns in this table */
850 r
= get_tablecolumns( db
, name
, NULL
, &column_count
);
851 if( r
!= ERROR_SUCCESS
)
854 /* if there's no columns, there's no table */
855 if( column_count
== 0 )
856 return ERROR_INVALID_PARAMETER
;
858 TRACE("Table found\n");
860 sz
= sizeof *tv
+ lstrlenW(name
)*sizeof name
[0] ;
861 tv
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sz
);
863 return ERROR_FUNCTION_FAILED
;
865 columns
= HeapAlloc( GetProcessHeap(), 0, column_count
*sizeof (MSICOLUMNINFO
));
868 HeapFree( GetProcessHeap(), 0, tv
);
869 return ERROR_FUNCTION_FAILED
;
872 r
= get_tablecolumns( db
, name
, columns
, &column_count
);
873 if( r
!= ERROR_SUCCESS
)
875 HeapFree( GetProcessHeap(), 0, columns
);
876 HeapFree( GetProcessHeap(), 0, tv
);
877 return ERROR_FUNCTION_FAILED
;
880 TRACE("Table has %d columns\n", column_count
);
882 last_col
= &columns
[column_count
-1];
884 /* fill the structure */
885 tv
->view
.ops
= &table_ops
;
887 tv
->columns
= columns
;
888 tv
->num_cols
= column_count
;
890 tv
->row_size
= last_col
->offset
+ bytes_per_column( last_col
);
892 TRACE("one row is %d bytes\n", tv
->row_size
);
894 *view
= (MSIVIEW
*) tv
;
895 lstrcpyW( tv
->name
, name
);
897 return ERROR_SUCCESS
;