msi: Improve the stub implementations of RemoveExistingProducts and MigrateFeatureStates.
[wine] / dlls / msi / database.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
5  *
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.
10  *
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.
15  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winreg.h"
30 #include "winnls.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "msi.h"
34 #include "msiquery.h"
35 #include "msipriv.h"
36 #include "objidl.h"
37 #include "objbase.h"
38 #include "msiserver.h"
39 #include "query.h"
40
41 #include "initguid.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msi);
44
45 /*
46  *  .MSI  file format
47  *
48  *  An .msi file is a structured storage file.
49  *  It contains a number of streams.
50  *  A stream for each table in the database.
51  *  Two streams for the string table in the database.
52  *  Any binary data in a table is a reference to a stream.
53  */
54
55 #define IS_INTMSIDBOPEN(x)      (((ULONG_PTR)(x) >> 16) == 0)
56
57 typedef struct tagMSITRANSFORM {
58     struct list entry;
59     IStorage *stg;
60 } MSITRANSFORM;
61
62 typedef struct tagMSISTREAM {
63     struct list entry;
64     IStream *stm;
65 } MSISTREAM;
66
67 static UINT find_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
68 {
69     MSISTREAM *stream;
70
71     LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry )
72     {
73         HRESULT r;
74         STATSTG stat;
75
76         r = IStream_Stat( stream->stm, &stat, 0 );
77         if( FAILED( r ) )
78         {
79             WARN("failed to stat stream r = %08x!\n", r);
80             continue;
81         }
82
83         if( !strcmpW( name, stat.pwcsName ) )
84         {
85             TRACE("found %s\n", debugstr_w(name));
86             *stm = stream->stm;
87             CoTaskMemFree( stat.pwcsName );
88             return ERROR_SUCCESS;
89         }
90
91         CoTaskMemFree( stat.pwcsName );
92     }
93
94     return ERROR_FUNCTION_FAILED;
95 }
96
97 static UINT clone_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
98 {
99     IStream *stream;
100
101     if (find_open_stream( db, name, &stream ) == ERROR_SUCCESS)
102     {
103         HRESULT r;
104         LARGE_INTEGER pos;
105
106         r = IStream_Clone( stream, stm );
107         if( FAILED( r ) )
108         {
109             WARN("failed to clone stream r = %08x!\n", r);
110             return ERROR_FUNCTION_FAILED;
111         }
112
113         pos.QuadPart = 0;
114         r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL );
115         if( FAILED( r ) )
116         {
117             IStream_Release( *stm );
118             return ERROR_FUNCTION_FAILED;
119         }
120
121         return ERROR_SUCCESS;
122     }
123
124     return ERROR_FUNCTION_FAILED;
125 }
126
127 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
128 {
129     HRESULT r;
130     WCHAR decoded[MAX_STREAM_NAME_LEN];
131
132     decode_streamname( stname, decoded );
133     TRACE("%s -> %s\n", debugstr_w(stname), debugstr_w(decoded));
134
135     if (clone_open_stream( db, stname, stm ) == ERROR_SUCCESS)
136         return ERROR_SUCCESS;
137
138     r = IStorage_OpenStream( db->storage, stname, NULL,
139                              STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
140     if( FAILED( r ) )
141     {
142         MSITRANSFORM *transform;
143
144         LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
145         {
146             r = IStorage_OpenStream( transform->stg, stname, NULL,
147                                      STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
148             if (SUCCEEDED(r))
149                 break;
150         }
151     }
152
153     if( SUCCEEDED(r) )
154     {
155         MSISTREAM *stream;
156
157         stream = msi_alloc( sizeof(MSISTREAM) );
158         if( !stream )
159             return ERROR_NOT_ENOUGH_MEMORY;
160
161         stream->stm = *stm;
162         IStream_AddRef( *stm );
163         list_add_tail( &db->streams, &stream->entry );
164     }
165
166     return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
167 }
168
169 static void free_transforms( MSIDATABASE *db )
170 {
171     while( !list_empty( &db->transforms ) )
172     {
173         MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
174                                       MSITRANSFORM, entry );
175         list_remove( &t->entry );
176         IStorage_Release( t->stg );
177         msi_free( t );
178     }
179 }
180
181 void db_destroy_stream( MSIDATABASE *db, LPCWSTR stname )
182 {
183     MSISTREAM *stream, *stream2;
184
185     LIST_FOR_EACH_ENTRY_SAFE( stream, stream2, &db->streams, MSISTREAM, entry )
186     {
187         HRESULT r;
188         STATSTG stat;
189
190         r = IStream_Stat( stream->stm, &stat, 0 );
191         if (FAILED(r))
192         {
193             WARN("failed to stat stream r = %08x\n", r);
194             continue;
195         }
196
197         if (!strcmpW( stname, stat.pwcsName ))
198         {
199             TRACE("destroying %s\n", debugstr_w(stname));
200
201             list_remove( &stream->entry );
202             IStream_Release( stream->stm );
203             msi_free( stream );
204             IStorage_DestroyElement( db->storage, stname );
205             CoTaskMemFree( stat.pwcsName );
206             break;
207         }
208         CoTaskMemFree( stat.pwcsName );
209     }
210 }
211
212 static void free_streams( MSIDATABASE *db )
213 {
214     while( !list_empty( &db->streams ) )
215     {
216         MSISTREAM *s = LIST_ENTRY( list_head( &db->streams ),
217                                    MSISTREAM, entry );
218         list_remove( &s->entry );
219         IStream_Release( s->stm );
220         msi_free( s );
221     }
222 }
223
224 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
225 {
226     MSITRANSFORM *t;
227
228     t = msi_alloc( sizeof *t );
229     t->stg = stg;
230     IStorage_AddRef( stg );
231     list_add_head( &db->transforms, &t->entry );
232
233     /* the transform may add or replace streams */
234     free_streams( db );
235 }
236
237 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
238 {
239     MSIDATABASE *db = (MSIDATABASE *) arg;
240
241     msi_free(db->path);
242     free_cached_tables( db );
243     free_streams( db );
244     free_transforms( db );
245     if (db->strings) msi_destroy_stringtable( db->strings );
246     IStorage_Release( db->storage );
247     if (db->deletefile)
248     {
249         DeleteFileW( db->deletefile );
250         msi_free( db->deletefile );
251     }
252     if (db->localfile)
253     {
254         DeleteFileW( db->localfile );
255         msi_free( db->localfile );
256     }
257 }
258
259 static HRESULT db_initialize( IStorage *stg, const GUID *clsid )
260 {
261     static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
262     HRESULT hr;
263
264     hr = IStorage_SetClass( stg, clsid );
265     if (FAILED( hr ))
266     {
267         WARN("failed to set class id 0x%08x\n", hr);
268         return hr;
269     }
270
271     /* create the _Tables stream */
272     hr = write_stream_data( stg, szTables, NULL, 0, TRUE );
273     if (FAILED( hr ))
274     {
275         WARN("failed to create _Tables stream 0x%08x\n", hr);
276         return hr;
277     }
278
279     hr = msi_init_string_table( stg );
280     if (FAILED( hr ))
281     {
282         WARN("failed to initialize string table 0x%08x\n", hr);
283         return hr;
284     }
285
286     hr = IStorage_Commit( stg, 0 );
287     if (FAILED( hr ))
288     {
289         WARN("failed to commit changes 0x%08x\n", hr);
290         return hr;
291     }
292
293     return S_OK;
294 }
295
296 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
297 {
298     IStorage *stg = NULL;
299     HRESULT r;
300     MSIDATABASE *db = NULL;
301     UINT ret = ERROR_FUNCTION_FAILED;
302     LPCWSTR szMode, save_path;
303     STATSTG stat;
304     BOOL created = FALSE, patch = FALSE;
305     WCHAR path[MAX_PATH];
306
307     TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
308
309     if( !pdb )
310         return ERROR_INVALID_PARAMETER;
311
312     if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
313         szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
314     {
315         TRACE("Database is a patch\n");
316         szPersist -= MSIDBOPEN_PATCHFILE;
317         patch = TRUE;
318     }
319
320     save_path = szDBPath;
321     szMode = szPersist;
322     if( !IS_INTMSIDBOPEN(szPersist) )
323     {
324         if (!CopyFileW( szDBPath, szPersist, FALSE ))
325             return ERROR_OPEN_FAILED;
326
327         szDBPath = szPersist;
328         szPersist = MSIDBOPEN_TRANSACT;
329         created = TRUE;
330     }
331
332     if( szPersist == MSIDBOPEN_READONLY )
333     {
334         r = StgOpenStorage( szDBPath, NULL,
335               STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
336     }
337     else if( szPersist == MSIDBOPEN_CREATE )
338     {
339         r = StgCreateDocfile( szDBPath,
340               STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
341
342         if( SUCCEEDED(r) )
343             r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
344         created = TRUE;
345     }
346     else if( szPersist == MSIDBOPEN_CREATEDIRECT )
347     {
348         r = StgCreateDocfile( szDBPath,
349               STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
350
351         if( SUCCEEDED(r) )
352             r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
353         created = TRUE;
354     }
355     else if( szPersist == MSIDBOPEN_TRANSACT )
356     {
357         r = StgOpenStorage( szDBPath, NULL,
358               STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
359     }
360     else if( szPersist == MSIDBOPEN_DIRECT )
361     {
362         r = StgOpenStorage( szDBPath, NULL,
363               STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
364     }
365     else
366     {
367         ERR("unknown flag %p\n",szPersist);
368         return ERROR_INVALID_PARAMETER;
369     }
370
371     if( FAILED( r ) || !stg )
372     {
373         FIXME("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
374         return ERROR_FUNCTION_FAILED;
375     }
376
377     r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
378     if( FAILED( r ) )
379     {
380         FIXME("Failed to stat storage\n");
381         goto end;
382     }
383
384     if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
385          !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
386          !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
387     {
388         ERR("storage GUID is not a MSI database GUID %s\n",
389              debugstr_guid(&stat.clsid) );
390         goto end;
391     }
392
393     if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
394     {
395         ERR("storage GUID is not the MSI patch GUID %s\n",
396              debugstr_guid(&stat.clsid) );
397         ret = ERROR_OPEN_FAILED;
398         goto end;
399     }
400
401     db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
402                               MSI_CloseDatabase );
403     if( !db )
404     {
405         FIXME("Failed to allocate a handle\n");
406         goto end;
407     }
408
409     if (!strchrW( save_path, '\\' ))
410     {
411         GetCurrentDirectoryW( MAX_PATH, path );
412         lstrcatW( path, szBackSlash );
413         lstrcatW( path, save_path );
414     }
415     else
416         lstrcpyW( path, save_path );
417
418     db->path = strdupW( path );
419
420     if( TRACE_ON( msi ) )
421         enum_stream_names( stg );
422
423     db->storage = stg;
424     db->mode = szMode;
425     if (created)
426         db->deletefile = strdupW( szDBPath );
427     list_init( &db->tables );
428     list_init( &db->transforms );
429     list_init( &db->streams );
430
431     db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
432     if( !db->strings )
433         goto end;
434
435     ret = ERROR_SUCCESS;
436
437     msiobj_addref( &db->hdr );
438     IStorage_AddRef( stg );
439     *pdb = db;
440
441 end:
442     if( db )
443         msiobj_release( &db->hdr );
444     if( stg )
445         IStorage_Release( stg );
446
447     return ret;
448 }
449
450 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
451 {
452     MSIDATABASE *db;
453     UINT ret;
454
455     TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
456
457     ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
458     if( ret == ERROR_SUCCESS )
459     {
460         *phDB = alloc_msihandle( &db->hdr );
461         if (! *phDB)
462             ret = ERROR_NOT_ENOUGH_MEMORY;
463         msiobj_release( &db->hdr );
464     }
465
466     return ret;
467 }
468
469 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
470 {
471     HRESULT r = ERROR_FUNCTION_FAILED;
472     LPWSTR szwDBPath = NULL, szwPersist = NULL;
473
474     TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
475
476     if( szDBPath )
477     {
478         szwDBPath = strdupAtoW( szDBPath );
479         if( !szwDBPath )
480             goto end;
481     }
482
483     if( !IS_INTMSIDBOPEN(szPersist) )
484     {
485         szwPersist = strdupAtoW( szPersist );
486         if( !szwPersist )
487             goto end;
488     }
489     else
490         szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
491
492     r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
493
494 end:
495     if( !IS_INTMSIDBOPEN(szPersist) )
496         msi_free( szwPersist );
497     msi_free( szwDBPath );
498
499     return r;
500 }
501
502 static LPWSTR msi_read_text_archive(LPCWSTR path)
503 {
504     HANDLE file;
505     LPSTR data = NULL;
506     LPWSTR wdata = NULL;
507     DWORD read, size = 0;
508
509     file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
510     if (file == INVALID_HANDLE_VALUE)
511         return NULL;
512
513     size = GetFileSize( file, NULL );
514     data = msi_alloc( size + 1 );
515     if (!data)
516         goto done;
517
518     if (!ReadFile( file, data, size, &read, NULL ))
519         goto done;
520
521     data[size] = '\0';
522     wdata = strdupAtoW( data );
523
524 done:
525     CloseHandle( file );
526     msi_free( data );
527     return wdata;
528 }
529
530 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
531 {
532     LPWSTR ptr = *line, save;
533     DWORD i, count = 1;
534
535     *entries = NULL;
536
537     /* stay on this line */
538     while (*ptr && *ptr != '\n')
539     {
540         /* entries are separated by tabs */
541         if (*ptr == '\t')
542             count++;
543
544         ptr++;
545     }
546
547     *entries = msi_alloc(count * sizeof(LPWSTR));
548     if (!*entries)
549         return;
550
551     /* store pointers into the data */
552     for (i = 0, ptr = *line; i < count; i++)
553     {
554         while (*ptr && *ptr == '\r') ptr++;
555         save = ptr;
556
557         while (*ptr && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') ptr++;
558
559         /* NULL-separate the data */
560         if (*ptr == '\n' || *ptr == '\r')
561         {
562             while (*ptr == '\n' || *ptr == '\r')
563                 *(ptr++) = '\0';
564         }
565         else if (*ptr)
566             *ptr++ = '\0';
567
568         (*entries)[i] = save;
569     }
570
571     /* move to the next line if there's more, else EOF */
572     *line = ptr;
573
574     if (num_entries)
575         *num_entries = count;
576 }
577
578 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
579 {
580     LPWSTR prelude;
581     DWORD size;
582
583     static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
584
585     size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
586     prelude = msi_alloc(size * sizeof(WCHAR));
587     if (!prelude)
588         return NULL;
589
590     sprintfW(prelude, create_fmt, table);
591     return prelude;
592 }
593
594 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
595 {
596     LPWSTR columns, p;
597     LPCWSTR type;
598     DWORD sql_size = 1, i, len;
599     WCHAR expanded[128], *ptr;
600     WCHAR size[10], comma[2], extra[30];
601
602     static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
603     static const WCHAR size_fmt[] = {'(','%','s',')',0};
604     static const WCHAR type_char[] = {'C','H','A','R',0};
605     static const WCHAR type_int[] = {'I','N','T',0};
606     static const WCHAR type_long[] = {'L','O','N','G',0};
607     static const WCHAR type_object[] = {'O','B','J','E','C','T',0};
608     static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
609     static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
610
611     columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
612     if (!columns)
613         return NULL;
614
615     for (i = 0; i < num_columns; i++)
616     {
617         type = NULL;
618         comma[1] = size[0] = extra[0] = '\0';
619
620         if (i == num_columns - 1)
621             comma[0] = '\0';
622         else
623             comma[0] = ',';
624
625         ptr = &types[i][1];
626         len = atolW(ptr);
627         extra[0] = '\0';
628
629         switch (types[i][0])
630         {
631             case 'l':
632                 lstrcpyW(extra, type_notnull);
633             case 'L':
634                 lstrcatW(extra, localizable);
635                 type = type_char;
636                 sprintfW(size, size_fmt, ptr);
637                 break;
638             case 's':
639                 lstrcpyW(extra, type_notnull);
640             case 'S':
641                 type = type_char;
642                 sprintfW(size, size_fmt, ptr);
643                 break;
644             case 'i':
645                 lstrcpyW(extra, type_notnull);
646             case 'I':
647                 if (len <= 2)
648                     type = type_int;
649                 else if (len == 4)
650                     type = type_long;
651                 else
652                 {
653                     WARN("invalid int width %u\n", len);
654                     msi_free(columns);
655                     return NULL;
656                 }
657                 break;
658             case 'v':
659                 lstrcpyW(extra, type_notnull);
660             case 'V':
661                 type = type_object;
662                 break;
663             default:
664                 ERR("Unknown type: %c\n", types[i][0]);
665                 msi_free(columns);
666                 return NULL;
667         }
668
669         sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
670         sql_size += lstrlenW(expanded);
671
672         p = msi_realloc(columns, sql_size * sizeof(WCHAR));
673         if (!p)
674         {
675             msi_free(columns);
676             return NULL;
677         }
678         columns = p;
679
680         lstrcatW(columns, expanded);
681     }
682
683     return columns;
684 }
685
686 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
687 {
688     LPWSTR postlude, keys, ptr;
689     DWORD size, key_size, i;
690
691     static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
692     static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
693
694     for (i = 0, size = 1; i < num_keys; i++)
695         size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
696
697     keys = msi_alloc(size * sizeof(WCHAR));
698     if (!keys)
699         return NULL;
700
701     for (i = 0, ptr = keys; i < num_keys; i++)
702     {
703         key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
704         sprintfW(ptr, key_fmt, primary_keys[i]);
705         ptr += key_size;
706     }
707
708     /* remove final ', ' */
709     *(ptr - 2) = '\0';
710
711     size = lstrlenW(postlude_fmt) + size - 1;
712     postlude = msi_alloc(size * sizeof(WCHAR));
713     if (!postlude)
714         goto done;
715
716     sprintfW(postlude, postlude_fmt, keys);
717
718 done:
719     msi_free(keys);
720     return postlude;
721 }
722
723 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
724 {
725     UINT r = ERROR_OUTOFMEMORY;
726     DWORD size;
727     MSIQUERY *view;
728     LPWSTR create_sql = NULL;
729     LPWSTR prelude, columns_sql, postlude;
730
731     prelude = msi_build_createsql_prelude(labels[0]);
732     columns_sql = msi_build_createsql_columns(columns, types, num_columns);
733     postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
734
735     if (!prelude || !columns_sql || !postlude)
736         goto done;
737
738     size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
739     create_sql = msi_alloc(size * sizeof(WCHAR));
740     if (!create_sql)
741         goto done;
742
743     lstrcpyW(create_sql, prelude);
744     lstrcatW(create_sql, columns_sql);
745     lstrcatW(create_sql, postlude);
746
747     r = MSI_DatabaseOpenViewW( db, create_sql, &view );
748     if (r != ERROR_SUCCESS)
749         goto done;
750
751     r = MSI_ViewExecute(view, NULL);
752     MSI_ViewClose(view);
753     msiobj_release(&view->hdr);
754
755 done:
756     msi_free(prelude);
757     msi_free(columns_sql);
758     msi_free(postlude);
759     msi_free(create_sql);
760     return r;
761 }
762
763 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
764 {
765     DWORD len;
766     LPWSTR fullname, ptr;
767
768     len = lstrlenW(path) + lstrlenW(name) + 1;
769     fullname = msi_alloc(len*sizeof(WCHAR));
770     if (!fullname)
771        return NULL;
772
773     lstrcpyW( fullname, path );
774
775     /* chop off extension from path */
776     ptr = strrchrW(fullname, '.');
777     if (!ptr)
778     {
779         msi_free (fullname);
780         return NULL;
781     }
782     *ptr++ = '\\';
783     lstrcpyW( ptr, name );
784     return fullname;
785 }
786
787 static UINT construct_record(DWORD num_columns, LPWSTR *types,
788                              LPWSTR *data, LPWSTR path, MSIRECORD **rec)
789 {
790     UINT i;
791
792     *rec = MSI_CreateRecord(num_columns);
793     if (!*rec)
794         return ERROR_OUTOFMEMORY;
795
796     for (i = 0; i < num_columns; i++)
797     {
798         switch (types[i][0])
799         {
800             case 'L': case 'l': case 'S': case 's':
801                 MSI_RecordSetStringW(*rec, i + 1, data[i]);
802                 break;
803             case 'I': case 'i':
804                 if (*data[i])
805                     MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
806                 break;
807             case 'V': case 'v':
808                 if (*data[i])
809                 {
810                     UINT r;
811                     LPWSTR file = msi_import_stream_filename(path, data[i]);
812                     if (!file)
813                         return ERROR_FUNCTION_FAILED;
814
815                     r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
816                     msi_free (file);
817                     if (r != ERROR_SUCCESS)
818                         return ERROR_FUNCTION_FAILED;
819                 }
820                 break;
821             default:
822                 ERR("Unhandled column type: %c\n", types[i][0]);
823                 msiobj_release(&(*rec)->hdr);
824                 return ERROR_FUNCTION_FAILED;
825         }
826     }
827
828     return ERROR_SUCCESS;
829 }
830
831 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
832                                      LPWSTR *labels, LPWSTR **records,
833                                      int num_columns, int num_records,
834                                      LPWSTR path)
835 {
836     UINT r;
837     int i;
838     MSIQUERY *view;
839     MSIRECORD *rec;
840
841     static const WCHAR select[] = {
842         'S','E','L','E','C','T',' ','*',' ',
843         'F','R','O','M',' ','`','%','s','`',0
844     };
845
846     r = MSI_OpenQuery(db, &view, select, labels[0]);
847     if (r != ERROR_SUCCESS)
848         return r;
849
850     while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
851     {
852         r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
853         msiobj_release(&rec->hdr);
854         if (r != ERROR_SUCCESS)
855             goto done;
856     }
857
858     for (i = 0; i < num_records; i++)
859     {
860         r = construct_record(num_columns, types, records[i], path, &rec);
861         if (r != ERROR_SUCCESS)
862             goto done;
863
864         r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
865         if (r != ERROR_SUCCESS)
866         {
867             msiobj_release(&rec->hdr);
868             goto done;
869         }
870
871         msiobj_release(&rec->hdr);
872     }
873
874 done:
875     msiobj_release(&view->hdr);
876     return r;
877 }
878
879 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
880 {
881     UINT r;
882     DWORD len, i;
883     DWORD num_labels, num_types;
884     DWORD num_columns, num_records = 0;
885     LPWSTR *columns, *types, *labels;
886     LPWSTR path, ptr, data;
887     LPWSTR **records = NULL;
888     LPWSTR **temp_records;
889
890     static const WCHAR suminfo[] =
891         {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
892     static const WCHAR forcecodepage[] =
893         {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0};
894
895     TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
896
897     if( folder == NULL || file == NULL )
898         return ERROR_INVALID_PARAMETER;
899
900     len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
901     path = msi_alloc( len * sizeof(WCHAR) );
902     if (!path)
903         return ERROR_OUTOFMEMORY;
904
905     lstrcpyW( path, folder );
906     lstrcatW( path, szBackSlash );
907     lstrcatW( path, file );
908
909     data = msi_read_text_archive( path );
910
911     ptr = data;
912     msi_parse_line( &ptr, &columns, &num_columns );
913     msi_parse_line( &ptr, &types, &num_types );
914     msi_parse_line( &ptr, &labels, &num_labels );
915
916     if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] &&
917         num_types == 2 && !strcmpW( types[1], forcecodepage ))
918     {
919         r = msi_set_string_table_codepage( db->strings, atoiW( types[0] ) );
920         goto done;
921     }
922
923     if (num_columns != num_types)
924     {
925         r = ERROR_FUNCTION_FAILED;
926         goto done;
927     }
928
929     records = msi_alloc(sizeof(LPWSTR *));
930     if (!records)
931     {
932         r = ERROR_OUTOFMEMORY;
933         goto done;
934     }
935
936     /* read in the table records */
937     while (*ptr)
938     {
939         msi_parse_line( &ptr, &records[num_records], NULL );
940
941         num_records++;
942         temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
943         if (!temp_records)
944         {
945             r = ERROR_OUTOFMEMORY;
946             goto done;
947         }
948         records = temp_records;
949     }
950
951     if (!strcmpW(labels[0], suminfo))
952     {
953         r = msi_add_suminfo( db, records, num_records, num_columns );
954         if (r != ERROR_SUCCESS)
955         {
956             r = ERROR_FUNCTION_FAILED;
957             goto done;
958         }
959     }
960     else
961     {
962         if (!TABLE_Exists(db, labels[0]))
963         {
964             r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
965             if (r != ERROR_SUCCESS)
966             {
967                 r = ERROR_FUNCTION_FAILED;
968                 goto done;
969             }
970         }
971
972         r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
973     }
974
975 done:
976     msi_free(path);
977     msi_free(data);
978     msi_free(columns);
979     msi_free(types);
980     msi_free(labels);
981
982     for (i = 0; i < num_records; i++)
983         msi_free(records[i]);
984
985     msi_free(records);
986
987     return r;
988 }
989
990 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
991 {
992     MSIDATABASE *db;
993     UINT r;
994
995     TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
996
997     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
998     if( !db )
999     {
1000         IWineMsiRemoteDatabase *remote_database;
1001
1002         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1003         if ( !remote_database )
1004             return ERROR_INVALID_HANDLE;
1005
1006         IWineMsiRemoteDatabase_Release( remote_database );
1007         WARN("MsiDatabaseImport not allowed during a custom action!\n");
1008
1009         return ERROR_SUCCESS;
1010     }
1011
1012     r = MSI_DatabaseImport( db, szFolder, szFilename );
1013     msiobj_release( &db->hdr );
1014     return r;
1015 }
1016
1017 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
1018                LPCSTR szFolder, LPCSTR szFilename )
1019 {
1020     LPWSTR path = NULL, file = NULL;
1021     UINT r = ERROR_OUTOFMEMORY;
1022
1023     TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
1024
1025     if( szFolder )
1026     {
1027         path = strdupAtoW( szFolder );
1028         if( !path )
1029             goto end;
1030     }
1031
1032     if( szFilename )
1033     {
1034         file = strdupAtoW( szFilename );
1035         if( !file )
1036             goto end;
1037     }
1038
1039     r = MsiDatabaseImportW( handle, path, file );
1040
1041 end:
1042     msi_free( path );
1043     msi_free( file );
1044
1045     return r;
1046 }
1047
1048 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
1049 {
1050     UINT i, count, len, r = ERROR_SUCCESS;
1051     const char *sep;
1052     char *buffer;
1053     DWORD sz;
1054
1055     len = 0x100;
1056     buffer = msi_alloc( len );
1057     if ( !buffer )
1058         return ERROR_OUTOFMEMORY;
1059
1060     count = MSI_RecordGetFieldCount( row );
1061     for ( i=start; i<=count; i++ )
1062     {
1063         sz = len;
1064         r = MSI_RecordGetStringA( row, i, buffer, &sz );
1065         if (r == ERROR_MORE_DATA)
1066         {
1067             char *p = msi_realloc( buffer, sz + 1 );
1068             if (!p)
1069                 break;
1070             len = sz + 1;
1071             buffer = p;
1072         }
1073         sz = len;
1074         r = MSI_RecordGetStringA( row, i, buffer, &sz );
1075         if (r != ERROR_SUCCESS)
1076             break;
1077
1078         if (!WriteFile( handle, buffer, sz, &sz, NULL ))
1079         {
1080             r = ERROR_FUNCTION_FAILED;
1081             break;
1082         }
1083
1084         sep = (i < count) ? "\t" : "\r\n";
1085         if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1086         {
1087             r = ERROR_FUNCTION_FAILED;
1088             break;
1089         }
1090     }
1091     msi_free( buffer );
1092     return r;
1093 }
1094
1095 static UINT msi_export_row( MSIRECORD *row, void *arg )
1096 {
1097     return msi_export_record( arg, row, 1 );
1098 }
1099
1100 static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage )
1101 {
1102     static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n";
1103     char data[sizeof(fmt) + 10];
1104     DWORD sz;
1105
1106     sprintf( data, fmt, codepage );
1107
1108     sz = lstrlenA(data) + 1;
1109     if (!WriteFile(handle, data, sz, &sz, NULL))
1110         return ERROR_FUNCTION_FAILED;
1111
1112     return ERROR_SUCCESS;
1113 }
1114
1115 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
1116                LPCWSTR folder, LPCWSTR file )
1117 {
1118     static const WCHAR query[] = {
1119         's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1120     static const WCHAR forcecodepage[] = {
1121         '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1122     MSIRECORD *rec = NULL;
1123     MSIQUERY *view = NULL;
1124     LPWSTR filename;
1125     HANDLE handle;
1126     UINT len, r;
1127
1128     TRACE("%p %s %s %s\n", db, debugstr_w(table),
1129           debugstr_w(folder), debugstr_w(file) );
1130
1131     if( folder == NULL || file == NULL )
1132         return ERROR_INVALID_PARAMETER;
1133
1134     len = lstrlenW(folder) + lstrlenW(file) + 2;
1135     filename = msi_alloc(len * sizeof (WCHAR));
1136     if (!filename)
1137         return ERROR_OUTOFMEMORY;
1138
1139     lstrcpyW( filename, folder );
1140     lstrcatW( filename, szBackSlash );
1141     lstrcatW( filename, file );
1142
1143     handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1144                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1145     msi_free( filename );
1146     if (handle == INVALID_HANDLE_VALUE)
1147         return ERROR_FUNCTION_FAILED;
1148
1149     if (!strcmpW( table, forcecodepage ))
1150     {
1151         UINT codepage = msi_get_string_table_codepage( db->strings );
1152         r = msi_export_forcecodepage( handle, codepage );
1153         goto done;
1154     }
1155
1156     r = MSI_OpenQuery( db, &view, query, table );
1157     if (r == ERROR_SUCCESS)
1158     {
1159         /* write out row 1, the column names */
1160         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1161         if (r == ERROR_SUCCESS)
1162         {
1163             msi_export_record( handle, rec, 1 );
1164             msiobj_release( &rec->hdr );
1165         }
1166
1167         /* write out row 2, the column types */
1168         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1169         if (r == ERROR_SUCCESS)
1170         {
1171             msi_export_record( handle, rec, 1 );
1172             msiobj_release( &rec->hdr );
1173         }
1174
1175         /* write out row 3, the table name + keys */
1176         r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1177         if (r == ERROR_SUCCESS)
1178         {
1179             MSI_RecordSetStringW( rec, 0, table );
1180             msi_export_record( handle, rec, 0 );
1181             msiobj_release( &rec->hdr );
1182         }
1183
1184         /* write out row 4 onwards, the data */
1185         r = MSI_IterateRecords( view, 0, msi_export_row, handle );
1186         msiobj_release( &view->hdr );
1187     }
1188
1189 done:
1190     CloseHandle( handle );
1191     return r;
1192 }
1193
1194 /***********************************************************************
1195  * MsiExportDatabaseW        [MSI.@]
1196  *
1197  * Writes a file containing the table data as tab separated ASCII.
1198  *
1199  * The format is as follows:
1200  *
1201  * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1202  * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1203  * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1204  *
1205  * Followed by the data, starting at row 1 with one row per line
1206  *
1207  * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1208  */
1209 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
1210                LPCWSTR szFolder, LPCWSTR szFilename )
1211 {
1212     MSIDATABASE *db;
1213     UINT r;
1214
1215     TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
1216           debugstr_w(szFolder), debugstr_w(szFilename));
1217
1218     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1219     if( !db )
1220     {
1221         IWineMsiRemoteDatabase *remote_database;
1222
1223         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1224         if ( !remote_database )
1225             return ERROR_INVALID_HANDLE;
1226
1227         IWineMsiRemoteDatabase_Release( remote_database );
1228         WARN("MsiDatabaseExport not allowed during a custom action!\n");
1229
1230         return ERROR_SUCCESS;
1231     }
1232
1233     r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1234     msiobj_release( &db->hdr );
1235     return r;
1236 }
1237
1238 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
1239                LPCSTR szFolder, LPCSTR szFilename )
1240 {
1241     LPWSTR path = NULL, file = NULL, table = NULL;
1242     UINT r = ERROR_OUTOFMEMORY;
1243
1244     TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1245           debugstr_a(szFolder), debugstr_a(szFilename));
1246
1247     if( szTable )
1248     {
1249         table = strdupAtoW( szTable );
1250         if( !table )
1251             goto end;
1252     }
1253
1254     if( szFolder )
1255     {
1256         path = strdupAtoW( szFolder );
1257         if( !path )
1258             goto end;
1259     }
1260
1261     if( szFilename )
1262     {
1263         file = strdupAtoW( szFilename );
1264         if( !file )
1265             goto end;
1266     }
1267
1268     r = MsiDatabaseExportW( handle, table, path, file );
1269
1270 end:
1271     msi_free( table );
1272     msi_free( path );
1273     msi_free( file );
1274
1275     return r;
1276 }
1277
1278 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1279                               LPCSTR szTableName)
1280 {
1281     UINT r;
1282     LPWSTR table;
1283
1284     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1285           debugstr_a(szTableName));
1286
1287     table = strdupAtoW(szTableName);
1288     r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1289
1290     msi_free(table);
1291     return r;
1292 }
1293
1294 typedef struct _tagMERGETABLE
1295 {
1296     struct list entry;
1297     struct list rows;
1298     LPWSTR name;
1299     DWORD numconflicts;
1300     LPWSTR *columns;
1301     DWORD numcolumns;
1302     LPWSTR *types;
1303     DWORD numtypes;
1304     LPWSTR *labels;
1305     DWORD numlabels;
1306 } MERGETABLE;
1307
1308 typedef struct _tagMERGEROW
1309 {
1310     struct list entry;
1311     MSIRECORD *data;
1312 } MERGEROW;
1313
1314 typedef struct _tagMERGEDATA
1315 {
1316     MSIDATABASE *db;
1317     MSIDATABASE *merge;
1318     MERGETABLE *curtable;
1319     MSIQUERY *curview;
1320     struct list *tabledata;
1321 } MERGEDATA;
1322
1323 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1324 {
1325     if (((type1[0] == 'l') || (type1[0] == 's')) &&
1326         ((type2[0] == 'l') || (type2[0] == 's')))
1327         return TRUE;
1328
1329     if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1330         ((type2[0] == 'L') || (type2[0] == 'S')))
1331         return TRUE;
1332
1333     return !strcmpW( type1, type2 );
1334 }
1335
1336 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1337 {
1338     MSIRECORD *dbrec, *mergerec;
1339     UINT r, i, count;
1340
1341     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1342     if (r != ERROR_SUCCESS)
1343         return r;
1344
1345     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1346     if (r != ERROR_SUCCESS)
1347         return r;
1348
1349     count = MSI_RecordGetFieldCount(dbrec);
1350     for (i = 1; i <= count; i++)
1351     {
1352         if (!MSI_RecordGetString(mergerec, i))
1353             break;
1354
1355         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1356         {
1357             r = ERROR_DATATYPE_MISMATCH;
1358             goto done;
1359         }
1360     }
1361
1362     msiobj_release(&dbrec->hdr);
1363     msiobj_release(&mergerec->hdr);
1364     dbrec = mergerec = NULL;
1365
1366     r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1367     if (r != ERROR_SUCCESS)
1368         return r;
1369
1370     r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1371     if (r != ERROR_SUCCESS)
1372         return r;
1373
1374     count = MSI_RecordGetFieldCount(dbrec);
1375     for (i = 1; i <= count; i++)
1376     {
1377         if (!MSI_RecordGetString(mergerec, i))
1378             break;
1379
1380         if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1381                      MSI_RecordGetString(mergerec, i)))
1382         {
1383             r = ERROR_DATATYPE_MISMATCH;
1384             break;
1385         }
1386     }
1387
1388 done:
1389     msiobj_release(&dbrec->hdr);
1390     msiobj_release(&mergerec->hdr);
1391
1392     return r;
1393 }
1394
1395 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1396                                       LPCWSTR table)
1397 {
1398     MSIRECORD *dbrec, *mergerec = NULL;
1399     UINT r, i, count;
1400
1401     r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1402     if (r != ERROR_SUCCESS)
1403         return r;
1404
1405     r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1406     if (r != ERROR_SUCCESS)
1407         goto done;
1408
1409     count = MSI_RecordGetFieldCount(dbrec);
1410     if (count != MSI_RecordGetFieldCount(mergerec))
1411     {
1412         r = ERROR_DATATYPE_MISMATCH;
1413         goto done;
1414     }
1415
1416     for (i = 1; i <= count; i++)
1417     {
1418         if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) ))
1419         {
1420             r = ERROR_DATATYPE_MISMATCH;
1421             goto done;
1422         }
1423     }
1424
1425 done:
1426     msiobj_release(&dbrec->hdr);
1427     msiobj_release(&mergerec->hdr);
1428
1429     return r;
1430 }
1431
1432 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1433 {
1434     MSIRECORD *colnames;
1435     LPWSTR str, val;
1436     UINT r, i = 0, sz = 0;
1437     int cmp;
1438
1439     r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1440     if (r != ERROR_SUCCESS)
1441         return NULL;
1442
1443     do
1444     {
1445         str = msi_dup_record_field(colnames, ++i);
1446         cmp = strcmpW( key, str );
1447         msi_free(str);
1448     } while (cmp);
1449
1450     msiobj_release(&colnames->hdr);
1451
1452     r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1453     if (r != ERROR_SUCCESS)
1454         return NULL;
1455     sz++;
1456
1457     if (MSI_RecordGetString(rec, i))  /* check record field is a string */
1458     {
1459         /* quote string record fields */
1460         const WCHAR szQuote[] = {'\'', 0};
1461         sz += 2;
1462         val = msi_alloc(sz*sizeof(WCHAR));
1463         if (!val)
1464             return NULL;
1465
1466         lstrcpyW(val, szQuote);
1467         r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1468         lstrcpyW(val+1+sz, szQuote);
1469     }
1470     else
1471     {
1472         /* do not quote integer record fields */
1473         val = msi_alloc(sz*sizeof(WCHAR));
1474         if (!val)
1475             return NULL;
1476
1477         r = MSI_RecordGetStringW(rec, i, val, &sz);
1478     }
1479
1480     if (r != ERROR_SUCCESS)
1481     {
1482         ERR("failed to get string!\n");
1483         msi_free(val);
1484         return NULL;
1485     }
1486
1487     return val;
1488 }
1489
1490 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1491                                     LPWSTR table, MSIRECORD *rec)
1492 {
1493     LPWSTR query = NULL, clause = NULL;
1494     LPWSTR ptr = NULL, val;
1495     LPCWSTR setptr;
1496     DWORD size = 1, oldsize;
1497     LPCWSTR key;
1498     MSIRECORD *keys;
1499     UINT r, i, count;
1500
1501     static const WCHAR keyset[] = {
1502         '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1503     static const WCHAR lastkeyset[] = {
1504         '`','%','s','`',' ','=',' ','%','s',' ',0};
1505     static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1506         'F','R','O','M',' ','`','%','s','`',' ',
1507         'W','H','E','R','E',' ','%','s',0};
1508
1509     r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1510     if (r != ERROR_SUCCESS)
1511         return NULL;
1512
1513     clause = msi_alloc_zero(size * sizeof(WCHAR));
1514     if (!clause)
1515         goto done;
1516
1517     ptr = clause;
1518     count = MSI_RecordGetFieldCount(keys);
1519     for (i = 1; i <= count; i++)
1520     {
1521         key = MSI_RecordGetString(keys, i);
1522         val = get_key_value(view, key, rec);
1523
1524         if (i == count)
1525             setptr = lastkeyset;
1526         else
1527             setptr = keyset;
1528
1529         oldsize = size;
1530         size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1531         clause = msi_realloc(clause, size * sizeof (WCHAR));
1532         if (!clause)
1533         {
1534             msi_free(val);
1535             goto done;
1536         }
1537
1538         ptr = clause + oldsize - 1;
1539         sprintfW(ptr, setptr, key, val);
1540         msi_free(val);
1541     }
1542
1543     size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1544     query = msi_alloc(size * sizeof(WCHAR));
1545     if (!query)
1546         goto done;
1547
1548     sprintfW(query, fmt, table, clause);
1549
1550 done:
1551     msi_free(clause);
1552     msiobj_release(&keys->hdr);
1553     return query;
1554 }
1555
1556 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1557 {
1558     MERGEDATA *data = param;
1559     MERGETABLE *table = data->curtable;
1560     MERGEROW *mergerow;
1561     MSIQUERY *dbview = NULL;
1562     MSIRECORD *row = NULL;
1563     LPWSTR query = NULL;
1564     UINT r = ERROR_SUCCESS;
1565
1566     if (TABLE_Exists(data->db, table->name))
1567     {
1568         query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1569         if (!query)
1570             return ERROR_OUTOFMEMORY;
1571
1572         r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1573         if (r != ERROR_SUCCESS)
1574             goto done;
1575
1576         r = MSI_ViewExecute(dbview, NULL);
1577         if (r != ERROR_SUCCESS)
1578             goto done;
1579
1580         r = MSI_ViewFetch(dbview, &row);
1581         if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1582         {
1583             table->numconflicts++;
1584             goto done;
1585         }
1586         else if (r != ERROR_NO_MORE_ITEMS)
1587             goto done;
1588
1589         r = ERROR_SUCCESS;
1590     }
1591
1592     mergerow = msi_alloc(sizeof(MERGEROW));
1593     if (!mergerow)
1594     {
1595         r = ERROR_OUTOFMEMORY;
1596         goto done;
1597     }
1598
1599     mergerow->data = MSI_CloneRecord(rec);
1600     if (!mergerow->data)
1601     {
1602         r = ERROR_OUTOFMEMORY;
1603         msi_free(mergerow);
1604         goto done;
1605     }
1606
1607     list_add_tail(&table->rows, &mergerow->entry);
1608
1609 done:
1610     msi_free(query);
1611     msiobj_release(&row->hdr);
1612     msiobj_release(&dbview->hdr);
1613     return r;
1614 }
1615
1616 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1617 {
1618     UINT r, i, count;
1619     MSIRECORD *prec = NULL;
1620
1621     r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1622     if (r != ERROR_SUCCESS)
1623         return r;
1624
1625     count = MSI_RecordGetFieldCount(prec);
1626     *numlabels = count + 1;
1627     *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1628     if (!*labels)
1629     {
1630         r = ERROR_OUTOFMEMORY;
1631         goto end;
1632     }
1633
1634     (*labels)[0] = strdupW(table);
1635     for (i=1; i<=count; i++ )
1636     {
1637         (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1638     }
1639
1640 end:
1641     msiobj_release( &prec->hdr );
1642     return r;
1643 }
1644
1645 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1646 {
1647     UINT r, i, count;
1648     MSIRECORD *prec = NULL;
1649
1650     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1651     if (r != ERROR_SUCCESS)
1652         return r;
1653
1654     count = MSI_RecordGetFieldCount(prec);
1655     *columns = msi_alloc(count*sizeof(LPWSTR));
1656     if (!*columns)
1657     {
1658         r = ERROR_OUTOFMEMORY;
1659         goto end;
1660     }
1661
1662     for (i=1; i<=count; i++ )
1663     {
1664         (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1665     }
1666
1667     *numcolumns = count;
1668
1669 end:
1670     msiobj_release( &prec->hdr );
1671     return r;
1672 }
1673
1674 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1675 {
1676     UINT r, i, count;
1677     MSIRECORD *prec = NULL;
1678
1679     r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1680     if (r != ERROR_SUCCESS)
1681         return r;
1682
1683     count = MSI_RecordGetFieldCount(prec);
1684     *types = msi_alloc(count*sizeof(LPWSTR));
1685     if (!*types)
1686     {
1687         r = ERROR_OUTOFMEMORY;
1688         goto end;
1689     }
1690
1691     *numtypes = count;
1692     for (i=1; i<=count; i++ )
1693     {
1694         (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1695     }
1696
1697 end:
1698     msiobj_release( &prec->hdr );
1699     return r;
1700 }
1701
1702 static void merge_free_rows(MERGETABLE *table)
1703 {
1704     struct list *item, *cursor;
1705
1706     LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1707     {
1708         MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1709
1710         list_remove(&row->entry);
1711         msiobj_release(&row->data->hdr);
1712         msi_free(row);
1713     }
1714 }
1715
1716 static void free_merge_table(MERGETABLE *table)
1717 {
1718     UINT i;
1719
1720     if (table->labels != NULL)
1721     {
1722         for (i = 0; i < table->numlabels; i++)
1723             msi_free(table->labels[i]);
1724
1725         msi_free(table->labels);
1726     }
1727
1728     if (table->columns != NULL)
1729     {
1730         for (i = 0; i < table->numcolumns; i++)
1731             msi_free(table->columns[i]);
1732
1733         msi_free(table->columns);
1734     }
1735
1736     if (table->types != NULL)
1737     {
1738         for (i = 0; i < table->numtypes; i++)
1739             msi_free(table->types[i]);
1740
1741         msi_free(table->types);
1742     }
1743
1744     msi_free(table->name);
1745     merge_free_rows(table);
1746
1747     msi_free(table);
1748 }
1749
1750 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1751 {
1752     UINT r;
1753     MERGETABLE *table;
1754     MSIQUERY *mergeview = NULL;
1755
1756     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1757         'F','R','O','M',' ','`','%','s','`',0};
1758
1759     table = msi_alloc_zero(sizeof(MERGETABLE));
1760     if (!table)
1761     {
1762        *ptable = NULL;
1763        return ERROR_OUTOFMEMORY;
1764     }
1765
1766     r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1767     if (r != ERROR_SUCCESS)
1768         goto err;
1769
1770     r = MSI_OpenQuery(db, &mergeview, query, name);
1771     if (r != ERROR_SUCCESS)
1772         goto err;
1773
1774     r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1775     if (r != ERROR_SUCCESS)
1776         goto err;
1777
1778     r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1779     if (r != ERROR_SUCCESS)
1780         goto err;
1781
1782     list_init(&table->rows);
1783
1784     table->name = strdupW(name);
1785     table->numconflicts = 0;
1786
1787     msiobj_release(&mergeview->hdr);
1788     *ptable = table;
1789     return ERROR_SUCCESS;
1790
1791 err:
1792     msiobj_release(&mergeview->hdr);
1793     free_merge_table(table);
1794     *ptable = NULL;
1795     return r;
1796 }
1797
1798 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1799 {
1800     MERGEDATA *data = param;
1801     MERGETABLE *table;
1802     MSIQUERY *dbview = NULL;
1803     MSIQUERY *mergeview = NULL;
1804     LPCWSTR name;
1805     UINT r;
1806
1807     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1808         'F','R','O','M',' ','`','%','s','`',0};
1809
1810     name = MSI_RecordGetString(rec, 1);
1811
1812     r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1813     if (r != ERROR_SUCCESS)
1814         goto done;
1815
1816     if (TABLE_Exists(data->db, name))
1817     {
1818         r = MSI_OpenQuery(data->db, &dbview, query, name);
1819         if (r != ERROR_SUCCESS)
1820             goto done;
1821
1822         r = merge_verify_colnames(dbview, mergeview);
1823         if (r != ERROR_SUCCESS)
1824             goto done;
1825
1826         r = merge_verify_primary_keys(data->db, data->merge, name);
1827         if (r != ERROR_SUCCESS)
1828             goto done;
1829     }
1830
1831     r = msi_get_merge_table(data->merge, name, &table);
1832     if (r != ERROR_SUCCESS)
1833         goto done;
1834
1835     data->curtable = table;
1836     data->curview = mergeview;
1837     r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1838     if (r != ERROR_SUCCESS)
1839     {
1840         free_merge_table(table);
1841         goto done;
1842     }
1843
1844     list_add_tail(data->tabledata, &table->entry);
1845
1846 done:
1847     msiobj_release(&dbview->hdr);
1848     msiobj_release(&mergeview->hdr);
1849     return r;
1850 }
1851
1852 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1853                               struct list *tabledata)
1854 {
1855     UINT r;
1856     MSIQUERY *view;
1857     MERGEDATA data;
1858
1859     static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1860         'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1861
1862     r = MSI_DatabaseOpenViewW(merge, query, &view);
1863     if (r != ERROR_SUCCESS)
1864         return r;
1865
1866     data.db = db;
1867     data.merge = merge;
1868     data.tabledata = tabledata;
1869     r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1870
1871     msiobj_release(&view->hdr);
1872     return r;
1873 }
1874
1875 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1876 {
1877     UINT r;
1878     MERGEROW *row;
1879     MSIVIEW *tv;
1880
1881     if (!TABLE_Exists(db, table->name))
1882     {
1883         r = msi_add_table_to_db(db, table->columns, table->types,
1884                table->labels, table->numlabels, table->numcolumns);
1885         if (r != ERROR_SUCCESS)
1886            return ERROR_FUNCTION_FAILED;
1887     }
1888
1889     LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1890     {
1891         r = TABLE_CreateView(db, table->name, &tv);
1892         if (r != ERROR_SUCCESS)
1893             return r;
1894
1895         r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1896         tv->ops->delete(tv);
1897
1898         if (r != ERROR_SUCCESS)
1899             return r;
1900     }
1901
1902     return ERROR_SUCCESS;
1903 }
1904
1905 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1906                                 LPWSTR table, DWORD numconflicts)
1907 {
1908     UINT r;
1909     MSIQUERY *view;
1910
1911     static const WCHAR create[] = {
1912         'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1913         '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1914         'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1915         'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1916         'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1917         'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1918         'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1919     static const WCHAR insert[] = {
1920         'I','N','S','E','R','T',' ','I','N','T','O',' ',
1921         '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1922         '`','N','u','m','R','o','w','M','e','r','g','e',
1923         'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1924         ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1925
1926     if (!TABLE_Exists(db, error))
1927     {
1928         r = MSI_OpenQuery(db, &view, create, error);
1929         if (r != ERROR_SUCCESS)
1930             return r;
1931
1932         r = MSI_ViewExecute(view, NULL);
1933         msiobj_release(&view->hdr);
1934         if (r != ERROR_SUCCESS)
1935             return r;
1936     }
1937
1938     r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1939     if (r != ERROR_SUCCESS)
1940         return r;
1941
1942     r = MSI_ViewExecute(view, NULL);
1943     msiobj_release(&view->hdr);
1944     return r;
1945 }
1946
1947 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1948                               LPCWSTR szTableName)
1949 {
1950     struct list tabledata = LIST_INIT(tabledata);
1951     struct list *item, *cursor;
1952     MSIDATABASE *db, *merge;
1953     MERGETABLE *table;
1954     BOOL conflicts;
1955     UINT r;
1956
1957     TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1958           debugstr_w(szTableName));
1959
1960     if (szTableName && !*szTableName)
1961         return ERROR_INVALID_TABLE;
1962
1963     db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1964     merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1965     if (!db || !merge)
1966     {
1967         r = ERROR_INVALID_HANDLE;
1968         goto done;
1969     }
1970
1971     r = gather_merge_data(db, merge, &tabledata);
1972     if (r != ERROR_SUCCESS)
1973         goto done;
1974
1975     conflicts = FALSE;
1976     LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1977     {
1978         if (table->numconflicts)
1979         {
1980             conflicts = TRUE;
1981
1982             r = update_merge_errors(db, szTableName, table->name,
1983                                     table->numconflicts);
1984             if (r != ERROR_SUCCESS)
1985                 break;
1986         }
1987         else
1988         {
1989             r = merge_table(db, table);
1990             if (r != ERROR_SUCCESS)
1991                 break;
1992         }
1993     }
1994
1995     LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1996     {
1997         MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1998         list_remove(&table->entry);
1999         free_merge_table(table);
2000     }
2001
2002     if (conflicts)
2003         r = ERROR_FUNCTION_FAILED;
2004
2005 done:
2006     msiobj_release(&db->hdr);
2007     msiobj_release(&merge->hdr);
2008     return r;
2009 }
2010
2011 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
2012 {
2013     MSIDBSTATE ret = MSIDBSTATE_READ;
2014     MSIDATABASE *db;
2015
2016     TRACE("%d\n", handle);
2017
2018     db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
2019     if( !db )
2020     {
2021         IWineMsiRemoteDatabase *remote_database;
2022
2023         remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
2024         if ( !remote_database )
2025             return MSIDBSTATE_ERROR;
2026
2027         IWineMsiRemoteDatabase_Release( remote_database );
2028         WARN("MsiGetDatabaseState not allowed during a custom action!\n");
2029
2030         return MSIDBSTATE_READ;
2031     }
2032
2033     if (db->mode != MSIDBOPEN_READONLY )
2034         ret = MSIDBSTATE_WRITE;
2035     msiobj_release( &db->hdr );
2036
2037     return ret;
2038 }
2039
2040 typedef struct _msi_remote_database_impl {
2041     const IWineMsiRemoteDatabaseVtbl *lpVtbl;
2042     MSIHANDLE database;
2043     LONG refs;
2044 } msi_remote_database_impl;
2045
2046 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
2047 {
2048     return (msi_remote_database_impl *)iface;
2049 }
2050
2051 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
2052                                           REFIID riid,LPVOID *ppobj)
2053 {
2054     if( IsEqualCLSID( riid, &IID_IUnknown ) ||
2055         IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
2056     {
2057         IUnknown_AddRef( iface );
2058         *ppobj = iface;
2059         return S_OK;
2060     }
2061
2062     return E_NOINTERFACE;
2063 }
2064
2065 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
2066 {
2067     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2068
2069     return InterlockedIncrement( &This->refs );
2070 }
2071
2072 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
2073 {
2074     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2075     ULONG r;
2076
2077     r = InterlockedDecrement( &This->refs );
2078     if (r == 0)
2079     {
2080         MsiCloseHandle( This->database );
2081         msi_free( This );
2082     }
2083     return r;
2084 }
2085
2086 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
2087                                              LPCWSTR table, MSICONDITION *persistent )
2088 {
2089     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2090     *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
2091     return S_OK;
2092 }
2093
2094 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
2095                                           LPCWSTR table, MSIHANDLE *keys )
2096 {
2097     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2098     UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
2099     return HRESULT_FROM_WIN32(r);
2100 }
2101
2102 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
2103                                                 UINT updatecount, MSIHANDLE *suminfo )
2104 {
2105     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2106     UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
2107     return HRESULT_FROM_WIN32(r);
2108 }
2109
2110 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
2111                                     LPCWSTR query, MSIHANDLE *view )
2112 {
2113     msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2114     UINT r = MsiDatabaseOpenViewW(This->database, query, view);
2115     return HRESULT_FROM_WIN32(r);
2116 }
2117
2118 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
2119 {
2120     msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2121     This->database = handle;
2122     return S_OK;
2123 }
2124
2125 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
2126 {
2127     mrd_QueryInterface,
2128     mrd_AddRef,
2129     mrd_Release,
2130     mrd_IsTablePersistent,
2131     mrd_GetPrimaryKeys,
2132     mrd_GetSummaryInformation,
2133     mrd_OpenView,
2134     mrd_SetMsiHandle,
2135 };
2136
2137 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
2138 {
2139     msi_remote_database_impl *This;
2140
2141     This = msi_alloc( sizeof *This );
2142     if (!This)
2143         return E_OUTOFMEMORY;
2144
2145     This->lpVtbl = &msi_remote_database_vtbl;
2146     This->database = 0;
2147     This->refs = 1;
2148
2149     *ppObj = This;
2150
2151     return S_OK;
2152 }