com_rs/comptr.rs
1// Copyright (c) 2016 com-rs developers
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9use std::mem;
10use std::ops::Deref;
11use std::os::raw::c_void;
12use std::ptr;
13
14use {IID, IUnknown};
15
16/**
17Wrapper type for COM interface pointers.
18
19# Usage
20## Passing COM pointers to/from FFI methods
21`ComPtr<T>` has the following methods for accessing the underlying pointer:
22
23* `as_ptr` returns the raw pointer `*const T`
24* `as_mut_ptr` returns a mutable reference to the raw pointer `&mut *mut T`
25
26The `AsComPtr` trait defines which pointer types can be returned by these
27methods. These methods should be used carefully to ensure the returned pointers
28do not outlive the `ComPtr` object.
29
30```
31extern crate com_rs;
32use com_rs::*;
33
34fn create_iunknown_object(p: *mut *mut IUnknown) { }
35fn use_iunknown_object(p: *const IUnknown) { }
36
37fn main() {
38 let mut unknown: ComPtr<IUnknown> = ComPtr::new();
39 create_iunknown_object(unknown.as_mut_ptr());
40 use_iunknown_object(unknown.as_ptr());
41}
42```
43
44## Reference Counting
45`ComPtr` implements the `Clone` and `Drop` traits, which call the
46`IUnknown::add_ref` and `IUnknown::release` methods respectively to handle the
47internal reference counting.
48
49## Accessing COM interface methods
50`ComPtr<T>` coerces into `T` using the `Deref` trait, allowing interface methods
51to be called directly. However, dereferencing a `ComPtr` containing a null
52pointer in this way results in a panic. All method calls should be guarded with
53`is_null` checks to prevent this.
54
55```
56# use com_rs::*;
57# fn create_iunknown_object(p: *mut *mut IUnknown) { }
58let mut ptr: ComPtr<IUnknown> = ComPtr::new();
59create_iunknown_object(ptr.as_mut_ptr());
60if !ptr.is_null() {
61 // This is just for demonstration, don't call these directly
62 unsafe { ptr.add_ref() };
63 unsafe { ptr.release() };
64}
65```
66
67## Conversion using `From`
68`ComPtr<T>` also implements the `From` trait for conversion between different
69COM interfaces. This is a wrapper around the `IUnknown::query_interface` method
70which automatically uses the IID of the target type.
71
72```
73# use com_rs::*;
74# fn create_iunknown_object(p: *mut *mut IUnknown) { }
75# type IFoobar = IUnknown;
76let mut unknown: ComPtr<IUnknown> = ComPtr::new();
77create_iunknown_object(unknown.as_mut_ptr());
78let other: ComPtr<IFoobar> = ComPtr::from(&unknown);
79```
80This will try to query the `IFoobar` interface on the unknown object. If the
81interface is unavailable (or `unknown` is null), the returned object will be
82null.
83*/
84
85#[derive(Debug)]
86pub struct ComPtr<T: ComInterface> {
87 ptr: *mut T
88}
89
90/// Helper trait for `ComPtr`. Implemented automatically by the
91/// `com_interface!` macro.
92pub unsafe trait ComInterface: AsComPtr<IUnknown> {
93 #[doc(hidden)]
94 type Vtable;
95 /// Get the IID associated with a COM interface struct.
96 fn iid() -> IID;
97}
98
99/// Helper trait for `ComPtr`. Defines which types of raw pointer can be
100/// returned by `as_ptr`/`as_mut_ptr`.
101pub unsafe trait AsComPtr<T> { }
102
103impl<T: ComInterface> ComPtr<T> {
104 /// Constructs a new `ComPtr<T>`.
105 pub fn new() -> ComPtr<T> {
106 ComPtr { ptr: ptr::null_mut() }
107 }
108
109 /// Returns the raw pointer as type `U`. Depends on the `AsComPtr` trait.
110 pub fn as_ptr<U>(&self) -> *const U where T: AsComPtr<U> {
111 self.ptr as *const U
112 }
113
114 /// Returns a mutable reference to the raw pointer.
115 /// Depends on the 'AsComPtr' trait.
116 pub fn as_mut_ptr<U>(&mut self) -> &mut *mut U where T: AsComPtr<U> {
117 unsafe { mem::transmute(&mut self.ptr) }
118 }
119
120 /// Returns true if the contained interface pointer is null. This should
121 /// always be checked before calling any methods on the contained interface.
122 pub fn is_null(&self) -> bool {
123 self.ptr.is_null()
124 }
125
126 /// Return the IID associated with type `T`.
127 pub fn iid(&self) -> IID {
128 T::iid()
129 }
130}
131
132
133/// All types can be cast into `c_void` pointers.
134unsafe impl<T: ComInterface> AsComPtr<c_void> for T { }
135
136impl<'a, T, U> From<&'a ComPtr<T>> for ComPtr<U>
137 where T: ComInterface, U: ComInterface + AsComPtr<c_void> {
138 /// Create a `ComPtr` of a different interface type `U`. Calls
139 /// `IUnknown::query_interface` and returns a new `ComPtr<U>` object.
140 /// If the requested interface is unavailable, the returned `ComPtr<U>`
141 /// will contain a null pointer.
142 fn from(other: &'a ComPtr<T>) -> ComPtr<U> {
143 let mut new: ComPtr<U> = ComPtr::new();
144 if !other.is_null() {
145 unsafe {
146 (*other.as_ptr()).query_interface(&U::iid(), new.as_mut_ptr());
147 }
148 }
149 new
150 }
151}
152
153impl<T: ComInterface> Deref for ComPtr<T> {
154 type Target = T;
155 /// Dereference into contained interface `T` to call methods directly.
156 ///
157 /// # Panics
158 /// If the contained pointer is null, any dereference will result in a
159 /// panic. Use the [`is_null`](#method.is_null) method before dereferencing.
160 fn deref<'a>(&'a self) -> &'a T {
161 assert!(!self.is_null(), "dereferenced null ComPtr");
162 unsafe { &*self.ptr }
163 }
164}
165
166impl<T: ComInterface> Clone for ComPtr<T> {
167 /// Clones the `ComPtr<T>`. Increments the internal reference counter by
168 /// calling `IUnknown::add_ref` on the contained COM object
169 /// (if the pointer is non-null).
170 fn clone(&self) -> ComPtr<T> {
171 if !self.is_null() {
172 unsafe { (*self.as_ptr()).add_ref() };
173 }
174 ComPtr { ptr: self.ptr }
175 }
176}
177
178impl<T: ComInterface> Drop for ComPtr<T> {
179 /// Drops the `ComPtr<T>`. Decrements the internal reference counter by
180 /// calling `IUnknown::release` on the contained COM object
181 /// (if the pointer is non-null).
182 fn drop(&mut self) {
183 if !self.is_null() {
184 unsafe { (*self.as_ptr()).release() };
185 }
186 }
187}