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}