summaryrefslogtreecommitdiffstats
path: root/third_party/rust/comedy/src/handle.rs
blob: e31d53773ecc8151ab1686957f2f8a1598418127 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.

//! Wrapping and automatically closing handles.

use winapi::shared::minwindef::DWORD;
use winapi::shared::ntdef::NULL;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::winnt::HANDLE;

/// Check and automatically close a Windows `HANDLE`.
#[repr(transparent)]
#[derive(Debug)]
pub struct Handle(HANDLE);

impl Handle {
    /// Take ownership of a `HANDLE`, which will be closed with `CloseHandle` upon drop.
    /// Returns an error in case of `INVALID_HANDLE_VALUE` or `NULL`.
    ///
    /// # Safety
    ///
    /// `h` should be the only copy of the handle. `GetLastError()` is called to
    /// return an error, so the last Windows API called on this thread should have been
    /// what produced the invalid handle.
    pub unsafe fn new(h: HANDLE) -> Result<Handle, DWORD> {
        if h == NULL || h == INVALID_HANDLE_VALUE {
            Err(GetLastError())
        } else {
            Ok(Handle(h))
        }
    }

    /// Obtains the raw `HANDLE` without transferring ownership.
    ///
    /// Do __not__ close this handle because it is still owned by the `Handle`.
    ///
    /// Do __not__ use this handle beyond the lifetime of the `Handle`.
    pub fn as_raw(&self) -> HANDLE {
        self.0
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe {
            CloseHandle(self.0);
        }
    }
}

/// Call a function that returns a `HANDLE` (`NULL` or `INVALID_HANDLE_VALUE` on failure), wrap result.
///
/// The handle is wrapped in a [`Handle`](handle/struct.Handle.html) which will automatically call
/// `CloseHandle()` on it. If the function fails, the error is retrieved via `GetLastError()` and
/// augmented with the name of the function and the file and line number of the macro usage.
///
/// # Example
///
/// ```no_run
/// # extern crate winapi;
/// # use std::ptr;
/// # use winapi::um::fileapi::FindFirstFileW;
/// # use winapi::um::minwinbase::WIN32_FIND_DATAW;
/// # use comedy::handle::Handle;
/// # use comedy::{call_handle_getter, Win32Error};
/// #
/// unsafe fn find_first(name: &[u16], data: &mut WIN32_FIND_DATAW) -> Result<Handle, Win32Error> {
///     call_handle_getter!(FindFirstFileW(name.as_ptr(), data))
/// }
/// ```
#[macro_export]
macro_rules! call_handle_getter {
    ($f:ident ( $($arg:expr),* )) => {
        {
            use $crate::error::{ErrorCode, FileLine, ResultExt, Win32Error};
            $crate::handle::Handle::new($f($($arg),*))
                .map_err(Win32Error::new)
                    .function(stringify!($f))
                    .file_line(file!(), line!())
        }
    };

    // support for trailing comma in argument list
    ($f:ident ( $($arg:expr),+ , )) => {
        $crate::call_valid_handle_getter!($f($($arg),*))
    };
}