Implement asn.1 encoding/decoding of times, with tests.
[wine] / dlls / msi / dialog.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define COBJMACROS
22
23 #include <stdarg.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winnls.h"
29 #include "wingdi.h"
30 #include "msi.h"
31 #include "msipriv.h"
32 #include "msidefs.h"
33 #include "ocidl.h"
34 #include "olectl.h"
35 #include "richedit.h"
36
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39
40 #include "action.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43
44
45 struct msi_control_tag;
46 typedef struct msi_control_tag msi_control;
47 typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM );
48
49 struct msi_control_tag
50 {
51     struct msi_control_tag *next;
52     HWND hwnd;
53     msi_handler handler;
54     LPWSTR property;
55     LPWSTR value;
56     IPicture *pic;
57     WCHAR name[1];
58 };
59
60 typedef struct msi_font_tag
61 {
62     struct msi_font_tag *next;
63     HFONT hfont;
64     WCHAR name[1];
65 } msi_font;
66
67 struct msi_dialog_tag
68 {
69     MSIPACKAGE *package;
70     msi_dialog_event_handler event_handler;
71     BOOL finished;
72     INT scale;
73     DWORD attributes;
74     HWND hwnd;
75     LPWSTR default_font;
76     msi_font *font_list;
77     msi_control *control_list;
78     WCHAR name[1];
79 };
80
81 typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec );
82 struct control_handler 
83 {
84     LPCWSTR control_type;
85     msi_dialog_control_func func;
86 };
87
88 typedef struct
89 {
90     msi_dialog* dialog;
91     msi_control *parent;
92     DWORD       attributes;
93 } radio_button_group_descr;
94
95 const WCHAR szMsiDialogClass[] = {
96     'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
97 };
98 const WCHAR szMsiHiddenWindow[] = {
99     'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 };
100 const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
101 const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
102 const static WCHAR szButtonData[] = { 'M','S','I','D','A','T','A',0 };
103 static const WCHAR szText[] = { 'T','e','x','t',0 };
104 static const WCHAR szPushButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
105 static const WCHAR szLine[] = { 'L','i','n','e',0 };
106 static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
107 static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 };
108 static const WCHAR szScrollableText[] = {
109     'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
110 static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 };
111 static const WCHAR szEdit[] = { 'E','d','i','t',0 };
112 static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
113 static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 };
114 static const WCHAR szRadioButtonGroup[] = { 
115     'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
116
117 static UINT msi_dialog_checkbox_handler( msi_dialog *, msi_control *, WPARAM );
118 static void msi_dialog_checkbox_sync_state( msi_dialog *, msi_control * );
119 static UINT msi_dialog_button_handler( msi_dialog *, msi_control *, WPARAM );
120 static UINT msi_dialog_edit_handler( msi_dialog *, msi_control *, WPARAM );
121 static UINT msi_dialog_radiogroup_handler( msi_dialog *, msi_control *, WPARAM param );
122 static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
123
124
125 /* dialog sequencing */
126
127 #define WM_MSI_DIALOG_CREATE  (WM_USER+0x100)
128 #define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
129
130 static DWORD uiThreadId;
131 static HWND hMsiHiddenWindow;
132 static HMODULE hRichedit;
133
134 static INT msi_dialog_scale_unit( msi_dialog *dialog, INT val )
135 {
136     return (dialog->scale * val + 5) / 10;
137 }
138
139 static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name )
140 {
141     msi_control *control;
142
143     for( control = dialog->control_list; control; control = control->next )
144         if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
145             break;
146     return control;
147 }
148
149 static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd )
150 {
151     msi_control *control;
152
153     for( control = dialog->control_list; control; control = control->next )
154         if( hwnd == control->hwnd )
155             break;
156     return control;
157 }
158
159 /*
160  * msi_dialog_get_style
161  *
162  * Extract the {\style} string from the front of the text to display and
163  *  update the pointer.
164  */
165 static LPWSTR msi_dialog_get_style( LPCWSTR *text )
166 {
167     LPWSTR ret = NULL;
168     LPCWSTR p = *text, q;
169     DWORD len;
170
171     if( *p++ != '{' )
172         return ret;
173     q = strchrW( p, '}' );
174     if( !q )
175         return ret;
176     *text = ++q;
177     if( *p++ != '\\' )
178         return ret;
179     len = q - p;
180     
181     ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
182     if( !ret )
183         return ret;
184     memcpy( ret, p, len*sizeof(WCHAR) );
185     ret[len-1] = 0;
186     return ret;
187 }
188
189 static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param )
190 {
191     msi_dialog *dialog = param;
192     msi_font *font;
193     LPCWSTR face, name;
194     LOGFONTW lf;
195     INT style;
196     HDC hdc;
197
198     /* create a font and add it to the list */
199     name = MSI_RecordGetString( rec, 1 );
200     font = HeapAlloc( GetProcessHeap(), 0,
201                       sizeof *font + strlenW( name )*sizeof (WCHAR) );
202     strcpyW( font->name, name );
203     font->next = dialog->font_list;
204     dialog->font_list = font;
205
206     memset( &lf, 0, sizeof lf );
207     face = MSI_RecordGetString( rec, 2 );
208     lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
209     style = MSI_RecordGetInteger( rec, 5 );
210     if( style & msidbTextStyleStyleBitsBold )
211         lf.lfWeight = FW_BOLD;
212     if( style & msidbTextStyleStyleBitsItalic )
213         lf.lfItalic = TRUE;
214     if( style & msidbTextStyleStyleBitsUnderline )
215         lf.lfUnderline = TRUE;
216     if( style & msidbTextStyleStyleBitsStrike )
217         lf.lfStrikeOut = TRUE;
218     lstrcpynW( lf.lfFaceName, face, LF_FACESIZE );
219
220     /* adjust the height */
221     hdc = GetDC( dialog->hwnd );
222     if (hdc)
223     {
224         lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
225         ReleaseDC( dialog->hwnd, hdc );
226     }
227
228     font->hfont = CreateFontIndirectW( &lf );
229
230     TRACE("Adding font style %s\n", debugstr_w(font->name) );
231
232     return ERROR_SUCCESS;
233 }
234
235 static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name )
236 {
237     msi_font *font;
238
239     for( font = dialog->font_list; font; font = font->next )
240         if( !strcmpW( font->name, name ) )  /* FIXME: case sensitive? */
241             break;
242
243     return font;
244 }
245
246 static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name )
247 {
248     msi_font *font;
249
250     font = msi_dialog_find_font( dialog, name );
251     if( font )
252         SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
253     else
254         ERR("No font entry for %s\n", debugstr_w(name));
255     return ERROR_SUCCESS;
256 }
257
258 static UINT msi_dialog_build_font_list( msi_dialog *dialog )
259 {
260     static const WCHAR query[] = {
261       'S','E','L','E','C','T',' ','*',' ',
262       'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
263     };
264     UINT r;
265     MSIQUERY *view = NULL;
266
267     TRACE("dialog %p\n", dialog );
268
269     r = MSI_OpenQuery( dialog->package->db, &view, query );
270     if( r != ERROR_SUCCESS )
271         return r;
272
273     r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog );
274     msiobj_release( &view->hdr );
275
276     return r;
277 }
278
279 static msi_control *msi_dialog_create_window( msi_dialog *dialog,
280                 MSIRECORD *rec, LPCWSTR szCls, LPCWSTR name, LPCWSTR text,
281                 DWORD style, HWND parent )
282 {
283     DWORD x, y, width, height;
284     LPWSTR font = NULL, title = NULL;
285     msi_control *control;
286
287     style |= WS_CHILD;
288
289     control = HeapAlloc( GetProcessHeap(), 0,
290                          sizeof *control + strlenW(name)*sizeof(WCHAR) );
291     strcpyW( control->name, name );
292     control->next = dialog->control_list;
293     dialog->control_list = control;
294     control->handler = NULL;
295     control->property = NULL;
296     control->value = NULL;
297     control->pic = NULL;
298
299     x = MSI_RecordGetInteger( rec, 4 );
300     y = MSI_RecordGetInteger( rec, 5 );
301     width = MSI_RecordGetInteger( rec, 6 );
302     height = MSI_RecordGetInteger( rec, 7 );
303
304     x = msi_dialog_scale_unit( dialog, x );
305     y = msi_dialog_scale_unit( dialog, y );
306     width = msi_dialog_scale_unit( dialog, width );
307     height = msi_dialog_scale_unit( dialog, height );
308
309     if( text )
310     {
311         font = msi_dialog_get_style( &text );
312         deformat_string( dialog->package, text, &title );
313     }
314
315     control->hwnd = CreateWindowW( szCls, title, style,
316                           x, y, width, height, parent, NULL, NULL, NULL );
317
318     TRACE("Dialog %s control %s hwnd %p\n",
319            debugstr_w(dialog->name), debugstr_w(text), control->hwnd );
320
321     msi_dialog_set_font( dialog, control->hwnd,
322                          font ? font : dialog->default_font );
323
324     HeapFree( GetProcessHeap(), 0, font );
325     HeapFree( GetProcessHeap(), 0, title );
326
327     return control;
328 }
329
330 /* called from the Control Event subscription code */
331 void msi_dialog_handle_event( msi_dialog* dialog, LPCWSTR control, 
332                               LPCWSTR attribute, MSIRECORD *rec )
333 {
334     msi_control* ctrl;
335     LPCWSTR text;
336
337     ctrl = msi_dialog_find_control( dialog, control );
338     if (!ctrl)
339         return;
340     if( lstrcmpW(attribute, szText) )
341         return;
342     text = MSI_RecordGetString( rec , 1 );
343     SetWindowTextW( ctrl->hwnd, text );
344 }
345
346 static void msi_dialog_map_events(msi_dialog* dialog, LPCWSTR control)
347 {
348     static WCHAR Query[] = {
349         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
350          '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
351         'W','H','E','R','E',' ',
352          '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
353         'A','N','D',' ',
354          '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0
355     };
356     MSIRECORD *row;
357     LPCWSTR event, attribute;
358
359     row = MSI_QueryGetRecord( dialog->package->db, Query, dialog->name, control );
360     if (!row)
361         return;
362
363     event = MSI_RecordGetString( row, 3 );
364     attribute = MSI_RecordGetString( row, 4 );
365     ControlEvent_SubscribeToEvent( dialog->package, event, control, attribute );
366     msiobj_release( &row->hdr );
367 }
368
369 /* everything except radio buttons */
370 static msi_control *msi_dialog_add_control( msi_dialog *dialog,
371                 MSIRECORD *rec, LPCWSTR szCls, DWORD style )
372 {
373     DWORD attributes;
374     LPCWSTR text, name;
375
376     name = MSI_RecordGetString( rec, 2 );
377     attributes = MSI_RecordGetInteger( rec, 8 );
378     text = MSI_RecordGetString( rec, 10 );
379     if( attributes & 1 )
380         style |= WS_VISIBLE;
381     if( ~attributes & 2 )
382         style |= WS_DISABLED;
383
384     msi_dialog_map_events(dialog, name);
385
386     return msi_dialog_create_window( dialog, rec, szCls, name, text,
387                                      style, dialog->hwnd );
388 }
389
390 static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec )
391 {
392     TRACE("%p %p\n", dialog, rec);
393
394     msi_dialog_add_control( dialog, rec, szStatic, 0 );
395     return ERROR_SUCCESS;
396 }
397
398 static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec )
399 {
400     msi_control *control;
401
402     TRACE("%p %p\n", dialog, rec);
403
404     control = msi_dialog_add_control( dialog, rec, szButton, 0 );
405     control->handler = msi_dialog_button_handler;
406
407     return ERROR_SUCCESS;
408 }
409
410 static LPWSTR msi_get_checkbox_value( msi_dialog *dialog, LPCWSTR prop )
411 {
412     const static WCHAR query[] = {
413         'S','E','L','E','C','T',' ','*',' ',
414         'F','R','O','M',' ','`','C','h','e','c','k','B','o','x',' ','`',
415         'W','H','E','R','E',' ',
416         '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
417         '\'','%','s','\'',0
418     };
419     MSIRECORD *rec = NULL;
420     LPCWSTR val = NULL;
421     LPWSTR ret = NULL;
422
423     /* find if there is a value associated with the checkbox */
424     rec = MSI_QueryGetRecord( dialog->package->db, query, prop );
425     if (!rec)
426         return ret;
427
428     val = MSI_RecordGetString( rec, 2 );
429     if (val)
430     {
431         deformat_string( dialog->package, val, &ret );
432         if( ret && !ret[0] )
433         {
434             HeapFree( GetProcessHeap(), 0, ret );
435             ret = NULL;
436         }
437     }
438     msiobj_release( &rec->hdr );
439     if (ret)
440         return ret;
441
442     ret = load_dynamic_property(dialog->package, prop, NULL);
443     if( ret && !ret[0] )
444     {
445         HeapFree( GetProcessHeap(), 0, ret );
446         ret = NULL;
447     }
448
449     return ret;
450 }
451
452 static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec )
453 {
454     msi_control *control;
455     LPCWSTR prop;
456
457     TRACE("%p %p\n", dialog, rec);
458
459     control = msi_dialog_add_control( dialog, rec, szButton,
460                                       BS_CHECKBOX | BS_MULTILINE );
461     control->handler = msi_dialog_checkbox_handler;
462     prop = MSI_RecordGetString( rec, 9 );
463     if( prop )
464     {
465         control->property = strdupW( prop );
466         control->value = msi_get_checkbox_value( dialog, prop );
467         TRACE("control %s value %s\n", debugstr_w(control->property),
468               debugstr_w(control->value));
469     }
470     msi_dialog_checkbox_sync_state( dialog, control );
471
472     return ERROR_SUCCESS;
473 }
474
475 static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec )
476 {
477     TRACE("%p %p\n", dialog, rec);
478
479     msi_dialog_add_control( dialog, rec, szStatic, SS_ETCHEDHORZ | SS_SUNKEN );
480     return ERROR_SUCCESS;
481 }
482
483 struct msi_streamin_info
484 {
485     LPSTR string;
486     DWORD offset;
487     DWORD length;
488 };
489
490 static DWORD CALLBACK
491 msi_richedit_stream_in( DWORD_PTR arg, LPBYTE buffer, LONG count, LONG *pcb )
492 {
493     struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
494
495     if( (count + info->offset) > info->length )
496         count = info->length - info->offset;
497     memcpy( buffer, &info->string[ info->offset ], count );
498     *pcb = count;
499     info->offset += count;
500
501     TRACE("%ld/%ld\n", info->offset, info->length);
502
503     return 0;
504 }
505
506 static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec )
507 {
508     const LPCWSTR szRichEdit = RICHEDIT_CLASS20W;
509     struct msi_streamin_info info;
510     msi_control *control;
511     LPCWSTR text;
512     EDITSTREAM es;
513     DWORD style;
514
515     style = WS_BORDER | ES_MULTILINE | WS_VSCROLL | ES_READONLY | ES_AUTOVSCROLL;
516     control = msi_dialog_add_control( dialog, rec, szRichEdit, style );
517
518     text = MSI_RecordGetString( rec, 10 );
519     info.string = strdupWtoA( text );
520     info.offset = 0;
521     info.length = lstrlenA( info.string ) + 1;
522
523     es.dwCookie = (DWORD_PTR) &info;
524     es.dwError = 0;
525     es.pfnCallback = msi_richedit_stream_in;
526
527     SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es );
528
529     HeapFree( GetProcessHeap(), 0, info.string );
530
531     return ERROR_SUCCESS;
532 }
533
534 static UINT msi_load_bitmap( MSIDATABASE *db, LPCWSTR name, IPicture **pic )
535 {
536     const static WCHAR query[] = {
537         's','e','l','e','c','t',' ','*',' ',
538         'f','r','o','m',' ','B','i','n','a','r','y',' ',
539         'w','h','e','r','e',' ',
540             '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
541     };
542     MSIRECORD *rec = NULL;
543     IStream *stm = NULL;
544     UINT r;
545
546     rec = MSI_QueryGetRecord( db, query, name );
547     if( !rec )
548         return ERROR_FUNCTION_FAILED;
549
550     r = MSI_RecordGetIStream( rec, 2, &stm );
551     msiobj_release( &rec->hdr );
552     if( r != ERROR_SUCCESS )
553         return r;
554
555     r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) pic );
556     IStream_Release( stm );
557     if( FAILED( r ) )
558         return ERROR_FUNCTION_FAILED;
559
560     return ERROR_SUCCESS;
561 }
562
563 static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec )
564 {
565     IPicture *pic = NULL;
566     msi_control *control;
567     OLE_HANDLE hBitmap = 0;
568     LPCWSTR text;
569     UINT r;
570
571     control = msi_dialog_add_control( dialog, rec, szStatic,
572                             SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
573     text = MSI_RecordGetString( rec, 10 );
574     r = msi_load_bitmap( dialog->package->db, text, &pic );
575     if( r == ERROR_SUCCESS )
576     {
577         r = IPicture_get_Handle( pic, &hBitmap );
578         if( SUCCEEDED( r ) )
579             SendMessageW( control->hwnd, STM_SETIMAGE, IMAGE_BITMAP, hBitmap );
580         control->pic = pic;
581     }
582     
583     return ERROR_SUCCESS;
584 }
585
586 static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec )
587 {
588     static const WCHAR szCombo[] = { 'C','O','M','B','O','B','O','X',0 };
589
590     msi_dialog_add_control( dialog, rec, szCombo,
591                             SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
592     return ERROR_SUCCESS;
593 }
594
595 static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec )
596 {
597     const static WCHAR szEdit[] = { 'E','D','I','T',0 };
598     msi_control *control;
599     LPCWSTR prop;
600     LPWSTR val;
601
602     control = msi_dialog_add_control( dialog, rec, szEdit, WS_BORDER );
603     control->handler = msi_dialog_edit_handler;
604     prop = MSI_RecordGetString( rec, 9 );
605     if( prop )
606         control->property = strdupW( prop );
607     val = load_dynamic_property( dialog->package, control->property, NULL );
608     SetWindowTextW( control->hwnd, val );
609     HeapFree( GetProcessHeap(), 0, val );
610     return ERROR_SUCCESS;
611 }
612
613 static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec )
614 {
615     FIXME("not implemented properly\n");
616     return msi_dialog_edit_control( dialog, rec );
617 }
618
619 /* radio buttons are a bit different from normal controls */
620 static UINT msi_dialog_create_radiobutton( MSIRECORD *rec, LPVOID param )
621 {
622     radio_button_group_descr *group = (radio_button_group_descr *)param;
623     msi_dialog *dialog = group->dialog;
624     msi_control *control;
625     LPCWSTR prop, text, name;
626     DWORD style;
627     DWORD attributes = group->attributes;
628
629     style = WS_CHILD | BS_AUTORADIOBUTTON | BS_MULTILINE;
630     name = MSI_RecordGetString( rec, 3 );
631     text = MSI_RecordGetString( rec, 8 );
632     if( attributes & 1 )
633         style |= WS_VISIBLE;
634     if( ~attributes & 2 )
635         style |= WS_DISABLED;
636
637     control = msi_dialog_create_window( dialog, rec, szButton, name, text,
638                                         style, group->parent->hwnd );
639     control->handler = msi_dialog_radiogroup_handler;
640
641     prop = MSI_RecordGetString( rec, 1 );
642     if( prop )
643         control->property = strdupW( prop );
644
645     return ERROR_SUCCESS;
646 }
647
648 static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec )
649 {
650     static const WCHAR query[] = {
651         'S','E','L','E','C','T',' ','*',' ',
652         'F','R','O','M',' ','R','a','d','i','o','B','u','t','t','o','n',' ',
653         'W','H','E','R','E',' ',
654            '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
655     UINT r;
656     LPCWSTR prop;
657     msi_control *control;
658     MSIQUERY *view = NULL;
659     radio_button_group_descr group;
660     MSIPACKAGE *package = dialog->package;
661     WNDPROC oldproc;
662
663     prop = MSI_RecordGetString( rec, 9 );
664
665     TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
666
667     /* Create parent group box to hold radio buttons */
668     control = msi_dialog_add_control( dialog, rec, szButton, BS_OWNERDRAW|WS_GROUP );
669     if( !control )
670         return ERROR_FUNCTION_FAILED;
671
672     oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
673                                            (LONG_PTR)MSIRadioGroup_WndProc );
674     SetPropW(control->hwnd, szButtonData, oldproc);
675
676     if( prop )
677         control->property = strdupW( prop );
678
679     /* query the Radio Button table for all control in this group */
680     r = MSI_OpenQuery( package->db, &view, query, prop );
681     if( r != ERROR_SUCCESS )
682     {
683         ERR("query failed for dialog %s radio group %s\n", 
684             debugstr_w(dialog->name), debugstr_w(prop));
685         return ERROR_INVALID_PARAMETER;
686     }
687
688     group.dialog = dialog;
689     group.parent = control;
690     group.attributes = MSI_RecordGetInteger( rec, 8 );
691
692     r = MSI_IterateRecords( view, 0, msi_dialog_create_radiobutton, &group );
693     msiobj_release( &view->hdr );
694
695     return r;
696 }
697
698 struct control_handler msi_dialog_handler[] =
699 {
700     { szText, msi_dialog_text_control },
701     { szPushButton, msi_dialog_button_control },
702     { szLine, msi_dialog_line_control },
703     { szBitmap, msi_dialog_bitmap_control },
704     { szCheckBox, msi_dialog_checkbox_control },
705     { szScrollableText, msi_dialog_scrolltext_control },
706     { szComboBox, msi_dialog_combo_control },
707     { szEdit, msi_dialog_edit_control },
708     { szMaskedEdit, msi_dialog_edit_control },
709     { szPathEdit, msi_dialog_pathedit_control },
710     { szRadioButtonGroup, msi_dialog_radiogroup_control },
711 };
712
713 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
714
715 static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
716 {
717     msi_dialog *dialog = param;
718     LPCWSTR control_type;
719     UINT i;
720
721     /* find and call the function that can create this type of control */
722     control_type = MSI_RecordGetString( rec, 3 );
723     for( i=0; i<NUM_CONTROL_TYPES; i++ )
724         if (!strcmpiW( msi_dialog_handler[i].control_type, control_type ))
725             break;
726     if( i != NUM_CONTROL_TYPES )
727         msi_dialog_handler[i].func( dialog, rec );
728     else
729         ERR("no handler for element type %s\n", debugstr_w(control_type));
730
731     return ERROR_SUCCESS;
732 }
733
734 static UINT msi_dialog_fill_controls( msi_dialog *dialog )
735 {
736     static const WCHAR query[] = {
737         'S','E','L','E','C','T',' ','*',' ',
738         'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
739         'W','H','E','R','E',' ',
740            '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
741     UINT r;
742     MSIQUERY *view = NULL;
743     MSIPACKAGE *package = dialog->package;
744
745     TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
746
747     /* query the Control table for all the elements of the control */
748     r = MSI_OpenQuery( package->db, &view, query, dialog->name );
749     if( r != ERROR_SUCCESS )
750     {
751         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
752         return ERROR_INVALID_PARAMETER;
753     }
754
755     r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog );
756     msiobj_release( &view->hdr );
757
758     return r;
759 }
760
761 static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param )
762 {
763     static const WCHAR szHide[] = { 'H','i','d','e',0 };
764     static const WCHAR szShow[] = { 'S','h','o','w',0 };
765     static const WCHAR szDisable[] = { 'D','i','s','a','b','l','e',0 };
766     static const WCHAR szEnable[] = { 'E','n','a','b','l','e',0 };
767     msi_dialog *dialog = param;
768     msi_control *control;
769     LPCWSTR name, action, condition;
770     UINT r;
771
772     name = MSI_RecordGetString( rec, 2 );
773     action = MSI_RecordGetString( rec, 3 );
774     condition = MSI_RecordGetString( rec, 4 );
775     r = MSI_EvaluateConditionW( dialog->package, condition );
776     control = msi_dialog_find_control( dialog, name );
777     if( r && control )
778     {
779         TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
780
781         /* FIXME: case sensitive? */
782         if(!strcmpW(action, szHide))
783             ShowWindow(control->hwnd, SW_HIDE);
784         else if(!strcmpW(action, szShow))
785             ShowWindow(control->hwnd, SW_SHOW);
786         else if(!strcmpW(action, szDisable))
787             EnableWindow(control->hwnd, FALSE);
788         else if(!strcmpW(action, szEnable))
789             EnableWindow(control->hwnd, TRUE);
790         else
791             FIXME("Unhandled action %s\n", debugstr_w(action));
792     }
793
794     return ERROR_SUCCESS;
795 }
796
797 static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog )
798 {
799     static const WCHAR query[] = {
800       'S','E','L','E','C','T',' ','*',' ',
801       'F','R','O','M',' ',
802         'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
803       'W','H','E','R','E',' ',
804         '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
805     };
806     UINT r;
807     MSIQUERY *view = NULL;
808     MSIPACKAGE *package = dialog->package;
809
810     TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
811
812     /* query the Control table for all the elements of the control */
813     r = MSI_OpenQuery( package->db, &view, query, dialog->name );
814     if( r != ERROR_SUCCESS )
815     {
816         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
817         return ERROR_INVALID_PARAMETER;
818     }
819
820     r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog );
821     msiobj_release( &view->hdr );
822
823     return r;
824 }
825
826 /* figure out the height of 10 point MS Sans Serif */
827 static INT msi_dialog_get_sans_serif_height( HWND hwnd )
828 {
829     static const WCHAR szSansSerif[] = {
830         'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
831     LOGFONTW lf;
832     TEXTMETRICW tm;
833     BOOL r;
834     LONG height = 0;
835     HFONT hFont, hOldFont;
836     HDC hdc;
837
838     hdc = GetDC( hwnd );
839     if (hdc)
840     {
841         memset( &lf, 0, sizeof lf );
842         lf.lfHeight = MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
843         strcpyW( lf.lfFaceName, szSansSerif );
844         hFont = CreateFontIndirectW(&lf);
845         if (hFont)
846         {
847             hOldFont = SelectObject( hdc, hFont );
848             r = GetTextMetricsW( hdc, &tm );
849             if (r)
850                 height = tm.tmHeight;
851             SelectObject( hdc, hOldFont );
852             DeleteObject( hFont );
853         }
854         ReleaseDC( hwnd, hdc );
855     }
856     return height;
857 }
858
859 /* fetch the associated record from the Dialog table */
860 static MSIRECORD *msi_get_dialog_record( msi_dialog *dialog )
861 {
862     static const WCHAR query[] = {
863         'S','E','L','E','C','T',' ','*',' ',
864         'F','R','O','M',' ','D','i','a','l','o','g',' ',
865         'W','H','E','R','E',' ',
866            '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
867     MSIPACKAGE *package = dialog->package;
868     MSIRECORD *rec = NULL;
869
870     TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
871
872     rec = MSI_QueryGetRecord( package->db, query, dialog->name );
873     if( !rec )
874         ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
875
876     return rec;
877 }
878
879 static void msi_dialog_adjust_dialog_size( msi_dialog *dialog, LPSIZE sz )
880 {
881     RECT rect;
882     LONG style;
883
884     /* turn the client size into the window rectangle */
885     rect.left = 0;
886     rect.top = 0;
887     rect.right = msi_dialog_scale_unit( dialog, sz->cx );
888     rect.bottom = msi_dialog_scale_unit( dialog, sz->cy );
889     style = GetWindowLongPtrW( dialog->hwnd, GWL_STYLE );
890     AdjustWindowRect( &rect, style, FALSE );
891     sz->cx = rect.right - rect.left;
892     sz->cy = rect.bottom - rect.top;
893 }
894
895 static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs )
896 {
897     static const WCHAR df[] = {
898         'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
899     msi_dialog *dialog = (msi_dialog*) cs->lpCreateParams;
900     MSIRECORD *rec = NULL;
901     LPCWSTR text;
902     LPWSTR title = NULL;
903     SIZE size;
904
905     TRACE("%p %p\n", dialog, dialog->package);
906
907     dialog->hwnd = hwnd;
908     SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog );
909
910     rec = msi_get_dialog_record( dialog );
911     if( !rec )
912     {
913         TRACE("No record found for dialog %s\n", debugstr_w(dialog->name));
914         return -1;
915     }
916
917     dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
918
919     size.cx = MSI_RecordGetInteger( rec, 4 );
920     size.cy = MSI_RecordGetInteger( rec, 5 );
921     msi_dialog_adjust_dialog_size( dialog, &size );
922
923     dialog->attributes = MSI_RecordGetInteger( rec, 6 );
924     text = MSI_RecordGetString( rec, 7 );
925
926     dialog->default_font = load_dynamic_property( dialog->package, df, NULL );
927
928     deformat_string( dialog->package, text, &title );
929     SetWindowTextW( hwnd, title );
930     SetWindowPos( hwnd, 0, 0, 0, size.cx, size.cy,
931                   SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
932
933     HeapFree( GetProcessHeap(), 0, title );
934     msiobj_release( &rec->hdr );
935
936     msi_dialog_build_font_list( dialog );
937     msi_dialog_fill_controls( dialog );
938     msi_dialog_evaluate_control_conditions( dialog );
939
940     return 0;
941 }
942
943 static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
944 {
945     LPWSTR event_fmt = NULL, arg_fmt = NULL;
946
947     TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
948
949     deformat_string( dialog->package, event, &event_fmt );
950     deformat_string( dialog->package, arg, &arg_fmt );
951
952     dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog );
953
954     HeapFree( GetProcessHeap(), 0, event_fmt );
955     HeapFree( GetProcessHeap(), 0, arg_fmt );
956
957     return ERROR_SUCCESS;
958 }
959
960 static UINT msi_dialog_set_property( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
961 {
962     static const WCHAR szNullArg[] = { '{','}',0 };
963     LPWSTR p, prop, arg_fmt = NULL;
964     UINT len;
965
966     len = strlenW(event);
967     prop = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR));
968     strcpyW( prop, &event[1] );
969     p = strchrW( prop, ']' );
970     if( p && p[1] == 0 )
971     {
972         *p = 0;
973         if( strcmpW( szNullArg, arg ) )
974             deformat_string( dialog->package, arg, &arg_fmt );
975         MSI_SetPropertyW( dialog->package, prop, arg_fmt );
976     }
977     else
978         ERR("Badly formatted property string - what happens?\n");
979     HeapFree( GetProcessHeap(), 0, prop );
980     return ERROR_SUCCESS;
981 }
982
983 static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param )
984 {
985     msi_dialog *dialog = param;
986     LPCWSTR condition, event, arg;
987     UINT r;
988
989     condition = MSI_RecordGetString( rec, 5 );
990     r = MSI_EvaluateConditionW( dialog->package, condition );
991     if( r )
992     {
993         event = MSI_RecordGetString( rec, 3 );
994         arg = MSI_RecordGetString( rec, 4 );
995         if( event[0] == '[' )
996             msi_dialog_set_property( dialog, event, arg );
997         else
998             msi_dialog_send_event( dialog, event, arg );
999     }
1000
1001     return ERROR_SUCCESS;
1002 }
1003
1004 static UINT msi_dialog_button_handler( msi_dialog *dialog,
1005                                        msi_control *control, WPARAM param )
1006 {
1007     static const WCHAR query[] = {
1008       'S','E','L','E','C','T',' ','*',' ',
1009       'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
1010       'W','H','E','R','E',' ',
1011          '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
1012       'A','N','D',' ',
1013          '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
1014       'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
1015     };
1016     MSIQUERY *view = NULL;
1017     UINT r;
1018
1019     if( HIWORD(param) != BN_CLICKED )
1020         return ERROR_SUCCESS;
1021
1022     r = MSI_OpenQuery( dialog->package->db, &view, query,
1023                        dialog->name, control->name );
1024     if( r != ERROR_SUCCESS )
1025     {
1026         ERR("query failed\n");
1027         return 0;
1028     }
1029
1030     r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
1031     msiobj_release( &view->hdr );
1032
1033     return r;
1034 }
1035
1036 static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog,
1037                 msi_control *control )
1038 {
1039     WCHAR state[2] = { 0 };
1040     DWORD sz = 2;
1041
1042     MSI_GetPropertyW( dialog->package, control->property, state, &sz );
1043     return state[0] ? 1 : 0;
1044 }
1045
1046 static void msi_dialog_set_checkbox_state( msi_dialog *dialog,
1047                 msi_control *control, UINT state )
1048 {
1049     static const WCHAR szState[] = { '1', 0 };
1050     LPCWSTR val;
1051
1052     /* if uncheck then the property is set to NULL */
1053     if (!state)
1054     {
1055         MSI_SetPropertyW( dialog->package, control->property, NULL );
1056         return;
1057     }
1058
1059     /* check for a custom state */
1060     if (control->value && control->value[0])
1061         val = control->value;
1062     else
1063         val = szState;
1064
1065     MSI_SetPropertyW( dialog->package, control->property, val );
1066 }
1067
1068 static void msi_dialog_checkbox_sync_state( msi_dialog *dialog,
1069                 msi_control *control )
1070 {
1071     UINT state;
1072
1073     state = msi_dialog_get_checkbox_state( dialog, control );
1074     SendMessageW( control->hwnd, BM_SETCHECK,
1075                   state ? BST_CHECKED : BST_UNCHECKED, 0 );
1076 }
1077
1078 static UINT msi_dialog_checkbox_handler( msi_dialog *dialog,
1079                 msi_control *control, WPARAM param )
1080 {
1081     UINT state;
1082
1083     if( HIWORD(param) != BN_CLICKED )
1084         return ERROR_SUCCESS;
1085
1086     TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name),
1087           debugstr_w(control->property));
1088
1089     state = msi_dialog_get_checkbox_state( dialog, control );
1090     state = state ? 0 : 1;
1091     msi_dialog_set_checkbox_state( dialog, control, state );
1092     msi_dialog_checkbox_sync_state( dialog, control );
1093
1094     return msi_dialog_button_handler( dialog, control, param );
1095 }
1096
1097 static UINT msi_dialog_edit_handler( msi_dialog *dialog,
1098                 msi_control *control, WPARAM param )
1099 {
1100     UINT sz, r;
1101     LPWSTR buf;
1102
1103     if( HIWORD(param) != EN_CHANGE )
1104         return ERROR_SUCCESS;
1105
1106     TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
1107           debugstr_w(control->property));
1108
1109     sz = 0x20;
1110     buf = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
1111     while( buf )
1112     {
1113         r = GetWindowTextW( control->hwnd, buf, sz );
1114         if( r < (sz-1) )
1115             break;
1116             sz *= 2;
1117         buf = HeapReAlloc( GetProcessHeap(), 0, buf, sz*sizeof(WCHAR) );
1118     }
1119
1120     MSI_SetPropertyW( dialog->package, control->property, buf );
1121
1122     HeapFree( GetProcessHeap(), 0, buf );
1123
1124     return ERROR_SUCCESS;
1125 }
1126
1127 static UINT msi_dialog_radiogroup_handler( msi_dialog *dialog,
1128                 msi_control *control, WPARAM param )
1129 {
1130     if( HIWORD(param) != BN_CLICKED )
1131         return ERROR_SUCCESS;
1132
1133     TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name),
1134           debugstr_w(control->property));
1135
1136     MSI_SetPropertyW( dialog->package, control->property, control->name );
1137
1138     return msi_dialog_button_handler( dialog, control, param );
1139 }
1140
1141 static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd )
1142 {
1143     msi_control *control;
1144
1145     TRACE("%p %p %08x\n", dialog, hwnd, param);
1146
1147     control = msi_dialog_find_control_by_hwnd( dialog, hwnd );
1148     if( control )
1149     {
1150         if( control->handler )
1151         {
1152             control->handler( dialog, control, param );
1153             msi_dialog_evaluate_control_conditions( dialog );
1154         }
1155     }
1156     else
1157         ERR("button click from nowhere\n");
1158     return 0;
1159 }
1160
1161 static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
1162                 WPARAM wParam, LPARAM lParam )
1163 {
1164     msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
1165
1166     TRACE("0x%04x\n", msg);
1167
1168     switch (msg)
1169     {
1170     case WM_CREATE:
1171         return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam );
1172
1173     case WM_COMMAND:
1174         return msi_dialog_oncommand( dialog, wParam, (HWND)lParam );
1175
1176     case WM_DESTROY:
1177         dialog->hwnd = NULL;
1178         return 0;
1179     }
1180     return DefWindowProcW(hwnd, msg, wParam, lParam);
1181 }
1182
1183 static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1184 {
1185     WNDPROC oldproc = (WNDPROC) GetPropW(hWnd, szButtonData);
1186
1187     TRACE("hWnd %p msg %04x wParam 0x%08x lParam 0x%08lx\n", hWnd, msg, wParam, lParam);
1188
1189     if (msg == WM_COMMAND) /* Forward notifications to dialog */
1190         SendMessageW(GetParent(hWnd), msg, wParam, lParam);
1191
1192     return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam);
1193 }
1194
1195 static LRESULT WINAPI MSIHiddenWindowProc( HWND hwnd, UINT msg,
1196                 WPARAM wParam, LPARAM lParam )
1197 {
1198     msi_dialog *dialog = (msi_dialog*) lParam;
1199
1200     TRACE("%d %p\n", msg, dialog);
1201
1202     switch (msg)
1203     {
1204     case WM_MSI_DIALOG_CREATE:
1205         return msi_dialog_run_message_loop( dialog );
1206     case WM_MSI_DIALOG_DESTROY:
1207         msi_dialog_destroy( dialog );
1208         return 0;
1209     }
1210     return DefWindowProcW( hwnd, msg, wParam, lParam );
1211 }
1212
1213 /* functions that interface to other modules within MSI */
1214
1215 msi_dialog *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
1216                                 msi_dialog_event_handler event_handler )
1217 {
1218     MSIRECORD *rec = NULL;
1219     msi_dialog *dialog;
1220
1221     TRACE("%p %s\n", package, debugstr_w(szDialogName));
1222
1223     /* allocate the structure for the dialog to use */
1224     dialog = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1225                         sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) );
1226     if( !dialog )
1227         return NULL;
1228     strcpyW( dialog->name, szDialogName );
1229     msiobj_addref( &package->hdr );
1230     dialog->package = package;
1231     dialog->event_handler = event_handler;
1232     dialog->finished = 0;
1233
1234     /* verify that the dialog exists */
1235     rec = msi_get_dialog_record( dialog );
1236     if( !rec )
1237     {
1238         HeapFree( GetProcessHeap(), 0, dialog );
1239         return NULL;
1240     }
1241     dialog->attributes = MSI_RecordGetInteger( rec, 6 );
1242     msiobj_release( &rec->hdr );
1243
1244     return dialog;
1245 }
1246
1247 static void msi_process_pending_messages(void)
1248 {
1249     MSG msg;
1250
1251     while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) )
1252     {
1253         TranslateMessage( &msg );
1254         DispatchMessageW( &msg );
1255     }
1256 }
1257
1258 void msi_dialog_end_dialog( msi_dialog *dialog )
1259 {
1260     TRACE("%p\n", dialog);
1261     dialog->finished = 1;
1262     PostMessageW(dialog->hwnd, WM_NULL, 0, 0);
1263 }
1264
1265 void msi_dialog_check_messages( HANDLE handle )
1266 {
1267     DWORD r;
1268
1269     /* in threads other than the UI thread, block */
1270     if( uiThreadId != GetCurrentThreadId() )
1271     {
1272         if( handle )
1273             WaitForSingleObject( handle, INFINITE );
1274         return;
1275     }
1276
1277     /* there's two choices for the UI thread */
1278     while (1)
1279     {
1280         msi_process_pending_messages();
1281
1282         if( !handle )
1283             break;
1284
1285         /*
1286          * block here until somebody creates a new dialog or
1287          * the handle we're waiting on becomes ready
1288          */
1289         r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLINPUT );
1290         if( r == WAIT_OBJECT_0 )
1291             break;
1292     }
1293 }
1294
1295 UINT msi_dialog_run_message_loop( msi_dialog *dialog )
1296 {
1297     HWND hwnd;
1298
1299     if( !(dialog->attributes & msidbDialogAttributesVisible) )
1300         return ERROR_SUCCESS;
1301
1302     if( uiThreadId != GetCurrentThreadId() )
1303         return SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_CREATE, 0, (LPARAM) dialog );
1304
1305     /* create the dialog window, don't show it yet */
1306     hwnd = CreateWindowW( szMsiDialogClass, dialog->name, WS_OVERLAPPEDWINDOW,
1307                      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1308                      NULL, NULL, NULL, dialog );
1309     if( !hwnd )
1310     {
1311         ERR("Failed to create dialog %s\n", debugstr_w( dialog->name ));
1312         return ERROR_FUNCTION_FAILED;
1313     }
1314
1315     ShowWindow( hwnd, SW_SHOW );
1316     UpdateWindow( hwnd );
1317
1318     if( dialog->attributes & msidbDialogAttributesModal )
1319     {
1320         while( !dialog->finished )
1321         {
1322             MsgWaitForMultipleObjects( 0, NULL, 0, INFINITE, QS_ALLEVENTS );
1323             msi_process_pending_messages();
1324         }
1325     }
1326     else
1327         return ERROR_IO_PENDING;
1328
1329     return ERROR_SUCCESS;
1330 }
1331
1332 void msi_dialog_do_preview( msi_dialog *dialog )
1333 {
1334     TRACE("\n");
1335     dialog->attributes |= msidbDialogAttributesVisible;
1336     dialog->attributes &= ~msidbDialogAttributesModal;
1337     msi_dialog_run_message_loop( dialog );
1338 }
1339
1340 void msi_dialog_destroy( msi_dialog *dialog )
1341 {
1342     if( uiThreadId != GetCurrentThreadId() )
1343     {
1344         SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_DESTROY, 0, (LPARAM) dialog );
1345         return;
1346     }
1347
1348     if( dialog->hwnd )
1349         ShowWindow( dialog->hwnd, SW_HIDE );
1350     
1351     /* destroy the list of controls */
1352     while( dialog->control_list )
1353     {
1354         msi_control *t = dialog->control_list;
1355         dialog->control_list = t->next;
1356         /* leave dialog->hwnd - destroying parent destroys child windows */
1357         HeapFree( GetProcessHeap(), 0, t->property );
1358         HeapFree( GetProcessHeap(), 0, t->value );
1359         if( t->pic )
1360             IPicture_Release( t->pic );
1361         HeapFree( GetProcessHeap(), 0, t );
1362     }
1363
1364     /* destroy the list of fonts */
1365     while( dialog->font_list )
1366     {
1367         msi_font *t = dialog->font_list;
1368         dialog->font_list = t->next;
1369         DeleteObject( t->hfont );
1370         HeapFree( GetProcessHeap(), 0, t );
1371     }
1372     HeapFree( GetProcessHeap(), 0, dialog->default_font );
1373
1374     if( dialog->hwnd )
1375         DestroyWindow( dialog->hwnd );
1376
1377     msiobj_release( &dialog->package->hdr );
1378     dialog->package = NULL;
1379     HeapFree( GetProcessHeap(), 0, dialog );
1380 }
1381
1382 BOOL msi_dialog_register_class( void )
1383 {
1384     WNDCLASSW cls;
1385
1386     ZeroMemory( &cls, sizeof cls );
1387     cls.lpfnWndProc   = MSIDialog_WndProc;
1388     cls.hInstance     = NULL;
1389     cls.hIcon         = LoadIconW(0, (LPWSTR)IDI_APPLICATION);
1390     cls.hCursor       = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1391     cls.hbrBackground = (HBRUSH)(COLOR_WINDOW);
1392     cls.lpszMenuName  = NULL;
1393     cls.lpszClassName = szMsiDialogClass;
1394
1395     if( !RegisterClassW( &cls ) )
1396         return FALSE;
1397
1398     cls.lpfnWndProc   = MSIHiddenWindowProc;
1399     cls.lpszClassName = szMsiHiddenWindow;
1400
1401     if( !RegisterClassW( &cls ) )
1402         return FALSE;
1403
1404     uiThreadId = GetCurrentThreadId();
1405
1406     hMsiHiddenWindow = CreateWindowW( szMsiHiddenWindow, NULL, WS_OVERLAPPED,
1407                                    0, 0, 100, 100, NULL, NULL, NULL, NULL );
1408     if( !hMsiHiddenWindow )
1409         return FALSE;
1410
1411     hRichedit = LoadLibraryA("riched20");
1412
1413     return TRUE;
1414 }
1415
1416 void msi_dialog_unregister_class( void )
1417 {
1418     DestroyWindow( hMsiHiddenWindow );
1419     UnregisterClassW( szMsiDialogClass, NULL );
1420     uiThreadId = 0;
1421     FreeLibrary( hRichedit );
1422 }