471 lines
13 KiB
C
471 lines
13 KiB
C
/*
|
|
* Implementation of the Microsoft Installer (msi.dll)
|
|
*
|
|
* Copyright 2005 Mike McCormack for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winuser.h"
|
|
#include "winnls.h"
|
|
#include "wingdi.h"
|
|
#include "msi.h"
|
|
#include "msipriv.h"
|
|
|
|
#include "wine/debug.h"
|
|
#include "wine/unicode.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
|
|
|
|
|
const WCHAR szMsiDialogClass[] = {
|
|
'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
|
|
};
|
|
|
|
struct tag_dialog_info
|
|
{
|
|
MSIPACKAGE *package;
|
|
msi_dialog_event_handler event_handler;
|
|
INT scale;
|
|
HWND hwnd;
|
|
WCHAR name[1];
|
|
};
|
|
|
|
typedef UINT (*msi_dialog_control_func)( dialog_info *dialog, MSIRECORD *rec );
|
|
struct control_handler
|
|
{
|
|
LPCWSTR control_type;
|
|
msi_dialog_control_func func;
|
|
};
|
|
|
|
static UINT msi_dialog_text_control( dialog_info *dialog, MSIRECORD *rec )
|
|
{
|
|
DWORD x, y, width, height;
|
|
LPCWSTR text;
|
|
const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
|
|
HWND hwnd;
|
|
|
|
TRACE("%p %p\n", dialog, rec);
|
|
|
|
x = MSI_RecordGetInteger( rec, 4 );
|
|
y = MSI_RecordGetInteger( rec, 5 );
|
|
width = MSI_RecordGetInteger( rec, 6 );
|
|
height = MSI_RecordGetInteger( rec, 7 );
|
|
text = MSI_RecordGetString( rec, 10 );
|
|
|
|
x = (dialog->scale * x) / 10;
|
|
y = (dialog->scale * y) / 10;
|
|
width = (dialog->scale * width) / 10;
|
|
height = (dialog->scale * height) / 10;
|
|
|
|
hwnd = CreateWindowW( szStatic, text, WS_CHILD | WS_VISIBLE |WS_GROUP,
|
|
x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
|
|
if (!hwnd)
|
|
ERR("Failed to create hwnd\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT msi_dialog_button_control( dialog_info *dialog, MSIRECORD *rec )
|
|
{
|
|
const static WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
|
|
DWORD x, y, width, height;
|
|
LPCWSTR text;
|
|
HWND hwnd;
|
|
|
|
TRACE("%p %p\n", dialog, rec);
|
|
|
|
x = MSI_RecordGetInteger( rec, 4 );
|
|
y = MSI_RecordGetInteger( rec, 5 );
|
|
width = MSI_RecordGetInteger( rec, 6 );
|
|
height = MSI_RecordGetInteger( rec, 7 );
|
|
text = MSI_RecordGetString( rec, 10 );
|
|
|
|
x = (dialog->scale * x) / 10;
|
|
y = (dialog->scale * y) / 10;
|
|
width = (dialog->scale * width) / 10;
|
|
height = (dialog->scale * height) / 10;
|
|
|
|
hwnd = CreateWindowW( szButton, text, WS_CHILD | WS_VISIBLE |WS_GROUP,
|
|
x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT msi_dialog_line_control( dialog_info *dialog, MSIRECORD *rec )
|
|
{
|
|
DWORD x, y, width, height;
|
|
LPCWSTR text;
|
|
const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
|
|
HWND hwnd;
|
|
|
|
TRACE("%p %p\n", dialog, rec);
|
|
|
|
x = MSI_RecordGetInteger( rec, 4 );
|
|
y = MSI_RecordGetInteger( rec, 5 );
|
|
width = MSI_RecordGetInteger( rec, 6 );
|
|
height = MSI_RecordGetInteger( rec, 7 );
|
|
text = MSI_RecordGetString( rec, 10 );
|
|
|
|
x = (dialog->scale * x) / 10;
|
|
y = (dialog->scale * y) / 10;
|
|
width = (dialog->scale * width) / 10;
|
|
height = (dialog->scale * height) / 10;
|
|
|
|
hwnd = CreateWindowW( szStatic, text, WS_CHILD | WS_VISIBLE |WS_GROUP |
|
|
SS_ETCHEDHORZ |SS_SUNKEN,
|
|
x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
|
|
if (!hwnd)
|
|
ERR("Failed to create hwnd\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
#if 0
|
|
static UINT msi_load_picture( MSIDATABASE *db, LPCWSTR name, HBITMAP *hbm )
|
|
{
|
|
IPicture *pic = NULL;
|
|
IPersistFile *pf = NULL;
|
|
IStream *stm = NULL;
|
|
HRESULT r;
|
|
|
|
r = CoCreateObject( &CLSID_Picture, NULL, CLSCTX_INPROC_SERVER,
|
|
&IID_IPicture, (LPVOID*)&pic );
|
|
if (FAILED(r))
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
r = IPicture_QueryInterface( pic, &IID_IPersistFile, (LPVOID*) &pf );
|
|
if (SUCCEEDED(r) )
|
|
{
|
|
r = IPersistFile_Load( pf, stm );
|
|
IPersistFile_Release( pf );
|
|
}
|
|
|
|
if (SUCCEEDED(r) )
|
|
IPicture_get_Handle( pic, hbm );
|
|
IPicture_Release( pic );
|
|
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
static UINT msi_dialog_bitmap_control( dialog_info *dialog, MSIRECORD *rec )
|
|
{
|
|
DWORD x, y, width, height;
|
|
LPCWSTR text;
|
|
const static WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
|
|
HWND hwnd;
|
|
|
|
TRACE("%p %p\n", dialog, rec);
|
|
|
|
x = MSI_RecordGetInteger( rec, 4 );
|
|
y = MSI_RecordGetInteger( rec, 5 );
|
|
width = MSI_RecordGetInteger( rec, 6 );
|
|
height = MSI_RecordGetInteger( rec, 7 );
|
|
text = MSI_RecordGetString( rec, 10 );
|
|
|
|
x = (dialog->scale * x) / 10;
|
|
y = (dialog->scale * y) / 10;
|
|
width = (dialog->scale * width) / 10;
|
|
height = (dialog->scale * height) / 10;
|
|
|
|
hwnd = CreateWindowW( szStatic, text, WS_CHILD | WS_VISIBLE |WS_GROUP | WS_DISABLED |
|
|
SS_BITMAP | SS_LEFT | SS_CENTERIMAGE,
|
|
x, y, width, height, dialog->hwnd, NULL, NULL, NULL );
|
|
if (!hwnd)
|
|
ERR("Failed to create hwnd\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static const WCHAR szText[] = { 'T','e','x','t',0 };
|
|
static const WCHAR szButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
|
|
static const WCHAR szLine[] = { 'L','i','n','e',0 };
|
|
static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
|
|
|
|
struct control_handler msi_dialog_handler[] =
|
|
{
|
|
{ szText, msi_dialog_text_control },
|
|
{ szButton, msi_dialog_button_control },
|
|
{ szLine, msi_dialog_line_control },
|
|
{ szBitmap, msi_dialog_bitmap_control },
|
|
};
|
|
|
|
#define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
|
|
|
|
typedef UINT (*record_func)( MSIRECORD *rec, LPVOID param );
|
|
|
|
static UINT msi_iterate_records( MSIQUERY *view, record_func func, LPVOID param )
|
|
{
|
|
MSIRECORD *rec = NULL;
|
|
UINT r;
|
|
|
|
r = MSI_ViewExecute( view, NULL );
|
|
if( r != ERROR_SUCCESS )
|
|
return r;
|
|
|
|
/* iterate a query */
|
|
while( 1 )
|
|
{
|
|
r = MSI_ViewFetch( view, &rec );
|
|
if( r != ERROR_SUCCESS )
|
|
break;
|
|
r = func( rec, param );
|
|
msiobj_release( &rec->hdr );
|
|
if( r != ERROR_SUCCESS )
|
|
break;
|
|
}
|
|
|
|
MSI_ViewClose( view );
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
|
|
{
|
|
dialog_info *dialog = param;
|
|
LPCWSTR control_type;
|
|
UINT i;
|
|
|
|
/* find and call the function that can create this type of control */
|
|
control_type = MSI_RecordGetString( rec, 3 );
|
|
for( i=0; i<NUM_CONTROL_TYPES; i++ )
|
|
if (!strcmpiW( msi_dialog_handler[i].control_type, control_type ))
|
|
break;
|
|
if( i != NUM_CONTROL_TYPES )
|
|
msi_dialog_handler[i].func( dialog, rec );
|
|
else
|
|
ERR("no handler for element type %s\n", debugstr_w(control_type));
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static UINT msi_dialog_fill_controls( dialog_info *dialog, LPCWSTR name )
|
|
{
|
|
static const WCHAR query[] = {
|
|
'S','E','L','E','C','T',' ','*',' ',
|
|
'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
|
|
'W','H','E','R','E',' ',
|
|
'`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
|
|
UINT r;
|
|
MSIQUERY *view = NULL;
|
|
MSIPACKAGE *package = dialog->package;
|
|
|
|
TRACE("%p %s\n", dialog, debugstr_w(name) );
|
|
|
|
/* query the Control table for all the elements of the control */
|
|
r = MSI_OpenQuery( package->db, &view, query, name );
|
|
if( r != ERROR_SUCCESS )
|
|
{
|
|
ERR("query failed for dialog %s\n", debugstr_w(name));
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
r = msi_iterate_records( view, msi_dialog_create_controls, dialog );
|
|
msiobj_release( &view->hdr );
|
|
|
|
return r;
|
|
}
|
|
|
|
/* figure out the height of 10 point MS Sans Serif */
|
|
static INT msi_dialog_get_sans_serif_height( HWND hwnd )
|
|
{
|
|
static const WCHAR szSansSerif[] = {
|
|
'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
|
|
LOGFONTW lf;
|
|
TEXTMETRICW tm;
|
|
BOOL r;
|
|
LONG height = 0;
|
|
HFONT hFont, hOldFont;
|
|
HDC hdc;
|
|
|
|
hdc = GetDC( hwnd );
|
|
if (hdc)
|
|
{
|
|
memset( &lf, 0, sizeof lf );
|
|
lf.lfHeight = -MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
|
|
strcpyW( lf.lfFaceName, szSansSerif );
|
|
hFont = CreateFontIndirectW(&lf);
|
|
if (hFont)
|
|
{
|
|
hOldFont = SelectObject( hdc, hFont );
|
|
r = GetTextMetricsW( hdc, &tm );
|
|
if (r)
|
|
height = tm.tmHeight;
|
|
SelectObject( hdc, hOldFont );
|
|
DeleteObject( hFont );
|
|
}
|
|
ReleaseDC( hwnd, hdc );
|
|
}
|
|
return height;
|
|
}
|
|
|
|
static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs )
|
|
{
|
|
static const WCHAR query[] = {
|
|
'S','E','L','E','C','T',' ','*',' ',
|
|
'F','R','O','M',' ','D','i','a','l','o','g',' ',
|
|
'W','H','E','R','E',' ',
|
|
'`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
|
|
dialog_info *dialog = (dialog_info*) cs->lpCreateParams;
|
|
MSIPACKAGE *package = dialog->package;
|
|
MSIQUERY *view = NULL;
|
|
MSIRECORD *rec = NULL;
|
|
DWORD width, height;
|
|
LPCWSTR title;
|
|
UINT r;
|
|
|
|
TRACE("%p %p\n", dialog, package);
|
|
|
|
dialog->hwnd = hwnd;
|
|
SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog );
|
|
|
|
/* fetch the associated record from the Dialog table */
|
|
r = MSI_OpenQuery( package->db, &view, query, dialog->name );
|
|
if( r != ERROR_SUCCESS )
|
|
{
|
|
ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
|
|
return 1;
|
|
}
|
|
MSI_ViewExecute( view, NULL );
|
|
MSI_ViewFetch( view, &rec );
|
|
MSI_ViewClose( view );
|
|
msiobj_release( &view->hdr );
|
|
|
|
if( !rec )
|
|
{
|
|
ERR("No record found for dialog %s\n", debugstr_w(dialog->name));
|
|
return 1;
|
|
}
|
|
|
|
dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
|
|
|
|
width = MSI_RecordGetInteger( rec, 4 );
|
|
height = MSI_RecordGetInteger( rec, 5 );
|
|
title = MSI_RecordGetString( rec, 7 );
|
|
|
|
width = (dialog->scale * width) / 10;
|
|
height = (dialog->scale * height) / 10;
|
|
|
|
SetWindowTextW( hwnd, title );
|
|
SetWindowPos( hwnd, 0, 0, 0, width, height,
|
|
SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
|
|
|
|
msiobj_release( &rec->hdr );
|
|
|
|
msi_dialog_fill_controls( dialog, dialog->name );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT msi_dialog_handle_click( dialog_info *dialog,
|
|
DWORD id, HWND handle )
|
|
{
|
|
TRACE("BN_CLICKED %08lx %p\n", id, handle);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
|
|
WPARAM wParam, LPARAM lParam )
|
|
{
|
|
dialog_info *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_CREATE:
|
|
return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam );
|
|
|
|
case WM_COMMAND:
|
|
if( HIWORD(wParam) == BN_CLICKED )
|
|
return msi_dialog_handle_click( dialog, LOWORD(wParam), (HWND)lParam );
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
dialog->hwnd = NULL;
|
|
return 0;
|
|
}
|
|
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
/* functions that interface to other modules within MSI */
|
|
|
|
dialog_info *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
|
|
msi_dialog_event_handler event_handler )
|
|
{
|
|
dialog_info *dialog;
|
|
HWND hwnd;
|
|
|
|
TRACE("%p %s\n", package, debugstr_w(szDialogName));
|
|
|
|
/* allocate the structure for the dialog to use */
|
|
dialog = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
|
|
sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) );
|
|
if( !dialog )
|
|
return NULL;
|
|
strcpyW( dialog->name, szDialogName );
|
|
dialog->package = package;
|
|
dialog->event_handler = event_handler;
|
|
msiobj_addref( &package->hdr );
|
|
|
|
/* create and show the dialog window */
|
|
hwnd = CreateWindowW( szMsiDialogClass, szDialogName, WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
NULL, NULL, NULL, dialog );
|
|
if( !hwnd )
|
|
{
|
|
msi_dialog_destroy( dialog );
|
|
return NULL;
|
|
}
|
|
ShowWindow( hwnd, SW_SHOW );
|
|
UpdateWindow( hwnd );
|
|
|
|
return dialog;
|
|
}
|
|
|
|
void msi_dialog_destroy( dialog_info *dialog )
|
|
{
|
|
if( dialog->hwnd )
|
|
DestroyWindow( dialog->hwnd );
|
|
msiobj_release( &dialog->package->hdr );
|
|
dialog->package = NULL;
|
|
HeapFree( GetProcessHeap(), 0, dialog );
|
|
}
|
|
|
|
void msi_dialog_register_class( void )
|
|
{
|
|
WNDCLASSW cls;
|
|
|
|
ZeroMemory( &cls, sizeof cls );
|
|
cls.lpfnWndProc = MSIDialog_WndProc;
|
|
cls.hInstance = NULL;
|
|
cls.hIcon = LoadIconW(0, (LPWSTR)IDI_APPLICATION);
|
|
cls.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
|
|
cls.hbrBackground = (HBRUSH)(COLOR_WINDOW);
|
|
cls.lpszMenuName = NULL;
|
|
cls.lpszClassName = szMsiDialogClass;
|
|
|
|
RegisterClassW( &cls );
|
|
}
|
|
|
|
void msi_dialog_unregister_class( void )
|
|
{
|
|
UnregisterClassW( szMsiDialogClass, NULL );
|
|
}
|