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