]> git.sesse.net Git - vlc/blob - projects/activex/connectioncontainer.cpp
fbceb7e450e30891ce2f1a03858cac926cb719fa
[vlc] / projects / activex / connectioncontainer.cpp
1 /*****************************************************************************
2  * connectioncontainer.cpp: ActiveX control for VLC
3  *****************************************************************************
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  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include "plugin.h"
26 #include "connectioncontainer.h"
27
28 #include "utils.h"
29
30 #include <assert.h>
31 #include <rpc.h>
32 #include <rpcndr.h>
33
34 using namespace std;
35
36 ////////////////////////////////////////////////////////////////////////////////////////////////
37 DEFINE_GUID(IID_IGlobalInterfaceTable,     0x00000146, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46);
38 DEFINE_GUID(CLSID_StdGlobalInterfaceTable, 0x00000323, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46);
39
40 const GUID  IID_IGlobalInterfaceTable = { 0x00000146, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
41 const CLSID CLSID_StdGlobalInterfaceTable = { 0x00000323, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
42 ////////////////////////////////////////////////////////////////////////////////////////////////
43
44 /* this function object is used to return the value from a map pair */
45 struct VLCEnumConnectionsDereference
46 {
47     CONNECTDATA operator()(const map<DWORD,LPUNKNOWN>::iterator& i)
48     {
49         CONNECTDATA cd;
50
51         i->second->AddRef();
52
53         cd.dwCookie = i->first;
54         cd.pUnk     = i->second;
55         return cd;
56     };
57 };
58
59 class VLCEnumConnections : public VLCEnumIterator<IID_IEnumConnections,
60     IEnumConnections,
61     CONNECTDATA,
62     map<DWORD,LPUNKNOWN>::iterator,
63     VLCEnumConnectionsDereference>
64 {
65 public:
66     VLCEnumConnections(map<DWORD,LPUNKNOWN> &m) :
67         VLCEnumIterator<IID_IEnumConnections,
68             IEnumConnections,
69             CONNECTDATA,
70             map<DWORD,LPUNKNOWN>::iterator,
71             VLCEnumConnectionsDereference> (m.begin(), m.end())
72     {};
73 };
74
75 ////////////////////////////////////////////////////////////////////////////////////////////////
76
77 /* this function object is used to retain the dereferenced iterator value */
78 struct VLCEnumConnectionPointsDereference
79 {
80     LPCONNECTIONPOINT operator()(const vector<LPCONNECTIONPOINT>::iterator& i)
81     {
82         LPCONNECTIONPOINT cp = *i;
83         cp->AddRef();
84         return cp;
85     }
86 };
87
88 class VLCEnumConnectionPoints: public VLCEnumIterator<IID_IEnumConnectionPoints,
89     IEnumConnectionPoints,
90     LPCONNECTIONPOINT,
91     vector<LPCONNECTIONPOINT>::iterator,
92     VLCEnumConnectionPointsDereference>
93 {
94 public:
95     VLCEnumConnectionPoints(vector<LPCONNECTIONPOINT>& v) :
96         VLCEnumIterator<IID_IEnumConnectionPoints,
97             IEnumConnectionPoints,
98             LPCONNECTIONPOINT,
99             vector<LPCONNECTIONPOINT>::iterator,
100             VLCEnumConnectionPointsDereference> (v.begin(), v.end())
101     {};
102 };
103
104 ////////////////////////////////////////////////////////////////////////////////////////////////
105 // Condition variable emulation
106 ////////////////////////////////////////////////////////////////////////////////////////////////
107
108 static void ConditionInit(HANDLE *handle)
109 {
110     *handle = CreateEvent(NULL, TRUE, FALSE, NULL);
111     assert(*handle != NULL);
112 }
113
114 static void ConditionDestroy(HANDLE *handle)
115 {
116     CloseHandle(*handle);
117 }
118
119 static void ConditionWait(HANDLE *handle, CRITICAL_SECTION *lock)
120 {
121     DWORD dwWaitResult;
122
123     do {
124         LeaveCriticalSection(lock);
125         dwWaitResult = WaitForSingleObjectEx(*handle, INFINITE, TRUE);
126         EnterCriticalSection(lock);
127     } while(dwWaitResult == WAIT_IO_COMPLETION);
128
129     assert(dwWaitResult != WAIT_ABANDONED); /* another thread failed to cleanup! */
130     assert(dwWaitResult != WAIT_FAILED);
131     ResetEvent(*handle);
132 }
133
134 static void ConditionSignal(HANDLE *handle)
135 {
136     SetEvent(*handle);
137 }
138
139 ////////////////////////////////////////////////////////////////////////////////////////////////
140 // Event handling thread
141 //
142 // It bridges the gap between libvlc thread context and ActiveX/COM thread context.
143 ////////////////////////////////////////////////////////////////////////////////////////////////
144 DWORD WINAPI ThreadProcEventHandler(LPVOID lpParam)
145 {
146     CoInitialize(NULL);
147     VLCConnectionPointContainer *pCPC = (VLCConnectionPointContainer *)lpParam;
148
149     while(pCPC->isRunning)
150     {
151         EnterCriticalSection(&(pCPC->csEvents));
152         ConditionWait(&(pCPC->sEvents), &(pCPC->csEvents));
153
154         if (!pCPC->isRunning)
155         {
156             LeaveCriticalSection(&(pCPC->csEvents));
157             break;
158         }
159
160         while(!pCPC->_q_events.empty())
161         {
162             VLCDispatchEvent *ev = pCPC->_q_events.front();
163             pCPC->_q_events.pop();
164             pCPC->_p_events->fireEvent(ev->_dispId, &ev->_dispParams);
165             delete ev;
166         }
167         LeaveCriticalSection(&(pCPC->csEvents));
168     }
169
170     CoUninitialize();
171     return 0;
172 }
173
174 ////////////////////////////////////////////////////////////////////////////////////////////////
175 // VLCConnectionPoint
176 ////////////////////////////////////////////////////////////////////////////////////////////////
177
178 VLCConnectionPoint::VLCConnectionPoint(IConnectionPointContainer *p_cpc, REFIID iid) :
179         _iid(iid), _p_cpc(p_cpc)
180 {
181     // Get the Global Interface Table per-process singleton:
182     CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0,
183                           CLSCTX_INPROC_SERVER,
184                           IID_IGlobalInterfaceTable,
185                           reinterpret_cast<void**>(&m_pGIT));
186 };
187
188 VLCConnectionPoint::~VLCConnectionPoint()
189 {
190     // Revoke interfaces from the GIT:
191     map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
192     map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
193
194     while( iter != end )
195     {
196         m_pGIT->RevokeInterfaceFromGlobal((DWORD)iter->second);
197         ++iter;
198     }
199     m_pGIT->Release();
200 };
201
202 STDMETHODIMP VLCConnectionPoint::GetConnectionInterface(IID *iid)
203 {
204     if( NULL == iid )
205         return E_POINTER;
206
207     *iid = _iid;
208     return S_OK;
209 };
210
211 STDMETHODIMP VLCConnectionPoint::GetConnectionPointContainer(LPCONNECTIONPOINTCONTAINER *ppCPC)
212 {
213     if( NULL == ppCPC )
214         return E_POINTER;
215
216     _p_cpc->AddRef();
217     *ppCPC = _p_cpc;
218     return S_OK;
219 };
220
221 STDMETHODIMP VLCConnectionPoint::Advise(IUnknown *pUnk, DWORD *pdwCookie)
222 {
223     static DWORD dwCookieCounter = 0;
224     HRESULT hr;
225
226     if( (NULL == pUnk) || (NULL == pdwCookie) )
227         return E_POINTER;
228
229     hr = pUnk->QueryInterface(_iid, (LPVOID *)&pUnk);
230     if( SUCCEEDED(hr) )
231     {
232         DWORD dwGITCookie;
233         hr = m_pGIT->RegisterInterfaceInGlobal( pUnk, _iid, &dwGITCookie );
234         if( SUCCEEDED(hr) )
235         {
236             *pdwCookie = ++dwCookieCounter;
237             _connections[*pdwCookie] = (LPUNKNOWN) dwGITCookie;
238         }
239         pUnk->Release();
240     }
241     return hr;
242 };
243
244 STDMETHODIMP VLCConnectionPoint::Unadvise(DWORD pdwCookie)
245 {
246     map<DWORD,LPUNKNOWN>::iterator pcd = _connections.find((DWORD)pdwCookie);
247     if( pcd != _connections.end() )
248     {
249         m_pGIT->RevokeInterfaceFromGlobal((DWORD)pcd->second);
250         _connections.erase(pdwCookie);
251         return S_OK;
252     }
253     return CONNECT_E_NOCONNECTION;
254 };
255
256 STDMETHODIMP VLCConnectionPoint::EnumConnections(IEnumConnections **ppEnum)
257 {
258     if( NULL == ppEnum )
259         return E_POINTER;
260
261     *ppEnum = dynamic_cast<LPENUMCONNECTIONS>(new VLCEnumConnections(_connections));
262
263     return (NULL != *ppEnum ) ? S_OK : E_OUTOFMEMORY;
264 };
265
266 void VLCConnectionPoint::fireEvent(DISPID dispId, DISPPARAMS *pDispParams)
267 {
268     map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
269     map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
270
271     HRESULT hr = S_OK;
272
273     while( iter != end )
274     {
275         DWORD dwCookie = (DWORD)iter->second;
276         LPUNKNOWN pUnk;
277
278         hr = m_pGIT->GetInterfaceFromGlobal( dwCookie, _iid,
279                                              reinterpret_cast<void **>(&pUnk) );
280         if( SUCCEEDED(hr) )
281         {
282             IDispatch *pDisp;
283             hr = pUnk->QueryInterface(_iid, (LPVOID *)&pDisp);
284             if( SUCCEEDED(hr) )
285             {
286                 pDisp->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT,
287                               DISPATCH_METHOD, pDispParams, NULL, NULL, NULL);
288                 pDisp->Release();
289             }
290             pUnk->Release();
291         }
292         ++iter;
293     }
294 };
295
296 void VLCConnectionPoint::firePropChangedEvent(DISPID dispId)
297 {
298     map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
299     map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
300
301     while( iter != end )
302     {
303         LPUNKNOWN pUnk;
304         HRESULT hr;
305
306         hr = m_pGIT->GetInterfaceFromGlobal( (DWORD)iter->second, IID_IUnknown,
307                                               reinterpret_cast<void **>(&pUnk) );
308         if( SUCCEEDED(hr) )
309         {
310             IPropertyNotifySink *pPropSink;
311             hr = pUnk->QueryInterface( IID_IPropertyNotifySink, (LPVOID *)&pPropSink );
312             if( SUCCEEDED(hr) )
313             {
314                 pPropSink->OnChanged(dispId);
315                 pPropSink->Release();
316             }
317             pUnk->Release();
318         }
319         ++iter;
320     }
321 };
322
323 ////////////////////////////////////////////////////////////////////////////////////////////////
324
325 VLCDispatchEvent::~VLCDispatchEvent()
326 {
327     //clear event arguments
328     if( NULL != _dispParams.rgvarg )
329     {
330         for(unsigned int c = 0; c < _dispParams.cArgs; ++c)
331             VariantClear(_dispParams.rgvarg + c);
332         CoTaskMemFree(_dispParams.rgvarg);
333     }
334     if( NULL != _dispParams.rgdispidNamedArgs )
335         CoTaskMemFree(_dispParams.rgdispidNamedArgs);
336 };
337
338 ////////////////////////////////////////////////////////////////////////////////////////////////
339 // VLCConnectionPointContainer
340 ////////////////////////////////////////////////////////////////////////////////////////////////
341
342 VLCConnectionPointContainer::VLCConnectionPointContainer(VLCPlugin *p_instance) :
343     _p_instance(p_instance), freeze(FALSE), isRunning(TRUE)
344 {
345     _p_events = new VLCConnectionPoint(dynamic_cast<LPCONNECTIONPOINTCONTAINER>(this),
346             _p_instance->getDispEventID());
347
348     _v_cps.push_back(dynamic_cast<LPCONNECTIONPOINT>(_p_events));
349
350     _p_props = new VLCConnectionPoint(dynamic_cast<LPCONNECTIONPOINTCONTAINER>(this),
351             IID_IPropertyNotifySink);
352
353     _v_cps.push_back(dynamic_cast<LPCONNECTIONPOINT>(_p_props));
354
355     // init protection
356     InitializeCriticalSection(&csEvents);
357     ConditionInit(&sEvents);
358
359     // create thread
360     hThread = CreateThread(NULL, NULL, ThreadProcEventHandler,
361                            dynamic_cast<LPVOID>(this), NULL, NULL);
362 };
363
364 VLCConnectionPointContainer::~VLCConnectionPointContainer()
365 {
366     EnterCriticalSection(&csEvents);
367     isRunning = FALSE;
368     freeze = TRUE;
369     ConditionSignal(&sEvents);
370     LeaveCriticalSection(&csEvents);
371
372     do {
373         /* nothing wait for thread to finish */;
374     } while(WaitForSingleObjectEx (hThread, INFINITE, TRUE) == WAIT_IO_COMPLETION);
375     CloseHandle(hThread);
376
377     ConditionDestroy(&sEvents);
378     DeleteCriticalSection(&csEvents);
379
380     _v_cps.clear();
381     while(!_q_events.empty()) {
382         _q_events.pop();
383     };
384
385     delete _p_props;
386     delete _p_events;
387 };
388
389 STDMETHODIMP VLCConnectionPointContainer::EnumConnectionPoints(LPENUMCONNECTIONPOINTS *ppEnum)
390 {
391     if( NULL == ppEnum )
392         return E_POINTER;
393
394     *ppEnum = dynamic_cast<LPENUMCONNECTIONPOINTS>(new VLCEnumConnectionPoints(_v_cps));
395
396     return (NULL != *ppEnum ) ? S_OK : E_OUTOFMEMORY;
397 };
398
399 STDMETHODIMP VLCConnectionPointContainer::FindConnectionPoint(REFIID riid, IConnectionPoint **ppCP)
400 {
401     if( NULL == ppCP )
402         return E_POINTER;
403
404     *ppCP = NULL;
405
406     if( IID_IPropertyNotifySink == riid )
407     {
408         _p_props->AddRef();
409         *ppCP = dynamic_cast<LPCONNECTIONPOINT>(_p_props);
410     }
411     else if( _p_instance->getDispEventID() == riid )
412     {
413         _p_events->AddRef();
414         *ppCP = dynamic_cast<LPCONNECTIONPOINT>(_p_events);
415     }
416     else
417         return CONNECT_E_NOCONNECTION;
418
419     return NOERROR;
420 };
421
422 void VLCConnectionPointContainer::freezeEvents(BOOL bFreeze)
423 {
424     EnterCriticalSection(&csEvents);
425     freeze = bFreeze;
426     LeaveCriticalSection(&csEvents);
427 };
428
429 void VLCConnectionPointContainer::fireEvent(DISPID dispId, DISPPARAMS* pDispParams)
430 {
431     EnterCriticalSection(&csEvents);
432
433     // queue event for later use when container is ready
434     _q_events.push(new VLCDispatchEvent(dispId, *pDispParams));
435     if( _q_events.size() > 1024 )
436     {
437         // too many events in queue, get rid of older one
438         delete _q_events.front();
439         _q_events.pop();
440     }
441     ConditionSignal(&sEvents);
442     LeaveCriticalSection(&csEvents);
443 };
444
445 void VLCConnectionPointContainer::firePropChangedEvent(DISPID dispId)
446 {
447     if( ! freeze )
448         _p_props->firePropChangedEvent(dispId);
449 };
450