| /* |
| * Custom Action processing for the Microsoft Installer (msi.dll) |
| * |
| * Copyright 2005 Aric Stewart for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| /* |
| * Pages I need |
| * |
| http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/summary_list_of_all_custom_action_types.asp |
| */ |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winerror.h" |
| #include "winreg.h" |
| #include "wine/debug.h" |
| #include "fdi.h" |
| #include "msi.h" |
| #include "msiquery.h" |
| #include "msvcrt/fcntl.h" |
| #include "objbase.h" |
| #include "objidl.h" |
| #include "msipriv.h" |
| #include "winnls.h" |
| #include "winuser.h" |
| #include "shlobj.h" |
| #include "wine/unicode.h" |
| #include "winver.h" |
| #include "action.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(msi); |
| |
| #define CUSTOM_ACTION_TYPE_MASK 0x3F |
| static const WCHAR c_collen[] = {'C',':','\\',0}; |
| static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0}; |
| |
| typedef struct tagMSIRUNNINGACTION |
| { |
| HANDLE handle; |
| BOOL process; |
| LPWSTR name; |
| } MSIRUNNINGACTION; |
| |
| static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action); |
| static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action); |
| static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action); |
| static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action); |
| static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action); |
| static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action); |
| |
| UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute) |
| { |
| UINT rc = ERROR_SUCCESS; |
| MSIQUERY * view; |
| MSIRECORD * row = 0; |
| static const WCHAR ExecSeqQuery[] = |
| {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o' |
| ,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i' |
| ,'o','n','`',' ','=',' ','`','%','s','`',0}; |
| UINT type; |
| LPWSTR source; |
| LPWSTR target; |
| WCHAR *deformated=NULL; |
| |
| rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, action); |
| if (rc != ERROR_SUCCESS) |
| return rc; |
| |
| rc = MSI_ViewExecute(view, 0); |
| if (rc != ERROR_SUCCESS) |
| { |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| return rc; |
| } |
| |
| rc = MSI_ViewFetch(view,&row); |
| if (rc != ERROR_SUCCESS) |
| { |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| return ERROR_CALL_NOT_IMPLEMENTED; |
| } |
| |
| type = MSI_RecordGetInteger(row,2); |
| |
| source = load_dynamic_stringW(row,3); |
| target = load_dynamic_stringW(row,4); |
| |
| TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type, |
| debugstr_w(source), debugstr_w(target)); |
| |
| /* handle some of the deferred actions */ |
| if (type & 0x400) |
| { |
| if (type & 0x100) |
| { |
| FIXME("Rollback only action... rollbacks not supported yet\n"); |
| HeapFree(GetProcessHeap(),0,source); |
| HeapFree(GetProcessHeap(),0,target); |
| msiobj_release(&row->hdr); |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| return ERROR_SUCCESS; |
| } |
| if (!execute) |
| { |
| LPWSTR *newbuf = NULL; |
| INT count; |
| if (type & 0x200) |
| { |
| TRACE("Deferring Commit Action!\n"); |
| count = package->CommitActionCount; |
| package->CommitActionCount++; |
| if (count != 0) |
| newbuf = HeapReAlloc(GetProcessHeap(),0, |
| package->CommitAction, |
| package->CommitActionCount * sizeof(LPWSTR)); |
| else |
| newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR)); |
| |
| newbuf[count] = strdupW(action); |
| package->CommitAction = newbuf; |
| } |
| else |
| { |
| TRACE("Deferring Action!\n"); |
| count = package->DeferredActionCount; |
| package->DeferredActionCount++; |
| if (count != 0) |
| newbuf = HeapReAlloc(GetProcessHeap(),0, |
| package->DeferredAction, |
| package->DeferredActionCount * sizeof(LPWSTR)); |
| else |
| newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(LPWSTR)); |
| |
| newbuf[count] = strdupW(action); |
| package->DeferredAction = newbuf; |
| } |
| |
| HeapFree(GetProcessHeap(),0,source); |
| HeapFree(GetProcessHeap(),0,target); |
| msiobj_release(&row->hdr); |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| return ERROR_SUCCESS; |
| } |
| else |
| { |
| /*Set ActionData*/ |
| |
| static const WCHAR szActionData[] = { |
| 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0}; |
| LPWSTR actiondata = load_dynamic_property(package,action,NULL); |
| if (actiondata) |
| MSI_SetPropertyW(package,szActionData,actiondata); |
| } |
| } |
| |
| switch (type & CUSTOM_ACTION_TYPE_MASK) |
| { |
| case 1: /* DLL file stored in a Binary table stream */ |
| rc = HANDLE_CustomType1(package,source,target,type,action); |
| break; |
| case 2: /* EXE file stored in a Binary table strem */ |
| rc = HANDLE_CustomType2(package,source,target,type,action); |
| break; |
| case 18: /*EXE file installed with package */ |
| rc = HANDLE_CustomType18(package,source,target,type,action); |
| break; |
| case 19: /* Error that halts install */ |
| rc = HANDLE_CustomType19(package,source,target,type,action); |
| break; |
| case 50: /*EXE file specified by a property value */ |
| rc = HANDLE_CustomType50(package,source,target,type,action); |
| break; |
| case 34: /*EXE to be run in specified directory */ |
| rc = HANDLE_CustomType34(package,source,target,type,action); |
| break; |
| case 35: /* Directory set with formatted text. */ |
| deformat_string(package,target,&deformated); |
| MSI_SetTargetPathW(package, source, deformated); |
| HeapFree(GetProcessHeap(),0,deformated); |
| break; |
| case 51: /* Property set with formatted text. */ |
| deformat_string(package,target,&deformated); |
| rc = MSI_SetPropertyW(package,source,deformated); |
| HeapFree(GetProcessHeap(),0,deformated); |
| break; |
| default: |
| FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n", |
| type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source), |
| debugstr_w(target)); |
| } |
| |
| HeapFree(GetProcessHeap(),0,source); |
| HeapFree(GetProcessHeap(),0,target); |
| msiobj_release(&row->hdr); |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| return rc; |
| } |
| |
| |
| static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source, |
| LPWSTR tmp_file) |
| { |
| DWORD sz=MAX_PATH; |
| static const WCHAR f1[] = {'m','s','i',0}; |
| WCHAR fmt[MAX_PATH]; |
| |
| if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) |
| != ERROR_SUCCESS) |
| GetTempPathW(MAX_PATH,fmt); |
| |
| if (GetTempFileNameW(fmt,f1,0,tmp_file) == 0) |
| { |
| TRACE("Unable to create file\n"); |
| return ERROR_FUNCTION_FAILED; |
| } |
| else |
| { |
| /* write out the file */ |
| UINT rc; |
| MSIQUERY * view; |
| MSIRECORD * row = 0; |
| static const WCHAR fmt[] = |
| {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','B','i' |
| ,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`','%','s','`',0}; |
| HANDLE the_file; |
| CHAR buffer[1024]; |
| |
| if (track_tempfile(package, tmp_file, tmp_file)!=0) |
| FIXME("File Name in temp tracking collision\n"); |
| |
| the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, |
| FILE_ATTRIBUTE_NORMAL, NULL); |
| |
| if (the_file == INVALID_HANDLE_VALUE) |
| return ERROR_FUNCTION_FAILED; |
| |
| rc = MSI_OpenQuery(package->db, &view, fmt, source); |
| if (rc != ERROR_SUCCESS) |
| return rc; |
| |
| rc = MSI_ViewExecute(view, 0); |
| if (rc != ERROR_SUCCESS) |
| { |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| return rc; |
| } |
| |
| rc = MSI_ViewFetch(view,&row); |
| if (rc != ERROR_SUCCESS) |
| { |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| return rc; |
| } |
| |
| do |
| { |
| DWORD write; |
| sz = 1024; |
| rc = MSI_RecordReadStream(row,2,buffer,&sz); |
| if (rc != ERROR_SUCCESS) |
| { |
| ERR("Failed to get stream\n"); |
| CloseHandle(the_file); |
| DeleteFileW(tmp_file); |
| break; |
| } |
| WriteFile(the_file,buffer,sz,&write,NULL); |
| } while (sz == 1024); |
| |
| CloseHandle(the_file); |
| |
| msiobj_release(&row->hdr); |
| MSI_ViewClose(view); |
| msiobj_release(&view->hdr); |
| } |
| |
| return ERROR_SUCCESS; |
| } |
| |
| static void file_running_action(MSIPACKAGE* package, HANDLE Handle, |
| BOOL process, LPCWSTR name) |
| { |
| MSIRUNNINGACTION *newbuf = NULL; |
| INT count; |
| count = package->RunningActionCount; |
| package->RunningActionCount++; |
| if (count != 0) |
| newbuf = HeapReAlloc(GetProcessHeap(),0, |
| package->RunningAction, |
| package->RunningActionCount * sizeof(MSIRUNNINGACTION)); |
| else |
| newbuf = HeapAlloc(GetProcessHeap(),0, sizeof(MSIRUNNINGACTION)); |
| |
| newbuf[count].handle = Handle; |
| newbuf[count].process = process; |
| newbuf[count].name = strdupW(name); |
| |
| package->RunningAction = newbuf; |
| } |
| |
| static UINT process_action_return_value(UINT type, HANDLE ThreadHandle) |
| { |
| DWORD rc; |
| |
| if (type == 2) |
| { |
| GetExitCodeProcess(ThreadHandle,&rc); |
| |
| if (rc == 0) |
| return ERROR_SUCCESS; |
| else |
| return ERROR_FUNCTION_FAILED; |
| } |
| |
| GetExitCodeThread(ThreadHandle,&rc); |
| |
| switch (rc) |
| { |
| case ERROR_FUNCTION_NOT_CALLED: |
| case ERROR_SUCCESS: |
| case ERROR_INSTALL_USEREXIT: |
| case ERROR_INSTALL_FAILURE: |
| return rc; |
| case ERROR_NO_MORE_ITEMS: |
| return ERROR_SUCCESS; |
| default: |
| return ERROR_FUNCTION_FAILED; |
| } |
| } |
| |
| static UINT process_handle(MSIPACKAGE* package, UINT type, |
| HANDLE ThreadHandle, HANDLE ProcessHandle, |
| LPCWSTR Name) |
| { |
| UINT rc = ERROR_SUCCESS; |
| |
| if (!(type & 0x80)) |
| { |
| /* synchronous */ |
| TRACE("Synchronous Execution of action %s\n",debugstr_w(Name)); |
| if (ProcessHandle) |
| msi_dialog_check_messages(ProcessHandle); |
| else |
| msi_dialog_check_messages(ThreadHandle); |
| |
| if (!(type & 0x40)) |
| { |
| if (ProcessHandle) |
| rc = process_action_return_value(2,ProcessHandle); |
| else |
| rc = process_action_return_value(1,ThreadHandle); |
| } |
| |
| CloseHandle(ThreadHandle); |
| if (ProcessHandle); |
| CloseHandle(ProcessHandle); |
| } |
| else |
| { |
| TRACE("Asynchronous Execution of action %s\n",debugstr_w(Name)); |
| /* asynchronous */ |
| if (type & 0x40) |
| { |
| if (ProcessHandle) |
| { |
| file_running_action(package, ProcessHandle, TRUE, Name); |
| CloseHandle(ThreadHandle); |
| } |
| else |
| file_running_action(package, ThreadHandle, FALSE, Name); |
| } |
| else |
| { |
| CloseHandle(ThreadHandle); |
| if (ProcessHandle); |
| CloseHandle(ProcessHandle); |
| } |
| } |
| |
| return rc; |
| } |
| |
| |
| typedef UINT __stdcall CustomEntry(MSIHANDLE); |
| |
| typedef struct |
| { |
| MSIPACKAGE *package; |
| WCHAR *target; |
| WCHAR *source; |
| } thread_struct; |
| |
| static DWORD WINAPI ACTION_CallDllFunction(thread_struct *stuff) |
| { |
| HANDLE hModule; |
| LPSTR proc; |
| CustomEntry *fn; |
| DWORD rc = ERROR_SUCCESS; |
| |
| TRACE("calling function (%s, %s) \n", debugstr_w(stuff->source), |
| debugstr_w(stuff->target)); |
| |
| hModule = LoadLibraryW(stuff->source); |
| if (hModule) |
| { |
| proc = strdupWtoA( stuff->target ); |
| fn = (CustomEntry*)GetProcAddress(hModule,proc); |
| if (fn) |
| { |
| MSIHANDLE hPackage; |
| MSIPACKAGE *package = stuff->package; |
| |
| TRACE("Calling function %s\n", proc); |
| hPackage = msiobj_findhandle( &package->hdr ); |
| if (hPackage ) |
| { |
| rc = fn(hPackage); |
| msiobj_release( &package->hdr ); |
| } |
| else |
| ERR("Handle for object %p not found\n", package ); |
| } |
| else |
| ERR("Cannot load functon\n"); |
| |
| HeapFree(GetProcessHeap(),0,proc); |
| FreeLibrary(hModule); |
| } |
| else |
| ERR("Unable to load library\n"); |
| msiobj_release( &stuff->package->hdr ); |
| HeapFree(GetProcessHeap(),0,stuff->source); |
| HeapFree(GetProcessHeap(),0,stuff->target); |
| HeapFree(GetProcessHeap(), 0, stuff); |
| return rc; |
| } |
| |
| static DWORD WINAPI DllThread(LPVOID info) |
| { |
| thread_struct *stuff; |
| DWORD rc = 0; |
| |
| TRACE("MSI Thread (0x%lx) started for custom action\n", |
| GetCurrentThreadId()); |
| |
| stuff = (thread_struct*)info; |
| rc = ACTION_CallDllFunction(stuff); |
| |
| TRACE("MSI Thread (0x%lx) finished\n",GetCurrentThreadId()); |
| /* clse all handles for this thread */ |
| MsiCloseAllHandles(); |
| return rc; |
| } |
| |
| static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action) |
| { |
| WCHAR tmp_file[MAX_PATH]; |
| thread_struct *info; |
| DWORD ThreadId; |
| HANDLE ThreadHandle; |
| UINT rc = ERROR_SUCCESS; |
| |
| store_binary_to_temp(package, source, tmp_file); |
| |
| TRACE("Calling function %s from %s\n",debugstr_w(target), |
| debugstr_w(tmp_file)); |
| |
| if (!strchrW(tmp_file,'.')) |
| { |
| static const WCHAR dot[]={'.',0}; |
| strcatW(tmp_file,dot); |
| } |
| |
| info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) ); |
| msiobj_addref( &package->hdr ); |
| info->package = package; |
| info->target = strdupW(target); |
| info->source = strdupW(tmp_file); |
| |
| ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId); |
| |
| rc = process_handle(package, type, ThreadHandle, NULL, action); |
| |
| return rc; |
| } |
| |
| static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action) |
| { |
| WCHAR tmp_file[MAX_PATH]; |
| STARTUPINFOW si; |
| PROCESS_INFORMATION info; |
| BOOL rc; |
| INT len; |
| WCHAR *deformated; |
| WCHAR *cmd; |
| static const WCHAR spc[] = {' ',0}; |
| UINT prc = ERROR_SUCCESS; |
| |
| memset(&si,0,sizeof(STARTUPINFOW)); |
| |
| store_binary_to_temp(package, source, tmp_file); |
| |
| deformat_string(package,target,&deformated); |
| |
| len = strlenW(tmp_file)+2; |
| |
| if (deformated) |
| len += strlenW(deformated); |
| |
| cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len); |
| |
| strcpyW(cmd,tmp_file); |
| if (deformated) |
| { |
| strcatW(cmd,spc); |
| strcatW(cmd,deformated); |
| |
| HeapFree(GetProcessHeap(),0,deformated); |
| } |
| |
| TRACE("executing exe %s \n",debugstr_w(cmd)); |
| |
| rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, |
| c_collen, &si, &info); |
| |
| HeapFree(GetProcessHeap(),0,cmd); |
| |
| if ( !rc ) |
| { |
| ERR("Unable to execute command\n"); |
| return ERROR_SUCCESS; |
| } |
| |
| prc = process_handle(package, type, info.hThread, info.hProcess, action); |
| |
| return prc; |
| } |
| |
| static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action) |
| { |
| STARTUPINFOW si; |
| PROCESS_INFORMATION info; |
| BOOL rc; |
| WCHAR *deformated; |
| WCHAR *cmd; |
| INT len; |
| static const WCHAR spc[] = {' ',0}; |
| int index; |
| UINT prc; |
| |
| memset(&si,0,sizeof(STARTUPINFOW)); |
| |
| index = get_loaded_file(package,source); |
| |
| len = strlenW(package->files[index].TargetPath); |
| |
| deformat_string(package,target,&deformated); |
| if (deformated) |
| len += strlenW(deformated); |
| len += 2; |
| |
| cmd = HeapAlloc(GetProcessHeap(),0,len * sizeof(WCHAR)); |
| |
| strcpyW(cmd, package->files[index].TargetPath); |
| if (deformated) |
| { |
| strcatW(cmd, spc); |
| strcatW(cmd, deformated); |
| |
| HeapFree(GetProcessHeap(),0,deformated); |
| } |
| |
| TRACE("executing exe %s \n",debugstr_w(cmd)); |
| |
| rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, |
| c_collen, &si, &info); |
| |
| HeapFree(GetProcessHeap(),0,cmd); |
| |
| if ( !rc ) |
| { |
| ERR("Unable to execute command\n"); |
| return ERROR_SUCCESS; |
| } |
| |
| prc = process_handle(package, type, info.hThread, info.hProcess, action); |
| |
| return prc; |
| } |
| |
| static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action) |
| { |
| static const WCHAR query[] = { |
| 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ', |
| 'F','R','O','M',' ','`','E','r','r','o','r','`',' ', |
| 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ','%','s',0 |
| }; |
| MSIQUERY *view = NULL; |
| MSIRECORD *row = 0; |
| UINT r; |
| LPWSTR deformated = NULL; |
| |
| deformat_string( package, target, &deformated ); |
| |
| /* first try treat the error as a number */ |
| r = MSI_OpenQuery( package->db, &view, query, deformated ); |
| if( r == ERROR_SUCCESS ) |
| { |
| r = MSI_ViewExecute( view, 0 ); |
| if( r == ERROR_SUCCESS ) |
| { |
| r = MSI_ViewFetch( view, &row ); |
| if( r == ERROR_SUCCESS ) |
| { |
| LPCWSTR error = MSI_RecordGetString( row, 1 ); |
| MessageBoxW( NULL, error, NULL, MB_OK ); |
| msiobj_release( &row->hdr ); |
| } |
| } |
| MSI_ViewClose( view ); |
| msiobj_release( &view->hdr ); |
| } |
| |
| if (r != ERROR_SUCCESS ) |
| { |
| MessageBoxW( NULL, deformated, NULL, MB_OK ); |
| HeapFree( GetProcessHeap(), 0, deformated ); |
| } |
| |
| return ERROR_FUNCTION_FAILED; |
| } |
| |
| static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action) |
| { |
| STARTUPINFOW si; |
| PROCESS_INFORMATION info; |
| WCHAR *prop; |
| BOOL rc; |
| WCHAR *deformated; |
| WCHAR *cmd; |
| INT len; |
| UINT prc; |
| static const WCHAR spc[] = {' ',0}; |
| |
| memset(&si,0,sizeof(STARTUPINFOW)); |
| memset(&info,0,sizeof(PROCESS_INFORMATION)); |
| |
| prop = load_dynamic_property(package,source,&prc); |
| if (!prop) |
| return prc; |
| |
| deformat_string(package,target,&deformated); |
| len = strlenW(prop) + 2; |
| if (deformated) |
| len += strlenW(deformated); |
| |
| cmd = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR)*len); |
| |
| strcpyW(cmd,prop); |
| if (deformated) |
| { |
| strcatW(cmd,spc); |
| strcatW(cmd,deformated); |
| |
| HeapFree(GetProcessHeap(),0,deformated); |
| } |
| |
| TRACE("executing exe %s \n",debugstr_w(cmd)); |
| |
| rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, |
| c_collen, &si, &info); |
| |
| HeapFree(GetProcessHeap(),0,cmd); |
| |
| if ( !rc ) |
| { |
| ERR("Unable to execute command\n"); |
| return ERROR_SUCCESS; |
| } |
| |
| prc = process_handle(package, type, info.hThread, info.hProcess, action); |
| |
| return prc; |
| } |
| |
| static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source, |
| LPCWSTR target, const INT type, LPCWSTR action) |
| { |
| LPWSTR filename, deformated; |
| STARTUPINFOW si; |
| PROCESS_INFORMATION info; |
| BOOL rc; |
| UINT prc; |
| |
| memset(&si,0,sizeof(STARTUPINFOW)); |
| |
| filename = resolve_folder(package, source, FALSE, FALSE, NULL); |
| |
| if (!filename) |
| return ERROR_FUNCTION_FAILED; |
| |
| SetCurrentDirectoryW(filename); |
| HeapFree(GetProcessHeap(),0,filename); |
| |
| deformat_string(package,target,&deformated); |
| |
| if (!deformated) |
| return ERROR_FUNCTION_FAILED; |
| |
| TRACE("executing exe %s \n",debugstr_w(deformated)); |
| |
| rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL, |
| c_collen, &si, &info); |
| HeapFree(GetProcessHeap(),0,deformated); |
| |
| if ( !rc ) |
| { |
| ERR("Unable to execute command\n"); |
| return ERROR_SUCCESS; |
| } |
| |
| prc = process_handle(package, type, info.hThread, info.hProcess, action); |
| |
| return prc; |
| } |
| |
| |
| void ACTION_FinishCustomActions(MSIPACKAGE* package) |
| { |
| INT i; |
| DWORD rc; |
| |
| for (i = 0; i < package->RunningActionCount; i++) |
| { |
| TRACE("Checking on action %s\n", |
| debugstr_w(package->RunningAction[i].name)); |
| |
| if (package->RunningAction[i].process) |
| GetExitCodeProcess(package->RunningAction[i].handle, &rc); |
| else |
| GetExitCodeThread(package->RunningAction[i].handle, &rc); |
| |
| if (rc == STILL_ACTIVE) |
| { |
| TRACE("Waiting on action %s\n", |
| debugstr_w(package->RunningAction[i].name)); |
| msi_dialog_check_messages(package->RunningAction[i].handle); |
| } |
| |
| HeapFree(GetProcessHeap(),0,package->RunningAction[i].name); |
| CloseHandle(package->RunningAction[i].handle); |
| } |
| |
| HeapFree(GetProcessHeap(),0,package->RunningAction); |
| } |