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 #ifdef _WIN64 40 41 #define IActiveScriptParse_Release IActiveScriptParse64_Release 42 #define IActiveScriptParse_InitNew IActiveScriptParse64_InitNew 43 #define IActiveScriptParse_ParseScriptText IActiveScriptParse64_ParseScriptText 44 45 #else 46 47 #define IActiveScriptParse_Release IActiveScriptParse32_Release 48 #define IActiveScriptParse_InitNew IActiveScriptParse32_InitNew 49 #define IActiveScriptParse_ParseScriptText IActiveScriptParse32_ParseScriptText 50 51 #endif 52 53 static const WCHAR szJScript[] = { 'J','S','c','r','i','p','t',0}; 54 static const WCHAR szVBScript[] = { 'V','B','S','c','r','i','p','t',0}; 55 static const WCHAR szSession[] = {'S','e','s','s','i','o','n',0}; 56 57 /* 58 * MsiActiveScriptSite - Our IActiveScriptSite implementation. 59 */ 60 typedef struct { 61 IActiveScriptSite IActiveScriptSite_iface; 62 IDispatch *installer; 63 IDispatch *session; 64 LONG ref; 65 } MsiActiveScriptSite; 66 67 static inline MsiActiveScriptSite *impl_from_IActiveScriptSite( IActiveScriptSite *iface ) 68 { 69 return CONTAINING_RECORD(iface, MsiActiveScriptSite, IActiveScriptSite_iface); 70 } 71 72 /* 73 * MsiActiveScriptSite 74 */ 75 static HRESULT WINAPI MsiActiveScriptSite_QueryInterface(IActiveScriptSite* iface, REFIID riid, void** obj) 76 { 77 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 78 79 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj); 80 81 if (IsEqualGUID(riid, &IID_IUnknown) || 82 IsEqualGUID(riid, &IID_IActiveScriptSite)) 83 { 84 IActiveScriptSite_AddRef(iface); 85 *obj = iface; 86 return S_OK; 87 } 88 89 *obj = NULL; 90 91 return E_NOINTERFACE; 92 } 93 94 static ULONG WINAPI MsiActiveScriptSite_AddRef(IActiveScriptSite* iface) 95 { 96 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 97 ULONG ref = InterlockedIncrement(&This->ref); 98 TRACE("(%p)->(%d)\n", This, ref); 99 return ref; 100 } 101 102 static ULONG WINAPI MsiActiveScriptSite_Release(IActiveScriptSite* iface) 103 { 104 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 105 ULONG ref = InterlockedDecrement(&This->ref); 106 107 TRACE("(%p)->(%d)\n", This, ref); 108 109 if (!ref) 110 msi_free(This); 111 112 return ref; 113 } 114 115 static HRESULT WINAPI MsiActiveScriptSite_GetLCID(IActiveScriptSite* iface, LCID* plcid) 116 { 117 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 118 TRACE("(%p)->(%p)\n", This, plcid); 119 return E_NOTIMPL; /* Script will use system-defined locale */ 120 } 121 122 static HRESULT WINAPI MsiActiveScriptSite_GetItemInfo(IActiveScriptSite* iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti) 123 { 124 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 125 126 TRACE("(%p)->(%p, %d, %p, %p)\n", This, pstrName, dwReturnMask, ppiunkItem, ppti); 127 128 /* Determine the kind of pointer that is requested, and make sure placeholder is valid */ 129 if (dwReturnMask & SCRIPTINFO_ITYPEINFO) { 130 if (!ppti) return E_INVALIDARG; 131 *ppti = NULL; 132 } 133 if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { 134 if (!ppiunkItem) return E_INVALIDARG; 135 *ppiunkItem = NULL; 136 } 137 138 /* Are we looking for the session object? */ 139 if (!strcmpW(szSession, pstrName)) { 140 if (dwReturnMask & SCRIPTINFO_ITYPEINFO) { 141 HRESULT hr = get_typeinfo(Session_tid, ppti); 142 if (SUCCEEDED(hr)) 143 ITypeInfo_AddRef(*ppti); 144 return hr; 145 } 146 else if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { 147 IDispatch_QueryInterface(This->session, &IID_IUnknown, (void **)ppiunkItem); 148 return S_OK; 149 } 150 } 151 152 return TYPE_E_ELEMENTNOTFOUND; 153 } 154 155 static HRESULT WINAPI MsiActiveScriptSite_GetDocVersionString(IActiveScriptSite* iface, BSTR* pbstrVersion) 156 { 157 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 158 TRACE("(%p)->(%p)\n", This, pbstrVersion); 159 return E_NOTIMPL; 160 } 161 162 static HRESULT WINAPI MsiActiveScriptSite_OnScriptTerminate(IActiveScriptSite* iface, const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo) 163 { 164 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 165 TRACE("(%p)->(%p, %p)\n", This, pvarResult, pexcepinfo); 166 return S_OK; 167 } 168 169 static HRESULT WINAPI MsiActiveScriptSite_OnStateChange(IActiveScriptSite* iface, SCRIPTSTATE ssScriptState) 170 { 171 switch (ssScriptState) { 172 case SCRIPTSTATE_UNINITIALIZED: 173 TRACE("State: Uninitialized.\n"); 174 break; 175 176 case SCRIPTSTATE_INITIALIZED: 177 TRACE("State: Initialized.\n"); 178 break; 179 180 case SCRIPTSTATE_STARTED: 181 TRACE("State: Started.\n"); 182 break; 183 184 case SCRIPTSTATE_CONNECTED: 185 TRACE("State: Connected.\n"); 186 break; 187 188 case SCRIPTSTATE_DISCONNECTED: 189 TRACE("State: Disconnected.\n"); 190 break; 191 192 case SCRIPTSTATE_CLOSED: 193 TRACE("State: Closed.\n"); 194 break; 195 196 default: 197 ERR("Unknown State: %d\n", ssScriptState); 198 break; 199 } 200 201 return S_OK; 202 } 203 204 static HRESULT WINAPI MsiActiveScriptSite_OnScriptError(IActiveScriptSite* iface, IActiveScriptError* pscripterror) 205 { 206 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 207 EXCEPINFO exception; 208 HRESULT hr; 209 210 TRACE("(%p)->(%p)\n", This, pscripterror); 211 212 memset(&exception, 0, sizeof(EXCEPINFO)); 213 hr = IActiveScriptError_GetExceptionInfo(pscripterror, &exception); 214 if (SUCCEEDED(hr)) 215 { 216 ERR("script error: %s\n", debugstr_w(exception.bstrDescription)); 217 SysFreeString(exception.bstrSource); 218 SysFreeString(exception.bstrDescription); 219 SysFreeString(exception.bstrHelpFile); 220 } 221 222 return S_OK; 223 } 224 225 static HRESULT WINAPI MsiActiveScriptSite_OnEnterScript(IActiveScriptSite* iface) 226 { 227 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 228 TRACE("(%p)\n", This); 229 return S_OK; 230 } 231 232 static HRESULT WINAPI MsiActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface) 233 { 234 MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface); 235 TRACE("(%p)\n", This); 236 return S_OK; 237 } 238 239 static const struct IActiveScriptSiteVtbl activescriptsitevtbl = 240 { 241 MsiActiveScriptSite_QueryInterface, 242 MsiActiveScriptSite_AddRef, 243 MsiActiveScriptSite_Release, 244 MsiActiveScriptSite_GetLCID, 245 MsiActiveScriptSite_GetItemInfo, 246 MsiActiveScriptSite_GetDocVersionString, 247 MsiActiveScriptSite_OnScriptTerminate, 248 MsiActiveScriptSite_OnStateChange, 249 MsiActiveScriptSite_OnScriptError, 250 MsiActiveScriptSite_OnEnterScript, 251 MsiActiveScriptSite_OnLeaveScript 252 }; 253 254 static HRESULT create_activescriptsite(MsiActiveScriptSite **obj) 255 { 256 MsiActiveScriptSite* object; 257 258 TRACE("(%p)\n", obj); 259 260 *obj = NULL; 261 262 object = msi_alloc( sizeof(MsiActiveScriptSite) ); 263 if (!object) 264 return E_OUTOFMEMORY; 265 266 object->IActiveScriptSite_iface.lpVtbl = &activescriptsitevtbl; 267 object->ref = 1; 268 object->installer = NULL; 269 object->session = NULL; 270 271 *obj = object; 272 273 return S_OK; 274 } 275 276 static UINT map_return_value(LONG val) 277 { 278 switch (val) 279 { 280 case 0: 281 case IDOK: 282 case IDIGNORE: return ERROR_SUCCESS; 283 case IDCANCEL: return ERROR_INSTALL_USEREXIT; 284 case IDRETRY: return ERROR_INSTALL_SUSPEND; 285 case IDABORT: 286 default: return ERROR_INSTALL_FAILURE; 287 } 288 } 289 290 /* 291 * Call a script. 292 */ 293 DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action) 294 { 295 HRESULT hr; 296 IActiveScript *pActiveScript = NULL; 297 IActiveScriptParse *pActiveScriptParse = NULL; 298 MsiActiveScriptSite *scriptsite; 299 IDispatch *pDispatch = NULL; 300 DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; 301 DISPID dispid; 302 CLSID clsid; 303 VARIANT var; 304 DWORD ret = ERROR_INSTALL_FAILURE; 305 306 CoInitialize(NULL); 307 308 /* Create MsiActiveScriptSite object */ 309 hr = create_activescriptsite(&scriptsite); 310 if (hr != S_OK) goto done; 311 312 /* Create an installer object */ 313 hr = create_msiserver(NULL, (void**)&scriptsite->installer); 314 if (hr != S_OK) goto done; 315 316 /* Create a session object */ 317 hr = create_session(hPackage, scriptsite->installer, &scriptsite->session); 318 if (hr != S_OK) goto done; 319 320 /* Create the scripting engine */ 321 type &= msidbCustomActionTypeJScript|msidbCustomActionTypeVBScript; 322 if (type == msidbCustomActionTypeJScript) 323 hr = CLSIDFromProgID(szJScript, &clsid); 324 else if (type == msidbCustomActionTypeVBScript) 325 hr = CLSIDFromProgID(szVBScript, &clsid); 326 else { 327 ERR("Unknown script type %d\n", type); 328 goto done; 329 } 330 if (FAILED(hr)) { 331 ERR("Could not find CLSID for Windows Script\n"); 332 goto done; 333 } 334 hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&pActiveScript); 335 if (FAILED(hr)) { 336 ERR("Could not instantiate class for Windows Script\n"); 337 goto done; 338 } 339 340 hr = IActiveScript_QueryInterface(pActiveScript, &IID_IActiveScriptParse, (void **)&pActiveScriptParse); 341 if (FAILED(hr)) goto done; 342 343 hr = IActiveScript_SetScriptSite(pActiveScript, &scriptsite->IActiveScriptSite_iface); 344 if (FAILED(hr)) goto done; 345 346 hr = IActiveScriptParse_InitNew(pActiveScriptParse); 347 if (FAILED(hr)) goto done; 348 349 hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_GLOBALMEMBERS|SCRIPTITEM_ISVISIBLE); 350 if (FAILED(hr)) goto done; 351 352 hr = IActiveScriptParse_ParseScriptText(pActiveScriptParse, script, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL); 353 if (FAILED(hr)) goto done; 354 355 hr = IActiveScript_SetScriptState(pActiveScript, SCRIPTSTATE_CONNECTED); 356 if (FAILED(hr)) goto done; 357 358 /* Call a function if necessary through the IDispatch interface */ 359 if (function && function[0]) { 360 TRACE("Calling function %s\n", debugstr_w(function)); 361 362 hr = IActiveScript_GetScriptDispatch(pActiveScript, NULL, &pDispatch); 363 if (FAILED(hr)) goto done; 364 365 hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, (WCHAR **)&function, 1,LOCALE_USER_DEFAULT, &dispid); 366 if (FAILED(hr)) goto done; 367 368 hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, &var, NULL, NULL); 369 if (FAILED(hr)) goto done; 370 371 hr = VariantChangeType(&var, &var, 0, VT_I4); 372 if (FAILED(hr)) goto done; 373 374 ret = map_return_value(V_I4(&var)); 375 376 VariantClear(&var); 377 } else { 378 /* If no function to be called, MSI behavior is to succeed */ 379 ret = ERROR_SUCCESS; 380 } 381 382 done: 383 384 if (pDispatch) IDispatch_Release(pDispatch); 385 if (pActiveScript) IActiveScript_Release(pActiveScript); 386 if (pActiveScriptParse) IActiveScriptParse_Release(pActiveScriptParse); 387 if (scriptsite) 388 { 389 if (scriptsite->session) IDispatch_Release(scriptsite->session); 390 if (scriptsite->installer) IDispatch_Release(scriptsite->installer); 391 IActiveScriptSite_Release(&scriptsite->IActiveScriptSite_iface); 392 } 393 CoUninitialize(); /* must call even if CoInitialize failed */ 394 return ret; 395 } 396