2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Aric Stewart 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
21 /* actions handled in this module
24 * RegisterExtensionInfo
26 * UnRegisterClassInfo (TODO)
27 * UnRegisterProgIdInfo (TODO)
28 * UnRegisterExtensionInfo (TODO)
29 * UnRegisterMIMEInfo (TODO)
38 #include "wine/debug.h"
41 #include "wine/unicode.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47 extern const WCHAR szRegisterClassInfo[];
48 extern const WCHAR szRegisterProgIdInfo[];
49 extern const WCHAR szRegisterExtensionInfo[];
50 extern const WCHAR szRegisterMIMEInfo[];
52 extern const WCHAR szUnregisterClassInfo[];
53 extern const WCHAR szUnregisterExtensionInfo[];
54 extern const WCHAR szUnregisterMIMEInfo[];
55 extern const WCHAR szUnregisterProgIdInfo[];
57 static INT load_appid(MSIPACKAGE* package, MSIRECORD *row)
59 DWORD index = package->loaded_appids;
63 /* fill in the data */
65 package->loaded_appids++;
66 if (package->loaded_appids == 1)
67 package->appids = HeapAlloc(GetProcessHeap(),0,sizeof(MSIAPPID));
69 package->appids = HeapReAlloc(GetProcessHeap(),0,
70 package->appids, package->loaded_appids * sizeof(MSIAPPID));
72 memset(&package->appids[index],0,sizeof(MSIAPPID));
75 MSI_RecordGetStringW(row, 1, package->appids[index].AppID, &sz);
76 TRACE("loading appid %s\n",debugstr_w(package->appids[index].AppID));
78 buffer = MSI_RecordGetString(row,2);
79 deformat_string(package,buffer,&package->appids[index].RemoteServerName);
81 package->appids[index].LocalServer = load_dynamic_stringW(row,3);
82 package->appids[index].ServiceParameters = load_dynamic_stringW(row,4);
83 package->appids[index].DllSurrogate = load_dynamic_stringW(row,5);
85 package->appids[index].ActivateAtStorage = !MSI_RecordIsNull(row,6);
86 package->appids[index].RunAsInteractiveUser = !MSI_RecordIsNull(row,7);
91 static INT load_given_appid(MSIPACKAGE *package, LPCWSTR appid)
96 static const WCHAR ExecSeqQuery[] =
97 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
98 '`','A','p','p','I','d','`',' ','W','H','E','R','E',' ',
99 '`','A','p','p','I','d','`',' ','=',' ','\'','%','s','\'',0};
104 /* check for appids already loaded */
105 for (i = 0; i < package->loaded_appids; i++)
106 if (strcmpiW(package->appids[i].AppID,appid)==0)
108 TRACE("found appid %s at index %i\n",debugstr_w(appid),i);
112 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, appid);
116 rc = load_appid(package, row);
117 msiobj_release(&row->hdr);
122 static INT load_given_progid(MSIPACKAGE *package, LPCWSTR progid);
123 static INT load_given_class(MSIPACKAGE *package, LPCWSTR classid);
125 static INT load_progid(MSIPACKAGE* package, MSIRECORD *row)
127 DWORD index = package->loaded_progids;
130 /* fill in the data */
132 package->loaded_progids++;
133 if (package->loaded_progids == 1)
134 package->progids = HeapAlloc(GetProcessHeap(),0,sizeof(MSIPROGID));
136 package->progids = HeapReAlloc(GetProcessHeap(),0,
137 package->progids , package->loaded_progids * sizeof(MSIPROGID));
139 memset(&package->progids[index],0,sizeof(MSIPROGID));
141 package->progids[index].ProgID = load_dynamic_stringW(row,1);
142 TRACE("loading progid %s\n",debugstr_w(package->progids[index].ProgID));
144 buffer = MSI_RecordGetString(row,2);
145 package->progids[index].ParentIndex = load_given_progid(package,buffer);
146 if (package->progids[index].ParentIndex < 0 && buffer)
147 FIXME("Unknown parent ProgID %s\n",debugstr_w(buffer));
149 buffer = MSI_RecordGetString(row,3);
150 package->progids[index].ClassIndex = load_given_class(package,buffer);
151 if (package->progids[index].ClassIndex< 0 && buffer)
152 FIXME("Unknown class %s\n",debugstr_w(buffer));
154 package->progids[index].Description = load_dynamic_stringW(row,4);
156 if (!MSI_RecordIsNull(row,6))
158 INT icon_index = MSI_RecordGetInteger(row,6);
159 LPWSTR FileName = load_dynamic_stringW(row,5);
161 static const WCHAR fmt[] = {'%','s',',','%','i',0};
163 build_icon_path(package,FileName,&FilePath);
165 package->progids[index].IconPath =
166 HeapAlloc(GetProcessHeap(),0,(strlenW(FilePath)+10)*
169 sprintfW(package->progids[index].IconPath,fmt,FilePath,icon_index);
171 HeapFree(GetProcessHeap(),0,FilePath);
172 HeapFree(GetProcessHeap(),0,FileName);
176 buffer = MSI_RecordGetString(row,5);
178 build_icon_path(package,buffer,&(package->progids[index].IconPath));
181 package->progids[index].CurVerIndex = -1;
182 package->progids[index].VersionIndIndex = -1;
184 /* if we have a parent then we may be that parents CurVer */
185 if (package->progids[index].ParentIndex >= 0 &&
186 package->progids[index].ParentIndex != index)
188 int pindex = package->progids[index].ParentIndex;
189 while (package->progids[pindex].ParentIndex>= 0 &&
190 package->progids[pindex].ParentIndex != pindex)
191 pindex = package->progids[pindex].ParentIndex;
193 FIXME("BAD BAD need to determing if we are really the CurVer\n");
195 package->progids[index].CurVerIndex = pindex;
196 package->progids[pindex].VersionIndIndex = index;
202 static INT load_given_progid(MSIPACKAGE *package, LPCWSTR progid)
207 static const WCHAR ExecSeqQuery[] =
208 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
209 '`','P','r','o','g','I','d','`',' ','W','H','E','R','E',' ',
210 '`','P','r','o','g','I','d','`',' ','=',' ','\'','%','s','\'',0};
215 /* check for progids already loaded */
216 for (i = 0; i < package->loaded_progids; i++)
217 if (strcmpiW(package->progids[i].ProgID,progid)==0)
219 TRACE("found progid %s at index %i\n",debugstr_w(progid), i);
223 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, progid);
227 rc = load_progid(package, row);
228 msiobj_release(&row->hdr);
233 static INT load_class(MSIPACKAGE* package, MSIRECORD *row)
235 DWORD index = package->loaded_classes;
239 /* fill in the data */
241 package->loaded_classes++;
242 if (package->loaded_classes== 1)
243 package->classes = HeapAlloc(GetProcessHeap(),0,sizeof(MSICLASS));
245 package->classes = HeapReAlloc(GetProcessHeap(),0,
246 package->classes, package->loaded_classes * sizeof(MSICLASS));
248 memset(&package->classes[index],0,sizeof(MSICLASS));
250 sz = IDENTIFIER_SIZE;
251 MSI_RecordGetStringW(row, 1, package->classes[index].CLSID, &sz);
252 TRACE("loading class %s\n",debugstr_w(package->classes[index].CLSID));
253 sz = IDENTIFIER_SIZE;
254 MSI_RecordGetStringW(row, 2, package->classes[index].Context, &sz);
255 buffer = MSI_RecordGetString(row,3);
256 package->classes[index].Component = get_loaded_component(package, buffer);
258 package->classes[index].ProgIDText = load_dynamic_stringW(row,4);
259 package->classes[index].ProgIDIndex =
260 load_given_progid(package, package->classes[index].ProgIDText);
262 package->classes[index].Description = load_dynamic_stringW(row,5);
264 buffer = MSI_RecordGetString(row,6);
266 package->classes[index].AppIDIndex =
267 load_given_appid(package, buffer);
269 package->classes[index].AppIDIndex = -1;
271 package->classes[index].FileTypeMask = load_dynamic_stringW(row,7);
273 if (!MSI_RecordIsNull(row,9))
276 INT icon_index = MSI_RecordGetInteger(row,9);
277 LPWSTR FileName = load_dynamic_stringW(row,8);
279 static const WCHAR fmt[] = {'%','s',',','%','i',0};
281 build_icon_path(package,FileName,&FilePath);
283 package->classes[index].IconPath =
284 HeapAlloc(GetProcessHeap(),0,(strlenW(FilePath)+5)*
287 sprintfW(package->classes[index].IconPath,fmt,FilePath,icon_index);
289 HeapFree(GetProcessHeap(),0,FilePath);
290 HeapFree(GetProcessHeap(),0,FileName);
294 buffer = MSI_RecordGetString(row,8);
296 build_icon_path(package,buffer,&(package->classes[index].IconPath));
299 if (!MSI_RecordIsNull(row,10))
301 i = MSI_RecordGetInteger(row,10);
302 if (i != MSI_NULL_INTEGER && i > 0 && i < 4)
304 static const WCHAR ole2[] = {'o','l','e','2','.','d','l','l',0};
305 static const WCHAR ole32[] = {'o','l','e','3','2','.','d','l','l',0};
310 package->classes[index].DefInprocHandler = strdupW(ole2);
313 package->classes[index].DefInprocHandler32 = strdupW(ole32);
316 package->classes[index].DefInprocHandler = strdupW(ole2);
317 package->classes[index].DefInprocHandler32 = strdupW(ole32);
323 package->classes[index].DefInprocHandler32 = load_dynamic_stringW(
325 reduce_to_longfilename(package->classes[index].DefInprocHandler32);
328 buffer = MSI_RecordGetString(row,11);
329 deformat_string(package,buffer,&package->classes[index].Argument);
331 buffer = MSI_RecordGetString(row,12);
332 package->classes[index].Feature = get_loaded_feature(package,buffer);
334 package->classes[index].Attributes = MSI_RecordGetInteger(row,13);
340 * the Class table has 3 primary keys. Generally it is only
341 * referenced through the first CLSID key. However when loading
342 * all of the classes we need to make sure we do not ignore rows
343 * with other Context and ComponentIndexs
345 static INT load_given_class(MSIPACKAGE *package, LPCWSTR classid)
350 static const WCHAR ExecSeqQuery[] =
351 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
352 '`','C','l','a','s','s','`',' ','W','H','E','R','E',' ',
353 '`','C','L','S','I','D','`',' ','=',' ','\'','%','s','\'',0};
359 /* check for classes already loaded */
360 for (i = 0; i < package->loaded_classes; i++)
361 if (strcmpiW(package->classes[i].CLSID,classid)==0)
363 TRACE("found class %s at index %i\n",debugstr_w(classid), i);
367 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, classid);
371 rc = load_class(package, row);
372 msiobj_release(&row->hdr);
377 static INT load_given_extension(MSIPACKAGE *package, LPCWSTR extension);
379 static INT load_mime(MSIPACKAGE* package, MSIRECORD *row)
381 DWORD index = package->loaded_mimes;
385 /* fill in the data */
387 package->loaded_mimes++;
388 if (package->loaded_mimes== 1)
389 package->mimes= HeapAlloc(GetProcessHeap(),0,sizeof(MSIMIME));
391 package->mimes= HeapReAlloc(GetProcessHeap(),0,
392 package->mimes, package->loaded_mimes*
395 memset(&package->mimes[index],0,sizeof(MSIMIME));
397 package->mimes[index].ContentType = load_dynamic_stringW(row,1);
398 TRACE("loading mime %s\n",debugstr_w(package->mimes[index].ContentType));
400 buffer = MSI_RecordGetString(row,2);
401 package->mimes[index].ExtensionIndex = load_given_extension(package,
404 sz = IDENTIFIER_SIZE;
405 MSI_RecordGetStringW(row,3,package->mimes[index].CLSID,&sz);
406 package->mimes[index].ClassIndex= load_given_class(package,
407 package->mimes[index].CLSID);
412 static INT load_given_mime(MSIPACKAGE *package, LPCWSTR mime)
417 static const WCHAR ExecSeqQuery[] =
418 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
419 '`','M','I','M','E','`',' ','W','H','E','R','E',' ',
420 '`','C','o','n','t','e','n','t','T','y','p','e','`',' ','=',' ',
421 '\'','%','s','\'',0};
426 /* check for mime already loaded */
427 for (i = 0; i < package->loaded_mimes; i++)
428 if (strcmpiW(package->mimes[i].ContentType,mime)==0)
430 TRACE("found mime %s at index %i\n",debugstr_w(mime), i);
434 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, mime);
438 rc = load_mime(package, row);
439 msiobj_release(&row->hdr);
444 static INT load_extension(MSIPACKAGE* package, MSIRECORD *row)
446 DWORD index = package->loaded_extensions;
450 /* fill in the data */
452 package->loaded_extensions++;
453 if (package->loaded_extensions == 1)
454 package->extensions = HeapAlloc(GetProcessHeap(),0,sizeof(MSIEXTENSION));
456 package->extensions = HeapReAlloc(GetProcessHeap(),0,
457 package->extensions, package->loaded_extensions*
458 sizeof(MSIEXTENSION));
460 memset(&package->extensions[index],0,sizeof(MSIEXTENSION));
463 MSI_RecordGetStringW(row,1,package->extensions[index].Extension,&sz);
464 TRACE("loading extension %s\n",
465 debugstr_w(package->extensions[index].Extension));
467 buffer = MSI_RecordGetString(row,2);
468 package->extensions[index].Component = get_loaded_component(package,buffer);
470 package->extensions[index].ProgIDText = load_dynamic_stringW(row,3);
471 package->extensions[index].ProgIDIndex = load_given_progid(package,
472 package->extensions[index].ProgIDText);
474 buffer = MSI_RecordGetString(row,4);
475 package->extensions[index].MIMEIndex = load_given_mime(package,buffer);
477 buffer = MSI_RecordGetString(row,5);
478 package->extensions[index].Feature = get_loaded_feature(package,buffer);
484 * While the extension table has 2 primary keys, this function is only looking
485 * at the Extension key which is what is referenced as a forign key
487 static INT load_given_extension(MSIPACKAGE *package, LPCWSTR extension)
492 static const WCHAR ExecSeqQuery[] =
493 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
494 '`','E','x','t','e','n','s','i','o','n','`',' ',
495 'W','H','E','R','E',' ',
496 '`','E','x','t','e','n','s','i','o','n','`',' ','=',' ',
497 '\'','%','s','\'',0};
502 /* check for extensions already loaded */
503 for (i = 0; i < package->loaded_extensions; i++)
504 if (strcmpiW(package->extensions[i].Extension,extension)==0)
506 TRACE("extension %s already loaded at %i\n",debugstr_w(extension),
511 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, extension);
515 rc = load_extension(package, row);
516 msiobj_release(&row->hdr);
521 static UINT iterate_load_verb(MSIRECORD *row, LPVOID param)
523 MSIPACKAGE* package = (MSIPACKAGE*)param;
524 DWORD index = package->loaded_verbs;
527 /* fill in the data */
529 package->loaded_verbs++;
530 if (package->loaded_verbs == 1)
531 package->verbs = HeapAlloc(GetProcessHeap(),0,sizeof(MSIVERB));
533 package->verbs = HeapReAlloc(GetProcessHeap(),0,
534 package->verbs , package->loaded_verbs * sizeof(MSIVERB));
536 memset(&package->verbs[index],0,sizeof(MSIVERB));
538 buffer = MSI_RecordGetString(row,1);
539 package->verbs[index].ExtensionIndex = load_given_extension(package,buffer);
540 if (package->verbs[index].ExtensionIndex < 0 && buffer)
541 ERR("Verb unable to find loaded extension %s\n", debugstr_w(buffer));
543 package->verbs[index].Verb = load_dynamic_stringW(row,2);
544 TRACE("loading verb %s\n",debugstr_w(package->verbs[index].Verb));
545 package->verbs[index].Sequence = MSI_RecordGetInteger(row,3);
547 buffer = MSI_RecordGetString(row,4);
548 deformat_string(package,buffer,&package->verbs[index].Command);
550 buffer = MSI_RecordGetString(row,5);
551 deformat_string(package,buffer,&package->verbs[index].Argument);
553 /* assosiate the verb with the correct extension */
554 if (package->verbs[index].ExtensionIndex >= 0)
556 MSIEXTENSION* extension = &package->extensions[package->verbs[index].
558 int count = extension->VerbCount;
561 FIXME("Exceeding max verb count! Increase that limit!!!\n");
564 extension->VerbCount++;
565 extension->Verbs[count] = index;
569 return ERROR_SUCCESS;
572 static UINT iterate_all_classes(MSIRECORD *rec, LPVOID param)
578 MSIPACKAGE* package =(MSIPACKAGE*)param;
582 clsid = MSI_RecordGetString(rec,1);
583 context = MSI_RecordGetString(rec,2);
584 buffer = MSI_RecordGetString(rec,3);
585 comp = get_loaded_component(package,buffer);
587 for (i = 0; i < package->loaded_classes; i++)
589 if (strcmpiW(clsid,package->classes[i].CLSID))
591 if (strcmpW(context,package->classes[i].Context))
593 if (comp == package->classes[i].Component)
601 load_class(package, rec);
603 return ERROR_SUCCESS;
606 static VOID load_all_classes(MSIPACKAGE *package)
608 UINT rc = ERROR_SUCCESS;
611 static const WCHAR ExecSeqQuery[] =
612 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
613 '`','C','l','a','s','s','`',0};
615 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
616 if (rc != ERROR_SUCCESS)
619 rc = MSI_IterateRecords(view, NULL, iterate_all_classes, package);
620 msiobj_release(&view->hdr);
623 static UINT iterate_all_extensions(MSIRECORD *rec, LPVOID param)
628 MSIPACKAGE* package =(MSIPACKAGE*)param;
632 extension = MSI_RecordGetString(rec,1);
633 buffer = MSI_RecordGetString(rec,2);
634 comp = get_loaded_component(package,buffer);
636 for (i = 0; i < package->loaded_extensions; i++)
638 if (strcmpiW(extension,package->extensions[i].Extension))
640 if (comp == package->extensions[i].Component)
648 load_extension(package, rec);
650 return ERROR_SUCCESS;
653 static VOID load_all_extensions(MSIPACKAGE *package)
655 UINT rc = ERROR_SUCCESS;
658 static const WCHAR ExecSeqQuery[] =
659 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
660 '`','E','x','t','e','n','s','i','o','n','`',0};
662 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
663 if (rc != ERROR_SUCCESS)
666 rc = MSI_IterateRecords(view, NULL, iterate_all_extensions, package);
667 msiobj_release(&view->hdr);
670 static UINT iterate_all_progids(MSIRECORD *rec, LPVOID param)
673 MSIPACKAGE* package =(MSIPACKAGE*)param;
675 buffer = MSI_RecordGetString(rec,1);
676 load_given_progid(package,buffer);
677 return ERROR_SUCCESS;
680 static VOID load_all_progids(MSIPACKAGE *package)
682 UINT rc = ERROR_SUCCESS;
685 static const WCHAR ExecSeqQuery[] =
686 {'S','E','L','E','C','T',' ','`','P','r','o','g','I','d','`',' ',
687 'F','R','O','M',' ', '`','P','r','o','g','I','d','`',0};
689 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
690 if (rc != ERROR_SUCCESS)
693 rc = MSI_IterateRecords(view, NULL, iterate_all_progids, package);
694 msiobj_release(&view->hdr);
697 static VOID load_all_verbs(MSIPACKAGE *package)
699 UINT rc = ERROR_SUCCESS;
702 static const WCHAR ExecSeqQuery[] =
703 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
704 '`','V','e','r','b','`',0};
706 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
707 if (rc != ERROR_SUCCESS)
710 rc = MSI_IterateRecords(view, NULL, iterate_load_verb, package);
711 msiobj_release(&view->hdr);
714 static UINT iterate_all_mimes(MSIRECORD *rec, LPVOID param)
717 MSIPACKAGE* package =(MSIPACKAGE*)param;
719 buffer = MSI_RecordGetString(rec,1);
720 load_given_mime(package,buffer);
721 return ERROR_SUCCESS;
724 static VOID load_all_mimes(MSIPACKAGE *package)
726 UINT rc = ERROR_SUCCESS;
729 static const WCHAR ExecSeqQuery[] =
730 {'S','E','L','E','C','T',' ',
731 '`','C','o','n','t','e','n','t','T','y','p','e','`',
732 ' ','F','R','O','M',' ',
733 '`','M','I','M','E','`',0};
735 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
736 if (rc != ERROR_SUCCESS)
739 rc = MSI_IterateRecords(view, NULL, iterate_all_mimes, package);
740 msiobj_release(&view->hdr);
743 static void load_classes_and_such(MSIPACKAGE *package)
745 TRACE("Loading all the class info and related tables\n");
747 /* check if already loaded */
748 if (package->classes || package->extensions || package->progids ||
749 package->verbs || package->mimes)
752 load_all_classes(package);
753 load_all_extensions(package);
754 load_all_progids(package);
755 /* these loads must come after the other loads */
756 load_all_verbs(package);
757 load_all_mimes(package);
760 static void mark_progid_for_install(MSIPACKAGE* package, INT index)
765 if (index < 0 || index >= package->loaded_progids)
768 progid = &package->progids[index];
770 if (progid->InstallMe == TRUE)
773 progid->InstallMe = TRUE;
775 /* all children if this is a parent also install */
776 for (i = 0; i < package->loaded_progids; i++)
777 if (package->progids[i].ParentIndex == index)
778 mark_progid_for_install(package,i);
781 static void mark_mime_for_install(MSIPACKAGE* package, INT index)
785 if (index < 0 || index >= package->loaded_mimes)
788 mime = &package->mimes[index];
790 if (mime->InstallMe == TRUE)
793 mime->InstallMe = TRUE;
796 static UINT register_appid(MSIPACKAGE *package, int appidIndex, LPCWSTR app )
798 static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
802 return ERROR_INVALID_HANDLE;
804 RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2);
805 RegCreateKeyW(hkey2,package->appids[appidIndex].AppID,&hkey3);
806 RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)app,
807 (strlenW(app)+1)*sizeof(WCHAR));
809 if (package->appids[appidIndex].RemoteServerName)
812 static const WCHAR szRemoteServerName[] =
813 {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',
816 size = (strlenW(package->appids[appidIndex].RemoteServerName)+1) *
819 RegSetValueExW(hkey3,szRemoteServerName,0,REG_SZ,
820 (LPVOID)package->appids[appidIndex].RemoteServerName,
824 if (package->appids[appidIndex].LocalServer)
826 static const WCHAR szLocalService[] =
827 {'L','o','c','a','l','S','e','r','v','i','c','e',0};
829 size = (strlenW(package->appids[appidIndex].LocalServer)+1) *
832 RegSetValueExW(hkey3,szLocalService,0,REG_SZ,
833 (LPVOID)package->appids[appidIndex].LocalServer,size);
836 if (package->appids[appidIndex].ServiceParameters)
838 static const WCHAR szService[] =
839 {'S','e','r','v','i','c','e',
840 'P','a','r','a','m','e','t','e','r','s',0};
842 size = (strlenW(package->appids[appidIndex].ServiceParameters)+1) *
844 RegSetValueExW(hkey3,szService,0,REG_SZ,
845 (LPVOID)package->appids[appidIndex].ServiceParameters,
849 if (package->appids[appidIndex].DllSurrogate)
851 static const WCHAR szDLL[] =
852 {'D','l','l','S','u','r','r','o','g','a','t','e',0};
854 size = (strlenW(package->appids[appidIndex].DllSurrogate)+1) *
856 RegSetValueExW(hkey3,szDLL,0,REG_SZ,
857 (LPVOID)package->appids[appidIndex].DllSurrogate,size);
860 if (package->appids[appidIndex].ActivateAtStorage)
862 static const WCHAR szActivate[] =
863 {'A','c','t','i','v','a','t','e','A','s',
864 'S','t','o','r','a','g','e',0};
865 static const WCHAR szY[] = {'Y',0};
867 RegSetValueExW(hkey3,szActivate,0,REG_SZ,(LPVOID)szY,4);
870 if (package->appids[appidIndex].RunAsInteractiveUser)
872 static const WCHAR szRunAs[] = {'R','u','n','A','s',0};
873 static const WCHAR szUser[] =
874 {'I','n','t','e','r','a','c','t','i','v','e',' ',
877 RegSetValueExW(hkey3,szRunAs,0,REG_SZ,(LPVOID)szUser,sizeof(szUser));
882 return ERROR_SUCCESS;
885 UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
888 * Again I am assuming the words, "Whose key file represents" when referring
889 * to a Component as to meaning that Components KeyPath file
894 static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
895 static const WCHAR szProgID[] = { 'P','r','o','g','I','D',0 };
896 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 };
897 static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
898 static const WCHAR szSpace[] = {' ',0};
899 static const WCHAR szInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
900 static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0};
901 HKEY hkey,hkey2,hkey3;
902 BOOL install_on_demand = FALSE;
906 return ERROR_INVALID_HANDLE;
908 load_classes_and_such(package);
909 rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey);
910 if (rc != ERROR_SUCCESS)
911 return ERROR_FUNCTION_FAILED;
913 /* install_on_demand should be set if OLE supports install on demand OLE
914 * servers. For now i am defaulting to FALSE because i do not know how to
915 * check, and i am told our builtin OLE does not support it
918 for (i = 0; i < package->loaded_classes; i++)
926 comp = package->classes[i].Component;
930 feature = package->classes[i].Feature;
933 * yes. MSDN says that these are based on _Feature_ not on
934 * Component. So verify the feature is to be installed
936 if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) &&
937 !(install_on_demand &&
938 ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED )))
940 TRACE("Skipping class %s reg due to disabled feature %s\n",
941 debugstr_w(package->classes[i].CLSID),
942 debugstr_w(feature->Feature));
947 TRACE("Registering index %i class %s\n",i,
948 debugstr_w(package->classes[i].CLSID));
950 package->classes[i].Installed = TRUE;
951 if (package->classes[i].ProgIDIndex >= 0)
952 mark_progid_for_install(package, package->classes[i].ProgIDIndex);
954 RegCreateKeyW(hkey,package->classes[i].CLSID,&hkey2);
956 if (package->classes[i].Description)
957 RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)package->classes[i].
958 Description, (strlenW(package->classes[i].
959 Description)+1)*sizeof(WCHAR));
961 RegCreateKeyW(hkey2,package->classes[i].Context,&hkey3);
962 file = get_loaded_file( package, comp->KeyPath );
965 /* the context server is a short path name
966 * except for if it is InprocServer32...
968 if (strcmpiW(package->classes[i].Context,szInprocServer32)!=0)
971 sz = GetShortPathNameW( file->TargetPath, NULL, 0 );
974 ERR("Unable to find short path for CLSID COM Server\n");
979 size = sz * sizeof(WCHAR);
981 if (package->classes[i].Argument)
983 size += strlenW(package->classes[i].Argument) *
985 size += sizeof(WCHAR);
988 argument = HeapAlloc(GetProcessHeap(), 0, size + sizeof(WCHAR));
989 GetShortPathNameW( file->TargetPath, argument, sz );
991 if (package->classes[i].Argument)
993 strcatW(argument,szSpace);
994 strcatW(argument,package->classes[i].Argument);
1000 size = lstrlenW( file->TargetPath ) * sizeof(WCHAR);
1002 if (package->classes[i].Argument)
1004 size += strlenW(package->classes[i].Argument) * sizeof(WCHAR);
1005 size += sizeof(WCHAR);
1008 argument = HeapAlloc(GetProcessHeap(), 0, size + sizeof(WCHAR));
1009 strcpyW( argument, file->TargetPath );
1011 if (package->classes[i].Argument)
1013 strcatW(argument,szSpace);
1014 strcatW(argument,package->classes[i].Argument);
1020 RegSetValueExW(hkey3,NULL,0,REG_SZ, (LPVOID)argument, size);
1021 HeapFree(GetProcessHeap(),0,argument);
1026 if (package->classes[i].ProgIDIndex >= 0 ||
1027 package->classes[i].ProgIDText)
1031 if (package->classes[i].ProgIDIndex >= 0)
1032 progid = package->progids[
1033 package->classes[i].ProgIDIndex].ProgID;
1035 progid = package->classes[i].ProgIDText;
1037 RegCreateKeyW(hkey2,szProgID,&hkey3);
1038 RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)progid,
1039 (strlenW(progid)+1) *sizeof(WCHAR));
1042 if (package->classes[i].ProgIDIndex >= 0 &&
1043 package->progids[package->classes[i].ProgIDIndex].
1044 VersionIndIndex >= 0)
1046 LPWSTR viprogid = strdupW(package->progids[package->progids[
1047 package->classes[i].ProgIDIndex].VersionIndIndex].
1049 RegCreateKeyW(hkey2,szVIProgID,&hkey3);
1050 RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)viprogid,
1051 (strlenW(viprogid)+1) *sizeof(WCHAR));
1053 HeapFree(GetProcessHeap(), 0, viprogid);
1057 if (package->classes[i].AppIDIndex >= 0)
1059 RegSetValueExW(hkey2,szAppID,0,REG_SZ,
1060 (LPVOID)package->appids[package->classes[i].AppIDIndex].AppID,
1061 (strlenW(package->appids[package->classes[i].AppIDIndex].AppID)+1)
1064 register_appid(package,package->classes[i].AppIDIndex,
1065 package->classes[i].Description);
1068 if (package->classes[i].IconPath)
1070 static const WCHAR szDefaultIcon[] =
1071 {'D','e','f','a','u','l','t','I','c','o','n',0};
1073 RegCreateKeyW(hkey2,szDefaultIcon,&hkey3);
1075 RegSetValueExW(hkey3,NULL,0,REG_SZ,
1076 (LPVOID)package->classes[i].IconPath,
1077 (strlenW(package->classes[i].IconPath)+1) *
1083 if (package->classes[i].DefInprocHandler)
1085 static const WCHAR szInproc[] =
1086 {'I','n','p','r','o','c','H','a','n','d','l','e','r',0};
1088 size = (strlenW(package->classes[i].DefInprocHandler) + 1) *
1090 RegCreateKeyW(hkey2,szInproc,&hkey3);
1091 RegSetValueExW(hkey3,NULL,0,REG_SZ,
1092 (LPVOID)package->classes[i].DefInprocHandler, size);
1096 if (package->classes[i].DefInprocHandler32)
1098 static const WCHAR szInproc32[] =
1099 {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',
1101 size = (strlenW(package->classes[i].DefInprocHandler32) + 1) *
1104 RegCreateKeyW(hkey2,szInproc32,&hkey3);
1105 RegSetValueExW(hkey3,NULL,0,REG_SZ,
1106 (LPVOID)package->classes[i].DefInprocHandler32,size);
1112 /* if there is a FileTypeMask, register the FileType */
1113 if (package->classes[i].FileTypeMask)
1118 ptr = package->classes[i].FileTypeMask;
1121 ptr2 = strchrW(ptr,';');
1124 keyname = HeapAlloc(GetProcessHeap(),0,(strlenW(szFileType_fmt)+
1125 strlenW(package->classes[i].CLSID) + 4)
1127 sprintfW(keyname,szFileType_fmt, package->classes[i].CLSID,
1130 RegCreateKeyW(HKEY_CLASSES_ROOT,keyname,&hkey2);
1131 RegSetValueExW(hkey2,NULL,0,REG_SZ, (LPVOID)ptr,
1132 strlenW(ptr)*sizeof(WCHAR));
1134 HeapFree(GetProcessHeap(), 0, keyname);
1145 uirow = MSI_CreateRecord(1);
1147 MSI_RecordSetStringW(uirow,1,package->classes[i].CLSID);
1148 ui_actiondata(package,szRegisterClassInfo,uirow);
1149 msiobj_release(&uirow->hdr);
1156 static UINT register_progid_base(MSIPACKAGE* package, MSIPROGID* progid,
1159 static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
1160 static const WCHAR szDefaultIcon[] =
1161 {'D','e','f','a','u','l','t','I','c','o','n',0};
1164 RegCreateKeyW(HKEY_CLASSES_ROOT,progid->ProgID,&hkey);
1166 if (progid->Description)
1168 RegSetValueExW(hkey,NULL,0,REG_SZ,
1169 (LPVOID)progid->Description,
1170 (strlenW(progid->Description)+1) *
1174 if (progid->ClassIndex >= 0)
1176 RegCreateKeyW(hkey,szCLSID,&hkey2);
1177 RegSetValueExW(hkey2,NULL,0,REG_SZ,
1178 (LPVOID)package->classes[progid->ClassIndex].CLSID,
1179 (strlenW(package->classes[progid->ClassIndex].CLSID)+1)
1183 strcpyW(clsid,package->classes[progid->ClassIndex].CLSID);
1189 FIXME("UNHANDLED case, Parent progid but classid is NULL\n");
1192 if (progid->IconPath)
1194 RegCreateKeyW(hkey,szDefaultIcon,&hkey2);
1196 RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)progid->IconPath,
1197 (strlenW(progid->IconPath)+1) * sizeof(WCHAR));
1200 return ERROR_SUCCESS;
1203 static UINT register_progid(MSIPACKAGE *package, MSIPROGID* progid,
1206 UINT rc = ERROR_SUCCESS;
1208 if (progid->ParentIndex < 0)
1209 rc = register_progid_base(package, progid, clsid);
1214 static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
1215 static const WCHAR szDefaultIcon[] =
1216 {'D','e','f','a','u','l','t','I','c','o','n',0};
1217 static const WCHAR szCurVer[] =
1218 {'C','u','r','V','e','r',0};
1220 /* check if already registered */
1221 RegCreateKeyExW(HKEY_CLASSES_ROOT, progid->ProgID, 0, NULL, 0,
1222 KEY_ALL_ACCESS, NULL, &hkey, &disp );
1223 if (disp == REG_OPENED_EXISTING_KEY)
1225 TRACE("Key already registered\n");
1230 TRACE("Registering Parent %s index %i\n",
1231 debugstr_w(package->progids[progid->ParentIndex].ProgID),
1232 progid->ParentIndex);
1233 rc = register_progid(package,&package->progids[progid->ParentIndex],
1236 /* clsid is same as parent */
1237 RegCreateKeyW(hkey,szCLSID,&hkey2);
1238 RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)clsid, (strlenW(clsid)+1) *
1244 if (progid->Description)
1246 RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)progid->Description,
1247 (strlenW(progid->Description)+1) * sizeof(WCHAR));
1250 if (progid->IconPath)
1252 RegCreateKeyW(hkey,szDefaultIcon,&hkey2);
1253 RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)progid->IconPath,
1254 (strlenW(progid->IconPath)+1) * sizeof(WCHAR));
1258 /* write out the current version */
1259 if (progid->CurVerIndex >= 0)
1261 RegCreateKeyW(hkey,szCurVer,&hkey2);
1262 RegSetValueExW(hkey2,NULL,0,REG_SZ,
1263 (LPVOID)package->progids[progid->CurVerIndex].ProgID,
1264 (strlenW(package->progids[progid->CurVerIndex].ProgID)+1) *
1274 UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
1280 return ERROR_INVALID_HANDLE;
1282 load_classes_and_such(package);
1284 for (i = 0; i < package->loaded_progids; i++)
1286 WCHAR clsid[0x1000];
1288 /* check if this progid is to be installed */
1289 package->progids[i].InstallMe = ((package->progids[i].InstallMe) ||
1290 (package->progids[i].ClassIndex >= 0 &&
1291 package->classes[package->progids[i].ClassIndex].Installed));
1293 if (!package->progids[i].InstallMe)
1295 TRACE("progid %s not scheduled to be installed\n",
1296 debugstr_w(package->progids[i].ProgID));
1300 TRACE("Registering progid %s index %i\n",
1301 debugstr_w(package->progids[i].ProgID), i);
1303 register_progid(package,&package->progids[i],clsid);
1305 uirow = MSI_CreateRecord(1);
1306 MSI_RecordSetStringW(uirow,1,package->progids[i].ProgID);
1307 ui_actiondata(package,szRegisterProgIdInfo,uirow);
1308 msiobj_release(&uirow->hdr);
1311 return ERROR_SUCCESS;
1314 static UINT register_verb(MSIPACKAGE *package, LPCWSTR progid,
1315 MSICOMPONENT* component, MSIEXTENSION* extension,
1316 MSIVERB* verb, INT* Sequence )
1320 static const WCHAR szShell[] = {'s','h','e','l','l',0};
1321 static const WCHAR szCommand[] = {'c','o','m','m','a','n','d',0};
1322 static const WCHAR fmt[] = {'\"','%','s','\"',' ','%','s',0};
1323 static const WCHAR fmt2[] = {'\"','%','s','\"',0};
1328 keyname = build_directory_name(4, progid, szShell, verb->Verb, szCommand);
1330 TRACE("Making Key %s\n",debugstr_w(keyname));
1331 RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key);
1332 size = strlenW(component->FullKeypath);
1334 size += strlenW(verb->Argument);
1337 command = HeapAlloc(GetProcessHeap(),0, size * sizeof (WCHAR));
1339 sprintfW(command, fmt, component->FullKeypath, verb->Argument);
1341 sprintfW(command, fmt2, component->FullKeypath);
1343 RegSetValueExW(key,NULL,0,REG_SZ, (LPVOID)command, (strlenW(command)+1)*
1345 HeapFree(GetProcessHeap(),0,command);
1347 advertise = create_component_advertise_string(package, component,
1348 extension->Feature->Feature);
1350 size = strlenW(advertise);
1353 size += strlenW(verb->Argument);
1356 command = HeapAlloc(GetProcessHeap(),0, size * sizeof (WCHAR));
1357 memset(command,0,size*sizeof(WCHAR));
1359 strcpyW(command,advertise);
1362 static const WCHAR szSpace[] = {' ',0};
1363 strcatW(command,szSpace);
1364 strcatW(command,verb->Argument);
1367 RegSetValueExW(key, szCommand, 0, REG_MULTI_SZ, (LPBYTE)command,
1368 (strlenW(command)+2)*sizeof(WCHAR));
1371 HeapFree(GetProcessHeap(),0,keyname);
1372 HeapFree(GetProcessHeap(),0,advertise);
1373 HeapFree(GetProcessHeap(),0,command);
1377 keyname = build_directory_name(3, progid, szShell, verb->Verb);
1378 RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key);
1379 RegSetValueExW(key,NULL,0,REG_SZ, (LPVOID)verb->Command,
1380 (strlenW(verb->Command)+1) *sizeof(WCHAR));
1382 HeapFree(GetProcessHeap(),0,keyname);
1385 if (verb->Sequence != MSI_NULL_INTEGER)
1387 if (*Sequence == MSI_NULL_INTEGER || verb->Sequence < *Sequence)
1389 *Sequence = verb->Sequence;
1390 keyname = build_directory_name(2, progid, szShell);
1391 RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key);
1392 RegSetValueExW(key,NULL,0,REG_SZ, (LPVOID)verb->Verb,
1393 (strlenW(verb->Verb)+1) *sizeof(WCHAR));
1395 HeapFree(GetProcessHeap(),0,keyname);
1398 return ERROR_SUCCESS;
1401 UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package)
1403 static const WCHAR szContentType[] =
1404 {'C','o','n','t','e','n','t',' ','T','y','p','e',0 };
1408 BOOL install_on_demand = TRUE;
1411 return ERROR_INVALID_HANDLE;
1413 load_classes_and_such(package);
1415 /* We need to set install_on_demand based on if the shell handles advertised
1416 * shortcuts and the like. Because Mike McCormack is working on this i am
1417 * going to default to TRUE
1420 for (i = 0; i < package->loaded_extensions; i++)
1422 WCHAR extension[257];
1423 MSIFEATURE *feature;
1425 if (!package->extensions[i].Component)
1428 feature = package->extensions[i].Feature;
1431 * yes. MSDN says that these are based on _Feature_ not on
1432 * Component. So verify the feature is to be installed
1434 if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) &&
1435 !(install_on_demand &&
1436 ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED )))
1438 TRACE("Skipping extension %s reg due to disabled feature %s\n",
1439 debugstr_w(package->extensions[i].Extension),
1440 debugstr_w(feature->Feature));
1445 TRACE("Registering extension %s index %i\n",
1446 debugstr_w(package->extensions[i].Extension), i);
1448 package->extensions[i].Installed = TRUE;
1450 /* this is only registered if the extension has at least 1 verb
1453 if (package->extensions[i].ProgIDIndex >= 0 &&
1454 package->extensions[i].VerbCount > 0)
1455 mark_progid_for_install(package, package->extensions[i].ProgIDIndex);
1457 if (package->extensions[i].MIMEIndex >= 0)
1458 mark_mime_for_install(package, package->extensions[i].MIMEIndex);
1462 strcatW(extension,package->extensions[i].Extension);
1464 RegCreateKeyW(HKEY_CLASSES_ROOT,extension,&hkey);
1466 if (package->extensions[i].MIMEIndex >= 0)
1468 RegSetValueExW(hkey,szContentType,0,REG_SZ,
1469 (LPVOID)package->mimes[package->extensions[i].
1470 MIMEIndex].ContentType,
1471 (strlenW(package->mimes[package->extensions[i].
1472 MIMEIndex].ContentType)+1)*sizeof(WCHAR));
1475 if (package->extensions[i].ProgIDIndex >= 0 ||
1476 package->extensions[i].ProgIDText)
1478 static const WCHAR szSN[] =
1479 {'\\','S','h','e','l','l','N','e','w',0};
1484 INT Sequence = MSI_NULL_INTEGER;
1486 if (package->extensions[i].ProgIDIndex >= 0)
1487 progid = package->progids[package->extensions[i].
1488 ProgIDIndex].ProgID;
1490 progid = package->extensions[i].ProgIDText;
1492 RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)progid,
1493 (strlenW(progid)+1)*sizeof(WCHAR));
1495 newkey = HeapAlloc(GetProcessHeap(),0,
1496 (strlenW(progid)+strlenW(szSN)+1) * sizeof(WCHAR));
1498 strcpyW(newkey,progid);
1499 strcatW(newkey,szSN);
1500 RegCreateKeyW(hkey,newkey,&hkey2);
1503 HeapFree(GetProcessHeap(),0,newkey);
1505 /* do all the verbs */
1506 for (v = 0; v < package->extensions[i].VerbCount; v++)
1507 register_verb(package, progid,
1508 package->extensions[i].Component,
1509 &package->extensions[i],
1510 &package->verbs[package->extensions[i].Verbs[v]],
1516 uirow = MSI_CreateRecord(1);
1517 MSI_RecordSetStringW(uirow,1,package->extensions[i].Extension);
1518 ui_actiondata(package,szRegisterExtensionInfo,uirow);
1519 msiobj_release(&uirow->hdr);
1522 return ERROR_SUCCESS;
1525 UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package)
1527 static const WCHAR szExten[] =
1528 {'E','x','t','e','n','s','i','o','n',0 };
1534 return ERROR_INVALID_HANDLE;
1536 load_classes_and_such(package);
1538 for (i = 0; i < package->loaded_mimes; i++)
1540 WCHAR extension[257];
1543 static const WCHAR fmt[] =
1544 {'M','I','M','E','\\','D','a','t','a','b','a','s','e','\\',
1545 'C','o','n','t','e','n','t',' ','T','y','p','e','\\', '%','s',0};
1549 * check if the MIME is to be installed. Either as requesed by an
1550 * extension or Class
1552 package->mimes[i].InstallMe = ((package->mimes[i].InstallMe) ||
1553 (package->mimes[i].ClassIndex >= 0 &&
1554 package->classes[package->mimes[i].ClassIndex].Installed) ||
1555 (package->mimes[i].ExtensionIndex >=0 &&
1556 package->extensions[package->mimes[i].ExtensionIndex].Installed));
1558 if (!package->mimes[i].InstallMe)
1560 TRACE("MIME %s not scheduled to be installed\n",
1561 debugstr_w(package->mimes[i].ContentType));
1565 mime = package->mimes[i].ContentType;
1566 exten = package->extensions[package->mimes[i].ExtensionIndex].Extension;
1569 strcatW(extension,exten);
1571 key = HeapAlloc(GetProcessHeap(),0,(strlenW(mime)+strlenW(fmt)+1) *
1573 sprintfW(key,fmt,mime);
1574 RegCreateKeyW(HKEY_CLASSES_ROOT,key,&hkey);
1575 RegSetValueExW(hkey,szExten,0,REG_SZ,(LPVOID)extension,
1576 (strlenW(extension)+1)*sizeof(WCHAR));
1578 HeapFree(GetProcessHeap(),0,key);
1580 if (package->mimes[i].CLSID[0])
1582 FIXME("Handle non null for field 3\n");
1587 uirow = MSI_CreateRecord(2);
1588 MSI_RecordSetStringW(uirow,1,package->mimes[i].ContentType);
1589 MSI_RecordSetStringW(uirow,2,exten);
1590 ui_actiondata(package,szRegisterMIMEInfo,uirow);
1591 msiobj_release(&uirow->hdr);
1594 return ERROR_SUCCESS;