2 * Custom Action processing for 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
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/summary_list_of_all_custom_action_types.asp
36 #include "wine/debug.h"
40 #include "msvcrt/fcntl.h"
47 #include "wine/unicode.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(msi);
53 #define CUSTOM_ACTION_TYPE_MASK 0x3F
54 static const WCHAR c_collen[] = {'C',':','\\',0};
55 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
57 typedef struct tagMSIRUNNINGACTION
64 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
65 LPCWSTR target, const INT type, LPCWSTR action);
66 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
67 LPCWSTR target, const INT type, LPCWSTR action);
68 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
69 LPCWSTR target, const INT type, LPCWSTR action);
70 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
71 LPCWSTR target, const INT type, LPCWSTR action);
72 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
73 LPCWSTR target, const INT type, LPCWSTR action);
74 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
75 LPCWSTR target, const INT type, LPCWSTR action);
77 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
79 UINT rc = ERROR_SUCCESS;
82 static const WCHAR ExecSeqQuery[] =
83 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
84 '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
85 ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
86 '=',' ','\'','%','s','\'',0};
90 WCHAR *deformated=NULL;
92 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, action);
93 if (rc != ERROR_SUCCESS)
96 rc = MSI_ViewExecute(view, 0);
97 if (rc != ERROR_SUCCESS)
100 msiobj_release(&view->hdr);
104 rc = MSI_ViewFetch(view,&row);
105 if (rc != ERROR_SUCCESS)
108 msiobj_release(&view->hdr);
109 return ERROR_CALL_NOT_IMPLEMENTED;
112 type = MSI_RecordGetInteger(row,2);
114 source = load_dynamic_stringW(row,3);
115 target = load_dynamic_stringW(row,4);
117 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
118 debugstr_w(source), debugstr_w(target));
120 /* handle some of the deferred actions */
125 FIXME("Rollback only action... rollbacks not supported yet\n");
126 schedule_action(package, ROLLBACK_SCRIPT, action);
127 HeapFree(GetProcessHeap(),0,source);
128 HeapFree(GetProcessHeap(),0,target);
129 msiobj_release(&row->hdr);
131 msiobj_release(&view->hdr);
132 return ERROR_SUCCESS;
138 TRACE("Deferring Commit Action!\n");
139 schedule_action(package, COMMIT_SCRIPT, action);
143 TRACE("Deferring Action!\n");
144 schedule_action(package, INSTALL_SCRIPT, action);
147 HeapFree(GetProcessHeap(),0,source);
148 HeapFree(GetProcessHeap(),0,target);
149 msiobj_release(&row->hdr);
151 msiobj_release(&view->hdr);
152 return ERROR_SUCCESS;
158 static const WCHAR szActionData[] = {
159 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
160 LPWSTR actiondata = load_dynamic_property(package,action,NULL);
162 MSI_SetPropertyW(package,szActionData,actiondata);
166 switch (type & CUSTOM_ACTION_TYPE_MASK)
168 case 1: /* DLL file stored in a Binary table stream */
169 rc = HANDLE_CustomType1(package,source,target,type,action);
171 case 2: /* EXE file stored in a Binary table strem */
172 rc = HANDLE_CustomType2(package,source,target,type,action);
174 case 18: /*EXE file installed with package */
175 rc = HANDLE_CustomType18(package,source,target,type,action);
177 case 19: /* Error that halts install */
178 rc = HANDLE_CustomType19(package,source,target,type,action);
180 case 50: /*EXE file specified by a property value */
181 rc = HANDLE_CustomType50(package,source,target,type,action);
183 case 34: /*EXE to be run in specified directory */
184 rc = HANDLE_CustomType34(package,source,target,type,action);
186 case 35: /* Directory set with formatted text. */
187 deformat_string(package,target,&deformated);
188 MSI_SetTargetPathW(package, source, deformated);
189 HeapFree(GetProcessHeap(),0,deformated);
191 case 51: /* Property set with formatted text. */
192 deformat_string(package,target,&deformated);
193 rc = MSI_SetPropertyW(package,source,deformated);
194 HeapFree(GetProcessHeap(),0,deformated);
197 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
198 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
202 HeapFree(GetProcessHeap(),0,source);
203 HeapFree(GetProcessHeap(),0,target);
204 msiobj_release(&row->hdr);
206 msiobj_release(&view->hdr);
211 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
215 static const WCHAR f1[] = {'m','s','i',0};
218 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz)
220 GetTempPathW(MAX_PATH,fmt);
222 if (GetTempFileNameW(fmt,f1,0,tmp_file) == 0)
224 TRACE("Unable to create file\n");
225 return ERROR_FUNCTION_FAILED;
229 /* write out the file */
233 static const WCHAR fmt[] =
234 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
235 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',
236 ' ','`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
240 if (track_tempfile(package, tmp_file, tmp_file)!=0)
241 FIXME("File Name in temp tracking collision\n");
243 the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
244 FILE_ATTRIBUTE_NORMAL, NULL);
246 if (the_file == INVALID_HANDLE_VALUE)
247 return ERROR_FUNCTION_FAILED;
249 rc = MSI_OpenQuery(package->db, &view, fmt, source);
250 if (rc != ERROR_SUCCESS)
253 rc = MSI_ViewExecute(view, 0);
254 if (rc != ERROR_SUCCESS)
257 msiobj_release(&view->hdr);
261 rc = MSI_ViewFetch(view,&row);
262 if (rc != ERROR_SUCCESS)
265 msiobj_release(&view->hdr);
273 rc = MSI_RecordReadStream(row,2,buffer,&sz);
274 if (rc != ERROR_SUCCESS)
276 ERR("Failed to get stream\n");
277 CloseHandle(the_file);
278 DeleteFileW(tmp_file);
281 WriteFile(the_file,buffer,sz,&write,NULL);
282 } while (sz == 1024);
284 CloseHandle(the_file);
286 msiobj_release(&row->hdr);
288 msiobj_release(&view->hdr);
291 return ERROR_SUCCESS;
294 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
295 BOOL process, LPCWSTR name)
297 MSIRUNNINGACTION *newbuf = NULL;
299 count = package->RunningActionCount;
300 package->RunningActionCount++;
302 newbuf = HeapReAlloc(GetProcessHeap(),0,
303 package->RunningAction,
304 package->RunningActionCount * sizeof(MSIRUNNINGACTION));
306 newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(MSIRUNNINGACTION));
308 newbuf[count].handle = Handle;
309 newbuf[count].process = process;
310 newbuf[count].name = strdupW(name);
312 package->RunningAction = newbuf;
315 static UINT process_action_return_value(UINT type, HANDLE ThreadHandle)
321 GetExitCodeProcess(ThreadHandle,&rc);
324 return ERROR_SUCCESS;
326 return ERROR_FUNCTION_FAILED;
329 GetExitCodeThread(ThreadHandle,&rc);
333 case ERROR_FUNCTION_NOT_CALLED:
335 case ERROR_INSTALL_USEREXIT:
336 case ERROR_INSTALL_FAILURE:
338 case ERROR_NO_MORE_ITEMS:
339 return ERROR_SUCCESS;
341 ERR("Invalid Return Code %lx\n",rc);
342 return ERROR_INSTALL_FAILURE;
346 static UINT process_handle(MSIPACKAGE* package, UINT type,
347 HANDLE ThreadHandle, HANDLE ProcessHandle,
350 UINT rc = ERROR_SUCCESS;
355 TRACE("Synchronous Execution of action %s\n",debugstr_w(Name));
357 msi_dialog_check_messages(ProcessHandle);
359 msi_dialog_check_messages(ThreadHandle);
364 rc = process_action_return_value(2,ProcessHandle);
366 rc = process_action_return_value(1,ThreadHandle);
369 CloseHandle(ThreadHandle);
371 CloseHandle(ProcessHandle);
375 TRACE("Asynchronous Execution of action %s\n",debugstr_w(Name));
381 file_running_action(package, ProcessHandle, TRUE, Name);
382 CloseHandle(ThreadHandle);
385 file_running_action(package, ThreadHandle, FALSE, Name);
389 CloseHandle(ThreadHandle);
391 CloseHandle(ProcessHandle);
399 typedef UINT __stdcall CustomEntry(MSIHANDLE);
408 static DWORD WINAPI ACTION_CallDllFunction(thread_struct *stuff)
413 DWORD rc = ERROR_SUCCESS;
415 TRACE("calling function (%s, %s) \n", debugstr_w(stuff->source),
416 debugstr_w(stuff->target));
418 hModule = LoadLibraryW(stuff->source);
421 proc = strdupWtoA( stuff->target );
422 fn = (CustomEntry*)GetProcAddress(hModule,proc);
426 MSIPACKAGE *package = stuff->package;
428 TRACE("Calling function %s\n", proc);
429 hPackage = msiobj_findhandle( &package->hdr );
433 msiobj_release( &package->hdr );
436 ERR("Handle for object %p not found\n", package );
439 ERR("Cannot load functon\n");
441 HeapFree(GetProcessHeap(),0,proc);
442 FreeLibrary(hModule);
445 ERR("Unable to load library\n");
446 msiobj_release( &stuff->package->hdr );
447 HeapFree(GetProcessHeap(),0,stuff->source);
448 HeapFree(GetProcessHeap(),0,stuff->target);
449 HeapFree(GetProcessHeap(), 0, stuff);
453 static DWORD WINAPI DllThread(LPVOID info)
455 thread_struct *stuff;
458 TRACE("MSI Thread (0x%lx) started for custom action\n",
459 GetCurrentThreadId());
461 stuff = (thread_struct*)info;
462 rc = ACTION_CallDllFunction(stuff);
464 TRACE("MSI Thread (0x%lx) finished (rc %li)\n",GetCurrentThreadId(), rc);
465 /* clse all handles for this thread */
466 MsiCloseAllHandles();
470 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
471 LPCWSTR target, const INT type, LPCWSTR action)
473 WCHAR tmp_file[MAX_PATH];
477 UINT rc = ERROR_SUCCESS;
479 store_binary_to_temp(package, source, tmp_file);
481 TRACE("Calling function %s from %s\n",debugstr_w(target),
482 debugstr_w(tmp_file));
484 if (!strchrW(tmp_file,'.'))
486 static const WCHAR dot[]={'.',0};
487 strcatW(tmp_file,dot);
490 info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
491 msiobj_addref( &package->hdr );
492 info->package = package;
493 info->target = strdupW(target);
494 info->source = strdupW(tmp_file);
496 ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId);
498 rc = process_handle(package, type, ThreadHandle, NULL, action);
503 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
504 LPCWSTR target, const INT type, LPCWSTR action)
506 WCHAR tmp_file[MAX_PATH];
508 PROCESS_INFORMATION info;
513 static const WCHAR spc[] = {' ',0};
514 UINT prc = ERROR_SUCCESS;
516 memset(&si,0,sizeof(STARTUPINFOW));
518 store_binary_to_temp(package, source, tmp_file);
520 deformat_string(package,target,&deformated);
522 len = strlenW(tmp_file)+2;
525 len += strlenW(deformated);
527 cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
529 strcpyW(cmd,tmp_file);
533 strcatW(cmd,deformated);
535 HeapFree(GetProcessHeap(),0,deformated);
538 TRACE("executing exe %s \n",debugstr_w(cmd));
540 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
541 c_collen, &si, &info);
543 HeapFree(GetProcessHeap(),0,cmd);
547 ERR("Unable to execute command\n");
548 return ERROR_SUCCESS;
551 prc = process_handle(package, type, info.hThread, info.hProcess, action);
556 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
557 LPCWSTR target, const INT type, LPCWSTR action)
560 PROCESS_INFORMATION info;
565 static const WCHAR spc[] = {' ',0};
569 memset(&si,0,sizeof(STARTUPINFOW));
571 index = get_loaded_file(package,source);
573 len = strlenW(package->files[index].TargetPath);
575 deformat_string(package,target,&deformated);
577 len += strlenW(deformated);
580 cmd = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
582 strcpyW(cmd, package->files[index].TargetPath);
586 strcatW(cmd, deformated);
588 HeapFree(GetProcessHeap(),0,deformated);
591 TRACE("executing exe %s \n",debugstr_w(cmd));
593 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
594 c_collen, &si, &info);
596 HeapFree(GetProcessHeap(),0,cmd);
600 ERR("Unable to execute command\n");
601 return ERROR_SUCCESS;
604 prc = process_handle(package, type, info.hThread, info.hProcess, action);
609 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
610 LPCWSTR target, const INT type, LPCWSTR action)
612 static const WCHAR query[] = {
613 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
614 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
615 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
618 MSIQUERY *view = NULL;
621 LPWSTR deformated = NULL;
623 deformat_string( package, target, &deformated );
625 /* first try treat the error as a number */
626 r = MSI_OpenQuery( package->db, &view, query, deformated );
627 if( r == ERROR_SUCCESS )
629 r = MSI_ViewExecute( view, 0 );
630 if( r == ERROR_SUCCESS )
632 r = MSI_ViewFetch( view, &row );
633 if( r == ERROR_SUCCESS )
635 LPCWSTR error = MSI_RecordGetString( row, 1 );
636 MessageBoxW( NULL, error, NULL, MB_OK );
637 msiobj_release( &row->hdr );
640 MSI_ViewClose( view );
641 msiobj_release( &view->hdr );
644 if (r != ERROR_SUCCESS )
646 MessageBoxW( NULL, deformated, NULL, MB_OK );
647 HeapFree( GetProcessHeap(), 0, deformated );
650 return ERROR_FUNCTION_FAILED;
653 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
654 LPCWSTR target, const INT type, LPCWSTR action)
657 PROCESS_INFORMATION info;
664 static const WCHAR spc[] = {' ',0};
666 memset(&si,0,sizeof(STARTUPINFOW));
667 memset(&info,0,sizeof(PROCESS_INFORMATION));
669 prop = load_dynamic_property(package,source,&prc);
671 return ERROR_SUCCESS;
673 deformat_string(package,target,&deformated);
674 len = strlenW(prop) + 2;
676 len += strlenW(deformated);
678 cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
684 strcatW(cmd,deformated);
686 HeapFree(GetProcessHeap(),0,deformated);
689 TRACE("executing exe %s \n",debugstr_w(cmd));
691 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
692 c_collen, &si, &info);
694 HeapFree(GetProcessHeap(),0,cmd);
698 ERR("Unable to execute command\n");
699 return ERROR_SUCCESS;
702 prc = process_handle(package, type, info.hThread, info.hProcess, action);
707 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
708 LPCWSTR target, const INT type, LPCWSTR action)
710 LPWSTR filename, deformated;
712 PROCESS_INFORMATION info;
716 memset(&si,0,sizeof(STARTUPINFOW));
718 filename = resolve_folder(package, source, FALSE, FALSE, NULL);
721 return ERROR_FUNCTION_FAILED;
723 SetCurrentDirectoryW(filename);
724 HeapFree(GetProcessHeap(),0,filename);
726 deformat_string(package,target,&deformated);
729 return ERROR_FUNCTION_FAILED;
731 TRACE("executing exe %s \n",debugstr_w(deformated));
733 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
734 c_collen, &si, &info);
735 HeapFree(GetProcessHeap(),0,deformated);
739 ERR("Unable to execute command\n");
740 return ERROR_SUCCESS;
743 prc = process_handle(package, type, info.hThread, info.hProcess, action);
749 void ACTION_FinishCustomActions(MSIPACKAGE* package)
754 for (i = 0; i < package->RunningActionCount; i++)
756 TRACE("Checking on action %s\n",
757 debugstr_w(package->RunningAction[i].name));
759 if (package->RunningAction[i].process)
760 GetExitCodeProcess(package->RunningAction[i].handle, &rc);
762 GetExitCodeThread(package->RunningAction[i].handle, &rc);
764 if (rc == STILL_ACTIVE)
766 TRACE("Waiting on action %s\n",
767 debugstr_w(package->RunningAction[i].name));
768 msi_dialog_check_messages(package->RunningAction[i].handle);
771 HeapFree(GetProcessHeap(),0,package->RunningAction[i].name);
772 CloseHandle(package->RunningAction[i].handle);
775 HeapFree(GetProcessHeap(),0,package->RunningAction);