script.c 11.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * Implementation of scripting for Microsoft Installer (msi.dll)
 *
 * Copyright 2007 Misha Koshelev
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#define COBJMACROS

#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winuser.h"
#include "msidefs.h"
#include "msipriv.h"
#include "activscp.h"
#include "oleauto.h"
#include "wine/debug.h"
#include "wine/unicode.h"

#include "msiserver.h"

WINE_DEFAULT_DEBUG_CHANNEL(msi);

39 40 41 42 43 44 45 46 47 48 49 50 51 52
#ifdef _WIN64

#define IActiveScriptParse_Release IActiveScriptParse64_Release
#define IActiveScriptParse_InitNew IActiveScriptParse64_InitNew
#define IActiveScriptParse_ParseScriptText IActiveScriptParse64_ParseScriptText

#else

#define IActiveScriptParse_Release IActiveScriptParse32_Release
#define IActiveScriptParse_InitNew IActiveScriptParse32_InitNew
#define IActiveScriptParse_ParseScriptText IActiveScriptParse32_ParseScriptText

#endif

53 54 55 56 57 58 59 60
static const WCHAR szJScript[] = { 'J','S','c','r','i','p','t',0};
static const WCHAR szVBScript[] = { 'V','B','S','c','r','i','p','t',0};
static const WCHAR szSession[] = {'S','e','s','s','i','o','n',0};

/*
 * MsiActiveScriptSite - Our IActiveScriptSite implementation.
 */
typedef struct {
61
    IActiveScriptSite IActiveScriptSite_iface;
62 63
    IDispatch *installer;
    IDispatch *session;
64 65 66
    LONG ref;
} MsiActiveScriptSite;

67
static inline MsiActiveScriptSite *impl_from_IActiveScriptSite( IActiveScriptSite *iface )
68
{
69
    return CONTAINING_RECORD(iface, MsiActiveScriptSite, IActiveScriptSite_iface);
70 71 72 73 74
}

/*
 * MsiActiveScriptSite
 */
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 82 83

    if (IsEqualGUID(riid, &IID_IUnknown) ||
        IsEqualGUID(riid, &IID_IActiveScriptSite))
    {
84
        IActiveScriptSite_AddRef(iface);
85
        *obj = iface;
86 87 88
        return S_OK;
    }

89
    *obj = NULL;
90 91 92 93 94 95

    return E_NOINTERFACE;
}

static ULONG WINAPI MsiActiveScriptSite_AddRef(IActiveScriptSite* iface)
{
96 97 98 99
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
    ULONG ref = InterlockedIncrement(&This->ref);
    TRACE("(%p)->(%d)\n", This, ref);
    return ref;
100 101 102 103
}

static ULONG WINAPI MsiActiveScriptSite_Release(IActiveScriptSite* iface)
{
104
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
105 106
    ULONG ref = InterlockedDecrement(&This->ref);

107
    TRACE("(%p)->(%d)\n", This, ref);
108 109

    if (!ref)
110
        msi_free(This);
111 112 113 114 115 116

    return ref;
}

static HRESULT WINAPI MsiActiveScriptSite_GetLCID(IActiveScriptSite* iface, LCID* plcid)
{
117 118
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
    TRACE("(%p)->(%p)\n", This, plcid);
119 120 121 122 123
    return E_NOTIMPL;  /* Script will use system-defined locale */
}

static HRESULT WINAPI MsiActiveScriptSite_GetItemInfo(IActiveScriptSite* iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti)
{
124 125 126
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);

    TRACE("(%p)->(%p, %d, %p, %p)\n", This, pstrName, dwReturnMask, ppiunkItem, ppti);
127 128 129

    /* Determine the kind of pointer that is requested, and make sure placeholder is valid */
    if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
130 131
        if (!ppti) return E_INVALIDARG;
        *ppti = NULL;
132 133
    }
    if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
134 135
        if (!ppiunkItem) return E_INVALIDARG;
        *ppiunkItem = NULL;
136 137 138 139
    }

    /* Are we looking for the session object? */
    if (!strcmpW(szSession, pstrName)) {
140 141 142 143 144 145
        if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
            HRESULT hr = get_typeinfo(Session_tid, ppti);
            if (SUCCEEDED(hr))
                ITypeInfo_AddRef(*ppti);
            return hr;
        }
146
        else if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
147
            IDispatch_QueryInterface(This->session, &IID_IUnknown, (void **)ppiunkItem);
148
            return S_OK;
149 150 151 152 153 154 155 156
        }
    }

    return TYPE_E_ELEMENTNOTFOUND;
}

static HRESULT WINAPI MsiActiveScriptSite_GetDocVersionString(IActiveScriptSite* iface, BSTR* pbstrVersion)
{
157 158
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
    TRACE("(%p)->(%p)\n", This, pbstrVersion);
159 160 161 162 163
    return E_NOTIMPL;
}

static HRESULT WINAPI MsiActiveScriptSite_OnScriptTerminate(IActiveScriptSite* iface, const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo)
{
164 165
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
    TRACE("(%p)->(%p, %p)\n", This, pvarResult, pexcepinfo);
166 167 168 169 170 171
    return S_OK;
}

static HRESULT WINAPI MsiActiveScriptSite_OnStateChange(IActiveScriptSite* iface, SCRIPTSTATE ssScriptState)
{
    switch (ssScriptState) {
172 173 174
        case SCRIPTSTATE_UNINITIALIZED:
              TRACE("State: Uninitialized.\n");
              break;
175

176 177 178
        case SCRIPTSTATE_INITIALIZED:
              TRACE("State: Initialized.\n");
              break;
179

180 181 182
        case SCRIPTSTATE_STARTED:
              TRACE("State: Started.\n");
              break;
183

184 185 186
        case SCRIPTSTATE_CONNECTED:
              TRACE("State: Connected.\n");
              break;
187

188 189 190
        case SCRIPTSTATE_DISCONNECTED:
              TRACE("State: Disconnected.\n");
              break;
191

192 193 194
        case SCRIPTSTATE_CLOSED:
              TRACE("State: Closed.\n");
              break;
195

196 197 198
        default:
              ERR("Unknown State: %d\n", ssScriptState);
              break;
199 200 201 202 203 204 205
    }

    return S_OK;
}

static HRESULT WINAPI MsiActiveScriptSite_OnScriptError(IActiveScriptSite* iface, IActiveScriptError* pscripterror)
{
206
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
207 208 209
    EXCEPINFO exception;
    HRESULT hr;

210
    TRACE("(%p)->(%p)\n", This, pscripterror);
211

212
    memset(&exception, 0, sizeof(EXCEPINFO));
213 214
    hr = IActiveScriptError_GetExceptionInfo(pscripterror, &exception);
    if (SUCCEEDED(hr))
215
    {
216
        ERR("script error: %s\n", debugstr_w(exception.bstrDescription));
217 218 219 220
        SysFreeString(exception.bstrSource);
        SysFreeString(exception.bstrDescription);
        SysFreeString(exception.bstrHelpFile);
    }
221 222 223 224 225 226

    return S_OK;
}

static HRESULT WINAPI MsiActiveScriptSite_OnEnterScript(IActiveScriptSite* iface)
{
227 228
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
    TRACE("(%p)\n", This);
229 230 231 232 233
    return S_OK;
}

static HRESULT WINAPI MsiActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface)
{
234 235
    MsiActiveScriptSite *This = impl_from_IActiveScriptSite(iface);
    TRACE("(%p)\n", This);
236 237 238
    return S_OK;
}

239
static const struct IActiveScriptSiteVtbl activescriptsitevtbl =
240 241 242 243 244 245 246 247 248 249 250 251 252
{
    MsiActiveScriptSite_QueryInterface,
    MsiActiveScriptSite_AddRef,
    MsiActiveScriptSite_Release,
    MsiActiveScriptSite_GetLCID,
    MsiActiveScriptSite_GetItemInfo,
    MsiActiveScriptSite_GetDocVersionString,
    MsiActiveScriptSite_OnScriptTerminate,
    MsiActiveScriptSite_OnStateChange,
    MsiActiveScriptSite_OnScriptError,
    MsiActiveScriptSite_OnEnterScript,
    MsiActiveScriptSite_OnLeaveScript
};
253

254
static HRESULT create_activescriptsite(MsiActiveScriptSite **obj)
255 256 257
{
    MsiActiveScriptSite* object;

258
    TRACE("(%p)\n", obj);
259

260
    *obj = NULL;
261

262
    object = msi_alloc( sizeof(MsiActiveScriptSite) );
263 264
    if (!object)
        return E_OUTOFMEMORY;
265 266 267

    object->IActiveScriptSite_iface.lpVtbl = &activescriptsitevtbl;
    object->ref = 1;
268 269
    object->installer = NULL;
    object->session = NULL;
270

271
    *obj = object;
272 273 274 275 276 277 278 279 280 281 282 283

    return S_OK;
}

/*
 * Call a script.
 */
DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action)
{
    HRESULT hr;
    IActiveScript *pActiveScript = NULL;
    IActiveScriptParse *pActiveScriptParse = NULL;
284
    MsiActiveScriptSite *scriptsite;
285 286 287 288 289 290 291 292 293 294
    IDispatch *pDispatch = NULL;
    DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
    DISPID dispid;
    CLSID clsid;
    VARIANT var;
    DWORD ret = ERROR_INSTALL_FAILURE;

    CoInitialize(NULL);

    /* Create MsiActiveScriptSite object */
295
    hr = create_activescriptsite(&scriptsite);
296 297 298
    if (hr != S_OK) goto done;

    /* Create an installer object */
299
    hr = create_msiserver(NULL, (void**)&scriptsite->installer);
300 301 302
    if (hr != S_OK) goto done;

    /* Create a session object */
303
    hr = create_session(hPackage, scriptsite->installer, &scriptsite->session);
304 305 306
    if (hr != S_OK) goto done;

    /* Create the scripting engine */
307 308
    type &= msidbCustomActionTypeJScript|msidbCustomActionTypeVBScript;
    if (type == msidbCustomActionTypeJScript)
309
        hr = CLSIDFromProgID(szJScript, &clsid);
310
    else if (type == msidbCustomActionTypeVBScript)
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
        hr = CLSIDFromProgID(szVBScript, &clsid);
    else {
        ERR("Unknown script type %d\n", type);
        goto done;
    }
    if (FAILED(hr)) {
        ERR("Could not find CLSID for Windows Script\n");
        goto done;
    }
    hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&pActiveScript);
    if (FAILED(hr)) {
        ERR("Could not instantiate class for Windows Script\n");
        goto done;
    }

    hr = IActiveScript_QueryInterface(pActiveScript, &IID_IActiveScriptParse, (void **)&pActiveScriptParse);
    if (FAILED(hr)) goto done;

329
    hr = IActiveScript_SetScriptSite(pActiveScript, &scriptsite->IActiveScriptSite_iface);
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
    if (FAILED(hr)) goto done;

    hr = IActiveScriptParse_InitNew(pActiveScriptParse);
    if (FAILED(hr)) goto done;

    hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_GLOBALMEMBERS|SCRIPTITEM_ISVISIBLE);
    if (FAILED(hr)) goto done;

    hr = IActiveScriptParse_ParseScriptText(pActiveScriptParse, script, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL);
    if (FAILED(hr)) goto done;

    hr = IActiveScript_SetScriptState(pActiveScript, SCRIPTSTATE_CONNECTED);
    if (FAILED(hr)) goto done;

    /* Call a function if necessary through the IDispatch interface */
    if (function != NULL && strlenW(function) > 0) {
        TRACE("Calling function %s\n", debugstr_w(function));

        hr = IActiveScript_GetScriptDispatch(pActiveScript, NULL, &pDispatch);
        if (FAILED(hr)) goto done;

        hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, (WCHAR **)&function, 1,LOCALE_USER_DEFAULT, &dispid);
        if (FAILED(hr)) goto done;

        hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, &var, NULL, NULL);
        if (FAILED(hr)) goto done;

        /* Check return value, if it's not IDOK we failed */
        hr = VariantChangeType(&var, &var, 0, VT_I4);
        if (FAILED(hr)) goto done;

        if (V_I4(&var) == IDOK)
            ret = ERROR_SUCCESS;
        else ret = ERROR_INSTALL_FAILURE;

        VariantClear(&var);
    } else {
        /* If no function to be called, MSI behavior is to succeed */
        ret = ERROR_SUCCESS;
    }

done:

    if (pDispatch) IDispatch_Release(pDispatch);
    if (pActiveScript) IActiveScript_Release(pActiveScript);
    if (pActiveScriptParse) IActiveScriptParse_Release(pActiveScriptParse);
376
    if (scriptsite)
377
    {
378 379 380
        if (scriptsite->session) IDispatch_Release(scriptsite->session);
        if (scriptsite->installer) IDispatch_Release(scriptsite->installer);
        IActiveScriptSite_Release(&scriptsite->IActiveScriptSite_iface);
381 382 383 384
    }
    CoUninitialize();    /* must call even if CoInitialize failed */
    return ret;
}