Skip to content

Commit f6ec06d

Browse files
committed
0.7
0 parents  commit f6ec06d

File tree

5 files changed

+281
-0
lines changed

5 files changed

+281
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target
2+
Cargo.lock

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "rust_win32error"
3+
version = "0.1.0"
4+
authors = ["jojonv <novakj@outlook.com>"]
5+
6+
[dependencies]
7+
winapi = "*"
8+
user32-sys = "*"
9+
kernel32-sys = "*"

License

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright (c) 2015 Jozef Nov�k
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

src/lib.rs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
2+
//! Error-like wrapper around win32 GetLastError and FormatMessage
3+
extern crate winapi;
4+
extern crate kernel32;
5+
extern crate user32;
6+
7+
use std::ptr;
8+
use std::slice;
9+
use std::fmt;
10+
use std::error::Error;
11+
12+
use self::kernel32::{ GetLastError, FormatMessageW};
13+
14+
15+
const FORMAT_MESSAGE_FROM_STRING: u32 = 0x00000400;
16+
const FORMAT_MESSAGE_ALLOCATE_BUFFER: u32 = 0x00000100;
17+
const FORMAT_MESSAGE_IGNORE_INSERTS: u32 = 0x00000200;
18+
const FORMAT_MESSAGE_FROM_SYSTEM: u32 = 0x00001000;
19+
const FORMAT_MESSAGE_ARGUMENT_ARRAY: u32 = 0x00002000;
20+
21+
const UNKNOWN_ERROR_TEXT: &'static str = "Unknown error";
22+
23+
24+
#[derive(Debug, Clone)]
25+
pub struct Win32Error
26+
{
27+
// Error code returned by GetLastError or passed as an arg
28+
//
29+
error_code: u32,
30+
// Message returned by FormatMessage
31+
//
32+
description: Option<String>,
33+
}
34+
35+
fn init_from_error_code(errno: u32) -> Win32Error
36+
{
37+
unsafe
38+
{
39+
let buff_size = 256;
40+
let mut buff: Vec<u16> = Vec::with_capacity(buff_size);
41+
for x in 0 .. buff_size
42+
{
43+
buff.push(0);
44+
}
45+
46+
// Should be zero or num of chars copied
47+
//
48+
let chars_copied = FormatMessageW(
49+
FORMAT_MESSAGE_IGNORE_INSERTS
50+
| FORMAT_MESSAGE_FROM_SYSTEM
51+
| FORMAT_MESSAGE_ARGUMENT_ARRAY
52+
, ptr::null()
53+
, errno
54+
, 0
55+
, buff.as_mut_ptr()
56+
, (buff_size + 1) as u32
57+
, ptr::null_mut());
58+
59+
// Very likely wrong err number was passed, and no message exists
60+
//
61+
if chars_copied == 0
62+
{
63+
return Win32Error { error_code: errno, description: None };
64+
}
65+
66+
67+
// Remove newline - "\r\n" and punctuation or space from the message
68+
//
69+
let mut curr_char: usize = chars_copied as usize;
70+
while curr_char > 0
71+
{
72+
let ch = buff[curr_char];
73+
74+
if ch >= ' ' as u16 { break; }
75+
curr_char -= 1;
76+
}
77+
let sl = slice::from_raw_parts(buff.as_ptr(), curr_char);
78+
let err_msg = String::from_utf16(sl);
79+
80+
let description = match err_msg { Ok(s) => Some(s), _ => None };
81+
Win32Error { error_code: errno, description: description }
82+
}
83+
}
84+
85+
86+
macro_rules! impl_from_trait
87+
{
88+
($($t:ty), *) =>
89+
{
90+
$(
91+
impl From<$t> for Win32Error
92+
{
93+
fn from(errno: $t) -> Self
94+
{
95+
init_from_error_code(errno as u32)
96+
}
97+
}
98+
)*
99+
};
100+
}
101+
102+
macro_rules! impl_into_trait
103+
{
104+
($($t:ty), *) =>
105+
{
106+
$(
107+
impl Into<$t> for Win32Error
108+
{
109+
fn into(self) -> $t
110+
{
111+
self.error_code as $t
112+
}
113+
}
114+
)*
115+
};
116+
}
117+
118+
impl_from_trait!(i32, i16, i8, u32, u16, u8);
119+
impl_into_trait!(i32, i16, i8, u32, u16, u8);
120+
121+
impl fmt::Display for Win32Error
122+
{
123+
/// Prints an error description in the following format:
124+
/// **Error code**: **Error message**, eg. 5: Access denied
125+
///
126+
/// # Examples
127+
///
128+
/// ```
129+
/// use rust_win32error::*;
130+
/// let err = Win32Error::new();
131+
/// println!("{}", err);
132+
/// ```
133+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
134+
{
135+
match self.description.as_ref()
136+
{
137+
Some(s) => format!("{}: {}", self.error_code, s),
138+
None => format!("{}: {}", self.error_code, UNKNOWN_ERROR_TEXT)
139+
}.fmt(f)
140+
}
141+
}
142+
143+
impl Win32Error
144+
{
145+
/// Initializes new Win32Error instance.
146+
/// Function behind the scenes calls native GetLastError, and uses error code it returned.
147+
///
148+
/// # Examples
149+
///
150+
/// ```
151+
/// use rust_win32error::*;
152+
/// let err = Win32Error::new();
153+
/// ```
154+
pub fn new() -> Self { Self::from(unsafe { GetLastError() }) }
155+
156+
/// Retrieves error code returned by GetLastError, or one that was passed through `std::convert::From::from` call
157+
/// # Examples
158+
///
159+
/// ```
160+
/// use rust_win32error::*;
161+
/// let err = Win32Error::new();
162+
/// assert_eq!(err.get_error_code(), 0);
163+
/// ```
164+
pub fn get_error_code(&self) -> u32 { self.error_code }
165+
}
166+
167+
168+
/// Retrieves localized description of the error, with one exception that's description
169+
/// of the error couldn't be retrieved, in which case *Unknown error* (in english) is returned.
170+
impl Error for Win32Error
171+
{
172+
fn description(&self) -> &str
173+
{
174+
match self.description.as_ref()
175+
{
176+
Some(s) => s,
177+
None => UNKNOWN_ERROR_TEXT
178+
}
179+
}
180+
fn cause(&self) -> Option<&Error> { None }
181+
}
182+
183+
184+
#[cfg(test)]
185+
mod test
186+
{
187+
use std::error::Error;
188+
use super::*;
189+
190+
// ugly duplication
191+
//
192+
const UNKNOWN_ERROR_TEXT: &'static str = "Unknown error";
193+
194+
195+
#[test]
196+
fn win32error_new_test()
197+
{
198+
let err = Win32Error::new();
199+
assert_eq!(0, err.get_error_code());
200+
}
201+
202+
// Test whether passed error code is returned from get_error_code
203+
//
204+
#[test]
205+
#[allow(unused_variables)]
206+
fn win32error_from_test_unknown_error_code()
207+
{
208+
let errno = 99999;
209+
let err = Win32Error::from(errno);
210+
assert_eq!(err.get_error_code(), errno);
211+
}
212+
213+
#[test]
214+
#[allow(unused_variables)]
215+
fn win32error_from_test_unknown_error_description()
216+
{
217+
let errno = 99999;
218+
let err = Win32Error::from(errno);
219+
assert_eq!(err.description(), UNKNOWN_ERROR_TEXT);
220+
}
221+
}
222+

tests/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
extern crate rust_win32error;
2+
extern crate kernel32;
3+
4+
use std::error::Error;
5+
use rust_win32error::*;
6+
use self::kernel32::{ OpenProcess };
7+
8+
9+
10+
const UNKNOWN_ERROR_TEXT: &'static str = "Unknown error";
11+
12+
13+
// Cause real error, and test whether there is concrete description
14+
//
15+
#[test]
16+
#[allow(unused_variables)]
17+
fn win32error_new_test_real_error_description()
18+
{
19+
let terminate = 0x0001;
20+
let h = unsafe { OpenProcess(terminate, 0, 4) };
21+
let err = Win32Error::new();
22+
assert!(err.description() != UNKNOWN_ERROR_TEXT);
23+
}
24+
25+
// Cause real error, and test whether there is right error code
26+
//
27+
#[test]
28+
#[allow(unused_variables)]
29+
fn win32error_new_test_real_error_code()
30+
{
31+
let terminate = 0x0001;
32+
let h = unsafe { OpenProcess(terminate, 0, 4) };
33+
let err = Win32Error::new();
34+
assert_eq!(5, err.get_error_code());
35+
}

0 commit comments

Comments
 (0)