winprint_ext/printer/
device.rs1use crate::utils::wchar;
2use std::alloc::{alloc, dealloc, Layout};
3use std::ffi::{OsStr, OsString};
4use std::mem;
5use std::ptr;
6use thiserror::Error;
7use windows::core::PCWSTR;
8use windows::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
9use windows::Win32::Graphics::Printing::*;
10#[derive(Clone, Debug)]
11pub struct PrinterDevice {
13 name: String,
14 os_server: OsString,
15 os_name: OsString,
16 os_attributes: u32,
17}
18
19#[derive(Error, Debug)]
20pub enum EnumDeviceError {
22 #[error("Failed to enum printer device")]
24 FailedToEnumPrinterDevice(#[source] windows::core::Error),
25}
26
27impl PrinterDevice {
28 pub fn all() -> Result<Vec<Self>, EnumDeviceError> {
30 let mut bytes_needed: u32 = 0;
31 let mut count_returned: u32 = 0;
32 let flags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
33 let name = PCWSTR::null();
34 let level = 4;
35 unsafe {
36 let mut buffer: *mut u8 = ptr::null_mut();
37 let mut buffer_size: usize = 0;
38
39 loop {
40 let slice = if buffer_size > 0 {
41 Some(std::slice::from_raw_parts_mut(buffer, buffer_size))
42 } else {
43 None
44 };
45 let result = EnumPrintersW(
46 flags,
47 name,
48 level,
49 slice,
50 &mut bytes_needed,
51 &mut count_returned,
52 );
53 match result {
54 Ok(()) => break,
55 Err(e) if e.code() == ERROR_INSUFFICIENT_BUFFER.to_hresult() => {
56 let new_size = bytes_needed as usize;
57 let align = mem::align_of::<PRINTER_INFO_4W>();
58 if buffer_size > 0 {
59 dealloc(
60 buffer,
61 Layout::from_size_align_unchecked(buffer_size, align),
62 );
63 }
64 buffer = alloc(Layout::from_size_align_unchecked(new_size, align));
65 buffer_size = new_size;
66 }
67 Err(e) => {
68 if buffer_size > 0 {
69 dealloc(
70 buffer,
71 Layout::from_size_align_unchecked(
72 buffer_size,
73 mem::align_of::<PRINTER_INFO_4W>(),
74 ),
75 );
76 }
77 return Err(EnumDeviceError::FailedToEnumPrinterDevice(e));
78 }
79 }
80 }
81
82 let mut result = Vec::<PrinterDevice>::with_capacity(count_returned as usize);
83 for i in 0..count_returned {
84 let info = &*(buffer as *const PRINTER_INFO_4W).offset(i as isize);
85 let os_name = wchar::from_wide_ptr(info.pPrinterName.0);
86 result.push(Self {
87 name: os_name.to_string_lossy().into_owned(),
88 os_server: wchar::from_wide_ptr(info.pServerName.0),
89 os_attributes: info.Attributes,
90 os_name,
91 });
92 }
93
94 if buffer_size > 0 {
95 dealloc(
96 buffer,
97 Layout::from_size_align_unchecked(
98 buffer_size,
99 mem::align_of::<PRINTER_INFO_4W>(),
100 ),
101 );
102 }
103
104 Ok(result)
105 }
106 }
107
108 pub fn name(&self) -> &str {
110 self.name.as_str()
111 }
112
113 pub fn os_attributes(&self) -> u32 {
115 self.os_attributes
116 }
117
118 pub fn os_server(&self) -> &OsStr {
120 self.os_server.as_ref()
121 }
122
123 pub fn os_name(&self) -> &OsStr {
125 self.os_name.as_ref()
126 }
127
128 pub fn is_local(&self) -> bool {
130 self.os_attributes & PRINTER_ATTRIBUTE_LOCAL != 0
131 }
132
133 pub fn is_remote(&self) -> bool {
135 self.os_attributes & PRINTER_ATTRIBUTE_NETWORK != 0
136 }
137}
138
139#[cfg(all(test, feature = "test-utils"))]
140mod tests {
141 use super::PrinterDevice;
142 use crate::test_utils::null_device;
143
144 #[test]
145 fn fetch_printer_device() {
146 let _devices = PrinterDevice::all().unwrap();
147 }
148
149 #[test]
150 fn test_printer_should_be_local() {
151 let device = null_device::thread_local();
152 assert!(device.is_local());
153 assert_eq!(device.is_remote(), false);
154 assert_eq!(device.os_server(), "");
155 assert!(device.os_attributes() & 0x00000040 != 0);
157 }
158}