connectioncontainer.cpp 14 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 30
 *****************************************************************************/

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

using namespace std;

31
#ifdef __MINGW32__
32 33
# include <_mingw.h>
# if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64_VERSION_MAJOR)
34
const GUID  IID_IGlobalInterfaceTable = { 0x00000146, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
35
# endif /* __MINGW32_MAJOR_VERSION && !__MINGW64_VERSION_MAJOR */
36
const CLSID CLSID_StdGlobalInterfaceTable = { 0x00000323, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
37
#endif /* __MINGW32__ */
38

39 40
/* this function object is used to return the value from a map pair */
struct VLCEnumConnectionsDereference
41
{
42
    CONNECTDATA operator()(const map<DWORD,LPUNKNOWN>::iterator& i)
43
    {
44
        CONNECTDATA cd;
45

46 47
        i->second->AddRef();

48 49 50
        cd.dwCookie = i->first;
        cd.pUnk     = i->second;
        return cd;
51
    };
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
};

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())
    {};
};

////////////////////////////////////////////////////////////////////////////////////////////////
71

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
/* 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())
    {};
97 98
};

99
////////////////////////////////////////////////////////////////////////////////////////////////
100
// EventSystemProxyWnd
101
////////////////////////////////////////////////////////////////////////////////////////////////
102
class EventSystemProxyWnd
103
{
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
private:
    enum{
        WM_NEW_EVENT_NOTIFY = WM_USER+1
    };
    static LPCTSTR getClassName(void) { return TEXT("VLC ActiveX ESP Class"); };
    static void RegisterWndClassName();
    static void UnRegisterWndClassName();
    static LRESULT CALLBACK ESPWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    static HINSTANCE _hInstance;
    static ATOM _ESP_wndclass_atom;
    HWND _hWnd;

public:
    static EventSystemProxyWnd* CreateESPWindow(HINSTANCE hInstance, VLCConnectionPointContainer *pCPC);

private:
    EventSystemProxyWnd(HWND hWnd, VLCConnectionPointContainer *pCPC)
122
        : _hWnd(hWnd), _pCPC(pCPC), _HasUnprocessedNotify(false){};
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    void ProcessNotify();

public:
    void DestroyWindow()
        {::DestroyWindow(_hWnd);};

    //allowed to invoke from any thread
    void NewEventNotify();

private:
    VLCConnectionPointContainer *_pCPC;
    volatile bool _HasUnprocessedNotify;
};

HINSTANCE EventSystemProxyWnd::_hInstance=0;
ATOM EventSystemProxyWnd::_ESP_wndclass_atom=0;
139 140


141
void EventSystemProxyWnd::RegisterWndClassName()
142
{
143 144
    WNDCLASS wClass;
    memset(&wClass, 0, sizeof(WNDCLASS));
145

146 147 148 149 150
    if( ! GetClassInfo(_hInstance, getClassName(), &wClass) )
    {
        wClass.lpfnWndProc    = ESPWindowProc;
        wClass.hInstance      = _hInstance;
        wClass.lpszClassName  = getClassName();
151

152 153 154 155 156 157
        _ESP_wndclass_atom = RegisterClass(&wClass);
    }
    else
    {
        _ESP_wndclass_atom = 0;
    }
158 159
}

160
void EventSystemProxyWnd::UnRegisterWndClassName()
161
{
162 163 164 165
    if(0 != _ESP_wndclass_atom){
        UnregisterClass(MAKEINTATOM(_ESP_wndclass_atom), _hInstance);
        _ESP_wndclass_atom = 0;
    }
166 167
}

168
LRESULT CALLBACK EventSystemProxyWnd::ESPWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
169
{
170
    EventSystemProxyWnd* ESP = reinterpret_cast<EventSystemProxyWnd*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
171

172
    switch( uMsg )
173
    {
174 175 176
        case WM_CREATE:{
            CREATESTRUCT* CreateStruct = (CREATESTRUCT*)(lParam);
            VLCConnectionPointContainer * pCPC = reinterpret_cast<VLCConnectionPointContainer *>(CreateStruct->lpCreateParams);
177

178 179
            ESP = new EventSystemProxyWnd(hWnd, pCPC);
            SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(ESP));
180 181
            break;
        }
182 183 184 185 186 187 188 189 190 191 192 193
        case WM_NCDESTROY:
            delete ESP;
            SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
            break;
        case WM_NEW_EVENT_NOTIFY:
            ESP->ProcessNotify();
            break;
        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}
194

195 196 197 198
EventSystemProxyWnd* EventSystemProxyWnd::CreateESPWindow(HINSTANCE hInstance, VLCConnectionPointContainer *pCPC)
{
    //save hInstance for future use
    _hInstance = hInstance;
199

200
    RegisterWndClassName();
201

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    HWND hWnd = CreateWindow(getClassName(),
                             0,
                             0,
                             0, 0, 0, 0,
                             0,
                             0,
                             _hInstance,
                             pCPC
                             );

    if(hWnd)
        return reinterpret_cast<EventSystemProxyWnd*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));

    return 0;
}

void EventSystemProxyWnd::NewEventNotify()
{
    if(!_HasUnprocessedNotify){
        _HasUnprocessedNotify=true;
        PostMessage(_hWnd, WM_NEW_EVENT_NOTIFY, 0, 0);
    }
}

void EventSystemProxyWnd::ProcessNotify()
{
    while(_pCPC->isRunning){
        VLCDispatchEvent *ev = 0;
        EnterCriticalSection(&(_pCPC->csEvents));
        if(!_pCPC->_q_events.empty()){
            ev = _pCPC->_q_events.front();
            _pCPC->_q_events.pop();
234
        }
235 236 237 238 239 240 241
        LeaveCriticalSection(&(_pCPC->csEvents));

        if(ev){
            _pCPC->_p_events->fireEvent(ev->_dispId, &ev->_dispParams);
            delete ev;
        }
        else break;
242
    }
243
    _HasUnprocessedNotify=false;
244 245 246 247
}

////////////////////////////////////////////////////////////////////////////////////////////////
// VLCConnectionPoint
248 249
////////////////////////////////////////////////////////////////////////////////////////////////

250 251 252 253 254 255 256 257 258 259 260 261 262
VLCConnectionPoint::VLCConnectionPoint(IConnectionPointContainer *p_cpc, REFIID iid) :
        _iid(iid), _p_cpc(p_cpc)
{
};

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

    while( iter != end )
    {
263
        iter->second->Release();
264 265
        ++iter;
    }
266
    _connections.clear();
267 268
};

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
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)
{
290
    static DWORD dwCookieCounter = 0;
291
    HRESULT hr;
292

293 294 295
    if( (NULL == pUnk) || (NULL == pdwCookie) )
        return E_POINTER;

296 297
    hr = pUnk->QueryInterface(_iid, (LPVOID *)&pUnk);
    if( SUCCEEDED(hr) )
298
    {
299 300
        *pdwCookie = ++dwCookieCounter;
        _connections[*pdwCookie] = pUnk;
301
    }
302
    return hr;
303 304 305 306
};

STDMETHODIMP VLCConnectionPoint::Unadvise(DWORD pdwCookie)
{
307 308
    map<DWORD,LPUNKNOWN>::iterator pcd = _connections.find((DWORD)pdwCookie);
    if( pcd != _connections.end() )
309
    {
310 311
        pcd->second->Release();
        _connections.erase(pcd);
312
        return S_OK;
313 314 315 316 317 318 319 320 321 322 323 324 325 326
    }
    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;
};

327
void VLCConnectionPoint::fireEvent(DISPID dispId, DISPPARAMS *pDispParams)
328
{
329 330
    map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
    map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
331

332 333
    HRESULT hr = S_OK;

334 335
    while( iter != end )
    {
336
        LPUNKNOWN pUnk = iter->second;
337

338 339
        IDispatch *pDisp;
        hr = pUnk->QueryInterface(_iid, (LPVOID *)&pDisp);
340
        if( SUCCEEDED(hr) )
341
        {
342 343 344
            pDisp->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT,
                          DISPATCH_METHOD, pDispParams, NULL, NULL, NULL);
            pDisp->Release();
345 346 347 348 349 350 351
        }
        ++iter;
    }
};

void VLCConnectionPoint::firePropChangedEvent(DISPID dispId)
{
352 353
    map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
    map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
354 355 356

    while( iter != end )
    {
357
        LPUNKNOWN pUnk = iter->second;
358 359
        HRESULT hr;

360 361
        IPropertyNotifySink *pPropSink;
        hr = pUnk->QueryInterface( IID_IPropertyNotifySink, (LPVOID *)&pPropSink );
362
        if( SUCCEEDED(hr) )
363
        {
364 365
            pPropSink->OnChanged(dispId);
            pPropSink->Release();
366 367 368 369 370 371 372
        }
        ++iter;
    }
};

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

Damien Fouilleul's avatar
Damien Fouilleul committed
373 374 375 376 377
VLCDispatchEvent::~VLCDispatchEvent()
{
    //clear event arguments
    if( NULL != _dispParams.rgvarg )
    {
378 379
        for(unsigned int c = 0; c < _dispParams.cArgs; ++c)
            VariantClear(_dispParams.rgvarg + c);
Damien Fouilleul's avatar
Damien Fouilleul committed
380 381 382 383 384 385
        CoTaskMemFree(_dispParams.rgvarg);
    }
    if( NULL != _dispParams.rgdispidNamedArgs )
        CoTaskMemFree(_dispParams.rgdispidNamedArgs);
};

386 387
////////////////////////////////////////////////////////////////////////////////////////////////
// VLCConnectionPointContainer
Damien Fouilleul's avatar
Damien Fouilleul committed
388
////////////////////////////////////////////////////////////////////////////////////////////////
389
extern HMODULE DllGetModule();
390
VLCConnectionPointContainer::VLCConnectionPointContainer(VLCPlugin *p_instance) :
391
    _ESProxyWnd(0), _p_instance(p_instance), isRunning(TRUE), freeze(FALSE)
392 393 394 395 396 397 398 399 400 401
{
    _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));
402 403 404 405

    // init protection
    InitializeCriticalSection(&csEvents);

406
    _ESProxyWnd = EventSystemProxyWnd::CreateESPWindow(DllGetModule(), this);
407 408 409 410
};

VLCConnectionPointContainer::~VLCConnectionPointContainer()
{
411
    isRunning = FALSE;
412
    freeze = TRUE;
413

414 415 416
    if(_ESProxyWnd)
        _ESProxyWnd->DestroyWindow();
    _ESProxyWnd=0;
417 418 419

    DeleteCriticalSection(&csEvents);

420 421
    _v_cps.clear();
    while(!_q_events.empty()) {
422
        delete _q_events.front();
423 424 425
        _q_events.pop();
    };

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
    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;
};

463
void VLCConnectionPointContainer::freezeEvents(BOOL bFreeze)
Damien Fouilleul's avatar
Damien Fouilleul committed
464
{
465 466 467
    EnterCriticalSection(&csEvents);
    freeze = bFreeze;
    LeaveCriticalSection(&csEvents);
Damien Fouilleul's avatar
Damien Fouilleul committed
468 469
};

470 471
void VLCConnectionPointContainer::fireEvent(DISPID dispId, DISPPARAMS* pDispParams)
{
472 473
    if(_ESProxyWnd){
        EnterCriticalSection(&csEvents);
474

475 476 477 478 479 480 481 482 483 484
        // queue event for later use when container is ready
        _q_events.push(new VLCDispatchEvent(dispId, *pDispParams));
        if( _q_events.size() > 1024 )
        {
            // too many events in queue, get rid of older one
            delete _q_events.front();
            _q_events.pop();
        }
        LeaveCriticalSection(&csEvents);
        _ESProxyWnd->NewEventNotify();
Damien Fouilleul's avatar
Damien Fouilleul committed
485
    }
486 487 488 489
};

void VLCConnectionPointContainer::firePropChangedEvent(DISPID dispId)
{
490
    if( ! freeze )
Damien Fouilleul's avatar
Damien Fouilleul committed
491
        _p_props->firePropChangedEvent(dispId);
492 493
};