1 /* 2 * Implementation of scripting for Microsoft Installer (msi.dll) 3 * 4 * Copyright 2007 Misha Koshelev 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #define COBJMACROS 22 23 #include <stdarg.h> 24 #include "windef.h" 25 #include "winbase.h" 26 #include "winerror.h" 27 #include "winuser.h" 28 #include "msidefs.h" 29 #include "msipriv.h" 30 #include "activscp.h" 31 #include "oleauto.h" 32 #include "wine/debug.h" 33 #include "wine/unicode.h" 34 35 #include "msiserver.h" 36 37 WINE_DEFAULT_DEBUG_CHANNEL(msi); 38 39 static const WCHAR szJScript[] = { 'J','S','c','r','i','p','t',0}; 40 static const WCHAR szVBScript[] = { 'V','B','S','c','r','i','p','t',0}; 41 static const WCHAR szSession[] = {'S','e','s','s','i','o','n',0}; 42 43 /* 44 * MsiActiveScriptSite - Our IActiveScriptSite implementation. 45 */ 46 47 typedef struct { 48 IActiveScriptSite lpVtbl; 49 IDispatch *pInstaller; 50 IDispatch *pSession; 51 LONG ref; 52 } MsiActiveScriptSite; 53 54 static const struct IActiveScriptSiteVtbl ASS_Vtbl; 55 56 static HRESULT create_ActiveScriptSite(IUnknown *pUnkOuter, LPVOID *ppObj) 57 { 58 MsiActiveScriptSite* object; 59 60 TRACE("(%p,%p)\n", pUnkOuter, ppObj); 61 62 if( pUnkOuter ) 63 return CLASS_E_NOAGGREGATION; 64 65 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MsiActiveScriptSite)); 66 67 object->lpVtbl.lpVtbl = &ASS_Vtbl; 68 object->ref = 1; 69 object->pInstaller = NULL; 70 object->pSession = NULL; 71 72 *ppObj = object; 73 74 return S_OK; 75 } 76 77 /* 78 * Call a script. 79 */ 80 DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action) 81 { 82 HRESULT hr; 83 IActiveScript *pActiveScript = NULL; 84 IActiveScriptParse *pActiveScriptParse = NULL; 85 MsiActiveScriptSite *pActiveScriptSite = NULL; 86 IDispatch *pDispatch = NULL; 87 DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; 88 DISPID dispid; 89 CLSID clsid; 90 VARIANT var; 91 92 /* Return success by default (if Windows Script not installed) - not native behavior. This 93 * should be here until we implement wine scripting. */ 94 DWORD ret = ERROR_SUCCESS; 95 96 CoInitialize(NULL); 97 98 /* Create MsiActiveScriptSite object */ 99 hr = create_ActiveScriptSite(NULL, (void **)&pActiveScriptSite); 100 if (hr != S_OK) goto done; 101 102 /* Create an installer object */ 103 hr = create_msiserver(NULL, (LPVOID *)&pActiveScriptSite->pInstaller); 104 if (hr != S_OK) goto done; 105 106 /* Create a session object */ 107 hr = create_session(hPackage, pActiveScriptSite->pInstaller, &pActiveScriptSite->pSession); 108 if (hr != S_OK) goto done; 109 110 /* Create the scripting engine */ 111 if ((type & 7) == msidbCustomActionTypeJScript) 112 hr = CLSIDFromProgID(szJScript, &clsid); 113 else if ((type & 7) == msidbCustomActionTypeVBScript) 114 hr = CLSIDFromProgID(szVBScript, &clsid); 115 else { 116 ERR("Unknown script type %d\n", type); 117 goto done; 118 } 119 if (FAILED(hr)) { 120 ERR("Could not find CLSID for Windows Script\n"); 121 goto done; 122 } 123 hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&pActiveScript); 124 if (FAILED(hr)) { 125 ERR("Could not instantiate class for Windows Script\n"); 126 goto done; 127 } 128 129 /* If we got this far, Windows Script is installed, so don't return success by default anymore */ 130 ret = ERROR_INSTALL_FAILURE; 131 132 /* Get the IActiveScriptParse engine interface */ 133 hr = IActiveScript_QueryInterface(pActiveScript, &IID_IActiveScriptParse, (void **)&pActiveScriptParse); 134 if (FAILED(hr)) goto done; 135 136 /* Give our host to the engine */ 137 hr = IActiveScript_SetScriptSite(pActiveScript, (IActiveScriptSite *)pActiveScriptSite); 138 if (FAILED(hr)) goto done; 139 140 /* Initialize the script engine */ 141 hr = IActiveScriptParse_InitNew(pActiveScriptParse); 142 if (FAILED(hr)) goto done; 143 144 /* Add the session object */ 145 hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_ISVISIBLE); 146 147 /* Pass the script to the engine */ 148 hr = IActiveScriptParse_ParseScriptText(pActiveScriptParse, script, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL); 149 if (FAILED(hr)) goto done; 150 151 /* Start processing the script */ 152 hr = IActiveScript_SetScriptState(pActiveScript, SCRIPTSTATE_CONNECTED); 153 if (FAILED(hr)) goto done; 154 155 /* Call a function if necessary through the IDispatch interface */ 156 if (function != NULL && strlenW(function) > 0) { 157 TRACE("Calling function %s\n", debugstr_w(function)); 158 159 hr = IActiveScript_GetScriptDispatch(pActiveScript, NULL, &pDispatch); 160 if (FAILED(hr)) goto done; 161 162 hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, (WCHAR **)&function, 1,LOCALE_USER_DEFAULT, &dispid); 163 if (FAILED(hr)) goto done; 164 165 hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, &var, NULL, NULL); 166 if (FAILED(hr)) goto done; 167 168 /* Check return value, if it's not IDOK we failed */ 169 hr = VariantChangeType(&var, &var, 0, VT_I4); 170 if (FAILED(hr)) goto done; 171 172 if (V_I4(&var) == IDOK) 173 ret = ERROR_SUCCESS; 174 else ret = ERROR_INSTALL_FAILURE; 175 176 VariantClear(&var); 177 } else { 178 /* If no function to be called, MSI behavior is to succeed */ 179 ret = ERROR_SUCCESS; 180 } 181 182 done: 183 184 /* Free everything that needs to be freed */ 185 if (pDispatch) IDispatch_Release(pDispatch); 186 if (pActiveScript) IActiveScriptSite_Release(pActiveScript); 187 if (pActiveScriptSite && 188 pActiveScriptSite->pSession) IUnknown_Release((IUnknown *)pActiveScriptSite->pSession); 189 if (pActiveScriptSite && 190 pActiveScriptSite->pInstaller) IUnknown_Release((IUnknown *)pActiveScriptSite->pInstaller); 191 if (pActiveScriptSite) IUnknown_Release((IUnknown *)pActiveScriptSite); 192 193 CoUninitialize(); /* must call even if CoInitialize failed */ 194 195 return ret; 196 } 197 198 /* 199 * MsiActiveScriptSite 200 */ 201 202 /*** IUnknown methods ***/ 203 static HRESULT WINAPI MsiActiveScriptSite_QueryInterface(IActiveScriptSite* iface, REFIID riid, void** ppvObject) 204 { 205 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 206 207 TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject); 208 209 if (IsEqualGUID(riid, &IID_IUnknown) || 210 IsEqualGUID(riid, &IID_IActiveScriptSite)) 211 { 212 IClassFactory_AddRef(iface); 213 *ppvObject = This; 214 return S_OK; 215 } 216 217 TRACE("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject); 218 219 return E_NOINTERFACE; 220 } 221 222 static ULONG WINAPI MsiActiveScriptSite_AddRef(IActiveScriptSite* iface) 223 { 224 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 225 226 TRACE("(%p/%p)\n", iface, This); 227 228 return InterlockedIncrement(&This->ref); 229 } 230 231 static ULONG WINAPI MsiActiveScriptSite_Release(IActiveScriptSite* iface) 232 { 233 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 234 ULONG ref = InterlockedDecrement(&This->ref); 235 236 TRACE("(%p/%p)\n", iface, This); 237 238 if (!ref) 239 HeapFree(GetProcessHeap(), 0, This); 240 241 return ref; 242 } 243 244 /*** IActiveScriptSite methods **/ 245 static HRESULT WINAPI MsiActiveScriptSite_GetLCID(IActiveScriptSite* iface, LCID* plcid) 246 { 247 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 248 TRACE("(%p/%p)->(%p)\n", This, iface, plcid); 249 return E_NOTIMPL; /* Script will use system-defined locale */ 250 } 251 252 static HRESULT WINAPI MsiActiveScriptSite_GetItemInfo(IActiveScriptSite* iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti) 253 { 254 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 255 TRACE("(%p/%p)->(%p,%d,%p,%p)\n", This, iface, pstrName, dwReturnMask, ppiunkItem, ppti); 256 257 /* Determine the kind of pointer that is requested, and make sure placeholder is valid */ 258 if (dwReturnMask & SCRIPTINFO_ITYPEINFO) { 259 if (!ppti) return E_INVALIDARG; 260 *ppti = NULL; 261 } 262 if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { 263 if (!ppiunkItem) return E_INVALIDARG; 264 *ppiunkItem = NULL; 265 } 266 267 /* Are we looking for the session object? */ 268 if (!strcmpW(szSession, pstrName)) { 269 if (dwReturnMask & SCRIPTINFO_ITYPEINFO) 270 return load_type_info(This->pSession, ppti, &DIID_Session, 0); 271 else if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { 272 IDispatch_QueryInterface(This->pSession, &IID_IUnknown, (void **)ppiunkItem); 273 return S_OK; 274 } 275 } 276 277 return TYPE_E_ELEMENTNOTFOUND; 278 } 279 280 static HRESULT WINAPI MsiActiveScriptSite_GetDocVersionString(IActiveScriptSite* iface, BSTR* pbstrVersion) 281 { 282 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 283 TRACE("(%p/%p)->(%p)\n", This, iface, pbstrVersion); 284 return E_NOTIMPL; 285 } 286 287 static HRESULT WINAPI MsiActiveScriptSite_OnScriptTerminate(IActiveScriptSite* iface, const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo) 288 { 289 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 290 TRACE("(%p/%p)->(%p,%p)\n", This, iface, pvarResult, pexcepinfo); 291 return S_OK; 292 } 293 294 static HRESULT WINAPI MsiActiveScriptSite_OnStateChange(IActiveScriptSite* iface, SCRIPTSTATE ssScriptState) 295 { 296 switch (ssScriptState) { 297 case SCRIPTSTATE_UNINITIALIZED: 298 TRACE("State: Uninitialized.\n"); 299 break; 300 301 case SCRIPTSTATE_INITIALIZED: 302 TRACE("State: Initialized.\n"); 303 break; 304 305 case SCRIPTSTATE_STARTED: 306 TRACE("State: Started.\n"); 307 break; 308 309 case SCRIPTSTATE_CONNECTED: 310 TRACE("State: Connected.\n"); 311 break; 312 313 case SCRIPTSTATE_DISCONNECTED: 314 TRACE("State: Disconnected.\n"); 315 break; 316 317 case SCRIPTSTATE_CLOSED: 318 TRACE("State: Closed.\n"); 319 break; 320 321 default: 322 ERR("Unknown State: %d\n", ssScriptState); 323 break; 324 } 325 326 return S_OK; 327 } 328 329 static HRESULT WINAPI MsiActiveScriptSite_OnScriptError(IActiveScriptSite* iface, IActiveScriptError* pscripterror) 330 { 331 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 332 EXCEPINFO exception; 333 HRESULT hr; 334 335 TRACE("(%p/%p)->(%p)\n", This, iface, pscripterror); 336 337 memset(&exception, 0, sizeof(EXCEPINFO)); 338 hr = IActiveScriptError_GetExceptionInfo(pscripterror, &exception); 339 if (SUCCEEDED(hr)) 340 { 341 ERR("script error: %s\n", debugstr_w(exception.bstrDescription)); 342 SysFreeString(exception.bstrSource); 343 SysFreeString(exception.bstrDescription); 344 SysFreeString(exception.bstrHelpFile); 345 } 346 347 return S_OK; 348 } 349 350 static HRESULT WINAPI MsiActiveScriptSite_OnEnterScript(IActiveScriptSite* iface) 351 { 352 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 353 TRACE("(%p/%p)\n", This, iface); 354 return S_OK; 355 } 356 357 static HRESULT WINAPI MsiActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface) 358 { 359 MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; 360 TRACE("(%p/%p)\n", This, iface); 361 return S_OK; 362 } 363 364 static const struct IActiveScriptSiteVtbl ASS_Vtbl = 365 { 366 MsiActiveScriptSite_QueryInterface, 367 MsiActiveScriptSite_AddRef, 368 MsiActiveScriptSite_Release, 369 MsiActiveScriptSite_GetLCID, 370 MsiActiveScriptSite_GetItemInfo, 371 MsiActiveScriptSite_GetDocVersionString, 372 MsiActiveScriptSite_OnScriptTerminate, 373 MsiActiveScriptSite_OnStateChange, 374 MsiActiveScriptSite_OnScriptError, 375 MsiActiveScriptSite_OnEnterScript, 376 MsiActiveScriptSite_OnLeaveScript 377 }; 378