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