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 UINT
read_table_from_storage(IStorage
*stg
, LPCWSTR name
, MSITABLE
**ptable
)
161 UINT ret
= ERROR_FUNCTION_FAILED
;
166 encode_streamname(TRUE
, name
, buffer
);
168 TRACE("%s -> %s\n",debugstr_w(name
),debugstr_w(buffer
));
170 r
= IStorage_OpenStream(stg
, buffer
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
173 ERR("open stream failed r = %08lx!\n",r
);
177 r
= IStream_Stat(stm
, &stat
, STATFLAG_NONAME
);
180 ERR("open stream failed r = %08lx!\n",r
);
184 if( stat
.cbSize
.QuadPart
>> 32 )
190 sz
= stat
.cbSize
.QuadPart
;
191 data
= HeapAlloc( GetProcessHeap(), 0, sz
);
194 ERR("couldn't allocate memory r=%08lx!\n",r
);
198 r
= IStream_Read(stm
, data
, sz
, &count
);
201 HeapFree( GetProcessHeap(), 0, data
);
202 ERR("read stream failed r = %08lx!\n",r
);
206 t
= HeapAlloc( GetProcessHeap(), 0, sizeof (MSITABLE
) + lstrlenW(name
)*sizeof (WCHAR
) );
209 HeapFree( GetProcessHeap(), 0, data
);
210 ERR("malloc failed!\n");
219 lstrcpyW( t
->name
, name
);
225 HeapFree( GetProcessHeap(), 0, data
);
226 ERR("Count != sz\n");
231 IStream_Release( stm
);
236 /* add this table to the list of cached tables in the database */
237 void add_table(MSIDATABASE
*db
, MSITABLE
*table
)
239 table
->next
= db
->first_table
;
241 if( db
->first_table
)
242 db
->first_table
->prev
= table
;
244 db
->last_table
= table
;
245 db
->first_table
= table
;
248 /* remove from the list of cached tables */
249 void remove_table( MSIDATABASE
*db
, MSITABLE
*table
)
252 table
->next
->prev
= table
->prev
;
254 db
->last_table
= table
->prev
;
256 table
->prev
->next
= table
->next
;
258 db
->first_table
= table
->next
;
263 void release_table( MSIDATABASE
*db
, MSITABLE
*table
)
265 if( !table
->ref_count
)
266 ERR("Trying to destroy table with refcount 0\n");
268 if( !table
->ref_count
)
270 remove_table( db
, table
);
271 HeapFree( GetProcessHeap(), 0, table
->data
);
272 HeapFree( GetProcessHeap(), 0, table
);
273 TRACE("Destroyed table %s\n", debugstr_w(table
->name
));
277 void free_cached_tables( MSIDATABASE
*db
)
279 while( db
->first_table
)
281 MSITABLE
*t
= db
->first_table
;
283 if ( --t
->ref_count
)
284 ERR("table ref count not zero for %s\n", debugstr_w(t
->name
));
285 remove_table( db
, t
);
286 HeapFree( GetProcessHeap(), 0, t
->data
);
287 HeapFree( GetProcessHeap(), 0, t
);
291 UINT
find_cached_table(MSIDATABASE
*db
, LPCWSTR name
, MSITABLE
**ptable
)
295 for( t
= db
->first_table
; t
; t
=t
->next
)
297 if( !lstrcmpW( name
, t
->name
) )
300 return ERROR_SUCCESS
;
304 return ERROR_FUNCTION_FAILED
;
307 UINT
get_table(MSIDATABASE
*db
, LPCWSTR name
, MSITABLE
**ptable
)
313 /* first, see if the table is cached */
314 r
= find_cached_table( db
, name
, ptable
);
315 if( r
== ERROR_SUCCESS
)
317 (*ptable
)->ref_count
++;
321 r
= read_table_from_storage( db
->storage
, name
, ptable
);
322 if( r
!= ERROR_SUCCESS
)
325 /* add the table to the list */
326 add_table( db
, *ptable
);
327 (*ptable
)->ref_count
++;
329 return ERROR_SUCCESS
;
332 UINT
dump_string_table(MSIDATABASE
*db
)
334 DWORD i
, count
, offset
, len
;
335 string_table
*st
= &db
->strings
;
337 MESSAGE("%d,%d bytes\n",st
->pool
.size
,st
->info
.size
);
339 count
= st
->pool
.size
/4;
342 for(i
=0; i
<count
; i
++)
344 len
= st
->pool
.data
[i
*2];
345 MESSAGE("[%2ld] = %s\n",i
, debugstr_an(st
->info
.data
+offset
,len
));
349 return ERROR_SUCCESS
;
352 UINT
load_string_table( MSIDATABASE
*db
, string_table
*pst
)
354 MSITABLE
*pool
= NULL
, *info
= NULL
;
355 UINT r
, ret
= ERROR_FUNCTION_FAILED
;
356 const WCHAR szStringData
[] = {
357 '_','S','t','r','i','n','g','D','a','t','a',0 };
358 const WCHAR szStringPool
[] = {
359 '_','S','t','r','i','n','g','P','o','o','l',0 };
361 r
= get_table( db
, szStringPool
, &pool
);
362 if( r
!= ERROR_SUCCESS
)
364 r
= get_table( db
, szStringData
, &info
);
365 if( r
!= ERROR_SUCCESS
)
368 pst
->pool
.size
= pool
->size
;
369 pst
->pool
.data
= pool
->data
;
370 pst
->info
.size
= info
->size
;
371 pst
->info
.data
= (CHAR
*)info
->data
;
373 TRACE("Loaded %d,%d bytes\n",pst
->pool
.size
,pst
->info
.size
);
379 release_table( db
, info
);
381 release_table( db
, pool
);
386 UINT
msi_id2string( string_table
*st
, UINT string_no
, LPWSTR buffer
, UINT
*sz
)
388 DWORD i
, count
, offset
, len
;
390 count
= st
->pool
.size
/4;
391 TRACE("Finding string %d of %ld\n", string_no
, count
);
392 if(string_no
>= count
)
393 return ERROR_FUNCTION_FAILED
;
396 for(i
=0; i
<string_no
; i
++)
398 len
= st
->pool
.data
[i
*2];
402 len
= st
->pool
.data
[i
*2];
407 return ERROR_SUCCESS
;
410 if( (offset
+len
) > st
->info
.size
)
411 return ERROR_FUNCTION_FAILED
;
413 len
= MultiByteToWideChar(CP_ACP
,0,&st
->info
.data
[offset
],len
,buffer
,*sz
-1);
417 return ERROR_SUCCESS
;
420 static UINT
msi_string2id( string_table
*st
, LPCWSTR buffer
, UINT
*id
)
422 DWORD i
, count
, offset
, len
, sz
;
423 UINT r
= ERROR_INVALID_PARAMETER
;
426 count
= st
->pool
.size
/4;
427 TRACE("Finding string %s in %ld strings\n", debugstr_w(buffer
), count
);
429 sz
= WideCharToMultiByte( CP_ACP
, 0, buffer
, -1, NULL
, 0, NULL
, NULL
);
432 str
= HeapAlloc( GetProcessHeap(), 0, sz
);
434 return ERROR_NOT_ENOUGH_MEMORY
;
435 WideCharToMultiByte( CP_ACP
, 0, buffer
, -1, str
, sz
, NULL
, NULL
);
438 sz
--; /* nul terminated */
439 for(i
=0; i
<count
; i
++)
441 len
= st
->pool
.data
[i
*2];
442 if ( ( sz
== len
) && !memcmp( str
, st
->info
.data
+offset
, sz
) )
444 TRACE("%ld <- %s\n",i
,debugstr_an(st
->info
.data
+offset
, len
) );
453 HeapFree( GetProcessHeap(), 0, str
);
458 static LPWSTR
strdupW( LPCWSTR str
)
460 UINT len
= lstrlenW( str
);
461 LPWSTR ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof (WCHAR
) );
463 lstrcpyW( ret
, str
);
467 static inline UINT
bytes_per_column( MSICOLUMNINFO
*col
)
469 if( col
->type
& MSITYPE_STRING
)
471 if( (col
->type
& 0xff) > 4 )
472 ERR("Invalid column size!\n");
473 return col
->type
& 0xff;
476 /* information for default tables */
477 const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
478 const WCHAR szTable
[] = { 'T','a','b','l','e',0 };
479 const WCHAR szName
[] = { 'N','a','m','e',0 };
480 const WCHAR szColumns
[] = { '_','C','o','l','u','m','n','s',0 };
481 const WCHAR szColumn
[] = { 'C','o','l','u','m','n',0 };
482 const WCHAR szNumber
[] = { 'N','u','m','b','e','r',0 };
483 const WCHAR szType
[] = { 'T','y','p','e',0 };
485 struct standard_table
{
490 } MSI_standard_tables
[] =
492 { szTables
, szName
, 1, MSITYPE_VALID
| MSITYPE_STRING
| 32},
493 { szColumns
, szTable
, 1, MSITYPE_VALID
| MSITYPE_STRING
| 32},
494 { szColumns
, szNumber
, 2, MSITYPE_VALID
| 2},
495 { szColumns
, szName
, 3, MSITYPE_VALID
| MSITYPE_STRING
| 32},
496 { szColumns
, szType
, 4, MSITYPE_VALID
| 2},
499 #define STANDARD_TABLE_COUNT \
500 (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
502 UINT
get_defaulttablecolumns( LPCWSTR szTable
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
506 for(i
=0; i
<STANDARD_TABLE_COUNT
; i
++)
508 if( lstrcmpW( szTable
, MSI_standard_tables
[i
].tablename
) )
510 if(colinfo
&& (n
< *sz
) )
512 colinfo
[n
].tablename
= strdupW(MSI_standard_tables
[i
].tablename
);
513 colinfo
[n
].colname
= strdupW(MSI_standard_tables
[i
].columnname
);
514 colinfo
[n
].number
= MSI_standard_tables
[i
].number
;
515 colinfo
[n
].type
= MSI_standard_tables
[i
].type
;
516 /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
517 debugstr_w(colinfo[n].colname)); */
519 colinfo
[n
].offset
= colinfo
[n
-1].offset
520 + bytes_per_column( &colinfo
[n
-1] );
522 colinfo
[n
].offset
= 0;
525 if( colinfo
&& (n
>= *sz
) )
529 return ERROR_SUCCESS
;
532 LPWSTR
MSI_makestring( MSIDATABASE
*db
, UINT stringid
)
537 r
= msi_id2string( &db
->strings
, stringid
, NULL
, &sz
);
538 if( r
!= ERROR_SUCCESS
)
540 sz
++; /* space for NUL char */
541 str
= HeapAlloc( GetProcessHeap(), 0, sz
*sizeof (WCHAR
));
544 r
= msi_id2string( &db
->strings
, stringid
, str
, &sz
);
545 if( r
== ERROR_SUCCESS
)
547 HeapFree( GetProcessHeap(), 0, str
);
551 UINT
get_tablecolumns( MSIDATABASE
*db
,
552 LPCWSTR szTableName
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
554 UINT r
, i
, n
=0, table_id
, count
, maxcount
= *sz
;
555 MSITABLE
*table
= NULL
;
556 const WCHAR szColumns
[] = { '_','C','o','l','u','m','n','s',0 };
558 /* first check if there is a default table with that name */
559 r
= get_defaulttablecolumns( szTableName
, colinfo
, sz
);
560 if( ( r
== ERROR_SUCCESS
) && *sz
)
563 r
= get_table( db
, szColumns
, &table
);
564 if( r
!= ERROR_SUCCESS
)
566 ERR("table %s not available\n", debugstr_w(szColumns
));
570 /* convert table and column names to IDs from the string table */
571 r
= msi_string2id( &db
->strings
, szTableName
, &table_id
);
572 if( r
!= ERROR_SUCCESS
)
574 release_table( db
, table
);
575 ERR("Couldn't find id for %s\n", debugstr_w(szTableName
));
579 TRACE("Table id is %d\n", table_id
);
581 count
= table
->size
/8;
582 for( i
=0; i
<count
; i
++ )
584 if( table
->data
[ i
] != table_id
)
588 UINT id
= table
->data
[ i
+ count
*2 ];
589 colinfo
[n
].tablename
= MSI_makestring( db
, table_id
);
590 colinfo
[n
].number
= table
->data
[ i
+ count
] - (1<<15);
591 colinfo
[n
].colname
= MSI_makestring( db
, id
);
592 colinfo
[n
].type
= table
->data
[ i
+ count
*3 ];
593 /* this assumes that columns are in order in the table */
595 colinfo
[n
].offset
= colinfo
[n
-1].offset
596 + bytes_per_column( &colinfo
[n
-1] );
598 colinfo
[n
].offset
= 0;
599 TRACE("table %s column %d is [%s] (%d) with type %08x "
600 "offset %d at row %d\n", debugstr_w(szTableName
),
601 colinfo
[n
].number
, debugstr_w(colinfo
[n
].colname
),
602 id
, colinfo
[n
].type
, colinfo
[n
].offset
, i
);
603 if( n
!= (colinfo
[n
].number
-1) )
605 ERR("oops. data in the _Columns table isn't in the right "
606 "order for table %s\n", debugstr_w(szTableName
));
607 return ERROR_FUNCTION_FAILED
;
611 if( colinfo
&& ( n
>= maxcount
) )
616 release_table( db
, table
);
618 return ERROR_SUCCESS
;
621 /* try to find the table name in the _Tables table */
622 BOOL
TABLE_Exists( MSIDATABASE
*db
, LPWSTR name
)
624 const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
625 const WCHAR szColumns
[] = { '_','C','o','l','u','m','n','s',0 };
626 UINT r
, table_id
= 0, i
, count
;
627 MSITABLE
*table
= NULL
;
629 if( !lstrcmpW( name
, szTables
) )
631 if( !lstrcmpW( name
, szColumns
) )
634 r
= msi_string2id( &db
->strings
, name
, &table_id
);
635 if( r
!= ERROR_SUCCESS
)
637 ERR("Couldn't find id for %s\n", debugstr_w(name
));
641 r
= get_table( db
, szTables
, &table
);
642 if( r
!= ERROR_SUCCESS
)
644 ERR("table %s not available\n", debugstr_w(szTables
));
648 count
= table
->size
/2;
649 for( i
=0; i
<count
; i
++ )
650 if( table
->data
[ i
] == table_id
)
653 release_table( db
, table
);
661 /* below is the query interface to a table */
663 typedef struct tagMSITABLEVIEW
668 MSICOLUMNINFO
*columns
;
675 static UINT
TABLE_fetch_int( struct tagMSIVIEW
*view
, UINT row
, UINT col
, UINT
*val
)
677 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
678 UINT offset
, num_rows
, n
;
681 return ERROR_INVALID_PARAMETER
;
683 if( (col
==0) || (col
>tv
->num_cols
) )
684 return ERROR_INVALID_PARAMETER
;
686 /* how many rows are there ? */
687 num_rows
= tv
->table
->size
/ tv
->row_size
;
688 if( row
>= num_rows
)
689 return ERROR_NO_MORE_ITEMS
;
691 if( tv
->columns
[col
-1].offset
>= tv
->row_size
)
693 ERR("Stuffed up %d >= %d\n", tv
->columns
[col
-1].offset
, tv
->row_size
);
694 ERR("%p %p\n", tv
, tv
->columns
);
695 return ERROR_FUNCTION_FAILED
;
698 offset
= row
+ (tv
->columns
[col
-1].offset
/2) * num_rows
;
699 n
= bytes_per_column( &tv
->columns
[col
-1] );
703 offset
= row
*2 + (tv
->columns
[col
-1].offset
/2) * num_rows
;
704 *val
= tv
->table
->data
[offset
] + (tv
->table
->data
[offset
+ 1] << 16);
707 offset
= row
+ (tv
->columns
[col
-1].offset
/2) * num_rows
;
708 *val
= tv
->table
->data
[offset
];
711 ERR("oops! what is %d bytes per column?\n", n
);
712 return ERROR_FUNCTION_FAILED
;
715 TRACE("Data [%d][%d] = %d \n", row
, col
, *val
);
717 return ERROR_SUCCESS
;
720 static UINT
TABLE_execute( struct tagMSIVIEW
*view
, MSIHANDLE record
)
722 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
725 TRACE("%p %ld\n", tv
, record
);
728 return ERROR_FUNCTION_FAILED
;
730 r
= get_table( tv
->db
, tv
->name
, &tv
->table
);
731 if( r
!= ERROR_SUCCESS
)
734 return ERROR_SUCCESS
;
737 static UINT
TABLE_close( struct tagMSIVIEW
*view
)
739 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
741 TRACE("%p\n", view
);
744 return ERROR_FUNCTION_FAILED
;
746 release_table( tv
->db
, tv
->table
);
749 return ERROR_SUCCESS
;
752 static UINT
TABLE_get_dimensions( struct tagMSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
754 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
756 TRACE("%p %p %p\n", view
, rows
, cols
);
759 *cols
= tv
->num_cols
;
763 return ERROR_INVALID_PARAMETER
;
764 *rows
= tv
->table
->size
/ tv
->row_size
;
767 return ERROR_SUCCESS
;
770 static UINT
TABLE_get_column_info( struct tagMSIVIEW
*view
,
771 UINT n
, LPWSTR
*name
, UINT
*type
)
773 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
775 TRACE("%p %d %p %p\n", tv
, n
, name
, type
);
777 if( ( n
== 0 ) || ( n
> tv
->num_cols
) )
778 return ERROR_INVALID_PARAMETER
;
782 *name
= strdupW( tv
->columns
[n
-1].colname
);
784 return ERROR_FUNCTION_FAILED
;
787 *type
= tv
->columns
[n
-1].type
;
789 return ERROR_SUCCESS
;
792 static UINT
TABLE_modify( struct tagMSIVIEW
*view
, MSIMODIFY eModifyMode
, MSIHANDLE hrec
)
794 FIXME("%p %d %ld\n", view
, eModifyMode
, hrec
);
795 return ERROR_CALL_NOT_IMPLEMENTED
;
798 static UINT
TABLE_delete( struct tagMSIVIEW
*view
)
800 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
802 TRACE("%p\n", view
);
805 release_table( tv
->db
, tv
->table
);
811 for( i
=0; i
<tv
->num_cols
; i
++)
813 HeapFree( GetProcessHeap(), 0, tv
->columns
[i
].colname
);
814 HeapFree( GetProcessHeap(), 0, tv
->columns
[i
].tablename
);
816 HeapFree( GetProcessHeap(), 0, tv
->columns
);
820 HeapFree( GetProcessHeap(), 0, tv
);
822 return ERROR_SUCCESS
;
826 MSIVIEWOPS table_ops
=
831 TABLE_get_dimensions
,
832 TABLE_get_column_info
,
837 UINT
TABLE_CreateView( MSIDATABASE
*db
, LPWSTR name
, MSIVIEW
**view
)
840 UINT r
, sz
, column_count
;
841 MSICOLUMNINFO
*columns
, *last_col
;
843 TRACE("%p %s %p\n", db
, debugstr_w(name
), view
);
845 /* get the number of columns in this table */
847 r
= get_tablecolumns( db
, name
, NULL
, &column_count
);
848 if( r
!= ERROR_SUCCESS
)
851 /* if there's no columns, there's no table */
852 if( column_count
== 0 )
853 return ERROR_INVALID_PARAMETER
;
855 TRACE("Table found\n");
857 sz
= sizeof *tv
+ lstrlenW(name
)*sizeof name
[0] ;
858 tv
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sz
);
860 return ERROR_FUNCTION_FAILED
;
862 columns
= HeapAlloc( GetProcessHeap(), 0, column_count
*sizeof (MSICOLUMNINFO
));
865 HeapFree( GetProcessHeap(), 0, tv
);
866 return ERROR_FUNCTION_FAILED
;
869 r
= get_tablecolumns( db
, name
, columns
, &column_count
);
870 if( r
!= ERROR_SUCCESS
)
872 HeapFree( GetProcessHeap(), 0, columns
);
873 HeapFree( GetProcessHeap(), 0, tv
);
874 return ERROR_FUNCTION_FAILED
;
877 TRACE("Table has %d columns\n", column_count
);
879 last_col
= &columns
[column_count
-1];
881 /* fill the structure */
882 tv
->view
.ops
= &table_ops
;
884 tv
->columns
= columns
;
885 tv
->num_cols
= column_count
;
887 tv
->row_size
= last_col
->offset
+ bytes_per_column( last_col
);
889 TRACE("one row is %d bytes\n", tv
->row_size
);
891 *view
= (MSIVIEW
*) tv
;
892 lstrcpyW( tv
->name
, name
);
894 return ERROR_SUCCESS
;