connectioncontainer.cpp 13.5 KB
Newer Older
1 2 3
/*****************************************************************************
 * connectioncontainer.cpp: ActiveX control for VLC
 *****************************************************************************
4
 * Copyright (C) 2005 the VideoLAN team
5
 * Copyright (C) 2010 M2X BV
6 7
 *
 * Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
8
 *          Jean-Paul Saman <jpsaman@videolan.org>
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28 29
 *****************************************************************************/

#include "plugin.h"
#include "connectioncontainer.h"

#include "utils.h"

30 31 32 33
#include <assert.h>
#include <rpc.h>
#include <rpcndr.h>

34 35
using namespace std;

36 37 38 39 40 41
////////////////////////////////////////////////////////////////////////////////////////////////
DEFINE_GUID(IID_IGlobalInterfaceTable,     0x00000146, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46);
DEFINE_GUID(CLSID_StdGlobalInterfaceTable, 0x00000323, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46);

const GUID  IID_IGlobalInterfaceTable = { 0x00000146, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
const CLSID CLSID_StdGlobalInterfaceTable = { 0x00000323, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
42 43
////////////////////////////////////////////////////////////////////////////////////////////////

44 45
/* this function object is used to return the value from a map pair */
struct VLCEnumConnectionsDereference
46
{
47
    CONNECTDATA operator()(const map<DWORD,LPUNKNOWN>::iterator& i)
48
    {
49
        CONNECTDATA cd;
50

51 52
        i->second->AddRef();

53 54 55
        cd.dwCookie = i->first;
        cd.pUnk     = i->second;
        return cd;
56
    };
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
};

class VLCEnumConnections : public VLCEnumIterator<IID_IEnumConnections,
    IEnumConnections,
    CONNECTDATA,
    map<DWORD,LPUNKNOWN>::iterator,
    VLCEnumConnectionsDereference>
{
public:
    VLCEnumConnections(map<DWORD,LPUNKNOWN> &m) :
        VLCEnumIterator<IID_IEnumConnections,
            IEnumConnections,
            CONNECTDATA,
            map<DWORD,LPUNKNOWN>::iterator,
            VLCEnumConnectionsDereference> (m.begin(), m.end())
    {};
};

////////////////////////////////////////////////////////////////////////////////////////////////
76

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
/* this function object is used to retain the dereferenced iterator value */
struct VLCEnumConnectionPointsDereference
{
    LPCONNECTIONPOINT operator()(const vector<LPCONNECTIONPOINT>::iterator& i)
    {
        LPCONNECTIONPOINT cp = *i;
        cp->AddRef();
        return cp;
    }
};

class VLCEnumConnectionPoints: public VLCEnumIterator<IID_IEnumConnectionPoints,
    IEnumConnectionPoints,
    LPCONNECTIONPOINT,
    vector<LPCONNECTIONPOINT>::iterator,
    VLCEnumConnectionPointsDereference>
{
public:
    VLCEnumConnectionPoints(vector<LPCONNECTIONPOINT>& v) :
        VLCEnumIterator<IID_IEnumConnectionPoints,
            IEnumConnectionPoints,
            LPCONNECTIONPOINT,
            vector<LPCONNECTIONPOINT>::iterator,
            VLCEnumConnectionPointsDereference> (v.begin(), v.end())
    {};
102 103
};

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
////////////////////////////////////////////////////////////////////////////////////////////////
// Condition variable emulation
////////////////////////////////////////////////////////////////////////////////////////////////

static void ConditionInit(HANDLE *handle)
{
    *handle = CreateEvent(NULL, TRUE, FALSE, NULL);
    assert(*handle != NULL);
}

static void ConditionDestroy(HANDLE *handle)
{
    CloseHandle(*handle);
}

static void ConditionWait(HANDLE *handle, CRITICAL_SECTION *lock)
{
    DWORD dwWaitResult;

    do {
        LeaveCriticalSection(lock);
        dwWaitResult = WaitForSingleObjectEx(*handle, INFINITE, TRUE);
        EnterCriticalSection(lock);
    } while(dwWaitResult == WAIT_IO_COMPLETION);

    assert(dwWaitResult != WAIT_ABANDONED); /* another thread failed to cleanup! */
    assert(dwWaitResult != WAIT_FAILED);
    ResetEvent(*handle);
}

static void ConditionSignal(HANDLE *handle)
{
    SetEvent(*handle);
}

////////////////////////////////////////////////////////////////////////////////////////////////
// Event handling thread
//
// It bridges the gap between libvlc thread context and ActiveX/COM thread context.
////////////////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI ThreadProcEventHandler(LPVOID lpParam)
{
    CoInitialize(NULL);
    VLCConnectionPointContainer *pCPC = (VLCConnectionPointContainer *)lpParam;

    while(pCPC->isRunning)
    {
        EnterCriticalSection(&(pCPC->csEvents));
        ConditionWait(&(pCPC->sEvents), &(pCPC->csEvents));

        if (!pCPC->isRunning)
        {
            LeaveCriticalSection(&(pCPC->csEvents));
            break;
        }

        while(!pCPC->_q_events.empty())
        {
            VLCDispatchEvent *ev = pCPC->_q_events.front();
            pCPC->_q_events.pop();
            pCPC->_p_events->fireEvent(ev->_dispId, &ev->_dispParams);
            delete ev;
166 167 168 169 170 171 172


            if (!pCPC->isRunning)
            {
                LeaveCriticalSection(&(pCPC->csEvents));
                goto out;
            }
173 174 175
        }
        LeaveCriticalSection(&(pCPC->csEvents));
    }
176
out:
177 178 179 180 181 182
    CoUninitialize();
    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////
// VLCConnectionPoint
183 184
////////////////////////////////////////////////////////////////////////////////////////////////

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
VLCConnectionPoint::VLCConnectionPoint(IConnectionPointContainer *p_cpc, REFIID iid) :
        _iid(iid), _p_cpc(p_cpc)
{
    // Get the Global Interface Table per-process singleton:
    CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0,
                          CLSCTX_INPROC_SERVER,
                          IID_IGlobalInterfaceTable,
                          reinterpret_cast<void**>(&m_pGIT));
};

VLCConnectionPoint::~VLCConnectionPoint()
{
    // Revoke interfaces from the GIT:
    map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
    map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();

    while( iter != end )
    {
        m_pGIT->RevokeInterfaceFromGlobal((DWORD)iter->second);
        ++iter;
    }
    m_pGIT->Release();
};

209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
STDMETHODIMP VLCConnectionPoint::GetConnectionInterface(IID *iid)
{
    if( NULL == iid )
        return E_POINTER;

    *iid = _iid;
    return S_OK;
};

STDMETHODIMP VLCConnectionPoint::GetConnectionPointContainer(LPCONNECTIONPOINTCONTAINER *ppCPC)
{
    if( NULL == ppCPC )
        return E_POINTER;

    _p_cpc->AddRef();
    *ppCPC = _p_cpc;
    return S_OK;
};

STDMETHODIMP VLCConnectionPoint::Advise(IUnknown *pUnk, DWORD *pdwCookie)
{
230
    static DWORD dwCookieCounter = 0;
231
    HRESULT hr;
232

233 234 235
    if( (NULL == pUnk) || (NULL == pdwCookie) )
        return E_POINTER;

236 237
    hr = pUnk->QueryInterface(_iid, (LPVOID *)&pUnk);
    if( SUCCEEDED(hr) )
238
    {
239 240 241 242 243 244 245 246
        DWORD dwGITCookie;
        hr = m_pGIT->RegisterInterfaceInGlobal( pUnk, _iid, &dwGITCookie );
        if( SUCCEEDED(hr) )
        {
            *pdwCookie = ++dwCookieCounter;
            _connections[*pdwCookie] = (LPUNKNOWN) dwGITCookie;
        }
        pUnk->Release();
247
    }
248
    return hr;
249 250 251 252
};

STDMETHODIMP VLCConnectionPoint::Unadvise(DWORD pdwCookie)
{
253 254
    map<DWORD,LPUNKNOWN>::iterator pcd = _connections.find((DWORD)pdwCookie);
    if( pcd != _connections.end() )
255
    {
256
        m_pGIT->RevokeInterfaceFromGlobal((DWORD)pcd->second);
257 258
        _connections.erase(pdwCookie);
        return S_OK;
259 260 261 262 263 264 265 266 267 268 269 270 271 272
    }
    return CONNECT_E_NOCONNECTION;
};

STDMETHODIMP VLCConnectionPoint::EnumConnections(IEnumConnections **ppEnum)
{
    if( NULL == ppEnum )
        return E_POINTER;

    *ppEnum = dynamic_cast<LPENUMCONNECTIONS>(new VLCEnumConnections(_connections));

    return (NULL != *ppEnum ) ? S_OK : E_OUTOFMEMORY;
};

273
void VLCConnectionPoint::fireEvent(DISPID dispId, DISPPARAMS *pDispParams)
274
{
275 276
    map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
    map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
277

278 279
    HRESULT hr = S_OK;

280 281
    while( iter != end )
    {
282 283 284 285 286 287
        DWORD dwCookie = (DWORD)iter->second;
        LPUNKNOWN pUnk;

        hr = m_pGIT->GetInterfaceFromGlobal( dwCookie, _iid,
                                             reinterpret_cast<void **>(&pUnk) );
        if( SUCCEEDED(hr) )
288 289
        {
            IDispatch *pDisp;
290 291
            hr = pUnk->QueryInterface(_iid, (LPVOID *)&pDisp);
            if( SUCCEEDED(hr) )
292
            {
293 294
                pDisp->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT,
                              DISPATCH_METHOD, pDispParams, NULL, NULL, NULL);
295 296
                pDisp->Release();
            }
297
            pUnk->Release();
298 299 300 301 302 303 304
        }
        ++iter;
    }
};

void VLCConnectionPoint::firePropChangedEvent(DISPID dispId)
{
305 306
    map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
    map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
307 308 309

    while( iter != end )
    {
310 311 312 313 314 315
        LPUNKNOWN pUnk;
        HRESULT hr;

        hr = m_pGIT->GetInterfaceFromGlobal( (DWORD)iter->second, IID_IUnknown,
                                              reinterpret_cast<void **>(&pUnk) );
        if( SUCCEEDED(hr) )
316 317
        {
            IPropertyNotifySink *pPropSink;
318 319
            hr = pUnk->QueryInterface( IID_IPropertyNotifySink, (LPVOID *)&pPropSink );
            if( SUCCEEDED(hr) )
320 321 322 323
            {
                pPropSink->OnChanged(dispId);
                pPropSink->Release();
            }
324
            pUnk->Release();
325 326 327 328 329 330 331
        }
        ++iter;
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////

332 333 334 335 336
VLCDispatchEvent::~VLCDispatchEvent()
{
    //clear event arguments
    if( NULL != _dispParams.rgvarg )
    {
337 338
        for(unsigned int c = 0; c < _dispParams.cArgs; ++c)
            VariantClear(_dispParams.rgvarg + c);
339 340 341 342 343 344
        CoTaskMemFree(_dispParams.rgvarg);
    }
    if( NULL != _dispParams.rgdispidNamedArgs )
        CoTaskMemFree(_dispParams.rgdispidNamedArgs);
};

345 346
////////////////////////////////////////////////////////////////////////////////////////////////
// VLCConnectionPointContainer
347 348
////////////////////////////////////////////////////////////////////////////////////////////////

349
VLCConnectionPointContainer::VLCConnectionPointContainer(VLCPlugin *p_instance) :
350
    _p_instance(p_instance), freeze(FALSE), isRunning(TRUE)
351 352 353 354 355 356 357 358 359 360
{
    _p_events = new VLCConnectionPoint(dynamic_cast<LPCONNECTIONPOINTCONTAINER>(this),
            _p_instance->getDispEventID());

    _v_cps.push_back(dynamic_cast<LPCONNECTIONPOINT>(_p_events));

    _p_props = new VLCConnectionPoint(dynamic_cast<LPCONNECTIONPOINTCONTAINER>(this),
            IID_IPropertyNotifySink);

    _v_cps.push_back(dynamic_cast<LPCONNECTIONPOINT>(_p_props));
361 362 363 364 365 366 367 368

    // init protection
    InitializeCriticalSection(&csEvents);
    ConditionInit(&sEvents);

    // create thread
    hThread = CreateThread(NULL, NULL, ThreadProcEventHandler,
                           dynamic_cast<LPVOID>(this), NULL, NULL);
369 370 371 372
};

VLCConnectionPointContainer::~VLCConnectionPointContainer()
{
373 374
    EnterCriticalSection(&csEvents);
    isRunning = FALSE;
375
    freeze = TRUE;
376 377 378 379 380 381 382 383 384 385 386
    ConditionSignal(&sEvents);
    LeaveCriticalSection(&csEvents);

    do {
        /* nothing wait for thread to finish */;
    } while(WaitForSingleObjectEx (hThread, INFINITE, TRUE) == WAIT_IO_COMPLETION);
    CloseHandle(hThread);

    ConditionDestroy(&sEvents);
    DeleteCriticalSection(&csEvents);

387 388 389 390 391
    _v_cps.clear();
    while(!_q_events.empty()) {
        _q_events.pop();
    };

392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
    delete _p_props;
    delete _p_events;
};

STDMETHODIMP VLCConnectionPointContainer::EnumConnectionPoints(LPENUMCONNECTIONPOINTS *ppEnum)
{
    if( NULL == ppEnum )
        return E_POINTER;

    *ppEnum = dynamic_cast<LPENUMCONNECTIONPOINTS>(new VLCEnumConnectionPoints(_v_cps));

    return (NULL != *ppEnum ) ? S_OK : E_OUTOFMEMORY;
};

STDMETHODIMP VLCConnectionPointContainer::FindConnectionPoint(REFIID riid, IConnectionPoint **ppCP)
{
    if( NULL == ppCP )
        return E_POINTER;

    *ppCP = NULL;

    if( IID_IPropertyNotifySink == riid )
    {
        _p_props->AddRef();
        *ppCP = dynamic_cast<LPCONNECTIONPOINT>(_p_props);
    }
    else if( _p_instance->getDispEventID() == riid )
    {
        _p_events->AddRef();
        *ppCP = dynamic_cast<LPCONNECTIONPOINT>(_p_events);
    }
    else
        return CONNECT_E_NOCONNECTION;

    return NOERROR;
};

429
void VLCConnectionPointContainer::freezeEvents(BOOL bFreeze)
430
{
431 432 433
    EnterCriticalSection(&csEvents);
    freeze = bFreeze;
    LeaveCriticalSection(&csEvents);
434 435
};

436 437
void VLCConnectionPointContainer::fireEvent(DISPID dispId, DISPPARAMS* pDispParams)
{
438 439 440 441 442
    EnterCriticalSection(&csEvents);

    // queue event for later use when container is ready
    _q_events.push(new VLCDispatchEvent(dispId, *pDispParams));
    if( _q_events.size() > 1024 )
443
    {
444 445 446
        // too many events in queue, get rid of older one
        delete _q_events.front();
        _q_events.pop();
447
    }
448 449
    ConditionSignal(&sEvents);
    LeaveCriticalSection(&csEvents);
450 451 452 453
};

void VLCConnectionPointContainer::firePropChangedEvent(DISPID dispId)
{
454
    if( ! freeze )
455
        _p_props->firePropChangedEvent(dispId);
456 457
};