2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Mike McCormack for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
45 struct msi_control_tag;
46 typedef struct msi_control_tag msi_control;
47 typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM );
49 struct msi_control_tag
51 struct msi_control_tag *next;
60 typedef struct msi_font_tag
62 struct msi_font_tag *next;
70 msi_dialog_event_handler event_handler;
77 msi_control *control_list;
81 typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec );
82 struct control_handler
85 msi_dialog_control_func func;
93 } radio_button_group_descr;
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
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 };
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 UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog );
123 static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
126 /* dialog sequencing */
128 #define WM_MSI_DIALOG_CREATE (WM_USER+0x100)
129 #define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
131 static DWORD uiThreadId;
132 static HWND hMsiHiddenWindow;
133 static HMODULE hRichedit;
135 static INT msi_dialog_scale_unit( msi_dialog *dialog, INT val )
137 return (dialog->scale * val + 5) / 10;
140 static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name )
142 msi_control *control;
144 for( control = dialog->control_list; control; control = control->next )
145 if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
150 static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd )
152 msi_control *control;
154 for( control = dialog->control_list; control; control = control->next )
155 if( hwnd == control->hwnd )
161 * msi_dialog_get_style
163 * Extract the {\style} string from the front of the text to display and
164 * update the pointer.
166 static LPWSTR msi_dialog_get_style( LPCWSTR *text )
169 LPCWSTR p = *text, q;
174 q = strchrW( p, '}' );
182 ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
185 memcpy( ret, p, len*sizeof(WCHAR) );
190 static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param )
192 msi_dialog *dialog = param;
199 /* create a font and add it to the list */
200 name = MSI_RecordGetString( rec, 1 );
201 font = HeapAlloc( GetProcessHeap(), 0,
202 sizeof *font + strlenW( name )*sizeof (WCHAR) );
203 strcpyW( font->name, name );
204 font->next = dialog->font_list;
205 dialog->font_list = font;
207 memset( &lf, 0, sizeof lf );
208 face = MSI_RecordGetString( rec, 2 );
209 lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
210 style = MSI_RecordGetInteger( rec, 5 );
211 if( style & msidbTextStyleStyleBitsBold )
212 lf.lfWeight = FW_BOLD;
213 if( style & msidbTextStyleStyleBitsItalic )
215 if( style & msidbTextStyleStyleBitsUnderline )
216 lf.lfUnderline = TRUE;
217 if( style & msidbTextStyleStyleBitsStrike )
218 lf.lfStrikeOut = TRUE;
219 lstrcpynW( lf.lfFaceName, face, LF_FACESIZE );
221 /* adjust the height */
222 hdc = GetDC( dialog->hwnd );
225 lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
226 ReleaseDC( dialog->hwnd, hdc );
229 font->hfont = CreateFontIndirectW( &lf );
231 TRACE("Adding font style %s\n", debugstr_w(font->name) );
233 return ERROR_SUCCESS;
236 static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name )
240 for( font = dialog->font_list; font; font = font->next )
241 if( !strcmpW( font->name, name ) ) /* FIXME: case sensitive? */
247 static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name )
251 font = msi_dialog_find_font( dialog, name );
253 SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
255 ERR("No font entry for %s\n", debugstr_w(name));
256 return ERROR_SUCCESS;
259 static UINT msi_dialog_build_font_list( msi_dialog *dialog )
261 static const WCHAR query[] = {
262 'S','E','L','E','C','T',' ','*',' ',
263 'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
266 MSIQUERY *view = NULL;
268 TRACE("dialog %p\n", dialog );
270 r = MSI_OpenQuery( dialog->package->db, &view, query );
271 if( r != ERROR_SUCCESS )
274 r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog );
275 msiobj_release( &view->hdr );
280 static msi_control *msi_dialog_create_window( msi_dialog *dialog,
281 MSIRECORD *rec, LPCWSTR szCls, LPCWSTR name, LPCWSTR text,
282 DWORD style, HWND parent )
284 DWORD x, y, width, height;
285 LPWSTR font = NULL, title = NULL;
286 msi_control *control;
290 control = HeapAlloc( GetProcessHeap(), 0,
291 sizeof *control + strlenW(name)*sizeof(WCHAR) );
292 strcpyW( control->name, name );
293 control->next = dialog->control_list;
294 dialog->control_list = control;
295 control->handler = NULL;
296 control->property = NULL;
297 control->value = NULL;
300 x = MSI_RecordGetInteger( rec, 4 );
301 y = MSI_RecordGetInteger( rec, 5 );
302 width = MSI_RecordGetInteger( rec, 6 );
303 height = MSI_RecordGetInteger( rec, 7 );
305 x = msi_dialog_scale_unit( dialog, x );
306 y = msi_dialog_scale_unit( dialog, y );
307 width = msi_dialog_scale_unit( dialog, width );
308 height = msi_dialog_scale_unit( dialog, height );
312 font = msi_dialog_get_style( &text );
313 deformat_string( dialog->package, text, &title );
316 control->hwnd = CreateWindowW( szCls, title, style,
317 x, y, width, height, parent, NULL, NULL, NULL );
319 TRACE("Dialog %s control %s hwnd %p\n",
320 debugstr_w(dialog->name), debugstr_w(text), control->hwnd );
322 msi_dialog_set_font( dialog, control->hwnd,
323 font ? font : dialog->default_font );
325 HeapFree( GetProcessHeap(), 0, font );
326 HeapFree( GetProcessHeap(), 0, title );
331 /* called from the Control Event subscription code */
332 void msi_dialog_handle_event( msi_dialog* dialog, LPCWSTR control,
333 LPCWSTR attribute, MSIRECORD *rec )
338 ctrl = msi_dialog_find_control( dialog, control );
341 if( lstrcmpW(attribute, szText) )
343 text = MSI_RecordGetString( rec , 1 );
344 SetWindowTextW( ctrl->hwnd, text );
347 static void msi_dialog_map_events(msi_dialog* dialog, LPCWSTR control)
349 static WCHAR Query[] = {
350 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
351 '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
352 'W','H','E','R','E',' ',
353 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
355 '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0
358 LPCWSTR event, attribute;
360 row = MSI_QueryGetRecord( dialog->package->db, Query, dialog->name, control );
364 event = MSI_RecordGetString( row, 3 );
365 attribute = MSI_RecordGetString( row, 4 );
366 ControlEvent_SubscribeToEvent( dialog->package, event, control, attribute );
367 msiobj_release( &row->hdr );
370 /* everything except radio buttons */
371 static msi_control *msi_dialog_add_control( msi_dialog *dialog,
372 MSIRECORD *rec, LPCWSTR szCls, DWORD style )
377 name = MSI_RecordGetString( rec, 2 );
378 attributes = MSI_RecordGetInteger( rec, 8 );
379 text = MSI_RecordGetString( rec, 10 );
382 if( ~attributes & 2 )
383 style |= WS_DISABLED;
385 msi_dialog_map_events(dialog, name);
387 return msi_dialog_create_window( dialog, rec, szCls, name, text,
388 style, dialog->hwnd );
391 static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec )
393 TRACE("%p %p\n", dialog, rec);
395 msi_dialog_add_control( dialog, rec, szStatic, 0 );
396 return ERROR_SUCCESS;
399 static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec )
401 msi_control *control;
403 TRACE("%p %p\n", dialog, rec);
405 control = msi_dialog_add_control( dialog, rec, szButton, 0 );
406 control->handler = msi_dialog_button_handler;
408 return ERROR_SUCCESS;
411 static LPWSTR msi_get_checkbox_value( msi_dialog *dialog, LPCWSTR prop )
413 const static WCHAR query[] = {
414 'S','E','L','E','C','T',' ','*',' ',
415 'F','R','O','M',' ','`','C','h','e','c','k','B','o','x',' ','`',
416 'W','H','E','R','E',' ',
417 '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
420 MSIRECORD *rec = NULL;
424 /* find if there is a value associated with the checkbox */
425 rec = MSI_QueryGetRecord( dialog->package->db, query, prop );
429 val = MSI_RecordGetString( rec, 2 );
432 deformat_string( dialog->package, val, &ret );
435 HeapFree( GetProcessHeap(), 0, ret );
439 msiobj_release( &rec->hdr );
443 ret = load_dynamic_property(dialog->package, prop, NULL);
446 HeapFree( GetProcessHeap(), 0, ret );
453 static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec )
455 msi_control *control;
458 TRACE("%p %p\n", dialog, rec);
460 control = msi_dialog_add_control( dialog, rec, szButton,
461 BS_CHECKBOX | BS_MULTILINE );
462 control->handler = msi_dialog_checkbox_handler;
463 prop = MSI_RecordGetString( rec, 9 );
466 control->property = strdupW( prop );
467 control->value = msi_get_checkbox_value( dialog, prop );
468 TRACE("control %s value %s\n", debugstr_w(control->property),
469 debugstr_w(control->value));
471 msi_dialog_checkbox_sync_state( dialog, control );
473 return ERROR_SUCCESS;
476 static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec )
478 TRACE("%p %p\n", dialog, rec);
480 msi_dialog_add_control( dialog, rec, szStatic, SS_ETCHEDHORZ | SS_SUNKEN );
481 return ERROR_SUCCESS;
484 struct msi_streamin_info
491 static DWORD CALLBACK
492 msi_richedit_stream_in( DWORD_PTR arg, LPBYTE buffer, LONG count, LONG *pcb )
494 struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
496 if( (count + info->offset) > info->length )
497 count = info->length - info->offset;
498 memcpy( buffer, &info->string[ info->offset ], count );
500 info->offset += count;
502 TRACE("%ld/%ld\n", info->offset, info->length);
507 static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec )
509 const static WCHAR szRichEdit20W[] = {
510 'R','i','c','h','E','d','i','t','2','0','W',0
512 struct msi_streamin_info info;
513 msi_control *control;
518 style = WS_BORDER | ES_MULTILINE | WS_VSCROLL | ES_READONLY | ES_AUTOVSCROLL;
519 control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style );
521 text = MSI_RecordGetString( rec, 10 );
522 info.string = strdupWtoA( text );
524 info.length = lstrlenA( info.string ) + 1;
526 es.dwCookie = (DWORD_PTR) &info;
528 es.pfnCallback = msi_richedit_stream_in;
530 SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es );
532 HeapFree( GetProcessHeap(), 0, info.string );
534 return ERROR_SUCCESS;
537 static UINT msi_load_bitmap( MSIDATABASE *db, LPCWSTR name, IPicture **pic )
539 const static WCHAR query[] = {
540 's','e','l','e','c','t',' ','*',' ',
541 'f','r','o','m',' ','B','i','n','a','r','y',' ',
542 'w','h','e','r','e',' ',
543 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
545 MSIRECORD *rec = NULL;
549 rec = MSI_QueryGetRecord( db, query, name );
551 return ERROR_FUNCTION_FAILED;
553 r = MSI_RecordGetIStream( rec, 2, &stm );
554 msiobj_release( &rec->hdr );
555 if( r != ERROR_SUCCESS )
558 r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) pic );
559 IStream_Release( stm );
561 return ERROR_FUNCTION_FAILED;
563 return ERROR_SUCCESS;
566 static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec )
568 IPicture *pic = NULL;
569 msi_control *control;
570 OLE_HANDLE hBitmap = 0;
574 control = msi_dialog_add_control( dialog, rec, szStatic,
575 SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
576 text = MSI_RecordGetString( rec, 10 );
577 r = msi_load_bitmap( dialog->package->db, text, &pic );
578 if( r == ERROR_SUCCESS )
580 r = IPicture_get_Handle( pic, &hBitmap );
582 SendMessageW( control->hwnd, STM_SETIMAGE, IMAGE_BITMAP, hBitmap );
586 return ERROR_SUCCESS;
589 static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec )
591 static const WCHAR szCombo[] = { 'C','O','M','B','O','B','O','X',0 };
593 msi_dialog_add_control( dialog, rec, szCombo,
594 SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
595 return ERROR_SUCCESS;
598 static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec )
600 msi_control *control;
604 control = msi_dialog_add_control( dialog, rec, szEdit, WS_BORDER );
605 control->handler = msi_dialog_edit_handler;
606 prop = MSI_RecordGetString( rec, 9 );
608 control->property = strdupW( prop );
609 val = load_dynamic_property( dialog->package, control->property, NULL );
610 SetWindowTextW( control->hwnd, val );
611 HeapFree( GetProcessHeap(), 0, val );
612 return ERROR_SUCCESS;
615 /******************** Masked Edit ********************************************/
617 #define MASK_MAX_GROUPS 10
619 struct msi_mask_group
627 struct msi_maskedit_info
635 struct msi_mask_group group[MASK_MAX_GROUPS];
638 static void msi_mask_control_change( struct msi_maskedit_info *info )
643 val = HeapAlloc( GetProcessHeap(), 0, (info->num_chars+1)*sizeof(WCHAR) );
644 for( i=0, n=0; i<info->num_groups; i++ )
646 if( (info->group[i].len + n) > info->num_chars )
648 ERR("can't fit control %d text into template\n",i);
651 r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
652 if( r != info->group[i].len )
657 TRACE("%d/%d controls were good\n", i, info->num_groups);
659 if( i == info->num_groups )
661 TRACE("Set property %s to %s\n",
662 debugstr_w(info->prop), debugstr_w(val) );
663 CharUpperBuffW( val, info->num_chars );
664 MSI_SetPropertyW( info->dialog->package, info->prop, val );
665 msi_dialog_evaluate_control_conditions( info->dialog );
667 HeapFree( GetProcessHeap(), 0, val );
670 static LRESULT WINAPI
671 MSIMaskedEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
673 struct msi_maskedit_info *info;
676 TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam);
678 info = GetPropW(hWnd, szButtonData);
680 r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
685 if (HIWORD(wParam) == EN_CHANGE)
686 msi_mask_control_change( info );
689 HeapFree( GetProcessHeap(), 0, info->prop );
690 HeapFree( GetProcessHeap(), 0, info );
691 RemovePropW( hWnd, szButtonData );
698 /* fish the various bits of the property out and put them in the control */
700 msi_maskedit_set_text( struct msi_maskedit_info *info, LPCWSTR text )
706 for( i = 0; i < info->num_groups; i++ )
708 if( info->group[i].len < lstrlenW( p ) )
710 LPWSTR chunk = strdupW( p );
711 chunk[ info->group[i].len ] = 0;
712 SetWindowTextW( info->group[i].hwnd, chunk );
713 HeapFree( GetProcessHeap(), 0, chunk );
717 SetWindowTextW( info->group[i].hwnd, p );
720 p += info->group[i].len;
724 static struct msi_maskedit_info * msi_dialog_parse_groups( LPCWSTR mask )
726 struct msi_maskedit_info * info = NULL;
727 int i = 0, n = 0, total = 0;
730 TRACE("masked control, template %s\n", debugstr_w(mask));
735 p = strchrW(mask, '<');
739 info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *info );
744 for( i=0; i<MASK_MAX_GROUPS; i++ )
746 /* stop at the end of the string */
747 if( p[0] == 0 || p[0] == '>' )
750 /* count the number of the same identifier */
751 for( n=0; p[n] == p[0]; n++ )
753 info->group[i].ofs = total;
754 info->group[i].type = p[0];
758 total++; /* an extra not part of the group */
760 info->group[i].len = n;
765 TRACE("%d characters in %d groups\n", total, info->num_groups );
766 if( i == MASK_MAX_GROUPS )
767 ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
769 info->num_chars = total;
770 info->num_groups = i;
776 msi_maskedit_create_children( struct msi_maskedit_info *info )
778 DWORD width, height, style, wx, ww;
779 LPCWSTR text, font = NULL;
784 style = WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP;
786 GetClientRect( info->hwnd, &rect );
788 width = rect.right - rect.left;
789 height = rect.bottom - rect.top;
792 font = msi_dialog_get_style( &text );
794 for( i = 0; i < info->num_groups; i++ )
796 wx = (info->group[i].ofs * width) / info->num_chars;
797 ww = (info->group[i].len * width) / info->num_chars;
799 hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height,
800 info->hwnd, NULL, NULL, NULL );
803 ERR("failed to create mask edit sub window\n");
807 SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
809 msi_dialog_set_font( info->dialog, hwnd,
810 font ? font : info->dialog->default_font );
811 info->group[i].hwnd = hwnd;
815 /* office 2003 uses "73931<````=````=````=````=`````>@@@@@" */
816 static UINT msi_dialog_maskedit_control( msi_dialog *dialog, MSIRECORD *rec )
818 const static WCHAR pidt[] = {'P','I','D','T','e','m','p','l','a','t','e',0};
819 LPWSTR mask = NULL, title = NULL, val = NULL;
820 struct msi_maskedit_info *info = NULL;
821 UINT ret = ERROR_SUCCESS;
822 msi_control *control;
825 mask = load_dynamic_property( dialog->package, pidt, NULL );
828 ERR("PIDTemplate is empty\n");
832 info = msi_dialog_parse_groups( mask );
835 ERR("template %s is invalid\n", debugstr_w(mask));
839 info->dialog = dialog;
841 control = msi_dialog_add_control( dialog, rec, szStatic, SS_OWNERDRAW|WS_GROUP );
844 ERR("Failed to create maskedit container\n");
845 ret = ERROR_FUNCTION_FAILED;
849 info->hwnd = control->hwnd;
851 /* subclass the static control */
852 info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
853 (LONG_PTR)MSIMaskedEdit_WndProc );
854 SetPropW( control->hwnd, szButtonData, info );
856 prop = MSI_RecordGetString( rec, 9 );
858 info->prop = strdupW( prop );
860 msi_maskedit_create_children( info );
864 val = load_dynamic_property( dialog->package, prop, NULL );
867 msi_maskedit_set_text( info, val );
868 HeapFree( GetProcessHeap(), 0, val );
873 if( ret != ERROR_SUCCESS )
874 HeapFree( GetProcessHeap(), 0, info );
875 HeapFree( GetProcessHeap(), 0, title );
876 HeapFree( GetProcessHeap(), 0, mask );
880 /******************** Path Edit ********************************************/
882 static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec )
884 FIXME("not implemented properly\n");
885 return msi_dialog_edit_control( dialog, rec );
888 /* radio buttons are a bit different from normal controls */
889 static UINT msi_dialog_create_radiobutton( MSIRECORD *rec, LPVOID param )
891 radio_button_group_descr *group = (radio_button_group_descr *)param;
892 msi_dialog *dialog = group->dialog;
893 msi_control *control;
894 LPCWSTR prop, text, name;
896 DWORD attributes = group->attributes;
898 style = WS_CHILD | BS_AUTORADIOBUTTON | BS_MULTILINE;
899 name = MSI_RecordGetString( rec, 3 );
900 text = MSI_RecordGetString( rec, 8 );
903 if( ~attributes & 2 )
904 style |= WS_DISABLED;
906 control = msi_dialog_create_window( dialog, rec, szButton, name, text,
907 style, group->parent->hwnd );
908 control->handler = msi_dialog_radiogroup_handler;
910 prop = MSI_RecordGetString( rec, 1 );
912 control->property = strdupW( prop );
914 return ERROR_SUCCESS;
917 static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec )
919 static const WCHAR query[] = {
920 'S','E','L','E','C','T',' ','*',' ',
921 'F','R','O','M',' ','R','a','d','i','o','B','u','t','t','o','n',' ',
922 'W','H','E','R','E',' ',
923 '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
926 msi_control *control;
927 MSIQUERY *view = NULL;
928 radio_button_group_descr group;
929 MSIPACKAGE *package = dialog->package;
932 prop = MSI_RecordGetString( rec, 9 );
934 TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
936 /* Create parent group box to hold radio buttons */
937 control = msi_dialog_add_control( dialog, rec, szButton, BS_OWNERDRAW|WS_GROUP );
939 return ERROR_FUNCTION_FAILED;
941 oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
942 (LONG_PTR)MSIRadioGroup_WndProc );
943 SetPropW(control->hwnd, szButtonData, oldproc);
946 control->property = strdupW( prop );
948 /* query the Radio Button table for all control in this group */
949 r = MSI_OpenQuery( package->db, &view, query, prop );
950 if( r != ERROR_SUCCESS )
952 ERR("query failed for dialog %s radio group %s\n",
953 debugstr_w(dialog->name), debugstr_w(prop));
954 return ERROR_INVALID_PARAMETER;
957 group.dialog = dialog;
958 group.parent = control;
959 group.attributes = MSI_RecordGetInteger( rec, 8 );
961 r = MSI_IterateRecords( view, 0, msi_dialog_create_radiobutton, &group );
962 msiobj_release( &view->hdr );
967 struct control_handler msi_dialog_handler[] =
969 { szText, msi_dialog_text_control },
970 { szPushButton, msi_dialog_button_control },
971 { szLine, msi_dialog_line_control },
972 { szBitmap, msi_dialog_bitmap_control },
973 { szCheckBox, msi_dialog_checkbox_control },
974 { szScrollableText, msi_dialog_scrolltext_control },
975 { szComboBox, msi_dialog_combo_control },
976 { szEdit, msi_dialog_edit_control },
977 { szMaskedEdit, msi_dialog_maskedit_control },
978 { szPathEdit, msi_dialog_pathedit_control },
979 { szRadioButtonGroup, msi_dialog_radiogroup_control },
982 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
984 static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
986 msi_dialog *dialog = param;
987 LPCWSTR control_type;
990 /* find and call the function that can create this type of control */
991 control_type = MSI_RecordGetString( rec, 3 );
992 for( i=0; i<NUM_CONTROL_TYPES; i++ )
993 if (!strcmpiW( msi_dialog_handler[i].control_type, control_type ))
995 if( i != NUM_CONTROL_TYPES )
996 msi_dialog_handler[i].func( dialog, rec );
998 ERR("no handler for element type %s\n", debugstr_w(control_type));
1000 return ERROR_SUCCESS;
1003 static UINT msi_dialog_fill_controls( msi_dialog *dialog )
1005 static const WCHAR query[] = {
1006 'S','E','L','E','C','T',' ','*',' ',
1007 'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
1008 'W','H','E','R','E',' ',
1009 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
1011 MSIQUERY *view = NULL;
1012 MSIPACKAGE *package = dialog->package;
1014 TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
1016 /* query the Control table for all the elements of the control */
1017 r = MSI_OpenQuery( package->db, &view, query, dialog->name );
1018 if( r != ERROR_SUCCESS )
1020 ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
1021 return ERROR_INVALID_PARAMETER;
1024 r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog );
1025 msiobj_release( &view->hdr );
1030 static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param )
1032 static const WCHAR szHide[] = { 'H','i','d','e',0 };
1033 static const WCHAR szShow[] = { 'S','h','o','w',0 };
1034 static const WCHAR szDisable[] = { 'D','i','s','a','b','l','e',0 };
1035 static const WCHAR szEnable[] = { 'E','n','a','b','l','e',0 };
1036 msi_dialog *dialog = param;
1037 msi_control *control;
1038 LPCWSTR name, action, condition;
1041 name = MSI_RecordGetString( rec, 2 );
1042 action = MSI_RecordGetString( rec, 3 );
1043 condition = MSI_RecordGetString( rec, 4 );
1044 r = MSI_EvaluateConditionW( dialog->package, condition );
1045 control = msi_dialog_find_control( dialog, name );
1048 TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
1050 /* FIXME: case sensitive? */
1051 if(!strcmpW(action, szHide))
1052 ShowWindow(control->hwnd, SW_HIDE);
1053 else if(!strcmpW(action, szShow))
1054 ShowWindow(control->hwnd, SW_SHOW);
1055 else if(!strcmpW(action, szDisable))
1056 EnableWindow(control->hwnd, FALSE);
1057 else if(!strcmpW(action, szEnable))
1058 EnableWindow(control->hwnd, TRUE);
1060 FIXME("Unhandled action %s\n", debugstr_w(action));
1063 return ERROR_SUCCESS;
1066 static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog )
1068 static const WCHAR query[] = {
1069 'S','E','L','E','C','T',' ','*',' ',
1070 'F','R','O','M',' ',
1071 'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
1072 'W','H','E','R','E',' ',
1073 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
1076 MSIQUERY *view = NULL;
1077 MSIPACKAGE *package = dialog->package;
1079 TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
1081 /* query the Control table for all the elements of the control */
1082 r = MSI_OpenQuery( package->db, &view, query, dialog->name );
1083 if( r != ERROR_SUCCESS )
1085 ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
1086 return ERROR_INVALID_PARAMETER;
1089 r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog );
1090 msiobj_release( &view->hdr );
1095 /* figure out the height of 10 point MS Sans Serif */
1096 static INT msi_dialog_get_sans_serif_height( HWND hwnd )
1098 static const WCHAR szSansSerif[] = {
1099 'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
1104 HFONT hFont, hOldFont;
1107 hdc = GetDC( hwnd );
1110 memset( &lf, 0, sizeof lf );
1111 lf.lfHeight = MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1112 strcpyW( lf.lfFaceName, szSansSerif );
1113 hFont = CreateFontIndirectW(&lf);
1116 hOldFont = SelectObject( hdc, hFont );
1117 r = GetTextMetricsW( hdc, &tm );
1119 height = tm.tmHeight;
1120 SelectObject( hdc, hOldFont );
1121 DeleteObject( hFont );
1123 ReleaseDC( hwnd, hdc );
1128 /* fetch the associated record from the Dialog table */
1129 static MSIRECORD *msi_get_dialog_record( msi_dialog *dialog )
1131 static const WCHAR query[] = {
1132 'S','E','L','E','C','T',' ','*',' ',
1133 'F','R','O','M',' ','D','i','a','l','o','g',' ',
1134 'W','H','E','R','E',' ',
1135 '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
1136 MSIPACKAGE *package = dialog->package;
1137 MSIRECORD *rec = NULL;
1139 TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
1141 rec = MSI_QueryGetRecord( package->db, query, dialog->name );
1143 ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
1148 static void msi_dialog_adjust_dialog_size( msi_dialog *dialog, LPSIZE sz )
1153 /* turn the client size into the window rectangle */
1156 rect.right = msi_dialog_scale_unit( dialog, sz->cx );
1157 rect.bottom = msi_dialog_scale_unit( dialog, sz->cy );
1158 style = GetWindowLongPtrW( dialog->hwnd, GWL_STYLE );
1159 AdjustWindowRect( &rect, style, FALSE );
1160 sz->cx = rect.right - rect.left;
1161 sz->cy = rect.bottom - rect.top;
1164 static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs )
1166 static const WCHAR df[] = {
1167 'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
1168 msi_dialog *dialog = (msi_dialog*) cs->lpCreateParams;
1169 MSIRECORD *rec = NULL;
1171 LPWSTR title = NULL;
1174 TRACE("%p %p\n", dialog, dialog->package);
1176 dialog->hwnd = hwnd;
1177 SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog );
1179 rec = msi_get_dialog_record( dialog );
1182 TRACE("No record found for dialog %s\n", debugstr_w(dialog->name));
1186 dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
1188 size.cx = MSI_RecordGetInteger( rec, 4 );
1189 size.cy = MSI_RecordGetInteger( rec, 5 );
1190 msi_dialog_adjust_dialog_size( dialog, &size );
1192 dialog->attributes = MSI_RecordGetInteger( rec, 6 );
1193 text = MSI_RecordGetString( rec, 7 );
1195 dialog->default_font = load_dynamic_property( dialog->package, df, NULL );
1197 deformat_string( dialog->package, text, &title );
1198 SetWindowTextW( hwnd, title );
1199 SetWindowPos( hwnd, 0, 0, 0, size.cx, size.cy,
1200 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
1202 HeapFree( GetProcessHeap(), 0, title );
1203 msiobj_release( &rec->hdr );
1205 msi_dialog_build_font_list( dialog );
1206 msi_dialog_fill_controls( dialog );
1207 msi_dialog_evaluate_control_conditions( dialog );
1212 static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
1214 LPWSTR event_fmt = NULL, arg_fmt = NULL;
1216 TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
1218 deformat_string( dialog->package, event, &event_fmt );
1219 deformat_string( dialog->package, arg, &arg_fmt );
1221 dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog );
1223 HeapFree( GetProcessHeap(), 0, event_fmt );
1224 HeapFree( GetProcessHeap(), 0, arg_fmt );
1226 return ERROR_SUCCESS;
1229 static UINT msi_dialog_set_property( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
1231 static const WCHAR szNullArg[] = { '{','}',0 };
1232 LPWSTR p, prop, arg_fmt = NULL;
1235 len = strlenW(event);
1236 prop = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR));
1237 strcpyW( prop, &event[1] );
1238 p = strchrW( prop, ']' );
1239 if( p && p[1] == 0 )
1242 if( strcmpW( szNullArg, arg ) )
1243 deformat_string( dialog->package, arg, &arg_fmt );
1244 MSI_SetPropertyW( dialog->package, prop, arg_fmt );
1247 ERR("Badly formatted property string - what happens?\n");
1248 HeapFree( GetProcessHeap(), 0, prop );
1249 return ERROR_SUCCESS;
1252 static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param )
1254 msi_dialog *dialog = param;
1255 LPCWSTR condition, event, arg;
1258 condition = MSI_RecordGetString( rec, 5 );
1259 r = MSI_EvaluateConditionW( dialog->package, condition );
1262 event = MSI_RecordGetString( rec, 3 );
1263 arg = MSI_RecordGetString( rec, 4 );
1264 if( event[0] == '[' )
1265 msi_dialog_set_property( dialog, event, arg );
1267 msi_dialog_send_event( dialog, event, arg );
1270 return ERROR_SUCCESS;
1273 static UINT msi_dialog_button_handler( msi_dialog *dialog,
1274 msi_control *control, WPARAM param )
1276 static const WCHAR query[] = {
1277 'S','E','L','E','C','T',' ','*',' ',
1278 'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
1279 'W','H','E','R','E',' ',
1280 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
1282 '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
1283 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
1285 MSIQUERY *view = NULL;
1288 if( HIWORD(param) != BN_CLICKED )
1289 return ERROR_SUCCESS;
1291 r = MSI_OpenQuery( dialog->package->db, &view, query,
1292 dialog->name, control->name );
1293 if( r != ERROR_SUCCESS )
1295 ERR("query failed\n");
1299 r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
1300 msiobj_release( &view->hdr );
1305 static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog,
1306 msi_control *control )
1308 WCHAR state[2] = { 0 };
1311 MSI_GetPropertyW( dialog->package, control->property, state, &sz );
1312 return state[0] ? 1 : 0;
1315 static void msi_dialog_set_checkbox_state( msi_dialog *dialog,
1316 msi_control *control, UINT state )
1318 static const WCHAR szState[] = { '1', 0 };
1321 /* if uncheck then the property is set to NULL */
1324 MSI_SetPropertyW( dialog->package, control->property, NULL );
1328 /* check for a custom state */
1329 if (control->value && control->value[0])
1330 val = control->value;
1334 MSI_SetPropertyW( dialog->package, control->property, val );
1337 static void msi_dialog_checkbox_sync_state( msi_dialog *dialog,
1338 msi_control *control )
1342 state = msi_dialog_get_checkbox_state( dialog, control );
1343 SendMessageW( control->hwnd, BM_SETCHECK,
1344 state ? BST_CHECKED : BST_UNCHECKED, 0 );
1347 static UINT msi_dialog_checkbox_handler( msi_dialog *dialog,
1348 msi_control *control, WPARAM param )
1352 if( HIWORD(param) != BN_CLICKED )
1353 return ERROR_SUCCESS;
1355 TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name),
1356 debugstr_w(control->property));
1358 state = msi_dialog_get_checkbox_state( dialog, control );
1359 state = state ? 0 : 1;
1360 msi_dialog_set_checkbox_state( dialog, control, state );
1361 msi_dialog_checkbox_sync_state( dialog, control );
1363 return msi_dialog_button_handler( dialog, control, param );
1366 static UINT msi_dialog_edit_handler( msi_dialog *dialog,
1367 msi_control *control, WPARAM param )
1372 if( HIWORD(param) != EN_CHANGE )
1373 return ERROR_SUCCESS;
1375 TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
1376 debugstr_w(control->property));
1379 buf = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
1382 r = GetWindowTextW( control->hwnd, buf, sz );
1386 buf = HeapReAlloc( GetProcessHeap(), 0, buf, sz*sizeof(WCHAR) );
1389 MSI_SetPropertyW( dialog->package, control->property, buf );
1391 HeapFree( GetProcessHeap(), 0, buf );
1393 return ERROR_SUCCESS;
1396 static UINT msi_dialog_radiogroup_handler( msi_dialog *dialog,
1397 msi_control *control, WPARAM param )
1399 if( HIWORD(param) != BN_CLICKED )
1400 return ERROR_SUCCESS;
1402 TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name),
1403 debugstr_w(control->property));
1405 MSI_SetPropertyW( dialog->package, control->property, control->name );
1407 return msi_dialog_button_handler( dialog, control, param );
1410 static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd )
1412 msi_control *control;
1414 TRACE("%p %p %08x\n", dialog, hwnd, param);
1416 control = msi_dialog_find_control_by_hwnd( dialog, hwnd );
1419 if( control->handler )
1421 control->handler( dialog, control, param );
1422 msi_dialog_evaluate_control_conditions( dialog );
1426 ERR("button click from nowhere\n");
1430 static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
1431 WPARAM wParam, LPARAM lParam )
1433 msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
1435 TRACE("0x%04x\n", msg);
1440 return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam );
1443 return msi_dialog_oncommand( dialog, wParam, (HWND)lParam );
1446 dialog->hwnd = NULL;
1449 return DefWindowProcW(hwnd, msg, wParam, lParam);
1452 static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1454 WNDPROC oldproc = (WNDPROC) GetPropW(hWnd, szButtonData);
1456 TRACE("hWnd %p msg %04x wParam 0x%08x lParam 0x%08lx\n", hWnd, msg, wParam, lParam);
1458 if (msg == WM_COMMAND) /* Forward notifications to dialog */
1459 SendMessageW(GetParent(hWnd), msg, wParam, lParam);
1461 return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam);
1464 static LRESULT WINAPI MSIHiddenWindowProc( HWND hwnd, UINT msg,
1465 WPARAM wParam, LPARAM lParam )
1467 msi_dialog *dialog = (msi_dialog*) lParam;
1469 TRACE("%d %p\n", msg, dialog);
1473 case WM_MSI_DIALOG_CREATE:
1474 return msi_dialog_run_message_loop( dialog );
1475 case WM_MSI_DIALOG_DESTROY:
1476 msi_dialog_destroy( dialog );
1479 return DefWindowProcW( hwnd, msg, wParam, lParam );
1482 /* functions that interface to other modules within MSI */
1484 msi_dialog *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
1485 msi_dialog_event_handler event_handler )
1487 MSIRECORD *rec = NULL;
1490 TRACE("%p %s\n", package, debugstr_w(szDialogName));
1492 /* allocate the structure for the dialog to use */
1493 dialog = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
1494 sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) );
1497 strcpyW( dialog->name, szDialogName );
1498 msiobj_addref( &package->hdr );
1499 dialog->package = package;
1500 dialog->event_handler = event_handler;
1501 dialog->finished = 0;
1503 /* verify that the dialog exists */
1504 rec = msi_get_dialog_record( dialog );
1507 HeapFree( GetProcessHeap(), 0, dialog );
1510 dialog->attributes = MSI_RecordGetInteger( rec, 6 );
1511 msiobj_release( &rec->hdr );
1516 static void msi_process_pending_messages(void)
1520 while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) )
1522 TranslateMessage( &msg );
1523 DispatchMessageW( &msg );
1527 void msi_dialog_end_dialog( msi_dialog *dialog )
1529 TRACE("%p\n", dialog);
1530 dialog->finished = 1;
1531 PostMessageW(dialog->hwnd, WM_NULL, 0, 0);
1534 void msi_dialog_check_messages( HANDLE handle )
1538 /* in threads other than the UI thread, block */
1539 if( uiThreadId != GetCurrentThreadId() )
1542 WaitForSingleObject( handle, INFINITE );
1546 /* there's two choices for the UI thread */
1549 msi_process_pending_messages();
1555 * block here until somebody creates a new dialog or
1556 * the handle we're waiting on becomes ready
1558 r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLINPUT );
1559 if( r == WAIT_OBJECT_0 )
1564 UINT msi_dialog_run_message_loop( msi_dialog *dialog )
1568 if( !(dialog->attributes & msidbDialogAttributesVisible) )
1569 return ERROR_SUCCESS;
1571 if( uiThreadId != GetCurrentThreadId() )
1572 return SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_CREATE, 0, (LPARAM) dialog );
1574 /* create the dialog window, don't show it yet */
1575 hwnd = CreateWindowW( szMsiDialogClass, dialog->name, WS_OVERLAPPEDWINDOW,
1576 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1577 NULL, NULL, NULL, dialog );
1580 ERR("Failed to create dialog %s\n", debugstr_w( dialog->name ));
1581 return ERROR_FUNCTION_FAILED;
1584 ShowWindow( hwnd, SW_SHOW );
1585 UpdateWindow( hwnd );
1587 if( dialog->attributes & msidbDialogAttributesModal )
1589 while( !dialog->finished )
1591 MsgWaitForMultipleObjects( 0, NULL, 0, INFINITE, QS_ALLEVENTS );
1592 msi_process_pending_messages();
1596 return ERROR_IO_PENDING;
1598 return ERROR_SUCCESS;
1601 void msi_dialog_do_preview( msi_dialog *dialog )
1604 dialog->attributes |= msidbDialogAttributesVisible;
1605 dialog->attributes &= ~msidbDialogAttributesModal;
1606 msi_dialog_run_message_loop( dialog );
1609 void msi_dialog_destroy( msi_dialog *dialog )
1611 if( uiThreadId != GetCurrentThreadId() )
1613 SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_DESTROY, 0, (LPARAM) dialog );
1618 ShowWindow( dialog->hwnd, SW_HIDE );
1620 /* destroy the list of controls */
1621 while( dialog->control_list )
1623 msi_control *t = dialog->control_list;
1624 dialog->control_list = t->next;
1625 /* leave dialog->hwnd - destroying parent destroys child windows */
1626 HeapFree( GetProcessHeap(), 0, t->property );
1627 HeapFree( GetProcessHeap(), 0, t->value );
1629 IPicture_Release( t->pic );
1630 HeapFree( GetProcessHeap(), 0, t );
1633 /* destroy the list of fonts */
1634 while( dialog->font_list )
1636 msi_font *t = dialog->font_list;
1637 dialog->font_list = t->next;
1638 DeleteObject( t->hfont );
1639 HeapFree( GetProcessHeap(), 0, t );
1641 HeapFree( GetProcessHeap(), 0, dialog->default_font );
1644 DestroyWindow( dialog->hwnd );
1646 msiobj_release( &dialog->package->hdr );
1647 dialog->package = NULL;
1648 HeapFree( GetProcessHeap(), 0, dialog );
1651 BOOL msi_dialog_register_class( void )
1655 ZeroMemory( &cls, sizeof cls );
1656 cls.lpfnWndProc = MSIDialog_WndProc;
1657 cls.hInstance = NULL;
1658 cls.hIcon = LoadIconW(0, (LPWSTR)IDI_APPLICATION);
1659 cls.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
1660 cls.hbrBackground = (HBRUSH)(COLOR_WINDOW);
1661 cls.lpszMenuName = NULL;
1662 cls.lpszClassName = szMsiDialogClass;
1664 if( !RegisterClassW( &cls ) )
1667 cls.lpfnWndProc = MSIHiddenWindowProc;
1668 cls.lpszClassName = szMsiHiddenWindow;
1670 if( !RegisterClassW( &cls ) )
1673 uiThreadId = GetCurrentThreadId();
1675 hMsiHiddenWindow = CreateWindowW( szMsiHiddenWindow, NULL, WS_OVERLAPPED,
1676 0, 0, 100, 100, NULL, NULL, NULL, NULL );
1677 if( !hMsiHiddenWindow )
1680 hRichedit = LoadLibraryA("riched20");
1685 void msi_dialog_unregister_class( void )
1687 DestroyWindow( hMsiHiddenWindow );
1688 UnregisterClassW( szMsiDialogClass, NULL );
1690 FreeLibrary( hRichedit );