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
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
44 const WCHAR szMsiDialogClass
[] = {
45 'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
47 const WCHAR szMsiHiddenWindow
[] = {
48 'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0
50 const static WCHAR szStatic
[] = { 'S','t','a','t','i','c',0 };
51 const static WCHAR szButton
[] = { 'B','U','T','T','O','N', 0 };
53 const static WCHAR szButtonData
[] = { 'M','S','I','D','A','T','A',0 };
55 struct msi_control_tag
;
56 typedef struct msi_control_tag msi_control
;
57 typedef UINT (*msi_handler
)( msi_dialog
*, msi_control
*, WPARAM
);
59 struct msi_control_tag
61 struct msi_control_tag
*next
;
70 typedef struct msi_font_tag
72 struct msi_font_tag
*next
;
80 msi_dialog_event_handler event_handler
;
87 msi_control
*control_list
;
91 typedef UINT (*msi_dialog_control_func
)( msi_dialog
*dialog
, MSIRECORD
*rec
);
92 struct control_handler
95 msi_dialog_control_func func
;
103 } radio_button_group_descr
;
105 static UINT
msi_dialog_checkbox_handler( msi_dialog
*, msi_control
*, WPARAM
);
106 static void msi_dialog_checkbox_sync_state( msi_dialog
*, msi_control
* );
107 static UINT
msi_dialog_button_handler( msi_dialog
*, msi_control
*, WPARAM
);
108 static UINT
msi_dialog_edit_handler( msi_dialog
*, msi_control
*, WPARAM
);
109 static UINT
msi_dialog_radiogroup_handler( msi_dialog
*, msi_control
*, WPARAM param
);
110 static LRESULT WINAPI
MSIRadioGroup_WndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
113 /* dialog sequencing */
115 #define WM_MSI_DIALOG_CREATE (WM_USER+0x100)
116 #define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
118 static DWORD uiThreadId
;
119 static HWND hMsiHiddenWindow
;
121 static INT
msi_dialog_scale_unit( msi_dialog
*dialog
, INT val
)
123 return (dialog
->scale
* val
+ 5) / 10;
127 * msi_dialog_get_style
129 * Extract the {\style} string from the front of the text to display and
130 * update the pointer.
132 static LPWSTR
msi_dialog_get_style( LPCWSTR
*text
)
135 LPCWSTR p
= *text
, q
;
140 q
= strchrW( p
, '}' );
148 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
151 memcpy( ret
, p
, len
*sizeof(WCHAR
) );
156 static UINT
msi_dialog_add_font( MSIRECORD
*rec
, LPVOID param
)
158 msi_dialog
*dialog
= param
;
165 /* create a font and add it to the list */
166 name
= MSI_RecordGetString( rec
, 1 );
167 font
= HeapAlloc( GetProcessHeap(), 0,
168 sizeof *font
+ strlenW( name
)*sizeof (WCHAR
) );
169 strcpyW( font
->name
, name
);
170 font
->next
= dialog
->font_list
;
171 dialog
->font_list
= font
;
173 memset( &lf
, 0, sizeof lf
);
174 face
= MSI_RecordGetString( rec
, 2 );
175 lf
.lfHeight
= MSI_RecordGetInteger( rec
, 3 );
176 style
= MSI_RecordGetInteger( rec
, 5 );
177 if( style
& msidbTextStyleStyleBitsBold
)
178 lf
.lfWeight
= FW_BOLD
;
179 if( style
& msidbTextStyleStyleBitsItalic
)
181 if( style
& msidbTextStyleStyleBitsUnderline
)
182 lf
.lfUnderline
= TRUE
;
183 if( style
& msidbTextStyleStyleBitsStrike
)
184 lf
.lfStrikeOut
= TRUE
;
185 lstrcpynW( lf
.lfFaceName
, face
, LF_FACESIZE
);
187 /* adjust the height */
188 hdc
= GetDC( dialog
->hwnd
);
191 lf
.lfHeight
= -MulDiv(lf
.lfHeight
, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
192 ReleaseDC( dialog
->hwnd
, hdc
);
195 font
->hfont
= CreateFontIndirectW( &lf
);
197 TRACE("Adding font style %s\n", debugstr_w(font
->name
) );
199 return ERROR_SUCCESS
;
202 static msi_font
*msi_dialog_find_font( msi_dialog
*dialog
, LPCWSTR name
)
206 for( font
= dialog
->font_list
; font
; font
= font
->next
)
207 if( !strcmpW( font
->name
, name
) ) /* FIXME: case sensitive? */
213 static UINT
msi_dialog_set_font( msi_dialog
*dialog
, HWND hwnd
, LPCWSTR name
)
217 font
= msi_dialog_find_font( dialog
, name
);
219 SendMessageW( hwnd
, WM_SETFONT
, (WPARAM
) font
->hfont
, TRUE
);
221 ERR("No font entry for %s\n", debugstr_w(name
));
222 return ERROR_SUCCESS
;
225 static UINT
msi_dialog_build_font_list( msi_dialog
*dialog
)
227 static const WCHAR query
[] = {
228 'S','E','L','E','C','T',' ','*',' ',
229 'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
232 MSIQUERY
*view
= NULL
;
234 TRACE("dialog %p\n", dialog
);
236 r
= MSI_OpenQuery( dialog
->package
->db
, &view
, query
);
237 if( r
!= ERROR_SUCCESS
)
240 r
= MSI_IterateRecords( view
, NULL
, msi_dialog_add_font
, dialog
);
241 msiobj_release( &view
->hdr
);
246 static msi_control
*msi_dialog_create_window( msi_dialog
*dialog
,
247 MSIRECORD
*rec
, LPCWSTR szCls
, LPCWSTR name
, LPCWSTR text
,
248 DWORD style
, HWND parent
)
250 DWORD x
, y
, width
, height
;
251 LPWSTR font
= NULL
, title
= NULL
;
252 msi_control
*control
;
254 style
|= WS_CHILD
| WS_GROUP
;
256 control
= HeapAlloc( GetProcessHeap(), 0,
257 sizeof *control
+ strlenW(name
)*sizeof(WCHAR
) );
258 strcpyW( control
->name
, name
);
259 control
->next
= dialog
->control_list
;
260 dialog
->control_list
= control
;
261 control
->handler
= NULL
;
262 control
->property
= NULL
;
263 control
->value
= NULL
;
266 x
= MSI_RecordGetInteger( rec
, 4 );
267 y
= MSI_RecordGetInteger( rec
, 5 );
268 width
= MSI_RecordGetInteger( rec
, 6 );
269 height
= MSI_RecordGetInteger( rec
, 7 );
271 x
= msi_dialog_scale_unit( dialog
, x
);
272 y
= msi_dialog_scale_unit( dialog
, y
);
273 width
= msi_dialog_scale_unit( dialog
, width
);
274 height
= msi_dialog_scale_unit( dialog
, height
);
278 font
= msi_dialog_get_style( &text
);
279 deformat_string( dialog
->package
, text
, &title
);
282 control
->hwnd
= CreateWindowW( szCls
, title
, style
,
283 x
, y
, width
, height
, parent
, NULL
, NULL
, NULL
);
285 TRACE("Dialog %s control %s hwnd %p\n",
286 debugstr_w(dialog
->name
), debugstr_w(text
), control
->hwnd
);
288 msi_dialog_set_font( dialog
, control
->hwnd
,
289 font
? font
: dialog
->default_font
);
291 HeapFree( GetProcessHeap(), 0, font
);
292 HeapFree( GetProcessHeap(), 0, title
);
297 /* everything except radio buttons */
298 static msi_control
*msi_dialog_add_control( msi_dialog
*dialog
,
299 MSIRECORD
*rec
, LPCWSTR szCls
, DWORD style
)
304 name
= MSI_RecordGetString( rec
, 2 );
305 attributes
= MSI_RecordGetInteger( rec
, 8 );
306 text
= MSI_RecordGetString( rec
, 10 );
309 if( ~attributes
& 2 )
310 style
|= WS_DISABLED
;
311 return msi_dialog_create_window( dialog
, rec
, szCls
, name
, text
,
312 style
, dialog
->hwnd
);
315 static UINT
msi_dialog_text_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
317 TRACE("%p %p\n", dialog
, rec
);
319 msi_dialog_add_control( dialog
, rec
, szStatic
, 0 );
320 return ERROR_SUCCESS
;
323 static UINT
msi_dialog_button_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
325 msi_control
*control
;
327 TRACE("%p %p\n", dialog
, rec
);
329 control
= msi_dialog_add_control( dialog
, rec
, szButton
, 0 );
330 control
->handler
= msi_dialog_button_handler
;
332 return ERROR_SUCCESS
;
335 static LPWSTR
msi_get_checkbox_value( msi_dialog
*dialog
, LPCWSTR prop
)
337 const static WCHAR query
[] = {
338 'S','E','L','E','C','T',' ','*',' ',
339 'F','R','O','M',' ','`','C','h','e','c','k','B','o','x',' ','`',
340 'W','H','E','R','E',' ',
341 '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
344 MSIRECORD
*rec
= NULL
;
348 /* find if there is a value associated with the checkbox */
349 rec
= MSI_QueryGetRecord( dialog
->package
->db
, query
, prop
);
353 val
= MSI_RecordGetString( rec
, 2 );
356 deformat_string( dialog
->package
, val
, &ret
);
359 HeapFree( GetProcessHeap(), 0, ret
);
363 msiobj_release( &rec
->hdr
);
367 ret
= load_dynamic_property(dialog
->package
, prop
, NULL
);
370 HeapFree( GetProcessHeap(), 0, ret
);
377 static UINT
msi_dialog_checkbox_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
379 msi_control
*control
;
382 TRACE("%p %p\n", dialog
, rec
);
384 control
= msi_dialog_add_control( dialog
, rec
, szButton
,
385 BS_CHECKBOX
| BS_MULTILINE
);
386 control
->handler
= msi_dialog_checkbox_handler
;
387 prop
= MSI_RecordGetString( rec
, 9 );
390 control
->property
= strdupW( prop
);
391 control
->value
= msi_get_checkbox_value( dialog
, prop
);
392 TRACE("control %s value %s\n", debugstr_w(control
->property
),
393 debugstr_w(control
->value
));
395 msi_dialog_checkbox_sync_state( dialog
, control
);
397 return ERROR_SUCCESS
;
400 static UINT
msi_dialog_line_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
402 TRACE("%p %p\n", dialog
, rec
);
404 msi_dialog_add_control( dialog
, rec
, szStatic
, SS_ETCHEDHORZ
| SS_SUNKEN
);
405 return ERROR_SUCCESS
;
408 static UINT
msi_dialog_scrolltext_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
410 const static WCHAR szEdit
[] = { 'E','D','I','T',0 };
412 FIXME("%p %p\n", dialog
, rec
);
414 msi_dialog_add_control( dialog
, rec
, szEdit
, WS_BORDER
|
415 ES_MULTILINE
| WS_VSCROLL
| ES_READONLY
| ES_AUTOVSCROLL
);
417 return ERROR_SUCCESS
;
420 static UINT
msi_load_bitmap( MSIDATABASE
*db
, LPCWSTR name
, IPicture
**pic
)
422 const static WCHAR query
[] = {
423 's','e','l','e','c','t',' ','*',' ',
424 'f','r','o','m',' ','B','i','n','a','r','y',' ',
425 'w','h','e','r','e',' ',
426 '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
428 MSIQUERY
*view
= NULL
;
429 MSIRECORD
*rec
= NULL
;
433 r
= MSI_OpenQuery( db
, &view
, query
, name
);
434 if( r
!= ERROR_SUCCESS
)
437 MSI_ViewExecute( view
, NULL
);
438 MSI_ViewFetch( view
, &rec
);
439 MSI_ViewClose( view
);
440 msiobj_release( &view
->hdr
);
443 return ERROR_FUNCTION_FAILED
;
445 r
= MSI_RecordGetIStream( rec
, 2, &stm
);
446 msiobj_release( &rec
->hdr
);
447 if( r
!= ERROR_SUCCESS
)
450 r
= OleLoadPicture( stm
, 0, TRUE
, &IID_IPicture
, (LPVOID
*) pic
);
451 IStream_Release( stm
);
453 return ERROR_FUNCTION_FAILED
;
455 return ERROR_SUCCESS
;
458 static UINT
msi_dialog_bitmap_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
460 IPicture
*pic
= NULL
;
461 msi_control
*control
;
462 OLE_HANDLE hBitmap
= 0;
466 control
= msi_dialog_add_control( dialog
, rec
, szStatic
,
467 SS_BITMAP
| SS_LEFT
| SS_CENTERIMAGE
);
468 text
= MSI_RecordGetString( rec
, 10 );
469 r
= msi_load_bitmap( dialog
->package
->db
, text
, &pic
);
470 if( r
== ERROR_SUCCESS
)
472 r
= IPicture_get_Handle( pic
, &hBitmap
);
474 SendMessageW( control
->hwnd
, STM_SETIMAGE
, IMAGE_BITMAP
, hBitmap
);
478 return ERROR_SUCCESS
;
481 static UINT
msi_dialog_combo_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
483 static const WCHAR szCombo
[] = { 'C','O','M','B','O','B','O','X',0 };
485 msi_dialog_add_control( dialog
, rec
, szCombo
,
486 SS_BITMAP
| SS_LEFT
| SS_CENTERIMAGE
);
487 return ERROR_SUCCESS
;
490 static UINT
msi_dialog_edit_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
492 const static WCHAR szEdit
[] = { 'E','D','I','T',0 };
493 msi_control
*control
;
497 control
= msi_dialog_add_control( dialog
, rec
, szEdit
, WS_BORDER
);
498 control
->handler
= msi_dialog_edit_handler
;
499 prop
= MSI_RecordGetString( rec
, 9 );
501 control
->property
= strdupW( prop
);
502 val
= load_dynamic_property( dialog
->package
, control
->property
, NULL
);
503 SetWindowTextW( control
->hwnd
, val
);
504 HeapFree( GetProcessHeap(), 0, val
);
505 return ERROR_SUCCESS
;
508 static UINT
msi_dialog_pathedit_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
510 FIXME("not implemented properly\n");
511 return msi_dialog_edit_control( dialog
, rec
);
514 /* radio buttons are a bit different from normal controls */
515 static UINT
msi_dialog_create_radiobutton( MSIRECORD
*rec
, LPVOID param
)
517 radio_button_group_descr
*group
= (radio_button_group_descr
*)param
;
518 msi_dialog
*dialog
= group
->dialog
;
519 msi_control
*control
;
520 LPCWSTR prop
, text
, name
;
522 DWORD attributes
= group
->attributes
;
524 style
= WS_CHILD
| BS_AUTORADIOBUTTON
| BS_MULTILINE
;
525 name
= MSI_RecordGetString( rec
, 3 );
526 text
= MSI_RecordGetString( rec
, 8 );
529 if( ~attributes
& 2 )
530 style
|= WS_DISABLED
;
532 control
= msi_dialog_create_window( dialog
, rec
, szButton
, name
, text
,
533 style
, group
->parent
->hwnd
);
534 control
->handler
= msi_dialog_radiogroup_handler
;
536 prop
= MSI_RecordGetString( rec
, 1 );
538 control
->property
= strdupW( prop
);
540 return ERROR_SUCCESS
;
543 static UINT
msi_dialog_radiogroup_control( msi_dialog
*dialog
, MSIRECORD
*rec
)
545 static const WCHAR query
[] = {
546 'S','E','L','E','C','T',' ','*',' ',
547 'F','R','O','M',' ','R','a','d','i','o','B','u','t','t','o','n',' ',
548 'W','H','E','R','E',' ',
549 '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
552 msi_control
*control
;
553 MSIQUERY
*view
= NULL
;
554 radio_button_group_descr group
;
555 MSIPACKAGE
*package
= dialog
->package
;
557 prop
= MSI_RecordGetString( rec
, 9 );
559 TRACE("%p %p %s\n", dialog
, rec
, debugstr_w( prop
));
561 /* Create parent group box to hold radio buttons */
562 control
= msi_dialog_add_control( dialog
, rec
, szButton
, BS_OWNERDRAW
);
566 WNDPROC oldproc
= (WNDPROC
) SetWindowLongPtrW(control
->hwnd
, GWLP_WNDPROC
,
567 (LONG_PTR
)MSIRadioGroup_WndProc
);
568 SetPropW(control
->hwnd
, szButtonData
, oldproc
);
572 control
->property
= strdupW( prop
);
574 /* query the Radio Button table for all control in this group */
575 r
= MSI_OpenQuery( package
->db
, &view
, query
, prop
);
576 if( r
!= ERROR_SUCCESS
)
578 ERR("query failed for dialog %s radio group %s\n",
579 debugstr_w(dialog
->name
), debugstr_w(prop
));
580 return ERROR_INVALID_PARAMETER
;
583 group
.dialog
= dialog
;
584 group
.parent
= control
;
585 group
.attributes
= MSI_RecordGetInteger( rec
, 8 );
587 r
= MSI_IterateRecords( view
, 0, msi_dialog_create_radiobutton
, &group
);
588 msiobj_release( &view
->hdr
);
593 static const WCHAR szText
[] = { 'T','e','x','t',0 };
594 static const WCHAR szPushButton
[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
595 static const WCHAR szLine
[] = { 'L','i','n','e',0 };
596 static const WCHAR szBitmap
[] = { 'B','i','t','m','a','p',0 };
597 static const WCHAR szCheckBox
[] = { 'C','h','e','c','k','B','o','x',0 };
598 static const WCHAR szScrollableText
[] = {
599 'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
600 static const WCHAR szComboBox
[] = { 'C','o','m','b','o','B','o','x',0 };
601 static const WCHAR szEdit
[] = { 'E','d','i','t',0 };
602 static const WCHAR szMaskedEdit
[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
603 static const WCHAR szPathEdit
[] = { 'P','a','t','h','E','d','i','t',0 };
604 static const WCHAR szRadioButtonGroup
[] = {
605 'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
607 struct control_handler msi_dialog_handler
[] =
609 { szText
, msi_dialog_text_control
},
610 { szPushButton
, msi_dialog_button_control
},
611 { szLine
, msi_dialog_line_control
},
612 { szBitmap
, msi_dialog_bitmap_control
},
613 { szCheckBox
, msi_dialog_checkbox_control
},
614 { szScrollableText
, msi_dialog_scrolltext_control
},
615 { szComboBox
, msi_dialog_combo_control
},
616 { szEdit
, msi_dialog_edit_control
},
617 { szMaskedEdit
, msi_dialog_edit_control
},
618 { szPathEdit
, msi_dialog_pathedit_control
},
619 { szRadioButtonGroup
, msi_dialog_radiogroup_control
},
622 #define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
624 static UINT
msi_dialog_create_controls( MSIRECORD
*rec
, LPVOID param
)
626 msi_dialog
*dialog
= param
;
627 LPCWSTR control_type
;
630 /* find and call the function that can create this type of control */
631 control_type
= MSI_RecordGetString( rec
, 3 );
632 for( i
=0; i
<NUM_CONTROL_TYPES
; i
++ )
633 if (!strcmpiW( msi_dialog_handler
[i
].control_type
, control_type
))
635 if( i
!= NUM_CONTROL_TYPES
)
636 msi_dialog_handler
[i
].func( dialog
, rec
);
638 ERR("no handler for element type %s\n", debugstr_w(control_type
));
640 return ERROR_SUCCESS
;
643 static UINT
msi_dialog_fill_controls( msi_dialog
*dialog
)
645 static const WCHAR query
[] = {
646 'S','E','L','E','C','T',' ','*',' ',
647 'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
648 'W','H','E','R','E',' ',
649 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
651 MSIQUERY
*view
= NULL
;
652 MSIPACKAGE
*package
= dialog
->package
;
654 TRACE("%p %s\n", dialog
, debugstr_w(dialog
->name
) );
656 /* query the Control table for all the elements of the control */
657 r
= MSI_OpenQuery( package
->db
, &view
, query
, dialog
->name
);
658 if( r
!= ERROR_SUCCESS
)
660 ERR("query failed for dialog %s\n", debugstr_w(dialog
->name
));
661 return ERROR_INVALID_PARAMETER
;
664 r
= MSI_IterateRecords( view
, 0, msi_dialog_create_controls
, dialog
);
665 msiobj_release( &view
->hdr
);
670 static msi_control
*msi_dialog_find_control( msi_dialog
*dialog
, LPCWSTR name
)
672 msi_control
*control
;
674 for( control
= dialog
->control_list
; control
; control
= control
->next
)
675 if( !strcmpW( control
->name
, name
) ) /* FIXME: case sensitive? */
680 static msi_control
*msi_dialog_find_control_by_hwnd( msi_dialog
*dialog
, HWND hwnd
)
682 msi_control
*control
;
684 for( control
= dialog
->control_list
; control
; control
= control
->next
)
685 if( hwnd
== control
->hwnd
)
690 static UINT
msi_dialog_set_control_condition( MSIRECORD
*rec
, LPVOID param
)
692 static const WCHAR szHide
[] = { 'H','i','d','e',0 };
693 static const WCHAR szShow
[] = { 'S','h','o','w',0 };
694 static const WCHAR szDisable
[] = { 'D','i','s','a','b','l','e',0 };
695 static const WCHAR szEnable
[] = { 'E','n','a','b','l','e',0 };
696 msi_dialog
*dialog
= param
;
697 msi_control
*control
;
698 LPCWSTR name
, action
, condition
;
701 name
= MSI_RecordGetString( rec
, 2 );
702 action
= MSI_RecordGetString( rec
, 3 );
703 condition
= MSI_RecordGetString( rec
, 4 );
704 r
= MSI_EvaluateConditionW( dialog
->package
, condition
);
705 control
= msi_dialog_find_control( dialog
, name
);
708 TRACE("%s control %s\n", debugstr_w(action
), debugstr_w(name
));
710 /* FIXME: case sensitive? */
711 if(!strcmpW(action
, szHide
))
712 ShowWindow(control
->hwnd
, SW_HIDE
);
713 else if(!strcmpW(action
, szShow
))
714 ShowWindow(control
->hwnd
, SW_SHOW
);
715 else if(!strcmpW(action
, szDisable
))
716 EnableWindow(control
->hwnd
, FALSE
);
717 else if(!strcmpW(action
, szEnable
))
718 EnableWindow(control
->hwnd
, TRUE
);
720 FIXME("Unhandled action %s\n", debugstr_w(action
));
723 return ERROR_SUCCESS
;
726 static UINT
msi_dialog_evaluate_control_conditions( msi_dialog
*dialog
)
728 static const WCHAR query
[] = {
729 'S','E','L','E','C','T',' ','*',' ',
731 'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
732 'W','H','E','R','E',' ',
733 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
736 MSIQUERY
*view
= NULL
;
737 MSIPACKAGE
*package
= dialog
->package
;
739 TRACE("%p %s\n", dialog
, debugstr_w(dialog
->name
) );
741 /* query the Control table for all the elements of the control */
742 r
= MSI_OpenQuery( package
->db
, &view
, query
, dialog
->name
);
743 if( r
!= ERROR_SUCCESS
)
745 ERR("query failed for dialog %s\n", debugstr_w(dialog
->name
));
746 return ERROR_INVALID_PARAMETER
;
749 r
= MSI_IterateRecords( view
, 0, msi_dialog_set_control_condition
, dialog
);
750 msiobj_release( &view
->hdr
);
755 /* figure out the height of 10 point MS Sans Serif */
756 static INT
msi_dialog_get_sans_serif_height( HWND hwnd
)
758 static const WCHAR szSansSerif
[] = {
759 'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
764 HFONT hFont
, hOldFont
;
770 memset( &lf
, 0, sizeof lf
);
771 lf
.lfHeight
= MulDiv(10, GetDeviceCaps(hdc
, LOGPIXELSY
), 72);
772 strcpyW( lf
.lfFaceName
, szSansSerif
);
773 hFont
= CreateFontIndirectW(&lf
);
776 hOldFont
= SelectObject( hdc
, hFont
);
777 r
= GetTextMetricsW( hdc
, &tm
);
779 height
= tm
.tmHeight
;
780 SelectObject( hdc
, hOldFont
);
781 DeleteObject( hFont
);
783 ReleaseDC( hwnd
, hdc
);
788 /* fetch the associated record from the Dialog table */
789 static MSIRECORD
*msi_get_dialog_record( msi_dialog
*dialog
)
791 static const WCHAR query
[] = {
792 'S','E','L','E','C','T',' ','*',' ',
793 'F','R','O','M',' ','D','i','a','l','o','g',' ',
794 'W','H','E','R','E',' ',
795 '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
796 MSIPACKAGE
*package
= dialog
->package
;
797 MSIRECORD
*rec
= NULL
;
799 TRACE("%p %s\n", dialog
, debugstr_w(dialog
->name
) );
801 rec
= MSI_QueryGetRecord( package
->db
, query
, dialog
->name
);
803 ERR("query failed for dialog %s\n", debugstr_w(dialog
->name
));
808 static LRESULT
msi_dialog_oncreate( HWND hwnd
, LPCREATESTRUCTW cs
)
810 static const WCHAR df
[] = {
811 'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
812 msi_dialog
*dialog
= (msi_dialog
*) cs
->lpCreateParams
;
813 MSIRECORD
*rec
= NULL
;
818 TRACE("%p %p\n", dialog
, dialog
->package
);
821 SetWindowLongPtrW( hwnd
, GWLP_USERDATA
, (LONG_PTR
) dialog
);
823 rec
= msi_get_dialog_record( dialog
);
826 TRACE("No record found for dialog %s\n", debugstr_w(dialog
->name
));
830 dialog
->scale
= msi_dialog_get_sans_serif_height(dialog
->hwnd
);
832 width
= MSI_RecordGetInteger( rec
, 4 );
833 height
= MSI_RecordGetInteger( rec
, 5 );
834 dialog
->attributes
= MSI_RecordGetInteger( rec
, 6 );
835 text
= MSI_RecordGetString( rec
, 7 );
837 width
= msi_dialog_scale_unit( dialog
, width
);
838 height
= msi_dialog_scale_unit( dialog
, height
) + 25; /* FIXME */
840 dialog
->default_font
= load_dynamic_property( dialog
->package
, df
, NULL
);
842 deformat_string( dialog
->package
, text
, &title
);
843 SetWindowTextW( hwnd
, title
);
844 SetWindowPos( hwnd
, 0, 0, 0, width
, height
,
845 SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_NOREDRAW
);
847 HeapFree( GetProcessHeap(), 0, title
);
848 msiobj_release( &rec
->hdr
);
850 msi_dialog_build_font_list( dialog
);
851 msi_dialog_fill_controls( dialog
);
852 msi_dialog_evaluate_control_conditions( dialog
);
857 static UINT
msi_dialog_send_event( msi_dialog
*dialog
, LPCWSTR event
, LPCWSTR arg
)
859 LPWSTR event_fmt
= NULL
, arg_fmt
= NULL
;
861 TRACE("Sending control event %s %s\n", debugstr_w(event
), debugstr_w(arg
));
863 deformat_string( dialog
->package
, event
, &event_fmt
);
864 deformat_string( dialog
->package
, arg
, &arg_fmt
);
866 dialog
->event_handler( dialog
->package
, event_fmt
, arg_fmt
, dialog
);
868 HeapFree( GetProcessHeap(), 0, event_fmt
);
869 HeapFree( GetProcessHeap(), 0, arg_fmt
);
871 return ERROR_SUCCESS
;
874 static UINT
msi_dialog_set_property( msi_dialog
*dialog
, LPCWSTR event
, LPCWSTR arg
)
876 static const WCHAR szNullArg
[] = { '{','}',0 };
877 LPWSTR p
, prop
, arg_fmt
= NULL
;
880 len
= strlenW(event
);
881 prop
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
));
882 strcpyW( prop
, &event
[1] );
883 p
= strchrW( prop
, ']' );
887 if( strcmpW( szNullArg
, arg
) )
888 deformat_string( dialog
->package
, arg
, &arg_fmt
);
889 MSI_SetPropertyW( dialog
->package
, prop
, arg_fmt
);
892 ERR("Badly formatted property string - what happens?\n");
893 HeapFree( GetProcessHeap(), 0, prop
);
894 return ERROR_SUCCESS
;
897 static UINT
msi_dialog_control_event( MSIRECORD
*rec
, LPVOID param
)
899 msi_dialog
*dialog
= param
;
900 LPCWSTR condition
, event
, arg
;
903 condition
= MSI_RecordGetString( rec
, 5 );
904 r
= MSI_EvaluateConditionW( dialog
->package
, condition
);
907 event
= MSI_RecordGetString( rec
, 3 );
908 arg
= MSI_RecordGetString( rec
, 4 );
909 if( event
[0] == '[' )
910 msi_dialog_set_property( dialog
, event
, arg
);
912 msi_dialog_send_event( dialog
, event
, arg
);
915 return ERROR_SUCCESS
;
918 static UINT
msi_dialog_button_handler( msi_dialog
*dialog
,
919 msi_control
*control
, WPARAM param
)
921 static const WCHAR query
[] = {
922 'S','E','L','E','C','T',' ','*',' ',
923 'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
924 'W','H','E','R','E',' ',
925 '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
927 '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
928 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
930 MSIQUERY
*view
= NULL
;
933 if( HIWORD(param
) != BN_CLICKED
)
934 return ERROR_SUCCESS
;
936 r
= MSI_OpenQuery( dialog
->package
->db
, &view
, query
,
937 dialog
->name
, control
->name
);
938 if( r
!= ERROR_SUCCESS
)
940 ERR("query failed\n");
944 r
= MSI_IterateRecords( view
, 0, msi_dialog_control_event
, dialog
);
945 msiobj_release( &view
->hdr
);
950 static UINT
msi_dialog_get_checkbox_state( msi_dialog
*dialog
,
951 msi_control
*control
)
953 WCHAR state
[2] = { 0 };
956 MSI_GetPropertyW( dialog
->package
, control
->property
, state
, &sz
);
957 return state
[0] ? 1 : 0;
960 static void msi_dialog_set_checkbox_state( msi_dialog
*dialog
,
961 msi_control
*control
, UINT state
)
963 static const WCHAR szState
[] = { '1', 0 };
966 /* if uncheck then the property is set to NULL */
969 MSI_SetPropertyW( dialog
->package
, control
->property
, NULL
);
973 /* check for a custom state */
974 if (control
->value
&& control
->value
[0])
975 val
= control
->value
;
979 MSI_SetPropertyW( dialog
->package
, control
->property
, val
);
982 static void msi_dialog_checkbox_sync_state( msi_dialog
*dialog
,
983 msi_control
*control
)
987 state
= msi_dialog_get_checkbox_state( dialog
, control
);
988 SendMessageW( control
->hwnd
, BM_SETCHECK
,
989 state
? BST_CHECKED
: BST_UNCHECKED
, 0 );
992 static UINT
msi_dialog_checkbox_handler( msi_dialog
*dialog
,
993 msi_control
*control
, WPARAM param
)
997 if( HIWORD(param
) != BN_CLICKED
)
998 return ERROR_SUCCESS
;
1000 TRACE("clicked checkbox %s, set %s\n", debugstr_w(control
->name
),
1001 debugstr_w(control
->property
));
1003 state
= msi_dialog_get_checkbox_state( dialog
, control
);
1004 state
= state
? 0 : 1;
1005 msi_dialog_set_checkbox_state( dialog
, control
, state
);
1006 msi_dialog_checkbox_sync_state( dialog
, control
);
1008 return msi_dialog_button_handler( dialog
, control
, param
);
1011 static UINT
msi_dialog_edit_handler( msi_dialog
*dialog
,
1012 msi_control
*control
, WPARAM param
)
1017 if( HIWORD(param
) != EN_CHANGE
)
1018 return ERROR_SUCCESS
;
1020 TRACE("edit %s contents changed, set %s\n", debugstr_w(control
->name
),
1021 debugstr_w(control
->property
));
1024 buf
= HeapAlloc( GetProcessHeap(), 0, sz
*sizeof(WCHAR
) );
1027 r
= GetWindowTextW( control
->hwnd
, buf
, sz
);
1031 buf
= HeapReAlloc( GetProcessHeap(), 0, buf
, sz
*sizeof(WCHAR
) );
1034 MSI_SetPropertyW( dialog
->package
, control
->property
, buf
);
1036 HeapFree( GetProcessHeap(), 0, buf
);
1038 return ERROR_SUCCESS
;
1041 static UINT
msi_dialog_radiogroup_handler( msi_dialog
*dialog
,
1042 msi_control
*control
, WPARAM param
)
1044 if( HIWORD(param
) != BN_CLICKED
)
1045 return ERROR_SUCCESS
;
1047 TRACE("clicked radio button %s, set %s\n", debugstr_w(control
->name
),
1048 debugstr_w(control
->property
));
1050 MSI_SetPropertyW( dialog
->package
, control
->property
, control
->name
);
1052 return msi_dialog_button_handler( dialog
, control
, param
);
1055 static LRESULT
msi_dialog_oncommand( msi_dialog
*dialog
, WPARAM param
, HWND hwnd
)
1057 msi_control
*control
;
1059 TRACE("%p %p %08x\n", dialog
, hwnd
, param
);
1061 control
= msi_dialog_find_control_by_hwnd( dialog
, hwnd
);
1064 if( control
->handler
)
1066 control
->handler( dialog
, control
, param
);
1067 msi_dialog_evaluate_control_conditions( dialog
);
1071 ERR("button click from nowhere\n");
1075 static LRESULT WINAPI
MSIDialog_WndProc( HWND hwnd
, UINT msg
,
1076 WPARAM wParam
, LPARAM lParam
)
1078 msi_dialog
*dialog
= (LPVOID
) GetWindowLongPtrW( hwnd
, GWLP_USERDATA
);
1080 TRACE("0x%04x\n", msg
);
1085 return msi_dialog_oncreate( hwnd
, (LPCREATESTRUCTW
)lParam
);
1088 return msi_dialog_oncommand( dialog
, wParam
, (HWND
)lParam
);
1091 dialog
->hwnd
= NULL
;
1094 return DefWindowProcW(hwnd
, msg
, wParam
, lParam
);
1097 static LRESULT WINAPI
MSIRadioGroup_WndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1099 WNDPROC oldproc
= (WNDPROC
) GetPropW(hWnd
, szButtonData
);
1101 TRACE("hWnd %p msg %04x wParam 0x%08x lParam 0x%08lx\n", hWnd
, msg
, wParam
, lParam
);
1103 if (msg
== WM_COMMAND
) /* Forward notifications to dialog */
1104 SendMessageW(GetParent(hWnd
), msg
, wParam
, lParam
);
1106 return CallWindowProcW(oldproc
, hWnd
, msg
, wParam
, lParam
);
1109 static LRESULT WINAPI
MSIHiddenWindowProc( HWND hwnd
, UINT msg
,
1110 WPARAM wParam
, LPARAM lParam
)
1112 msi_dialog
*dialog
= (msi_dialog
*) lParam
;
1114 TRACE("%d %p\n", msg
, dialog
);
1118 case WM_MSI_DIALOG_CREATE
:
1119 return msi_dialog_run_message_loop( dialog
);
1120 case WM_MSI_DIALOG_DESTROY
:
1121 msi_dialog_destroy( dialog
);
1124 return DefWindowProcW( hwnd
, msg
, wParam
, lParam
);
1127 /* functions that interface to other modules within MSI */
1129 msi_dialog
*msi_dialog_create( MSIPACKAGE
* package
, LPCWSTR szDialogName
,
1130 msi_dialog_event_handler event_handler
)
1132 MSIRECORD
*rec
= NULL
;
1135 TRACE("%p %s\n", package
, debugstr_w(szDialogName
));
1137 /* allocate the structure for the dialog to use */
1138 dialog
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
,
1139 sizeof *dialog
+ sizeof(WCHAR
)*strlenW(szDialogName
) );
1142 strcpyW( dialog
->name
, szDialogName
);
1143 msiobj_addref( &package
->hdr
);
1144 dialog
->package
= package
;
1145 dialog
->event_handler
= event_handler
;
1146 dialog
->finished
= 0;
1148 /* verify that the dialog exists */
1149 rec
= msi_get_dialog_record( dialog
);
1152 HeapFree( GetProcessHeap(), 0, dialog
);
1155 dialog
->attributes
= MSI_RecordGetInteger( rec
, 6 );
1156 msiobj_release( &rec
->hdr
);
1161 static void msi_process_pending_messages(void)
1165 while( PeekMessageW( &msg
, 0, 0, 0, PM_REMOVE
) )
1167 TranslateMessage( &msg
);
1168 DispatchMessageW( &msg
);
1172 void msi_dialog_end_dialog( msi_dialog
*dialog
)
1174 TRACE("%p\n", dialog
);
1175 dialog
->finished
= 1;
1176 PostMessageW(dialog
->hwnd
, WM_NULL
, 0, 0);
1179 void msi_dialog_check_messages( HANDLE handle
)
1183 /* in threads other than the UI thread, block */
1184 if( uiThreadId
!= GetCurrentThreadId() )
1187 WaitForSingleObject( handle
, INFINITE
);
1191 /* there's two choices for the UI thread */
1194 msi_process_pending_messages();
1200 * block here until somebody creates a new dialog or
1201 * the handle we're waiting on becomes ready
1203 r
= MsgWaitForMultipleObjects( 1, &handle
, 0, INFINITE
, QS_ALLINPUT
);
1204 if( r
== WAIT_OBJECT_0
)
1209 UINT
msi_dialog_run_message_loop( msi_dialog
*dialog
)
1213 if( !(dialog
->attributes
& msidbDialogAttributesVisible
) )
1214 return ERROR_SUCCESS
;
1216 if( uiThreadId
!= GetCurrentThreadId() )
1217 return SendMessageW( hMsiHiddenWindow
, WM_MSI_DIALOG_CREATE
, 0, (LPARAM
) dialog
);
1219 /* create the dialog window, don't show it yet */
1220 hwnd
= CreateWindowW( szMsiDialogClass
, dialog
->name
, WS_OVERLAPPEDWINDOW
,
1221 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
1222 NULL
, NULL
, NULL
, dialog
);
1225 ERR("Failed to create dialog %s\n", debugstr_w( dialog
->name
));
1226 return ERROR_FUNCTION_FAILED
;
1229 ShowWindow( hwnd
, SW_SHOW
);
1230 UpdateWindow( hwnd
);
1232 if( dialog
->attributes
& msidbDialogAttributesModal
)
1234 while( !dialog
->finished
)
1236 MsgWaitForMultipleObjects( 0, NULL
, 0, INFINITE
, QS_ALLEVENTS
);
1237 msi_process_pending_messages();
1241 return ERROR_IO_PENDING
;
1243 return ERROR_SUCCESS
;
1246 void msi_dialog_do_preview( msi_dialog
*dialog
)
1249 dialog
->attributes
|= msidbDialogAttributesVisible
;
1250 dialog
->attributes
&= ~msidbDialogAttributesModal
;
1251 msi_dialog_run_message_loop( dialog
);
1254 void msi_dialog_destroy( msi_dialog
*dialog
)
1256 if( uiThreadId
!= GetCurrentThreadId() )
1258 SendMessageW( hMsiHiddenWindow
, WM_MSI_DIALOG_DESTROY
, 0, (LPARAM
) dialog
);
1263 ShowWindow( dialog
->hwnd
, SW_HIDE
);
1265 /* destroy the list of controls */
1266 while( dialog
->control_list
)
1268 msi_control
*t
= dialog
->control_list
;
1269 dialog
->control_list
= t
->next
;
1270 /* leave dialog->hwnd - destroying parent destroys child windows */
1271 HeapFree( GetProcessHeap(), 0, t
->property
);
1272 HeapFree( GetProcessHeap(), 0, t
->value
);
1274 IPicture_Release( t
->pic
);
1275 HeapFree( GetProcessHeap(), 0, t
);
1278 /* destroy the list of fonts */
1279 while( dialog
->font_list
)
1281 msi_font
*t
= dialog
->font_list
;
1282 dialog
->font_list
= t
->next
;
1283 DeleteObject( t
->hfont
);
1284 HeapFree( GetProcessHeap(), 0, t
);
1286 HeapFree( GetProcessHeap(), 0, dialog
->default_font
);
1289 DestroyWindow( dialog
->hwnd
);
1291 msiobj_release( &dialog
->package
->hdr
);
1292 dialog
->package
= NULL
;
1293 HeapFree( GetProcessHeap(), 0, dialog
);
1296 BOOL
msi_dialog_register_class( void )
1300 ZeroMemory( &cls
, sizeof cls
);
1301 cls
.lpfnWndProc
= MSIDialog_WndProc
;
1302 cls
.hInstance
= NULL
;
1303 cls
.hIcon
= LoadIconW(0, (LPWSTR
)IDI_APPLICATION
);
1304 cls
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
1305 cls
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
);
1306 cls
.lpszMenuName
= NULL
;
1307 cls
.lpszClassName
= szMsiDialogClass
;
1309 if( !RegisterClassW( &cls
) )
1312 cls
.lpfnWndProc
= MSIHiddenWindowProc
;
1313 cls
.lpszClassName
= szMsiHiddenWindow
;
1315 if( !RegisterClassW( &cls
) )
1318 uiThreadId
= GetCurrentThreadId();
1320 hMsiHiddenWindow
= CreateWindowW( szMsiHiddenWindow
, NULL
, WS_OVERLAPPED
,
1321 0, 0, 100, 100, NULL
, NULL
, NULL
, NULL
);
1322 if( !hMsiHiddenWindow
)
1328 void msi_dialog_unregister_class( void )
1330 DestroyWindow( hMsiHiddenWindow
);
1331 UnregisterClassW( szMsiDialogClass
, NULL
);