VirtualBox

source: vbox/trunk/include/VBox/com/ptr.h@ 97583

Last change on this file since 97583 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/** @file
2 * MS COM / XPCOM Abstraction Layer - Smart COM pointer classes declaration.
3 */
4
5/*
6 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * The contents of this file may alternatively be used under the terms
25 * of the Common Development and Distribution License Version 1.0
26 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
27 * in the VirtualBox distribution, in which case the provisions of the
28 * CDDL are applicable instead of those of the GPL.
29 *
30 * You may elect to license modified versions of this file under the
31 * terms and conditions of either the GPL or the CDDL or both.
32 *
33 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
34 */
35
36#ifndef VBOX_INCLUDED_com_ptr_h
37#define VBOX_INCLUDED_com_ptr_h
38#ifndef RT_WITHOUT_PRAGMA_ONCE
39# pragma once
40#endif
41
42/* Make sure all the stdint.h macros are included - must come first! */
43#ifndef __STDC_LIMIT_MACROS
44# define __STDC_LIMIT_MACROS
45#endif
46#ifndef __STDC_CONSTANT_MACROS
47# define __STDC_CONSTANT_MACROS
48#endif
49
50#ifdef VBOX_WITH_XPCOM
51# include <nsISupportsUtils.h>
52#endif /* VBOX_WITH_XPCOM */
53
54#include <VBox/com/defs.h>
55#include <new> /* For bad_alloc. */
56
57
58/** @defgroup grp_com_ptr Smart COM Pointer Classes
59 * @ingroup grp_com
60 * @{
61 */
62
63#ifdef VBOX_WITH_XPCOM
64
65namespace com
66{
67// declare a couple of XPCOM helper methods (defined in glue/com.cpp)
68// so we don't have to include a ton of XPCOM implementation headers here
69HRESULT GlueCreateObjectOnServer(const CLSID &clsid,
70 const char *serverName,
71 const nsIID &id,
72 void **ppobj);
73HRESULT GlueCreateInstance(const CLSID &clsid,
74 const nsIID &id,
75 void **ppobj);
76}
77
78#endif // VBOX_WITH_XPCOM
79
80/**
81 * COM autopointer class which takes care of all required reference counting.
82 *
83 * This automatically calls the required basic COM methods on COM pointers
84 * given to it:
85 *
86 * -- AddRef() gets called automatically whenever a new COM pointer is assigned
87 * to the ComPtr instance (either in the copy constructor or by assignment);
88 *
89 * -- Release() gets called automatically by the destructor and when an existing
90 * object gets released in assignment;
91 *
92 * -- QueryInterface() gets called automatically when COM pointers get converted
93 * from one interface to another.
94 *
95 * Example usage:
96 *
97 * @code
98 *
99 * {
100 * ComPtr<IMachine> pMachine = findMachine("blah"); // calls AddRef()
101 * ComPtr<IUnknown> pUnknown = pMachine; // calls QueryInterface()
102 * } # ComPtr destructor of both instances calls Release()
103 *
104 * @endcode
105 */
106template <class T>
107class ComPtr
108{
109public:
110
111 /**
112 * Default constructor, sets up a NULL pointer.
113 */
114 ComPtr()
115 : m_p(NULL)
116 { }
117
118 /**
119 * Destructor. Calls Release() on the contained COM object.
120 */
121 ~ComPtr()
122 {
123 cleanup();
124 }
125
126 /**
127 * Copy constructor from another ComPtr of any interface.
128 *
129 * This calls QueryInterface(T) and can result in a NULL pointer if the input
130 * pointer p does not support the ComPtr interface T.
131 *
132 * Does not call AddRef explicitly because if QueryInterface succeeded, then
133 * the refcount will have been increased by one already.
134 */
135 template <class T2>
136 ComPtr(const ComPtr<T2> &that)
137 {
138 m_p = NULL;
139 if (!that.isNull())
140 that->QueryInterface(COM_IIDOF(T), (void **)&m_p);
141 }
142
143 /**
144 * Specialization: copy constructor from another ComPtr<T>. Calls AddRef().
145 */
146 ComPtr(const ComPtr &that)
147 {
148 copyFrom(that.m_p);
149 }
150
151 /**
152 * Copy constructor from another interface pointer of any interface.
153 *
154 * This calls QueryInterface(T) and can result in a NULL pointer if the input
155 * pointer p does not support the ComPtr interface T.
156 *
157 * Does not call AddRef explicitly because if QueryInterface succeeded, then
158 * the refcount will have been increased by one already.
159 */
160 template <class T2>
161 ComPtr(T2 *p)
162 {
163 m_p = NULL;
164 if (p)
165 p->QueryInterface(COM_IIDOF(T), (void **)&m_p);
166 }
167
168 /**
169 * Specialization: copy constructor from a plain T * pointer. Calls AddRef().
170 */
171 ComPtr(T *that_p)
172 {
173 copyFrom(that_p);
174 }
175
176 /**
177 * Assignment from another ComPtr of any interface.
178 *
179 * This calls QueryInterface(T) and can result in a NULL pointer if the input
180 * pointer p does not support the ComPtr interface T.
181 *
182 * Does not call AddRef explicitly because if QueryInterface succeeded, then
183 * the refcount will have been increased by one already.
184 */
185 template <class T2>
186 ComPtr& operator=(const ComPtr<T2> &that)
187 {
188 return operator=((T2 *)that);
189 }
190
191 /**
192 * Specialization of the previous: assignment from another ComPtr<T>.
193 * Calls Release() on the previous member pointer, if any, and AddRef() on the new one.
194 */
195 ComPtr& operator=(const ComPtr &that)
196 {
197 return operator=((T *)that);
198 }
199
200 /**
201 * Assignment from another interface pointer of any interface.
202 *
203 * This calls QueryInterface(T) and can result in a NULL pointer if the input
204 * pointer p does not support the ComPtr interface T.
205 *
206 * Does not call AddRef explicitly because if QueryInterface succeeded, then
207 * the refcount will have been increased by one already.
208 */
209 template <class T2>
210 ComPtr& operator=(T2 *p)
211 {
212 cleanup();
213 if (p)
214 p->QueryInterface(COM_IIDOF(T), (void **)&m_p);
215 return *this;
216 }
217
218 /**
219 * Specialization of the previous: assignment from a plain T * pointer.
220 * Calls Release() on the previous member pointer, if any, and AddRef() on the new one.
221 */
222 ComPtr& operator=(T *p)
223 {
224 cleanup();
225 copyFrom(p);
226 return *this;
227 }
228
229 /**
230 * Resets the ComPtr to NULL. Works like a NULL assignment except it avoids the templates.
231 */
232 void setNull()
233 {
234 cleanup();
235 }
236
237 /**
238 * Returns true if the pointer is NULL.
239 */
240 bool isNull() const
241 {
242 return (m_p == NULL);
243 }
244
245 /**
246 * Returns true if the pointer is not NULL.
247 */
248 bool isNotNull() const
249 {
250 return (m_p != NULL);
251 }
252
253 bool operator<(T *p) const
254 {
255 return m_p < p;
256 }
257
258 /**
259 * Conversion operator, most often used to pass ComPtr instances as
260 * parameters to COM method calls.
261 */
262 operator T *() const
263 {
264 return m_p;
265 }
266
267 /**
268 * Dereferences the instance (redirects the -> operator to the managed
269 * pointer).
270 */
271 T *operator->() const
272 {
273 return m_p;
274 }
275
276 /**
277 * Special method which allows using a ComPtr as an output argument of a COM method.
278 * The ComPtr will then accept the method's interface pointer without calling AddRef()
279 * itself, since by COM convention this must has been done by the method which created
280 * the object that is being accepted.
281 *
282 * The ComPtr destructor will then still invoke Release() so that the returned object
283 * can get cleaned up properly.
284 */
285 T **asOutParam()
286 {
287 cleanup();
288 return &m_p;
289 }
290
291 /**
292 * Converts the contained pointer to a different interface
293 * by calling QueryInterface() on it.
294 * @param pp
295 * @return
296 */
297 template <class T2>
298 HRESULT queryInterfaceTo(T2 **pp) const
299 {
300 if (pp)
301 {
302 if (m_p)
303 return m_p->QueryInterface(COM_IIDOF(T2), (void **)pp);
304 *pp = NULL;
305 return S_OK;
306 }
307 return E_INVALIDARG;
308 }
309
310 /**
311 * Equality test operator. By COM definition, two COM objects are considered
312 * equal if their IUnknown interface pointers are equal.
313 */
314 template <class T2>
315 bool operator==(T2 *p)
316 {
317 IUnknown *p1 = NULL;
318 bool fNeedsRelease1 = false;
319 if (m_p)
320 fNeedsRelease1 = (SUCCEEDED(m_p->QueryInterface(COM_IIDOF(IUnknown), (void **)&p1)));
321
322 IUnknown *p2 = NULL;
323 bool fNeedsRelease2 = false;
324 if (p)
325 fNeedsRelease2 = (SUCCEEDED(p->QueryInterface(COM_IIDOF(IUnknown), (void **)&p2)));
326
327 bool f = p1 == p2;
328 if (fNeedsRelease1)
329 p1->Release();
330 if (fNeedsRelease2)
331 p2->Release();
332 return f;
333 }
334
335 /**
336 * Creates an in-process object of the given class ID and starts to
337 * manage a reference to the created object in case of success.
338 */
339 HRESULT createInprocObject(const CLSID &clsid)
340 {
341 HRESULT rc;
342 T *obj = NULL;
343#ifndef VBOX_WITH_XPCOM
344 rc = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, COM_IIDOF(T),
345 (void **)&obj);
346#else /* VBOX_WITH_XPCOM */
347 using namespace com;
348 rc = GlueCreateInstance(clsid, NS_GET_IID(T), (void **)&obj);
349#endif /* VBOX_WITH_XPCOM */
350 *this = obj;
351 if (SUCCEEDED(rc))
352 obj->Release();
353 return rc;
354 }
355
356 /**
357 * Creates a local (out-of-process) object of the given class ID and starts
358 * to manage a reference to the created object in case of success.
359 *
360 * Note: In XPCOM, the out-of-process functionality is currently emulated
361 * through in-process wrapper objects (that start a dedicated process and
362 * redirect all object requests to that process). For this reason, this
363 * method is fully equivalent to #createInprocObject() for now.
364 */
365 HRESULT createLocalObject(const CLSID &clsid)
366 {
367#ifndef VBOX_WITH_XPCOM
368 HRESULT rc;
369 T *obj = NULL;
370 rc = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, COM_IIDOF(T),
371 (void **)&obj);
372 *this = obj;
373 if (SUCCEEDED(rc))
374 obj->Release();
375 return rc;
376#else /* VBOX_WITH_XPCOM */
377 return createInprocObject(clsid);
378#endif /* VBOX_WITH_XPCOM */
379 }
380
381#ifdef VBOX_WITH_XPCOM
382 /**
383 * Creates an object of the given class ID on the specified server and
384 * starts to manage a reference to the created object in case of success.
385 *
386 * @param serverName Name of the server to create an object within.
387 */
388 HRESULT createObjectOnServer(const CLSID &clsid, const char *serverName)
389 {
390 T *obj = NULL;
391 HRESULT rc = GlueCreateObjectOnServer(clsid, serverName, NS_GET_IID(T), (void **)&obj);
392 *this = obj;
393 if (SUCCEEDED(rc))
394 obj->Release();
395 return rc;
396 }
397#endif
398
399protected:
400 void copyFrom(T *p)
401 {
402 m_p = p;
403 if (m_p)
404 m_p->AddRef();
405 }
406
407 void cleanup()
408 {
409 if (m_p)
410 {
411 m_p->Release();
412 m_p = NULL;
413 }
414 }
415
416public:
417 // Do NOT access this member unless you really know what you're doing!
418 T *m_p;
419};
420
421/**
422 * ComObjPtr is a more specialized variant of ComPtr designed to be used for implementation
423 * objects. For example, use ComPtr<IMachine> for a client pointer that calls the interface
424 * but ComObjPtr<Machine> for a pointer to an implementation object.
425 *
426 * The methods behave the same except that ComObjPtr has the additional createObject()
427 * method which allows for instantiating a new implementation object.
428 *
429 * Note: To convert a ComObjPtr<InterfaceImpl> to a ComObj<IInterface> you have
430 * to query the interface. See the following example code for the IProgress
431 * interface:
432 *
433 * @code
434 *
435 * {
436 * ComObjPtr<Progress> pProgress; // create the server side object
437 * pProgress.createObject(); // ...
438 * pProgress->init(...); // ...
439 * ComPtr<IProgress> pProgress2; // create an interface pointer
440 * pProgress.queryInterfaceTo(pProgress2.asOutParam()); // transfer the interface
441 * }
442 *
443 * @endcode
444 */
445template <class T>
446class ComObjPtr : public ComPtr<T>
447{
448public:
449
450 ComObjPtr()
451 : ComPtr<T>()
452 {}
453
454 ComObjPtr(const ComObjPtr &that)
455 : ComPtr<T>(that)
456 {}
457
458 ComObjPtr(T *that_p)
459 : ComPtr<T>(that_p)
460 {}
461
462 ComObjPtr& operator=(const ComObjPtr &that)
463 {
464 ComPtr<T>::operator=(that);
465 return *this;
466 }
467
468 ComObjPtr& operator=(T *that_p)
469 {
470 ComPtr<T>::operator=(that_p);
471 return *this;
472 }
473
474 /**
475 * Creates a new server-side object of the given component class and
476 * immediately starts to manage a pointer to the created object (the
477 * previous pointer, if any, is of course released when appropriate).
478 *
479 * @note Win32: when VBOX_COM_OUTOFPROC_MODULE is defined, the created
480 * object doesn't increase the lock count of the server module, as it
481 * does otherwise.
482 *
483 * @note In order to make it easier to use, this method does _not_ throw
484 * bad_alloc, but instead returns E_OUTOFMEMORY.
485 */
486 HRESULT createObject()
487 {
488 HRESULT hrc;
489#ifndef VBOX_WITH_XPCOM
490# ifdef VBOX_COM_OUTOFPROC_MODULE
491 ATL::CComObjectNoLock<T> *obj = NULL;
492 try
493 {
494 obj = new ATL::CComObjectNoLock<T>();
495 }
496 catch (std::bad_alloc &)
497 {
498 obj = NULL;
499 }
500 if (obj)
501 {
502 obj->InternalFinalConstructAddRef();
503 try
504 {
505 hrc = obj->FinalConstruct();
506 }
507 catch (std::bad_alloc &)
508 {
509 hrc = E_OUTOFMEMORY;
510 }
511 obj->InternalFinalConstructRelease();
512 if (FAILED(hrc))
513 {
514 delete obj;
515 obj = NULL;
516 }
517 }
518 else
519 hrc = E_OUTOFMEMORY;
520# else
521 ATL::CComObject<T> *obj = NULL;
522 hrc = ATL::CComObject<T>::CreateInstance(&obj);
523# endif
524#else /* VBOX_WITH_XPCOM */
525 ATL::CComObject<T> *obj;
526# ifndef RT_EXCEPTIONS_ENABLED
527 obj = new ATL::CComObject<T>();
528# else
529 try
530 {
531 obj = new ATL::CComObject<T>();
532 }
533 catch (std::bad_alloc &)
534 {
535 obj = NULL;
536 }
537# endif
538 if (obj)
539 {
540# ifndef RT_EXCEPTIONS_ENABLED
541 hrc = obj->FinalConstruct();
542# else
543 try
544 {
545 hrc = obj->FinalConstruct();
546 }
547 catch (std::bad_alloc &)
548 {
549 hrc = E_OUTOFMEMORY;
550 }
551# endif
552 if (FAILED(hrc))
553 {
554 delete obj;
555 obj = NULL;
556 }
557 }
558 else
559 hrc = E_OUTOFMEMORY;
560#endif /* VBOX_WITH_XPCOM */
561 *this = obj;
562 return hrc;
563 }
564};
565
566/** @} */
567
568#endif /* !VBOX_INCLUDED_com_ptr_h */
569
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette