]> git.sesse.net Git - vlc/blob - projects/activex/connectioncontainer.cpp
Remove extra braces.
[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
168             if (!pCPC->isRunning)
169             {
170                 LeaveCriticalSection(&(pCPC->csEvents));
171                 goto out;
172             }
173         }
174         LeaveCriticalSection(&(pCPC->csEvents));
175     }
176 out:
177     CoUninitialize();
178     return 0;
179 }
180
181 ////////////////////////////////////////////////////////////////////////////////////////////////
182 // VLCConnectionPoint
183 ////////////////////////////////////////////////////////////////////////////////////////////////
184
185 VLCConnectionPoint::VLCConnectionPoint(IConnectionPointContainer *p_cpc, REFIID iid) :
186         _iid(iid), _p_cpc(p_cpc)
187 {
188     // Get the Global Interface Table per-process singleton:
189     CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0,
190                           CLSCTX_INPROC_SERVER,
191                           IID_IGlobalInterfaceTable,
192                           reinterpret_cast<void**>(&m_pGIT));
193 };
194
195 VLCConnectionPoint::~VLCConnectionPoint()
196 {
197     // Revoke interfaces from the GIT:
198     map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
199     map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
200
201     while( iter != end )
202     {
203         m_pGIT->RevokeInterfaceFromGlobal((DWORD)iter->second);
204         ++iter;
205     }
206     m_pGIT->Release();
207 };
208
209 STDMETHODIMP VLCConnectionPoint::GetConnectionInterface(IID *iid)
210 {
211     if( NULL == iid )
212         return E_POINTER;
213
214     *iid = _iid;
215     return S_OK;
216 };
217
218 STDMETHODIMP VLCConnectionPoint::GetConnectionPointContainer(LPCONNECTIONPOINTCONTAINER *ppCPC)
219 {
220     if( NULL == ppCPC )
221         return E_POINTER;
222
223     _p_cpc->AddRef();
224     *ppCPC = _p_cpc;
225     return S_OK;
226 };
227
228 STDMETHODIMP VLCConnectionPoint::Advise(IUnknown *pUnk, DWORD *pdwCookie)
229 {
230     static DWORD dwCookieCounter = 0;
231     HRESULT hr;
232
233     if( (NULL == pUnk) || (NULL == pdwCookie) )
234         return E_POINTER;
235
236     hr = pUnk->QueryInterface(_iid, (LPVOID *)&pUnk);
237     if( SUCCEEDED(hr) )
238     {
239         DWORD dwGITCookie;
240         hr = m_pGIT->RegisterInterfaceInGlobal( pUnk, _iid, &dwGITCookie );
241         if( SUCCEEDED(hr) )
242         {
243             *pdwCookie = ++dwCookieCounter;
244             _connections[*pdwCookie] = (LPUNKNOWN) dwGITCookie;
245         }
246         pUnk->Release();
247     }
248     return hr;
249 };
250
251 STDMETHODIMP VLCConnectionPoint::Unadvise(DWORD pdwCookie)
252 {
253     map<DWORD,LPUNKNOWN>::iterator pcd = _connections.find((DWORD)pdwCookie);
254     if( pcd != _connections.end() )
255     {
256         m_pGIT->RevokeInterfaceFromGlobal((DWORD)pcd->second);
257         _connections.erase(pdwCookie);
258         return S_OK;
259     }
260     return CONNECT_E_NOCONNECTION;
261 };
262
263 STDMETHODIMP VLCConnectionPoint::EnumConnections(IEnumConnections **ppEnum)
264 {
265     if( NULL == ppEnum )
266         return E_POINTER;
267
268     *ppEnum = dynamic_cast<LPENUMCONNECTIONS>(new VLCEnumConnections(_connections));
269
270     return (NULL != *ppEnum ) ? S_OK : E_OUTOFMEMORY;
271 };
272
273 void VLCConnectionPoint::fireEvent(DISPID dispId, DISPPARAMS *pDispParams)
274 {
275     map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
276     map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
277
278     HRESULT hr = S_OK;
279
280     while( iter != end )
281     {
282         DWORD dwCookie = (DWORD)iter->second;
283         LPUNKNOWN pUnk;
284
285         hr = m_pGIT->GetInterfaceFromGlobal( dwCookie, _iid,
286                                              reinterpret_cast<void **>(&pUnk) );
287         if( SUCCEEDED(hr) )
288         {
289             IDispatch *pDisp;
290             hr = pUnk->QueryInterface(_iid, (LPVOID *)&pDisp);
291             if( SUCCEEDED(hr) )
292             {
293                 pDisp->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT,
294                               DISPATCH_METHOD, pDispParams, NULL, NULL, NULL);
295                 pDisp->Release();
296             }
297             pUnk->Release();
298         }
299         ++iter;
300     }
301 };
302
303 void VLCConnectionPoint::firePropChangedEvent(DISPID dispId)
304 {
305     map<DWORD,LPUNKNOWN>::iterator end = _connections.end();
306     map<DWORD,LPUNKNOWN>::iterator iter = _connections.begin();
307
308     while( iter != end )
309     {
310         LPUNKNOWN pUnk;
311         HRESULT hr;
312
313         hr = m_pGIT->GetInterfaceFromGlobal( (DWORD)iter->second, IID_IUnknown,
314                                               reinterpret_cast<void **>(&pUnk) );
315         if( SUCCEEDED(hr) )
316         {
317             IPropertyNotifySink *pPropSink;
318             hr = pUnk->QueryInterface( IID_IPropertyNotifySink, (LPVOID *)&pPropSink );
319             if( SUCCEEDED(hr) )
320             {
321                 pPropSink->OnChanged(dispId);
322                 pPropSink->Release();
323             }
324             pUnk->Release();
325         }
326         ++iter;
327     }
328 };
329
330 ////////////////////////////////////////////////////////////////////////////////////////////////
331
332 VLCDispatchEvent::~VLCDispatchEvent()
333 {
334     //clear event arguments
335     if( NULL != _dispParams.rgvarg )
336     {
337         for(unsigned int c = 0; c < _dispParams.cArgs; ++c)
338             VariantClear(_dispParams.rgvarg + c);
339         CoTaskMemFree(_dispParams.rgvarg);
340     }
341     if( NULL != _dispParams.rgdispidNamedArgs )
342         CoTaskMemFree(_dispParams.rgdispidNamedArgs);
343 };
344
345 ////////////////////////////////////////////////////////////////////////////////////////////////
346 // VLCConnectionPointContainer
347 ////////////////////////////////////////////////////////////////////////////////////////////////
348
349 VLCConnectionPointContainer::VLCConnectionPointContainer(VLCPlugin *p_instance) :
350     _p_instance(p_instance), freeze(FALSE), isRunning(TRUE)
351 {
352     _p_events = new VLCConnectionPoint(dynamic_cast<LPCONNECTIONPOINTCONTAINER>(this),
353             _p_instance->getDispEventID());
354
355     _v_cps.push_back(dynamic_cast<LPCONNECTIONPOINT>(_p_events));
356
357     _p_props = new VLCConnectionPoint(dynamic_cast<LPCONNECTIONPOINTCONTAINER>(this),
358             IID_IPropertyNotifySink);
359
360     _v_cps.push_back(dynamic_cast<LPCONNECTIONPOINT>(_p_props));
361
362     // init protection
363     InitializeCriticalSection(&csEvents);
364     ConditionInit(&sEvents);
365
366     // create thread
367     hThread = CreateThread(NULL, NULL, ThreadProcEventHandler,
368                            dynamic_cast<LPVOID>(this), NULL, NULL);
369 };
370
371 VLCConnectionPointContainer::~VLCConnectionPointContainer()
372 {
373     EnterCriticalSection(&csEvents);
374     isRunning = FALSE;
375     freeze = TRUE;
376     ConditionSignal(&sEvents);
377     LeaveCriticalSection(&csEvents);
378
379     do {
380         /* nothing wait for thread to finish */;
381     } while(WaitForSingleObjectEx (hThread, INFINITE, TRUE) == WAIT_IO_COMPLETION);
382     CloseHandle(hThread);
383
384     ConditionDestroy(&sEvents);
385     DeleteCriticalSection(&csEvents);
386
387     _v_cps.clear();
388     while(!_q_events.empty()) {
389         _q_events.pop();
390     };
391
392     delete _p_props;
393     delete _p_events;
394 };
395
396 STDMETHODIMP VLCConnectionPointContainer::EnumConnectionPoints(LPENUMCONNECTIONPOINTS *ppEnum)
397 {
398     if( NULL == ppEnum )
399         return E_POINTER;
400
401     *ppEnum = dynamic_cast<LPENUMCONNECTIONPOINTS>(new VLCEnumConnectionPoints(_v_cps));
402
403     return (NULL != *ppEnum ) ? S_OK : E_OUTOFMEMORY;
404 };
405
406 STDMETHODIMP VLCConnectionPointContainer::FindConnectionPoint(REFIID riid, IConnectionPoint **ppCP)
407 {
408     if( NULL == ppCP )
409         return E_POINTER;
410
411     *ppCP = NULL;
412
413     if( IID_IPropertyNotifySink == riid )
414     {
415         _p_props->AddRef();
416         *ppCP = dynamic_cast<LPCONNECTIONPOINT>(_p_props);
417     }
418     else if( _p_instance->getDispEventID() == riid )
419     {
420         _p_events->AddRef();
421         *ppCP = dynamic_cast<LPCONNECTIONPOINT>(_p_events);
422     }
423     else
424         return CONNECT_E_NOCONNECTION;
425
426     return NOERROR;
427 };
428
429 void VLCConnectionPointContainer::freezeEvents(BOOL bFreeze)
430 {
431     EnterCriticalSection(&csEvents);
432     freeze = bFreeze;
433     LeaveCriticalSection(&csEvents);
434 };
435
436 void VLCConnectionPointContainer::fireEvent(DISPID dispId, DISPPARAMS* pDispParams)
437 {
438     EnterCriticalSection(&csEvents);
439
440     // queue event for later use when container is ready
441     _q_events.push(new VLCDispatchEvent(dispId, *pDispParams));
442     if( _q_events.size() > 1024 )
443     {
444         // too many events in queue, get rid of older one
445         delete _q_events.front();
446         _q_events.pop();
447     }
448     ConditionSignal(&sEvents);
449     LeaveCriticalSection(&csEvents);
450 };
451
452 void VLCConnectionPointContainer::firePropChangedEvent(DISPID dispId)
453 {
454     if( ! freeze )
455         _p_props->firePropChangedEvent(dispId);
456 };
457