Implement NtAccessCheck.
[wine/gsoc-2012-control.git] / dlls / msi / custom.c
blob8c45444330491965e82e6fe185d1b118ff98b771
1 /*
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
22 * Pages I need
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/summary_list_of_all_custom_action_types.asp
27 #include <stdarg.h>
28 #include <stdio.h>
30 #define COBJMACROS
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winerror.h"
35 #include "winreg.h"
36 #include "wine/debug.h"
37 #include "fdi.h"
38 #include "msi.h"
39 #include "msiquery.h"
40 #include "msvcrt/fcntl.h"
41 #include "objbase.h"
42 #include "objidl.h"
43 #include "msipriv.h"
44 #include "winnls.h"
45 #include "winuser.h"
46 #include "shlobj.h"
47 #include "wine/unicode.h"
48 #include "winver.h"
49 #include "action.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
59 HANDLE handle;
60 BOOL process;
61 LPWSTR name;
62 } MSIRUNNINGACTION;
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;
80 MSIQUERY * view;
81 MSIRECORD * row = 0;
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};
87 UINT type;
88 LPWSTR source;
89 LPWSTR target;
90 WCHAR *deformated=NULL;
92 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, action);
93 if (rc != ERROR_SUCCESS)
94 return rc;
96 rc = MSI_ViewExecute(view, 0);
97 if (rc != ERROR_SUCCESS)
99 MSI_ViewClose(view);
100 msiobj_release(&view->hdr);
101 return rc;
104 rc = MSI_ViewFetch(view,&row);
105 if (rc != ERROR_SUCCESS)
107 MSI_ViewClose(view);
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 */
121 if (type & 0x400)
123 if (type & 0x100)
125 FIXME("Rollback only action... rollbacks not supported yet\n");
126 HeapFree(GetProcessHeap(),0,source);
127 HeapFree(GetProcessHeap(),0,target);
128 msiobj_release(&row->hdr);
129 MSI_ViewClose(view);
130 msiobj_release(&view->hdr);
131 return ERROR_SUCCESS;
133 if (!execute)
135 LPWSTR *newbuf = NULL;
136 INT count;
137 if (type & 0x200)
139 TRACE("Deferring Commit Action!\n");
140 count = package->CommitActionCount;
141 package->CommitActionCount++;
142 if (count != 0)
143 newbuf = HeapReAlloc(GetProcessHeap(),0,
144 package->CommitAction,
145 package->CommitActionCount * sizeof(LPWSTR));
146 else
147 newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR));
149 newbuf[count] = strdupW(action);
150 package->CommitAction = newbuf;
152 else
154 TRACE("Deferring Action!\n");
155 count = package->DeferredActionCount;
156 package->DeferredActionCount++;
157 if (count != 0)
158 newbuf = HeapReAlloc(GetProcessHeap(),0,
159 package->DeferredAction,
160 package->DeferredActionCount * sizeof(LPWSTR));
161 else
162 newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR));
164 newbuf[count] = strdupW(action);
165 package->DeferredAction = newbuf;
168 HeapFree(GetProcessHeap(),0,source);
169 HeapFree(GetProcessHeap(),0,target);
170 msiobj_release(&row->hdr);
171 MSI_ViewClose(view);
172 msiobj_release(&view->hdr);
173 return ERROR_SUCCESS;
175 else
177 /*Set ActionData*/
179 static const WCHAR szActionData[] = {
180 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
181 LPWSTR actiondata = load_dynamic_property(package,action,NULL);
182 if (actiondata)
183 MSI_SetPropertyW(package,szActionData,actiondata);
187 switch (type & CUSTOM_ACTION_TYPE_MASK)
189 case 1: /* DLL file stored in a Binary table stream */
190 rc = HANDLE_CustomType1(package,source,target,type,action);
191 break;
192 case 2: /* EXE file stored in a Binary table strem */
193 rc = HANDLE_CustomType2(package,source,target,type,action);
194 break;
195 case 18: /*EXE file installed with package */
196 rc = HANDLE_CustomType18(package,source,target,type,action);
197 break;
198 case 19: /* Error that halts install */
199 rc = HANDLE_CustomType19(package,source,target,type,action);
200 break;
201 case 50: /*EXE file specified by a property value */
202 rc = HANDLE_CustomType50(package,source,target,type,action);
203 break;
204 case 34: /*EXE to be run in specified directory */
205 rc = HANDLE_CustomType34(package,source,target,type,action);
206 break;
207 case 35: /* Directory set with formatted text. */
208 deformat_string(package,target,&deformated);
209 MSI_SetTargetPathW(package, source, deformated);
210 HeapFree(GetProcessHeap(),0,deformated);
211 break;
212 case 51: /* Property set with formatted text. */
213 deformat_string(package,target,&deformated);
214 rc = MSI_SetPropertyW(package,source,deformated);
215 HeapFree(GetProcessHeap(),0,deformated);
216 break;
217 default:
218 FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
219 type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
220 debugstr_w(target));
223 HeapFree(GetProcessHeap(),0,source);
224 HeapFree(GetProcessHeap(),0,target);
225 msiobj_release(&row->hdr);
226 MSI_ViewClose(view);
227 msiobj_release(&view->hdr);
228 return rc;
232 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source,
233 LPWSTR tmp_file)
235 DWORD sz=MAX_PATH;
236 static const WCHAR f1[] = {'m','s','i',0};
237 WCHAR fmt[MAX_PATH];
239 if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz)
240 != ERROR_SUCCESS)
241 GetTempPathW(MAX_PATH,fmt);
243 if (GetTempFileNameW(fmt,f1,0,tmp_file) == 0)
245 TRACE("Unable to create file\n");
246 return ERROR_FUNCTION_FAILED;
248 else
250 /* write out the file */
251 UINT rc;
252 MSIQUERY * view;
253 MSIRECORD * row = 0;
254 static const WCHAR fmt[] =
255 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
256 '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',
257 ' ','`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
258 HANDLE the_file;
259 CHAR buffer[1024];
261 if (track_tempfile(package, tmp_file, tmp_file)!=0)
262 FIXME("File Name in temp tracking collision\n");
264 the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
265 FILE_ATTRIBUTE_NORMAL, NULL);
267 if (the_file == INVALID_HANDLE_VALUE)
268 return ERROR_FUNCTION_FAILED;
270 rc = MSI_OpenQuery(package->db, &view, fmt, source);
271 if (rc != ERROR_SUCCESS)
272 return rc;
274 rc = MSI_ViewExecute(view, 0);
275 if (rc != ERROR_SUCCESS)
277 MSI_ViewClose(view);
278 msiobj_release(&view->hdr);
279 return rc;
282 rc = MSI_ViewFetch(view,&row);
283 if (rc != ERROR_SUCCESS)
285 MSI_ViewClose(view);
286 msiobj_release(&view->hdr);
287 return rc;
292 DWORD write;
293 sz = 1024;
294 rc = MSI_RecordReadStream(row,2,buffer,&sz);
295 if (rc != ERROR_SUCCESS)
297 ERR("Failed to get stream\n");
298 CloseHandle(the_file);
299 DeleteFileW(tmp_file);
300 break;
302 WriteFile(the_file,buffer,sz,&write,NULL);
303 } while (sz == 1024);
305 CloseHandle(the_file);
307 msiobj_release(&row->hdr);
308 MSI_ViewClose(view);
309 msiobj_release(&view->hdr);
312 return ERROR_SUCCESS;
315 static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
316 BOOL process, LPCWSTR name)
318 MSIRUNNINGACTION *newbuf = NULL;
319 INT count;
320 count = package->RunningActionCount;
321 package->RunningActionCount++;
322 if (count != 0)
323 newbuf = HeapReAlloc(GetProcessHeap(),0,
324 package->RunningAction,
325 package->RunningActionCount * sizeof(MSIRUNNINGACTION));
326 else
327 newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(MSIRUNNINGACTION));
329 newbuf[count].handle = Handle;
330 newbuf[count].process = process;
331 newbuf[count].name = strdupW(name);
333 package->RunningAction = newbuf;
336 static UINT process_action_return_value(UINT type, HANDLE ThreadHandle)
338 DWORD rc=0;
340 if (type == 2)
342 GetExitCodeProcess(ThreadHandle,&rc);
344 if (rc == 0)
345 return ERROR_SUCCESS;
346 else
347 return ERROR_FUNCTION_FAILED;
350 GetExitCodeThread(ThreadHandle,&rc);
352 switch (rc)
354 case ERROR_FUNCTION_NOT_CALLED:
355 case ERROR_SUCCESS:
356 case ERROR_INSTALL_USEREXIT:
357 case ERROR_INSTALL_FAILURE:
358 return rc;
359 case ERROR_NO_MORE_ITEMS:
360 return ERROR_SUCCESS;
361 default:
362 ERR("Invalid Return Code %lx\n",rc);
363 return ERROR_INSTALL_FAILURE;
367 static UINT process_handle(MSIPACKAGE* package, UINT type,
368 HANDLE ThreadHandle, HANDLE ProcessHandle,
369 LPCWSTR Name)
371 UINT rc = ERROR_SUCCESS;
373 if (!(type & 0x80))
375 /* synchronous */
376 TRACE("Synchronous Execution of action %s\n",debugstr_w(Name));
377 if (ProcessHandle)
378 msi_dialog_check_messages(ProcessHandle);
379 else
380 msi_dialog_check_messages(ThreadHandle);
382 if (!(type & 0x40))
384 if (ProcessHandle)
385 rc = process_action_return_value(2,ProcessHandle);
386 else
387 rc = process_action_return_value(1,ThreadHandle);
390 CloseHandle(ThreadHandle);
391 if (ProcessHandle);
392 CloseHandle(ProcessHandle);
394 else
396 TRACE("Asynchronous Execution of action %s\n",debugstr_w(Name));
397 /* asynchronous */
398 if (type & 0x40)
400 if (ProcessHandle)
402 file_running_action(package, ProcessHandle, TRUE, Name);
403 CloseHandle(ThreadHandle);
405 else
406 file_running_action(package, ThreadHandle, FALSE, Name);
408 else
410 CloseHandle(ThreadHandle);
411 if (ProcessHandle);
412 CloseHandle(ProcessHandle);
416 return rc;
420 typedef UINT __stdcall CustomEntry(MSIHANDLE);
422 typedef struct
424 MSIPACKAGE *package;
425 WCHAR *target;
426 WCHAR *source;
427 } thread_struct;
429 static DWORD WINAPI ACTION_CallDllFunction(thread_struct *stuff)
431 HANDLE hModule;
432 LPSTR proc;
433 CustomEntry *fn;
434 DWORD rc = ERROR_SUCCESS;
436 TRACE("calling function (%s, %s) \n", debugstr_w(stuff->source),
437 debugstr_w(stuff->target));
439 hModule = LoadLibraryW(stuff->source);
440 if (hModule)
442 proc = strdupWtoA( stuff->target );
443 fn = (CustomEntry*)GetProcAddress(hModule,proc);
444 if (fn)
446 MSIHANDLE hPackage;
447 MSIPACKAGE *package = stuff->package;
449 TRACE("Calling function %s\n", proc);
450 hPackage = msiobj_findhandle( &package->hdr );
451 if (hPackage )
453 rc = fn(hPackage);
454 msiobj_release( &package->hdr );
456 else
457 ERR("Handle for object %p not found\n", package );
459 else
460 ERR("Cannot load functon\n");
462 HeapFree(GetProcessHeap(),0,proc);
463 FreeLibrary(hModule);
465 else
466 ERR("Unable to load library\n");
467 msiobj_release( &stuff->package->hdr );
468 HeapFree(GetProcessHeap(),0,stuff->source);
469 HeapFree(GetProcessHeap(),0,stuff->target);
470 HeapFree(GetProcessHeap(), 0, stuff);
471 return rc;
474 static DWORD WINAPI DllThread(LPVOID info)
476 thread_struct *stuff;
477 DWORD rc = 0;
479 TRACE("MSI Thread (0x%lx) started for custom action\n",
480 GetCurrentThreadId());
482 stuff = (thread_struct*)info;
483 rc = ACTION_CallDllFunction(stuff);
485 TRACE("MSI Thread (0x%lx) finished (rc %li)\n",GetCurrentThreadId(), rc);
486 /* clse all handles for this thread */
487 MsiCloseAllHandles();
488 return rc;
491 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
492 LPCWSTR target, const INT type, LPCWSTR action)
494 WCHAR tmp_file[MAX_PATH];
495 thread_struct *info;
496 DWORD ThreadId;
497 HANDLE ThreadHandle;
498 UINT rc = ERROR_SUCCESS;
500 store_binary_to_temp(package, source, tmp_file);
502 TRACE("Calling function %s from %s\n",debugstr_w(target),
503 debugstr_w(tmp_file));
505 if (!strchrW(tmp_file,'.'))
507 static const WCHAR dot[]={'.',0};
508 strcatW(tmp_file,dot);
511 info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
512 msiobj_addref( &package->hdr );
513 info->package = package;
514 info->target = strdupW(target);
515 info->source = strdupW(tmp_file);
517 ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId);
519 rc = process_handle(package, type, ThreadHandle, NULL, action);
521 return rc;
524 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
525 LPCWSTR target, const INT type, LPCWSTR action)
527 WCHAR tmp_file[MAX_PATH];
528 STARTUPINFOW si;
529 PROCESS_INFORMATION info;
530 BOOL rc;
531 INT len;
532 WCHAR *deformated;
533 WCHAR *cmd;
534 static const WCHAR spc[] = {' ',0};
535 UINT prc = ERROR_SUCCESS;
537 memset(&si,0,sizeof(STARTUPINFOW));
539 store_binary_to_temp(package, source, tmp_file);
541 deformat_string(package,target,&deformated);
543 len = strlenW(tmp_file)+2;
545 if (deformated)
546 len += strlenW(deformated);
548 cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
550 strcpyW(cmd,tmp_file);
551 if (deformated)
553 strcatW(cmd,spc);
554 strcatW(cmd,deformated);
556 HeapFree(GetProcessHeap(),0,deformated);
559 TRACE("executing exe %s \n",debugstr_w(cmd));
561 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
562 c_collen, &si, &info);
564 HeapFree(GetProcessHeap(),0,cmd);
566 if ( !rc )
568 ERR("Unable to execute command\n");
569 return ERROR_SUCCESS;
572 prc = process_handle(package, type, info.hThread, info.hProcess, action);
574 return prc;
577 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
578 LPCWSTR target, const INT type, LPCWSTR action)
580 STARTUPINFOW si;
581 PROCESS_INFORMATION info;
582 BOOL rc;
583 WCHAR *deformated;
584 WCHAR *cmd;
585 INT len;
586 static const WCHAR spc[] = {' ',0};
587 int index;
588 UINT prc;
590 memset(&si,0,sizeof(STARTUPINFOW));
592 index = get_loaded_file(package,source);
594 len = strlenW(package->files[index].TargetPath);
596 deformat_string(package,target,&deformated);
597 if (deformated)
598 len += strlenW(deformated);
599 len += 2;
601 cmd = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
603 strcpyW(cmd, package->files[index].TargetPath);
604 if (deformated)
606 strcatW(cmd, spc);
607 strcatW(cmd, deformated);
609 HeapFree(GetProcessHeap(),0,deformated);
612 TRACE("executing exe %s \n",debugstr_w(cmd));
614 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
615 c_collen, &si, &info);
617 HeapFree(GetProcessHeap(),0,cmd);
619 if ( !rc )
621 ERR("Unable to execute command\n");
622 return ERROR_SUCCESS;
625 prc = process_handle(package, type, info.hThread, info.hProcess, action);
627 return prc;
630 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
631 LPCWSTR target, const INT type, LPCWSTR action)
633 static const WCHAR query[] = {
634 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
635 'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
636 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
637 '\'','%','s','\'',0
639 MSIQUERY *view = NULL;
640 MSIRECORD *row = 0;
641 UINT r;
642 LPWSTR deformated = NULL;
644 deformat_string( package, target, &deformated );
646 /* first try treat the error as a number */
647 r = MSI_OpenQuery( package->db, &view, query, deformated );
648 if( r == ERROR_SUCCESS )
650 r = MSI_ViewExecute( view, 0 );
651 if( r == ERROR_SUCCESS )
653 r = MSI_ViewFetch( view, &row );
654 if( r == ERROR_SUCCESS )
656 LPCWSTR error = MSI_RecordGetString( row, 1 );
657 MessageBoxW( NULL, error, NULL, MB_OK );
658 msiobj_release( &row->hdr );
661 MSI_ViewClose( view );
662 msiobj_release( &view->hdr );
665 if (r != ERROR_SUCCESS )
667 MessageBoxW( NULL, deformated, NULL, MB_OK );
668 HeapFree( GetProcessHeap(), 0, deformated );
671 return ERROR_FUNCTION_FAILED;
674 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
675 LPCWSTR target, const INT type, LPCWSTR action)
677 STARTUPINFOW si;
678 PROCESS_INFORMATION info;
679 WCHAR *prop;
680 BOOL rc;
681 WCHAR *deformated;
682 WCHAR *cmd;
683 INT len;
684 UINT prc;
685 static const WCHAR spc[] = {' ',0};
687 memset(&si,0,sizeof(STARTUPINFOW));
688 memset(&info,0,sizeof(PROCESS_INFORMATION));
690 prop = load_dynamic_property(package,source,&prc);
691 if (!prop)
692 return ERROR_SUCCESS;
694 deformat_string(package,target,&deformated);
695 len = strlenW(prop) + 2;
696 if (deformated)
697 len += strlenW(deformated);
699 cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
701 strcpyW(cmd,prop);
702 if (deformated)
704 strcatW(cmd,spc);
705 strcatW(cmd,deformated);
707 HeapFree(GetProcessHeap(),0,deformated);
710 TRACE("executing exe %s \n",debugstr_w(cmd));
712 rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
713 c_collen, &si, &info);
715 HeapFree(GetProcessHeap(),0,cmd);
717 if ( !rc )
719 ERR("Unable to execute command\n");
720 return ERROR_SUCCESS;
723 prc = process_handle(package, type, info.hThread, info.hProcess, action);
725 return prc;
728 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
729 LPCWSTR target, const INT type, LPCWSTR action)
731 LPWSTR filename, deformated;
732 STARTUPINFOW si;
733 PROCESS_INFORMATION info;
734 BOOL rc;
735 UINT prc;
737 memset(&si,0,sizeof(STARTUPINFOW));
739 filename = resolve_folder(package, source, FALSE, FALSE, NULL);
741 if (!filename)
742 return ERROR_FUNCTION_FAILED;
744 SetCurrentDirectoryW(filename);
745 HeapFree(GetProcessHeap(),0,filename);
747 deformat_string(package,target,&deformated);
749 if (!deformated)
750 return ERROR_FUNCTION_FAILED;
752 TRACE("executing exe %s \n",debugstr_w(deformated));
754 rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
755 c_collen, &si, &info);
756 HeapFree(GetProcessHeap(),0,deformated);
758 if ( !rc )
760 ERR("Unable to execute command\n");
761 return ERROR_SUCCESS;
764 prc = process_handle(package, type, info.hThread, info.hProcess, action);
766 return prc;
770 void ACTION_FinishCustomActions(MSIPACKAGE* package)
772 INT i;
773 DWORD rc;
775 for (i = 0; i < package->RunningActionCount; i++)
777 TRACE("Checking on action %s\n",
778 debugstr_w(package->RunningAction[i].name));
780 if (package->RunningAction[i].process)
781 GetExitCodeProcess(package->RunningAction[i].handle, &rc);
782 else
783 GetExitCodeThread(package->RunningAction[i].handle, &rc);
785 if (rc == STILL_ACTIVE)
787 TRACE("Waiting on action %s\n",
788 debugstr_w(package->RunningAction[i].name));
789 msi_dialog_check_messages(package->RunningAction[i].handle);
792 HeapFree(GetProcessHeap(),0,package->RunningAction[i].name);
793 CloseHandle(package->RunningAction[i].handle);
796 HeapFree(GetProcessHeap(),0,package->RunningAction);