| /* | |
| * 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 "ver.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(package->dialog, ProcessHandle); | |
| else | |
| msi_dialog_check_messages(package->dialog, 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 = (WCHAR*)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 = (WCHAR*)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 = (WCHAR*)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->dialog, | |
| package->RunningAction[i].handle); | |
| } | |
| HeapFree(GetProcessHeap(),0,package->RunningAction[i].name); | |
| CloseHandle(package->RunningAction[i].handle); | |
| } | |
| HeapFree(GetProcessHeap(),0,package->RunningAction); | |
| } |