/* * Implementation of the Microsoft Installer (msi.dll) * * Copyright 2005 Aric Stewart 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 */ /* actions handled in this module * RegisterClassInfo * RegisterProgIdInfo * RegisterExtensionInfo * RegisterMIMEInfo * UnRegisterClassInfo (TODO) * UnRegisterProgIdInfo (TODO) * UnRegisterExtensionInfo (TODO) * UnRegisterMIMEInfo (TODO) */ #include #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winreg.h" #include "wine/debug.h" #include "msipriv.h" #include "winuser.h" #include "wine/unicode.h" #include "action.h" WINE_DEFAULT_DEBUG_CHANNEL(msi); extern const WCHAR szRegisterClassInfo[]; extern const WCHAR szRegisterProgIdInfo[]; extern const WCHAR szRegisterExtensionInfo[]; extern const WCHAR szRegisterMIMEInfo[]; extern const WCHAR szUnregisterClassInfo[]; extern const WCHAR szUnregisterExtensionInfo[]; extern const WCHAR szUnregisterMIMEInfo[]; extern const WCHAR szUnregisterProgIdInfo[]; static MSIAPPID *load_appid( MSIPACKAGE* package, MSIRECORD *row ) { LPCWSTR buffer; MSIAPPID *appid; /* fill in the data */ appid = msi_alloc_zero( sizeof(MSIAPPID) ); if (!appid) return NULL; appid->AppID = msi_dup_record_field( row, 1 ); TRACE("loading appid %s\n", debugstr_w( appid->AppID )); buffer = MSI_RecordGetString(row,2); deformat_string( package, buffer, &appid->RemoteServerName ); appid->LocalServer = msi_dup_record_field(row,3); appid->ServiceParameters = msi_dup_record_field(row,4); appid->DllSurrogate = msi_dup_record_field(row,5); appid->ActivateAtStorage = !MSI_RecordIsNull(row,6); appid->RunAsInteractiveUser = !MSI_RecordIsNull(row,7); list_add_tail( &package->appids, &appid->entry ); return appid; } static MSIAPPID *load_given_appid( MSIPACKAGE *package, LPCWSTR name ) { MSIRECORD *row; MSIAPPID *appid; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','A','p','p','I','d','`',' ','W','H','E','R','E',' ', '`','A','p','p','I','d','`',' ','=',' ','\'','%','s','\'',0}; if (!name) return NULL; /* check for appids already loaded */ LIST_FOR_EACH_ENTRY( appid, &package->appids, MSIAPPID, entry ) { if (lstrcmpiW( appid->AppID, name )==0) { TRACE("found appid %s %p\n", debugstr_w(name), appid); return appid; } } row = MSI_QueryGetRecord(package->db, ExecSeqQuery, name); if (!row) return NULL; appid = load_appid(package, row); msiobj_release(&row->hdr); return appid; } static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR progid); static MSICLASS *load_given_class( MSIPACKAGE *package, LPCWSTR classid ); static MSIPROGID *load_progid( MSIPACKAGE* package, MSIRECORD *row ) { MSIPROGID *progid; LPCWSTR buffer; /* fill in the data */ progid = msi_alloc_zero( sizeof(MSIPROGID) ); if (!progid) return NULL; list_add_tail( &package->progids, &progid->entry ); progid->ProgID = msi_dup_record_field(row,1); TRACE("loading progid %s\n",debugstr_w(progid->ProgID)); buffer = MSI_RecordGetString(row,2); progid->Parent = load_given_progid(package,buffer); if (progid->Parent == NULL && buffer) FIXME("Unknown parent ProgID %s\n",debugstr_w(buffer)); buffer = MSI_RecordGetString(row,3); progid->Class = load_given_class(package,buffer); if (progid->Class == NULL && buffer) FIXME("Unknown class %s\n",debugstr_w(buffer)); progid->Description = msi_dup_record_field(row,4); if (!MSI_RecordIsNull(row,6)) { INT icon_index = MSI_RecordGetInteger(row,6); LPCWSTR FileName = MSI_RecordGetString(row,5); LPWSTR FilePath; static const WCHAR fmt[] = {'%','s',',','%','i',0}; FilePath = build_icon_path(package,FileName); progid->IconPath = msi_alloc( (strlenW(FilePath)+10)* sizeof(WCHAR) ); sprintfW(progid->IconPath,fmt,FilePath,icon_index); msi_free(FilePath); } else { buffer = MSI_RecordGetString(row,5); if (buffer) progid->IconPath = build_icon_path(package,buffer); } progid->CurVer = NULL; progid->VersionInd = NULL; /* if we have a parent then we may be that parents CurVer */ if (progid->Parent && progid->Parent != progid) { MSIPROGID *parent = progid->Parent; while (parent->Parent && parent->Parent != parent) parent = parent->Parent; FIXME("need to determing if we are really the CurVer\n"); progid->CurVer = parent; parent->VersionInd = progid; } return progid; } static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR name) { MSIPROGID *progid; MSIRECORD *row; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','P','r','o','g','I','d','`',' ','W','H','E','R','E',' ', '`','P','r','o','g','I','d','`',' ','=',' ','\'','%','s','\'',0}; if (!name) return NULL; /* check for progids already loaded */ LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) { if (strcmpiW( progid->ProgID,name )==0) { TRACE("found progid %s (%p)\n",debugstr_w(name), progid ); return progid; } } row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name ); if (!row) return NULL; progid = load_progid(package, row); msiobj_release(&row->hdr); return progid; } static MSICLASS *load_class( MSIPACKAGE* package, MSIRECORD *row ) { MSICLASS *cls; DWORD i; LPCWSTR buffer; /* fill in the data */ cls = msi_alloc_zero( sizeof(MSICLASS) ); if (!cls) return NULL; list_add_tail( &package->classes, &cls->entry ); cls->clsid = msi_dup_record_field( row, 1 ); TRACE("loading class %s\n",debugstr_w(cls->clsid)); cls->Context = msi_dup_record_field( row, 2 ); buffer = MSI_RecordGetString(row,3); cls->Component = get_loaded_component(package, buffer); cls->ProgIDText = msi_dup_record_field(row,4); cls->ProgID = load_given_progid(package, cls->ProgIDText); cls->Description = msi_dup_record_field(row,5); buffer = MSI_RecordGetString(row,6); if (buffer) cls->AppID = load_given_appid(package, buffer); cls->FileTypeMask = msi_dup_record_field(row,7); if (!MSI_RecordIsNull(row,9)) { INT icon_index = MSI_RecordGetInteger(row,9); LPCWSTR FileName = MSI_RecordGetString(row,8); LPWSTR FilePath; static const WCHAR fmt[] = {'%','s',',','%','i',0}; FilePath = build_icon_path(package,FileName); cls->IconPath = msi_alloc( (strlenW(FilePath)+5)* sizeof(WCHAR) ); sprintfW(cls->IconPath,fmt,FilePath,icon_index); msi_free(FilePath); } else { buffer = MSI_RecordGetString(row,8); if (buffer) cls->IconPath = build_icon_path(package,buffer); } if (!MSI_RecordIsNull(row,10)) { i = MSI_RecordGetInteger(row,10); if (i != MSI_NULL_INTEGER && i > 0 && i < 4) { static const WCHAR ole2[] = {'o','l','e','2','.','d','l','l',0}; static const WCHAR ole32[] = {'o','l','e','3','2','.','d','l','l',0}; switch(i) { case 1: cls->DefInprocHandler = strdupW(ole2); break; case 2: cls->DefInprocHandler32 = strdupW(ole32); break; case 3: cls->DefInprocHandler = strdupW(ole2); cls->DefInprocHandler32 = strdupW(ole32); break; } } else { cls->DefInprocHandler32 = msi_dup_record_field( row, 10); reduce_to_longfilename(cls->DefInprocHandler32); } } buffer = MSI_RecordGetString(row,11); deformat_string(package,buffer,&cls->Argument); buffer = MSI_RecordGetString(row,12); cls->Feature = get_loaded_feature(package,buffer); cls->Attributes = MSI_RecordGetInteger(row,13); return cls; } /* * the Class table has 3 primary keys. Generally it is only * referenced through the first CLSID key. However when loading * all of the classes we need to make sure we do not ignore rows * with other Context and ComponentIndexs */ static MSICLASS *load_given_class(MSIPACKAGE *package, LPCWSTR classid) { MSICLASS *cls; MSIRECORD *row; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','C','l','a','s','s','`',' ','W','H','E','R','E',' ', '`','C','L','S','I','D','`',' ','=',' ','\'','%','s','\'',0}; if (!classid) return NULL; /* check for classes already loaded */ LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) { if (lstrcmpiW( cls->clsid, classid )==0) { TRACE("found class %s (%p)\n",debugstr_w(classid), cls); return cls; } } row = MSI_QueryGetRecord(package->db, ExecSeqQuery, classid); if (!row) return NULL; cls = load_class(package, row); msiobj_release(&row->hdr); return cls; } static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR extension ); static MSIMIME *load_mime( MSIPACKAGE* package, MSIRECORD *row ) { LPCWSTR buffer; MSIMIME *mt; /* fill in the data */ mt = msi_alloc_zero( sizeof(MSIMIME) ); if (!mt) return mt; mt->ContentType = msi_dup_record_field( row, 1 ); TRACE("loading mime %s\n", debugstr_w(mt->ContentType)); buffer = MSI_RecordGetString( row, 2 ); mt->Extension = load_given_extension( package, buffer ); mt->clsid = msi_dup_record_field( row, 3 ); mt->Class = load_given_class( package, mt->clsid ); list_add_tail( &package->mimes, &mt->entry ); return mt; } static MSIMIME *load_given_mime( MSIPACKAGE *package, LPCWSTR mime ) { MSIRECORD *row; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','M','I','M','E','`',' ','W','H','E','R','E',' ', '`','C','o','n','t','e','n','t','T','y','p','e','`',' ','=',' ', '\'','%','s','\'',0}; MSIMIME *mt; if (!mime) return NULL; /* check for mime already loaded */ LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry ) { if (strcmpiW(mt->ContentType,mime)==0) { TRACE("found mime %s (%p)\n",debugstr_w(mime), mt); return mt; } } row = MSI_QueryGetRecord(package->db, ExecSeqQuery, mime); if (!row) return NULL; mt = load_mime(package, row); msiobj_release(&row->hdr); return mt; } static MSIEXTENSION *load_extension( MSIPACKAGE* package, MSIRECORD *row ) { MSIEXTENSION *ext; LPCWSTR buffer; /* fill in the data */ ext = msi_alloc_zero( sizeof(MSIEXTENSION) ); if (!ext) return NULL; list_init( &ext->verbs ); list_add_tail( &package->extensions, &ext->entry ); ext->Extension = msi_dup_record_field( row, 1 ); TRACE("loading extension %s\n", debugstr_w(ext->Extension)); buffer = MSI_RecordGetString( row, 2 ); ext->Component = get_loaded_component( package,buffer ); ext->ProgIDText = msi_dup_record_field( row, 3 ); ext->ProgID = load_given_progid( package, ext->ProgIDText ); buffer = MSI_RecordGetString( row, 4 ); ext->Mime = load_given_mime( package, buffer ); buffer = MSI_RecordGetString(row,5); ext->Feature = get_loaded_feature( package, buffer ); return ext; } /* * While the extension table has 2 primary keys, this function is only looking * at the Extension key which is what is referenced as a forign key */ static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR name ) { MSIRECORD *row; MSIEXTENSION *ext; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','E','x','t','e','n','s','i','o','n','`',' ', 'W','H','E','R','E',' ', '`','E','x','t','e','n','s','i','o','n','`',' ','=',' ', '\'','%','s','\'',0}; if (!name) return NULL; /* check for extensions already loaded */ LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) { if (strcmpiW( ext->Extension, name )==0) { TRACE("extension %s already loaded %p\n", debugstr_w(name), ext); return ext; } } row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name ); if (!row) return NULL; ext = load_extension(package, row); msiobj_release(&row->hdr); return ext; } static UINT iterate_load_verb(MSIRECORD *row, LPVOID param) { MSIPACKAGE* package = (MSIPACKAGE*)param; MSIVERB *verb; LPCWSTR buffer; MSIEXTENSION *extension; buffer = MSI_RecordGetString(row,1); extension = load_given_extension( package, buffer ); if (!extension) { ERR("Verb unable to find loaded extension %s\n", debugstr_w(buffer)); return ERROR_SUCCESS; } /* fill in the data */ verb = msi_alloc_zero( sizeof(MSIVERB) ); if (!verb) return ERROR_OUTOFMEMORY; verb->Verb = msi_dup_record_field(row,2); TRACE("loading verb %s\n",debugstr_w(verb->Verb)); verb->Sequence = MSI_RecordGetInteger(row,3); buffer = MSI_RecordGetString(row,4); deformat_string(package,buffer,&verb->Command); buffer = MSI_RecordGetString(row,5); deformat_string(package,buffer,&verb->Argument); /* assosiate the verb with the correct extension */ list_add_tail( &extension->verbs, &verb->entry ); return ERROR_SUCCESS; } static UINT iterate_all_classes(MSIRECORD *rec, LPVOID param) { MSICOMPONENT *comp; LPCWSTR clsid; LPCWSTR context; LPCWSTR buffer; MSIPACKAGE* package =(MSIPACKAGE*)param; MSICLASS *cls; BOOL match = FALSE; clsid = MSI_RecordGetString(rec,1); context = MSI_RecordGetString(rec,2); buffer = MSI_RecordGetString(rec,3); comp = get_loaded_component(package,buffer); LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) { if (strcmpiW( clsid, cls->clsid )) continue; if (strcmpW( context, cls->Context )) continue; if (comp == cls->Component) { match = TRUE; break; } } if (!match) load_class(package, rec); return ERROR_SUCCESS; } static VOID load_all_classes(MSIPACKAGE *package) { UINT rc = ERROR_SUCCESS; MSIQUERY *view; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', '`','C','l','a','s','s','`',0}; rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); if (rc != ERROR_SUCCESS) return; rc = MSI_IterateRecords(view, NULL, iterate_all_classes, package); msiobj_release(&view->hdr); } static UINT iterate_all_extensions(MSIRECORD *rec, LPVOID param) { MSICOMPONENT *comp; LPCWSTR buffer; LPCWSTR extension; MSIPACKAGE* package =(MSIPACKAGE*)param; BOOL match = FALSE; MSIEXTENSION *ext; extension = MSI_RecordGetString(rec,1); buffer = MSI_RecordGetString(rec,2); comp = get_loaded_component(package,buffer); LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) { if (strcmpiW(extension,ext->Extension)) continue; if (comp == ext->Component) { match = TRUE; break; } } if (!match) load_extension(package, rec); return ERROR_SUCCESS; } static VOID load_all_extensions(MSIPACKAGE *package) { UINT rc = ERROR_SUCCESS; MSIQUERY *view; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','E','x','t','e','n','s','i','o','n','`',0}; rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); if (rc != ERROR_SUCCESS) return; rc = MSI_IterateRecords(view, NULL, iterate_all_extensions, package); msiobj_release(&view->hdr); } static UINT iterate_all_progids(MSIRECORD *rec, LPVOID param) { LPCWSTR buffer; MSIPACKAGE* package =(MSIPACKAGE*)param; buffer = MSI_RecordGetString(rec,1); load_given_progid(package,buffer); return ERROR_SUCCESS; } static VOID load_all_progids(MSIPACKAGE *package) { UINT rc = ERROR_SUCCESS; MSIQUERY *view; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','`','P','r','o','g','I','d','`',' ', 'F','R','O','M',' ', '`','P','r','o','g','I','d','`',0}; rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); if (rc != ERROR_SUCCESS) return; rc = MSI_IterateRecords(view, NULL, iterate_all_progids, package); msiobj_release(&view->hdr); } static VOID load_all_verbs(MSIPACKAGE *package) { UINT rc = ERROR_SUCCESS; MSIQUERY *view; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', '`','V','e','r','b','`',0}; rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); if (rc != ERROR_SUCCESS) return; rc = MSI_IterateRecords(view, NULL, iterate_load_verb, package); msiobj_release(&view->hdr); } static UINT iterate_all_mimes(MSIRECORD *rec, LPVOID param) { LPCWSTR buffer; MSIPACKAGE* package =(MSIPACKAGE*)param; buffer = MSI_RecordGetString(rec,1); load_given_mime(package,buffer); return ERROR_SUCCESS; } static VOID load_all_mimes(MSIPACKAGE *package) { UINT rc = ERROR_SUCCESS; MSIQUERY *view; static const WCHAR ExecSeqQuery[] = {'S','E','L','E','C','T',' ', '`','C','o','n','t','e','n','t','T','y','p','e','`', ' ','F','R','O','M',' ', '`','M','I','M','E','`',0}; rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); if (rc != ERROR_SUCCESS) return; rc = MSI_IterateRecords(view, NULL, iterate_all_mimes, package); msiobj_release(&view->hdr); } static void load_classes_and_such(MSIPACKAGE *package) { TRACE("Loading all the class info and related tables\n"); /* check if already loaded */ if (!list_empty( &package->classes ) || !list_empty( &package->mimes ) || !list_empty( &package->extensions ) || !list_empty( &package->progids ) ) return; load_all_classes(package); load_all_extensions(package); load_all_progids(package); /* these loads must come after the other loads */ load_all_verbs(package); load_all_mimes(package); } static void mark_progid_for_install( MSIPACKAGE* package, MSIPROGID *progid ) { MSIPROGID *child; if (!progid) return; if (progid->InstallMe == TRUE) return; progid->InstallMe = TRUE; /* all children if this is a parent also install */ LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry ) { if (child->Parent == progid) mark_progid_for_install( package, child ); } } static void mark_mime_for_install( MSIMIME *mime ) { if (!mime) return; mime->InstallMe = TRUE; } LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) { DWORD len = value ? (lstrlenW(value) + 1) * sizeof (WCHAR) : 0; return RegSetValueExW( hkey, name, 0, REG_SZ, (LPBYTE)value, len ); } LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) { LPCWSTR p = value; while (*p) p += lstrlenW(p) + 1; return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ, (LPBYTE)value, (p + 1 - value) * sizeof(WCHAR) ); } LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val ) { return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) ); } LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val ) { HKEY hsubkey = 0; LONG r; r = RegCreateKeyW( hkey, path, &hsubkey ); if (r != ERROR_SUCCESS) return r; r = msi_reg_set_val_str( hsubkey, name, val ); RegCloseKey( hsubkey ); return r; } static UINT register_appid(MSIAPPID *appid, LPCWSTR app ) { static const WCHAR szAppID[] = { 'A','p','p','I','D',0 }; static const WCHAR szRemoteServerName[] = {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0}; static const WCHAR szLocalService[] = {'L','o','c','a','l','S','e','r','v','i','c','e',0}; static const WCHAR szService[] = {'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0}; static const WCHAR szDLL[] = {'D','l','l','S','u','r','r','o','g','a','t','e',0}; static const WCHAR szActivate[] = {'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0}; static const WCHAR szY[] = {'Y',0}; static const WCHAR szRunAs[] = {'R','u','n','A','s',0}; static const WCHAR szUser[] = {'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0}; HKEY hkey2,hkey3; RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2); RegCreateKeyW( hkey2, appid->AppID, &hkey3 ); RegCloseKey(hkey2); msi_reg_set_val_str( hkey3, NULL, app ); if (appid->RemoteServerName) msi_reg_set_val_str( hkey3, szRemoteServerName, appid->RemoteServerName ); if (appid->LocalServer) msi_reg_set_val_str( hkey3, szLocalService, appid->LocalServer ); if (appid->ServiceParameters) msi_reg_set_val_str( hkey3, szService, appid->ServiceParameters ); if (appid->DllSurrogate) msi_reg_set_val_str( hkey3, szDLL, appid->DllSurrogate ); if (appid->ActivateAtStorage) msi_reg_set_val_str( hkey3, szActivate, szY ); if (appid->RunAsInteractiveUser) msi_reg_set_val_str( hkey3, szRunAs, szUser ); RegCloseKey(hkey3); return ERROR_SUCCESS; } UINT ACTION_RegisterClassInfo(MSIPACKAGE *package) { /* * Again I am assuming the words, "Whose key file represents" when referring * to a Component as to meaning that Components KeyPath file */ UINT rc; MSIRECORD *uirow; static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 }; static const WCHAR szProgID[] = { 'P','r','o','g','I','D',0 }; static const WCHAR szVIProgID[] = { 'V','e','r','s','i','o','n','I','n','d','e','p','e','n','d','e','n','t','P','r','o','g','I','D',0 }; static const WCHAR szAppID[] = { 'A','p','p','I','D',0 }; static const WCHAR szSpace[] = {' ',0}; static const WCHAR szInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0}; static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0}; HKEY hkey,hkey2,hkey3; BOOL install_on_demand = FALSE; MSICLASS *cls; if (!package) return ERROR_INVALID_HANDLE; load_classes_and_such(package); rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey); if (rc != ERROR_SUCCESS) return ERROR_FUNCTION_FAILED; /* install_on_demand should be set if OLE supports install on demand OLE * servers. For now i am defaulting to FALSE because i do not know how to * check, and i am told our builtin OLE does not support it */ LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) { MSICOMPONENT *comp; MSIFILE *file; DWORD size, sz; LPWSTR argument; MSIFEATURE *feature; comp = cls->Component; if ( !comp ) continue; feature = cls->Feature; /* * yes. MSDN says that these are based on _Feature_ not on * Component. So verify the feature is to be installed */ if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) && !(install_on_demand && ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))) { TRACE("Skipping class %s reg due to disabled feature %s\n", debugstr_w(cls->clsid), debugstr_w(feature->Feature)); continue; } TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls); cls->Installed = TRUE; mark_progid_for_install( package, cls->ProgID ); RegCreateKeyW( hkey, cls->clsid, &hkey2 ); if (cls->Description) msi_reg_set_val_str( hkey2, NULL, cls->Description ); RegCreateKeyW( hkey2, cls->Context, &hkey3 ); file = get_loaded_file( package, comp->KeyPath ); /* the context server is a short path name * except for if it is InprocServer32... */ if (strcmpiW( cls->Context, szInprocServer32 )!=0) { sz = GetShortPathNameW( file->TargetPath, NULL, 0 ); if (sz == 0) { ERR("Unable to find short path for CLSID COM Server\n"); argument = NULL; } else { size = sz * sizeof(WCHAR); if (cls->Argument) { size += strlenW(cls->Argument) * sizeof(WCHAR); size += sizeof(WCHAR); } argument = msi_alloc( size + sizeof(WCHAR)); GetShortPathNameW( file->TargetPath, argument, sz ); if (cls->Argument) { strcatW(argument,szSpace); strcatW( argument, cls->Argument ); } } } else { size = lstrlenW( file->TargetPath ) * sizeof(WCHAR); if (cls->Argument) { size += strlenW(cls->Argument) * sizeof(WCHAR); size += sizeof(WCHAR); } argument = msi_alloc( size + sizeof(WCHAR)); strcpyW( argument, file->TargetPath ); if (cls->Argument) { strcatW(argument,szSpace); strcatW( argument, cls->Argument ); } } if (argument) { msi_reg_set_val_str( hkey3, NULL, argument ); msi_free(argument); } RegCloseKey(hkey3); if (cls->ProgID || cls->ProgIDText) { LPCWSTR progid; if (cls->ProgID) progid = cls->ProgID->ProgID; else progid = cls->ProgIDText; msi_reg_set_subkey_val( hkey2, szProgID, NULL, progid ); if (cls->ProgID && cls->ProgID->VersionInd) { msi_reg_set_subkey_val( hkey2, szVIProgID, NULL, cls->ProgID->VersionInd->ProgID ); } } if (cls->AppID) { MSIAPPID *appid = cls->AppID; msi_reg_set_val_str( hkey2, szAppID, appid->AppID ); register_appid( appid, cls->Description ); } if (cls->IconPath) { static const WCHAR szDefaultIcon[] = {'D','e','f','a','u','l','t','I','c','o','n',0}; msi_reg_set_subkey_val( hkey2, szDefaultIcon, NULL, cls->IconPath ); } if (cls->DefInprocHandler) { static const WCHAR szInproc[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r',0}; msi_reg_set_subkey_val( hkey2, szInproc, NULL, cls->DefInprocHandler ); } if (cls->DefInprocHandler32) { static const WCHAR szInproc32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0}; msi_reg_set_subkey_val( hkey2, szInproc32, NULL, cls->DefInprocHandler32 ); } RegCloseKey(hkey2); /* if there is a FileTypeMask, register the FileType */ if (cls->FileTypeMask) { LPWSTR ptr, ptr2; LPWSTR keyname; INT index = 0; ptr = cls->FileTypeMask; while (ptr && *ptr) { ptr2 = strchrW(ptr,';'); if (ptr2) *ptr2 = 0; keyname = msi_alloc( (strlenW(szFileType_fmt) + strlenW(cls->clsid) + 4) * sizeof(WCHAR)); sprintfW( keyname, szFileType_fmt, cls->clsid, index ); msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, ptr ); msi_free(keyname); if (ptr2) ptr = ptr2+1; else ptr = NULL; index ++; } } uirow = MSI_CreateRecord(1); MSI_RecordSetStringW( uirow, 1, cls->clsid ); ui_actiondata(package,szRegisterClassInfo,uirow); msiobj_release(&uirow->hdr); } RegCloseKey(hkey); return rc; } static UINT register_progid_base( MSIPROGID* progid, LPWSTR clsid ) { static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 }; static const WCHAR szDefaultIcon[] = {'D','e','f','a','u','l','t','I','c','o','n',0}; HKEY hkey; RegCreateKeyW(HKEY_CLASSES_ROOT,progid->ProgID,&hkey); if (progid->Description) msi_reg_set_val_str( hkey, NULL, progid->Description ); if (progid->Class) { msi_reg_set_subkey_val( hkey, szCLSID, NULL, progid->Class->clsid ); if (clsid) strcpyW( clsid, progid->Class->clsid ); } else { FIXME("progid (%s) with null classid\n", debugstr_w(progid->ProgID)); } if (progid->IconPath) msi_reg_set_subkey_val( hkey, szDefaultIcon, NULL, progid->IconPath ); return ERROR_SUCCESS; } static UINT register_progid(MSIPACKAGE *package, MSIPROGID* progid, LPWSTR clsid) { UINT rc = ERROR_SUCCESS; if (progid->Parent == NULL) rc = register_progid_base( progid, clsid ); else { DWORD disp; HKEY hkey; static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 }; static const WCHAR szDefaultIcon[] = {'D','e','f','a','u','l','t','I','c','o','n',0}; static const WCHAR szCurVer[] = {'C','u','r','V','e','r',0}; /* check if already registered */ RegCreateKeyExW(HKEY_CLASSES_ROOT, progid->ProgID, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, &disp ); if (disp == REG_OPENED_EXISTING_KEY) { TRACE("Key already registered\n"); RegCloseKey(hkey); return rc; } TRACE("Registering Parent %s\n", debugstr_w(progid->Parent->ProgID) ); rc = register_progid( package, progid->Parent, clsid ); /* clsid is same as parent */ msi_reg_set_subkey_val( hkey, szCLSID, NULL, clsid ); if (progid->Description) msi_reg_set_val_str( hkey, NULL, progid->Description ); if (progid->IconPath) msi_reg_set_subkey_val( hkey, szDefaultIcon, NULL, progid->IconPath ); /* write out the current version */ if (progid->CurVer) msi_reg_set_subkey_val( hkey, szCurVer, NULL, progid->CurVer->ProgID ); RegCloseKey(hkey); } return rc; } UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package) { MSIPROGID *progid; MSIRECORD *uirow; if (!package) return ERROR_INVALID_HANDLE; load_classes_and_such(package); LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) { WCHAR clsid[0x1000]; /* check if this progid is to be installed */ if (progid->Class && progid->Class->Installed) progid->InstallMe = TRUE; if (!progid->InstallMe) { TRACE("progid %s not scheduled to be installed\n", debugstr_w(progid->ProgID)); continue; } TRACE("Registering progid %s\n", debugstr_w(progid->ProgID)); register_progid( package, progid, clsid ); uirow = MSI_CreateRecord(1); MSI_RecordSetStringW( uirow, 1, progid->ProgID ); ui_actiondata( package, szRegisterProgIdInfo, uirow ); msiobj_release( &uirow->hdr ); } return ERROR_SUCCESS; } static UINT register_verb(MSIPACKAGE *package, LPCWSTR progid, MSICOMPONENT* component, MSIEXTENSION* extension, MSIVERB* verb, INT* Sequence ) { LPWSTR keyname; HKEY key; static const WCHAR szShell[] = {'s','h','e','l','l',0}; static const WCHAR szCommand[] = {'c','o','m','m','a','n','d',0}; static const WCHAR fmt[] = {'\"','%','s','\"',' ','%','s',0}; static const WCHAR fmt2[] = {'\"','%','s','\"',0}; LPWSTR command; DWORD size; LPWSTR advertise; keyname = build_directory_name(4, progid, szShell, verb->Verb, szCommand); TRACE("Making Key %s\n",debugstr_w(keyname)); RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key); size = strlenW(component->FullKeypath); if (verb->Argument) size += strlenW(verb->Argument); size += 4; command = msi_alloc(size * sizeof (WCHAR)); if (verb->Argument) sprintfW(command, fmt, component->FullKeypath, verb->Argument); else sprintfW(command, fmt2, component->FullKeypath); msi_reg_set_val_str( key, NULL, command ); msi_free(command); advertise = create_component_advertise_string(package, component, extension->Feature->Feature); size = strlenW(advertise); if (verb->Argument) size += strlenW(verb->Argument); size += 4; command = msi_alloc(size * sizeof (WCHAR)); memset(command,0,size*sizeof(WCHAR)); strcpyW(command,advertise); if (verb->Argument) { static const WCHAR szSpace[] = {' ',0}; strcatW(command,szSpace); strcatW(command,verb->Argument); } msi_reg_set_val_multi_str( key, szCommand, command ); RegCloseKey(key); msi_free(keyname); msi_free(advertise); msi_free(command); if (verb->Command) { keyname = build_directory_name(3, progid, szShell, verb->Verb); msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Command ); msi_free(keyname); } if (verb->Sequence != MSI_NULL_INTEGER) { if (*Sequence == MSI_NULL_INTEGER || verb->Sequence < *Sequence) { *Sequence = verb->Sequence; keyname = build_directory_name(2, progid, szShell); msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Verb ); msi_free(keyname); } } return ERROR_SUCCESS; } UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package) { static const WCHAR szContentType[] = {'C','o','n','t','e','n','t',' ','T','y','p','e',0 }; HKEY hkey; MSIEXTENSION *ext; MSIRECORD *uirow; BOOL install_on_demand = TRUE; if (!package) return ERROR_INVALID_HANDLE; load_classes_and_such(package); /* We need to set install_on_demand based on if the shell handles advertised * shortcuts and the like. Because Mike McCormack is working on this i am * going to default to TRUE */ LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) { LPWSTR extension; MSIFEATURE *feature; if (!ext->Component) continue; feature = ext->Feature; /* * yes. MSDN says that these are based on _Feature_ not on * Component. So verify the feature is to be installed */ if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) && !(install_on_demand && ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))) { TRACE("Skipping extension %s reg due to disabled feature %s\n", debugstr_w(ext->Extension), debugstr_w(feature->Feature)); continue; } TRACE("Registering extension %s (%p)\n", debugstr_w(ext->Extension), ext); ext->Installed = TRUE; /* this is only registered if the extension has at least 1 verb * according to MSDN */ if (ext->ProgID && !list_empty( &ext->verbs ) ) mark_progid_for_install( package, ext->ProgID ); mark_mime_for_install(ext->Mime); extension = msi_alloc( (lstrlenW( ext->Extension ) + 2)*sizeof(WCHAR) ); extension[0] = '.'; lstrcpyW(extension+1,ext->Extension); RegCreateKeyW(HKEY_CLASSES_ROOT,extension,&hkey); msi_free( extension ); if (ext->Mime) msi_reg_set_val_str( hkey, szContentType, ext->Mime->ContentType ); if (ext->ProgID || ext->ProgIDText) { static const WCHAR szSN[] = {'\\','S','h','e','l','l','N','e','w',0}; HKEY hkey2; LPWSTR newkey; LPCWSTR progid; MSIVERB *verb; INT Sequence = MSI_NULL_INTEGER; if (ext->ProgID) progid = ext->ProgID->ProgID; else progid = ext->ProgIDText; msi_reg_set_val_str( hkey, NULL, progid ); newkey = msi_alloc( (strlenW(progid)+strlenW(szSN)+1) * sizeof(WCHAR)); strcpyW(newkey,progid); strcatW(newkey,szSN); RegCreateKeyW(hkey,newkey,&hkey2); RegCloseKey(hkey2); msi_free(newkey); /* do all the verbs */ LIST_FOR_EACH_ENTRY( verb, &ext->verbs, MSIVERB, entry ) { register_verb( package, progid, ext->Component, ext, verb, &Sequence); } } RegCloseKey(hkey); uirow = MSI_CreateRecord(1); MSI_RecordSetStringW( uirow, 1, ext->Extension ); ui_actiondata(package,szRegisterExtensionInfo,uirow); msiobj_release(&uirow->hdr); } return ERROR_SUCCESS; } UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package) { static const WCHAR szExten[] = {'E','x','t','e','n','s','i','o','n',0 }; MSIRECORD *uirow; MSIMIME *mt; if (!package) return ERROR_INVALID_HANDLE; load_classes_and_such(package); LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry ) { LPWSTR extension; LPCWSTR exten; LPCWSTR mime; static const WCHAR fmt[] = {'M','I','M','E','\\','D','a','t','a','b','a','s','e','\\', 'C','o','n','t','e','n','t',' ','T','y','p','e','\\', '%','s',0}; LPWSTR key; /* * check if the MIME is to be installed. Either as requesed by an * extension or Class */ mt->InstallMe = (mt->InstallMe || (mt->Class && mt->Class->Installed) || (mt->Extension && mt->Extension->Installed)); if (!mt->InstallMe) { TRACE("MIME %s not scheduled to be installed\n", debugstr_w(mt->ContentType)); continue; } mime = mt->ContentType; exten = mt->Extension->Extension; extension = msi_alloc( (lstrlenW( exten ) + 2)*sizeof(WCHAR) ); extension[0] = '.'; lstrcpyW(extension+1,exten); key = msi_alloc( (strlenW(mime)+strlenW(fmt)+1) * sizeof(WCHAR) ); sprintfW(key,fmt,mime); msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szExten, extension ); msi_free(extension); msi_free(key); if (mt->clsid) FIXME("Handle non null for field 3\n"); uirow = MSI_CreateRecord(2); MSI_RecordSetStringW(uirow,1,mt->ContentType); MSI_RecordSetStringW(uirow,2,exten); ui_actiondata(package,szRegisterMIMEInfo,uirow); msiobj_release(&uirow->hdr); } return ERROR_SUCCESS; }