Add prototype and fix RtlpNtCreateKey.
[wine] / dlls / msi / custom.c
1 /*
2  * Custom Action processing for the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
21 /*
22  * Pages I need
23  *
24 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/summary_list_of_all_custom_action_types.asp
25  */
26
27 #include <stdarg.h>
28 #include <stdio.h>
29
30 #define COBJMACROS
31
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"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(msi);
52
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};
56
57 typedef struct tagMSIRUNNINGACTION
58 {
59     HANDLE handle;
60     BOOL   process;
61     LPWSTR name;
62 } MSIRUNNINGACTION;
63
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);
76
77 UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
78 {
79     UINT rc = ERROR_SUCCESS;
80     MSIRECORD * row = 0;
81     static const WCHAR ExecSeqQuery[] =
82     {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
83      '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
84      ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
85      '=',' ','\'','%','s','\'',0};
86     UINT type;
87     LPWSTR source;
88     LPWSTR target;
89     WCHAR *deformated=NULL;
90
91     row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
92     if (!row)
93         return ERROR_CALL_NOT_IMPLEMENTED;
94
95     type = MSI_RecordGetInteger(row,2);
96
97     source = load_dynamic_stringW(row,3);
98     target = load_dynamic_stringW(row,4);
99
100     TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
101           debugstr_w(source), debugstr_w(target));
102
103     /* handle some of the deferred actions */
104     if (type & 0x400)
105     {
106         if (type & 0x100)
107         {
108             FIXME("Rollback only action... rollbacks not supported yet\n");
109             schedule_action(package, ROLLBACK_SCRIPT, action);
110             HeapFree(GetProcessHeap(),0,source);
111             HeapFree(GetProcessHeap(),0,target);
112             msiobj_release(&row->hdr);
113             return ERROR_SUCCESS;
114         }
115         if (!execute)
116         {
117             if (type & 0x200)
118             {
119                 TRACE("Deferring Commit Action!\n");
120                 schedule_action(package, COMMIT_SCRIPT, action);
121             }
122             else
123             {
124                 TRACE("Deferring Action!\n");
125                 schedule_action(package, INSTALL_SCRIPT, action);
126             }
127
128             HeapFree(GetProcessHeap(),0,source);
129             HeapFree(GetProcessHeap(),0,target);
130             msiobj_release(&row->hdr);
131             return ERROR_SUCCESS;
132         }
133         else
134         {
135             /*Set ActionData*/
136
137             static const WCHAR szActionData[] = {
138             'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
139             LPWSTR actiondata = load_dynamic_property(package,action,NULL);
140             if (actiondata)
141                 MSI_SetPropertyW(package,szActionData,actiondata);
142         }
143     }
144
145     switch (type & CUSTOM_ACTION_TYPE_MASK)
146     {
147         case 1: /* DLL file stored in a Binary table stream */
148             rc = HANDLE_CustomType1(package,source,target,type,action);
149             break;
150         case 2: /* EXE file stored in a Binary table strem */
151             rc = HANDLE_CustomType2(package,source,target,type,action);
152             break;
153         case 18: /*EXE file installed with package */
154             rc = HANDLE_CustomType18(package,source,target,type,action);
155             break;
156         case 19: /* Error that halts install */
157             rc = HANDLE_CustomType19(package,source,target,type,action);
158             break;
159         case 50: /*EXE file specified by a property value */
160             rc = HANDLE_CustomType50(package,source,target,type,action);
161             break;
162         case 34: /*EXE to be run in specified directory */
163             rc = HANDLE_CustomType34(package,source,target,type,action);
164             break;
165         case 35: /* Directory set with formatted text. */
166             deformat_string(package,target,&deformated);
167             MSI_SetTargetPathW(package, source, deformated);
168             HeapFree(GetProcessHeap(),0,deformated);
169             break;
170         case 51: /* Property set with formatted text. */
171             deformat_string(package,target,&deformated);
172             rc = MSI_SetPropertyW(package,source,deformated);
173             HeapFree(GetProcessHeap(),0,deformated);
174             break;
175         default:
176             FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
177              type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
178              debugstr_w(target));
179     }
180
181     HeapFree(GetProcessHeap(),0,source);
182     HeapFree(GetProcessHeap(),0,target);
183     msiobj_release(&row->hdr);
184     return rc;
185 }
186
187
188 static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source, 
189                                 LPWSTR tmp_file)
190 {
191     DWORD sz=MAX_PATH;
192     static const WCHAR f1[] = {'m','s','i',0};
193     WCHAR fmt[MAX_PATH];
194
195     if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) 
196         != ERROR_SUCCESS)
197         GetTempPathW(MAX_PATH,fmt);
198
199     if (GetTempFileNameW(fmt,f1,0,tmp_file) == 0)
200     {
201         TRACE("Unable to create file\n");
202         return ERROR_FUNCTION_FAILED;
203     }
204     else
205     {
206         /* write out the file */
207         UINT rc;
208         MSIRECORD * row = 0;
209         static const WCHAR fmt[] =
210         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
211          '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',
212          ' ','`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
213         HANDLE the_file;
214         CHAR buffer[1024];
215
216         the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
217                            FILE_ATTRIBUTE_NORMAL, NULL);
218     
219         if (the_file == INVALID_HANDLE_VALUE)
220             return ERROR_FUNCTION_FAILED;
221
222         row = MSI_QueryGetRecord(package->db, fmt, source);
223         if (!row)
224             return ERROR_FUNCTION_FAILED;
225
226         do 
227         {
228             DWORD write;
229             sz = 1024;
230             rc = MSI_RecordReadStream(row,2,buffer,&sz);
231             if (rc != ERROR_SUCCESS)
232             {
233                 ERR("Failed to get stream\n");
234                 CloseHandle(the_file);  
235                 DeleteFileW(tmp_file);
236                 break;
237             }
238             WriteFile(the_file,buffer,sz,&write,NULL);
239         } while (sz == 1024);
240
241         CloseHandle(the_file);
242
243         msiobj_release(&row->hdr);
244     }
245
246     return ERROR_SUCCESS;
247 }
248
249 static void file_running_action(MSIPACKAGE* package, HANDLE Handle, 
250                                 BOOL process, LPCWSTR name)
251 {
252     MSIRUNNINGACTION *newbuf = NULL;
253     INT count;
254     count = package->RunningActionCount;
255     package->RunningActionCount++;
256     if (count != 0)
257         newbuf = HeapReAlloc(GetProcessHeap(),0,
258                         package->RunningAction,
259                         package->RunningActionCount * sizeof(MSIRUNNINGACTION));
260     else
261         newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(MSIRUNNINGACTION));
262
263     newbuf[count].handle = Handle;
264     newbuf[count].process = process;
265     newbuf[count].name = strdupW(name);
266
267     package->RunningAction = newbuf;
268 }
269
270 static UINT process_action_return_value(UINT type, HANDLE ThreadHandle)
271 {
272     DWORD rc=0;
273     
274     if (type == 2)
275     {
276         GetExitCodeProcess(ThreadHandle,&rc);
277     
278         if (rc == 0)
279             return ERROR_SUCCESS;
280         else
281             return ERROR_FUNCTION_FAILED;
282     }
283
284     GetExitCodeThread(ThreadHandle,&rc);
285
286     switch (rc)
287     {
288         case ERROR_FUNCTION_NOT_CALLED:
289         case ERROR_SUCCESS:
290         case ERROR_INSTALL_USEREXIT:
291         case ERROR_INSTALL_FAILURE:
292             return rc;
293         case ERROR_NO_MORE_ITEMS:
294             return ERROR_SUCCESS;
295         default:
296             ERR("Invalid Return Code %lx\n",rc);
297             return ERROR_INSTALL_FAILURE;
298     }
299 }
300
301 static UINT process_handle(MSIPACKAGE* package, UINT type, 
302                            HANDLE ThreadHandle, HANDLE ProcessHandle,
303                            LPCWSTR Name, BOOL *finished)
304 {
305     UINT rc = ERROR_SUCCESS;
306
307     if (!(type & 0x80))
308     {
309         /* synchronous */
310         TRACE("Synchronous Execution of action %s\n",debugstr_w(Name));
311         if (ProcessHandle)
312             msi_dialog_check_messages(ProcessHandle);
313         else
314             msi_dialog_check_messages(ThreadHandle);
315
316         if (!(type & 0x40))
317         {
318             if (ProcessHandle)
319                 rc = process_action_return_value(2,ProcessHandle);
320             else
321                 rc = process_action_return_value(1,ThreadHandle);
322         }
323
324         CloseHandle(ThreadHandle);
325         if (ProcessHandle);
326             CloseHandle(ProcessHandle);
327         if (finished)
328             *finished = TRUE;
329     }
330     else 
331     {
332         TRACE("Asynchronous Execution of action %s\n",debugstr_w(Name));
333         /* asynchronous */
334         if (type & 0x40)
335         {
336             if (ProcessHandle)
337             {
338                 file_running_action(package, ProcessHandle, TRUE, Name);
339                 CloseHandle(ThreadHandle);
340             }
341             else
342             file_running_action(package, ThreadHandle, FALSE, Name);
343         }
344         else
345         {
346             CloseHandle(ThreadHandle);
347             if (ProcessHandle);
348                 CloseHandle(ProcessHandle);
349         }
350         if (finished)
351             *finished = FALSE;
352     }
353
354     return rc;
355 }
356
357
358 typedef UINT __stdcall CustomEntry(MSIHANDLE);
359
360 typedef struct 
361 {
362         MSIPACKAGE *package;
363         WCHAR *target;
364         WCHAR *source;
365 } thread_struct;
366
367 static DWORD WINAPI ACTION_CallDllFunction(thread_struct *stuff)
368 {
369     HANDLE hModule;
370     LPSTR proc;
371     CustomEntry *fn;
372     DWORD rc = ERROR_SUCCESS;
373
374     TRACE("calling function (%s, %s) \n", debugstr_w(stuff->source),
375           debugstr_w(stuff->target));
376
377     hModule = LoadLibraryW(stuff->source);
378     if (hModule)
379     {
380         proc = strdupWtoA( stuff->target );
381         fn = (CustomEntry*)GetProcAddress(hModule,proc);
382         if (fn)
383         {
384             MSIHANDLE hPackage;
385             MSIPACKAGE *package = stuff->package;
386
387             TRACE("Calling function %s\n", proc);
388             hPackage = msiobj_findhandle( &package->hdr );
389             if (hPackage )
390             {
391                 rc = fn(hPackage);
392                 msiobj_release( &package->hdr );
393             }
394             else
395                 ERR("Handle for object %p not found\n", package );
396         }
397         else
398             ERR("Cannot load functon\n");
399
400         HeapFree(GetProcessHeap(),0,proc);
401         FreeLibrary(hModule);
402     }
403     else
404         ERR("Unable to load library\n");
405     msiobj_release( &stuff->package->hdr );
406     HeapFree(GetProcessHeap(),0,stuff->source);
407     HeapFree(GetProcessHeap(),0,stuff->target);
408     HeapFree(GetProcessHeap(), 0, stuff);
409     return rc;
410 }
411
412 static DWORD WINAPI DllThread(LPVOID info)
413 {
414     thread_struct *stuff;
415     DWORD rc = 0;
416   
417     TRACE("MSI Thread (0x%lx) started for custom action\n",
418                         GetCurrentThreadId());
419     
420     stuff = (thread_struct*)info;
421     rc = ACTION_CallDllFunction(stuff);
422
423     TRACE("MSI Thread (0x%lx) finished (rc %li)\n",GetCurrentThreadId(), rc);
424     /* clse all handles for this thread */
425     MsiCloseAllHandles();
426     return rc;
427 }
428
429 static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, 
430                                LPCWSTR target, const INT type, LPCWSTR action)
431 {
432     WCHAR tmp_file[MAX_PATH];
433     thread_struct *info;
434     DWORD ThreadId;
435     HANDLE ThreadHandle;
436     UINT rc = ERROR_SUCCESS;
437     BOOL finished = FALSE;
438
439     store_binary_to_temp(package, source, tmp_file);
440
441     TRACE("Calling function %s from %s\n",debugstr_w(target),
442           debugstr_w(tmp_file));
443
444     if (!strchrW(tmp_file,'.'))
445     {
446         static const WCHAR dot[]={'.',0};
447         strcatW(tmp_file,dot);
448     } 
449
450     info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
451     msiobj_addref( &package->hdr );
452     info->package = package;
453     info->target = strdupW(target);
454     info->source = strdupW(tmp_file);
455
456     ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId);
457
458     rc = process_handle(package, type, ThreadHandle, NULL, action, &finished );
459
460     if (!finished)
461         track_tempfile(package, tmp_file, tmp_file);
462     else
463         DeleteFileW(tmp_file);
464  
465     return rc;
466 }
467
468 static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, 
469                                LPCWSTR target, const INT type, LPCWSTR action)
470 {
471     WCHAR tmp_file[MAX_PATH];
472     STARTUPINFOW si;
473     PROCESS_INFORMATION info;
474     BOOL rc;
475     INT len;
476     WCHAR *deformated;
477     WCHAR *cmd;
478     static const WCHAR spc[] = {' ',0};
479     UINT prc = ERROR_SUCCESS;
480     BOOL finished = FALSE;
481
482     memset(&si,0,sizeof(STARTUPINFOW));
483
484     store_binary_to_temp(package, source, tmp_file);
485
486     deformat_string(package,target,&deformated);
487
488     len = strlenW(tmp_file)+2;
489
490     if (deformated)
491         len += strlenW(deformated);
492    
493     cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
494
495     strcpyW(cmd,tmp_file);
496     if (deformated)
497     {
498         strcatW(cmd,spc);
499         strcatW(cmd,deformated);
500
501         HeapFree(GetProcessHeap(),0,deformated);
502     }
503
504     TRACE("executing exe %s \n",debugstr_w(cmd));
505
506     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
507                   c_collen, &si, &info);
508
509     HeapFree(GetProcessHeap(),0,cmd);
510
511     if ( !rc )
512     {
513         ERR("Unable to execute command\n");
514         return ERROR_SUCCESS;
515     }
516
517     prc = process_handle(package, type, info.hThread, info.hProcess, action, 
518                           &finished);
519
520     if (!finished)
521         track_tempfile(package, tmp_file, tmp_file);
522     else
523         DeleteFileW(tmp_file);
524     
525     return prc;
526 }
527
528 static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
529                                 LPCWSTR target, const INT type, LPCWSTR action)
530 {
531     STARTUPINFOW si;
532     PROCESS_INFORMATION info;
533     BOOL rc;
534     WCHAR *deformated;
535     WCHAR *cmd;
536     INT len;
537     static const WCHAR spc[] = {' ',0};
538     int index;
539     UINT prc;
540
541     memset(&si,0,sizeof(STARTUPINFOW));
542
543     index = get_loaded_file(package,source);
544
545     len = strlenW(package->files[index].TargetPath);
546
547     deformat_string(package,target,&deformated);
548     if (deformated)
549         len += strlenW(deformated);
550     len += 2;
551
552     cmd = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR));
553
554     strcpyW(cmd, package->files[index].TargetPath);
555     if (deformated)
556     {
557         strcatW(cmd, spc);
558         strcatW(cmd, deformated);
559
560         HeapFree(GetProcessHeap(),0,deformated);
561     }
562
563     TRACE("executing exe %s \n",debugstr_w(cmd));
564
565     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
566                   c_collen, &si, &info);
567
568     HeapFree(GetProcessHeap(),0,cmd);
569     
570     if ( !rc )
571     {
572         ERR("Unable to execute command\n");
573         return ERROR_SUCCESS;
574     }
575
576     prc = process_handle(package, type, info.hThread, info.hProcess, action, 
577                          NULL);
578
579     return prc;
580 }
581
582 static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
583                                 LPCWSTR target, const INT type, LPCWSTR action)
584 {
585     static const WCHAR query[] = {
586       'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
587       'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
588       'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
589       '\'','%','s','\'',0
590     };
591     MSIRECORD *row = 0;
592     LPWSTR deformated = NULL;
593
594     deformat_string( package, target, &deformated );
595
596     /* first try treat the error as a number */
597     row = MSI_QueryGetRecord( package->db, query, deformated );
598     if( row )
599     {
600         LPCWSTR error = MSI_RecordGetString( row, 1 );
601         MessageBoxW( NULL, error, NULL, MB_OK );
602         msiobj_release( &row->hdr );
603     }
604     else
605         MessageBoxW( NULL, deformated, NULL, MB_OK );
606
607     HeapFree( GetProcessHeap(), 0, deformated );
608
609     return ERROR_FUNCTION_FAILED;
610 }
611
612 static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
613                                 LPCWSTR target, const INT type, LPCWSTR action)
614 {
615     STARTUPINFOW si;
616     PROCESS_INFORMATION info;
617     WCHAR *prop;
618     BOOL rc;
619     WCHAR *deformated;
620     WCHAR *cmd;
621     INT len;
622     UINT prc;
623     static const WCHAR spc[] = {' ',0};
624
625     memset(&si,0,sizeof(STARTUPINFOW));
626     memset(&info,0,sizeof(PROCESS_INFORMATION));
627
628     prop = load_dynamic_property(package,source,&prc);
629     if (!prop)
630         return ERROR_SUCCESS;
631
632     deformat_string(package,target,&deformated);
633     len = strlenW(prop) + 2;
634     if (deformated)
635          len += strlenW(deformated);
636
637     cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len);
638
639     strcpyW(cmd,prop);
640     if (deformated)
641     {
642         strcatW(cmd,spc);
643         strcatW(cmd,deformated);
644
645         HeapFree(GetProcessHeap(),0,deformated);
646     }
647
648     TRACE("executing exe %s \n",debugstr_w(cmd));
649
650     rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
651                   c_collen, &si, &info);
652
653     HeapFree(GetProcessHeap(),0,cmd);
654     
655     if ( !rc )
656     {
657         ERR("Unable to execute command\n");
658         return ERROR_SUCCESS;
659     }
660
661     prc = process_handle(package, type, info.hThread, info.hProcess, action, 
662                          NULL);
663
664     return prc;
665 }
666
667 static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
668                                 LPCWSTR target, const INT type, LPCWSTR action)
669 {
670     LPWSTR filename, deformated;
671     STARTUPINFOW si;
672     PROCESS_INFORMATION info;
673     BOOL rc;
674     UINT prc;
675
676     memset(&si,0,sizeof(STARTUPINFOW));
677
678     filename = resolve_folder(package, source, FALSE, FALSE, NULL);
679
680     if (!filename)
681         return ERROR_FUNCTION_FAILED;
682
683     SetCurrentDirectoryW(filename);
684     HeapFree(GetProcessHeap(),0,filename);
685
686     deformat_string(package,target,&deformated);
687
688     if (!deformated)
689         return ERROR_FUNCTION_FAILED;
690
691     TRACE("executing exe %s \n",debugstr_w(deformated));
692
693     rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
694                   c_collen, &si, &info);
695     HeapFree(GetProcessHeap(),0,deformated);
696
697     if ( !rc )
698     {
699         ERR("Unable to execute command\n");
700         return ERROR_SUCCESS;
701     }
702
703     prc = process_handle(package, type, info.hThread, info.hProcess, action,
704                          NULL);
705
706     return prc;
707 }
708
709
710 void ACTION_FinishCustomActions(MSIPACKAGE* package)
711 {
712     INT i;
713     DWORD rc;
714
715     for (i = 0; i < package->RunningActionCount; i++)
716     {
717         TRACE("Checking on action %s\n",
718                debugstr_w(package->RunningAction[i].name));
719
720         if (package->RunningAction[i].process)
721             GetExitCodeProcess(package->RunningAction[i].handle, &rc);
722         else
723             GetExitCodeThread(package->RunningAction[i].handle, &rc);
724
725         if (rc == STILL_ACTIVE)
726         {
727             TRACE("Waiting on action %s\n",
728                debugstr_w(package->RunningAction[i].name));
729             msi_dialog_check_messages(package->RunningAction[i].handle);
730         }
731
732         HeapFree(GetProcessHeap(),0,package->RunningAction[i].name);
733         CloseHandle(package->RunningAction[i].handle);
734     }
735
736     HeapFree(GetProcessHeap(),0,package->RunningAction);
737 }