1use crate::{windows, Null};
2use std::ops::Deref;
3
4pub struct ResGuard<R: Copy> {
10 resource: R,
11 free_fn: fn(R),
12}
13
14impl<R: Copy> ResGuard<R> {
15 pub fn new(resource: R, free: fn(R)) -> Self {
16 Self {
19 resource: resource,
20 free_fn: free,
21 }
22 }
23
24 pub fn with_acquisition<A, E>(acquire: A, free: fn(R)) -> Result<Self, E>
25 where
26 A: FnOnce() -> Result<R, E>,
27 {
28 Ok(Self {
31 resource: acquire()?,
32 free_fn: free,
33 })
34 }
35
36 pub fn with_mut_acquisition<A, T, E>(acquire: A, free: fn(R)) -> Result<Self, E>
37 where
38 R: Null,
39 A: FnOnce(&mut R) -> Result<T, E>,
40 {
41 let mut resource = R::NULL;
44 acquire(&mut resource)?;
45
46 Ok(Self {
47 resource,
48 free_fn: free,
49 })
50 }
51
52 pub fn two_with_mut_acquisition<A, T, E>(
53 acquire_both: A,
54 free_first: fn(R),
55 free_second: fn(R),
56 ) -> Result<(Self, Self), E>
57 where
58 R: Null,
59 A: FnOnce(&mut R, &mut R) -> Result<T, E>,
60 {
61 let mut first_resource = R::NULL;
64 let mut second_resource = R::NULL;
65 acquire_both(&mut first_resource, &mut second_resource)?;
66
67 Ok((
68 Self {
69 resource: first_resource,
70 free_fn: free_first,
71 },
72 Self {
73 resource: second_resource,
74 free_fn: free_second,
75 },
76 ))
77 }
78}
79
80macro_rules! impl_with_acq_and_free_fn {
81 ($type:ty, $with_res:ident, $with_acq:ident, $with_acq_mut:ident, $free_fn:expr) => {
82 impl ResGuard<$type> {
83 const FREE_FN: fn($type) = $free_fn;
84
85 pub fn $with_res(resource: $type) -> Self {
86 Self::new(resource, Self::FREE_FN)
87 }
88
89 pub fn $with_acq<A, E>(acquire: A) -> Result<Self, E>
90 where
91 A: FnOnce() -> Result<$type, E>,
92 {
93 Self::with_acquisition(acquire, Self::FREE_FN)
94 }
95
96 pub fn $with_acq_mut<A, T, E>(acquire: A) -> Result<Self, E>
97 where
98 A: FnOnce(&mut $type) -> Result<T, E>,
99 {
100 Self::with_mut_acquisition(acquire, Self::FREE_FN)
101 }
102 }
103 };
104}
105
106#[cfg(all(feature = "f_Win32_Foundation"))]
107impl_with_acq_and_free_fn!(
108 windows::Win32::Foundation::HANDLE,
109 with_res_and_close_handle,
110 with_acq_and_close_handle,
111 with_mut_acq_and_close_handle,
112 |handle| {
113 let _ = unsafe { windows::Win32::Foundation::CloseHandle(handle) };
114 }
115);
116
117#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
118impl_with_acq_and_free_fn!(
119 windows::Win32::Graphics::Gdi::HBITMAP,
120 with_res_and_delete_object,
121 with_acq_and_delete_object,
122 with_mut_acq_and_delete_object,
123 |h_bitmap| {
124 unsafe { windows::Win32::Graphics::Gdi::DeleteObject(h_bitmap) };
125 }
126);
127
128#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
129impl_with_acq_and_free_fn!(
130 windows::Win32::Graphics::Gdi::HBRUSH,
131 with_res_and_delete_object,
132 with_acq_and_delete_object,
133 with_mut_acq_and_delete_object,
134 |h_brush| {
135 unsafe { windows::Win32::Graphics::Gdi::DeleteObject(h_brush) };
136 }
137);
138
139#[cfg(feature = "windows_v0_48")]
140#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
141impl_with_acq_and_free_fn!(
142 windows::Win32::Graphics::Gdi::CreatedHDC,
143 with_res_and_delete_dc,
144 with_acq_and_delete_dc,
145 with_mut_acq_and_delete_dc,
146 |h_dc| {
147 unsafe { windows::Win32::Graphics::Gdi::DeleteDC(h_dc) };
148 }
149);
150
151#[cfg(not(feature = "windows_v0_48"))]
152#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
153impl_with_acq_and_free_fn!(
154 windows::Win32::Graphics::Gdi::HDC,
155 with_res_and_delete_dc,
156 with_acq_and_delete_dc,
157 with_mut_acq_and_delete_dc,
158 |h_dc| {
159 unsafe { windows::Win32::Graphics::Gdi::DeleteDC(h_dc) };
160 }
161);
162
163#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
164impl_with_acq_and_free_fn!(
165 windows::Win32::Graphics::Gdi::HFONT,
166 with_res_and_delete_object,
167 with_acq_and_delete_object,
168 with_mut_acq_and_delete_object,
169 |h_font| {
170 unsafe { windows::Win32::Graphics::Gdi::DeleteObject(h_font) };
171 }
172);
173
174#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
175impl_with_acq_and_free_fn!(
176 windows::Win32::Graphics::Gdi::HGDIOBJ,
177 with_res_and_delete_object,
178 with_acq_and_delete_object,
179 with_mut_acq_and_delete_object,
180 |h_gdi_obj| {
181 unsafe { windows::Win32::Graphics::Gdi::DeleteObject(h_gdi_obj) };
182 }
183);
184
185#[cfg(feature = "windows_v0_48")]
186#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_System_Memory"))]
187impl_with_acq_and_free_fn!(
188 windows::Win32::Foundation::HGLOBAL,
189 with_res_and_global_free,
190 with_acq_and_global_free,
191 with_mut_acq_and_global_free,
192 |h_global| {
193 let _ = unsafe { windows::Win32::System::Memory::GlobalFree(h_global) };
194 }
195);
196
197#[cfg(not(feature = "windows_v0_48"))]
198#[cfg(all(feature = "f_Win32_Foundation"))]
199impl_with_acq_and_free_fn!(
200 windows::Win32::Foundation::HGLOBAL,
201 with_res_and_global_free,
202 with_acq_and_global_free,
203 with_mut_acq_and_global_free,
204 |h_global| {
205 let _ = unsafe { windows::Win32::Foundation::GlobalFree(h_global) };
206 }
207);
208
209#[cfg(all(
210 feature = "f_Win32_Foundation",
211 feature = "f_Win32_UI_WindowsAndMessaging"
212))]
213impl_with_acq_and_free_fn!(
214 windows::Win32::UI::WindowsAndMessaging::HICON,
215 with_res_and_destroy_icon,
216 with_acq_and_destroy_icon,
217 with_mut_acq_and_destroy_icon,
218 |h_icon| {
219 let _ = unsafe { windows::Win32::UI::WindowsAndMessaging::DestroyIcon(h_icon) };
220 }
221);
222
223#[cfg(feature = "windows_v0_48")]
224#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_System_Memory"))]
225impl_with_acq_and_free_fn!(
226 windows::Win32::Foundation::HLOCAL,
227 with_res_and_local_free,
228 with_acq_and_local_free,
229 with_mut_acq_and_local_free,
230 |h_local| {
231 let _ = unsafe { windows::Win32::System::Memory::LocalFree(h_local) };
232 }
233);
234
235#[cfg(not(feature = "windows_v0_48"))]
236#[cfg(all(feature = "f_Win32_Foundation"))]
237impl_with_acq_and_free_fn!(
238 windows::Win32::Foundation::HLOCAL,
239 with_res_and_local_free,
240 with_acq_and_local_free,
241 with_mut_acq_and_local_free,
242 |h_local| {
243 let _ = unsafe { windows::Win32::Foundation::LocalFree(h_local) };
244 }
245);
246
247#[cfg(all(
248 feature = "f_Win32_Foundation",
249 feature = "f_Win32_UI_WindowsAndMessaging"
250))]
251impl_with_acq_and_free_fn!(
252 windows::Win32::UI::WindowsAndMessaging::HMENU,
253 with_res_and_destroy_menu,
254 with_acq_and_destroy_menu,
255 with_mut_acq_and_destroy_menu,
256 |h_menu| {
257 let _ = unsafe { windows::Win32::UI::WindowsAndMessaging::DestroyMenu(h_menu) };
258 }
259);
260
261#[cfg(feature = "windows_v0_48")]
262#[cfg(all(
263 feature = "f_Win32_Foundation",
264 feature = "f_Win32_System_LibraryLoader"
265))]
266impl_with_acq_and_free_fn!(
267 windows::Win32::Foundation::HMODULE,
268 with_res_and_free_library,
269 with_acq_and_free_library,
270 with_mut_acq_and_free_library,
271 |h_module| {
272 let _ = unsafe { windows::Win32::System::LibraryLoader::FreeLibrary(h_module) };
273 }
274);
275
276#[cfg(not(feature = "windows_v0_48"))]
277#[cfg(all(feature = "f_Win32_Foundation"))]
278impl_with_acq_and_free_fn!(
279 windows::Win32::Foundation::HMODULE,
280 with_res_and_free_library,
281 with_acq_and_free_library,
282 with_mut_acq_and_free_library,
283 |h_module| {
284 let _ = unsafe { windows::Win32::Foundation::FreeLibrary(h_module) };
285 }
286);
287
288#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
289impl_with_acq_and_free_fn!(
290 windows::Win32::Graphics::Gdi::HPALETTE,
291 with_res_and_delete_object,
292 with_acq_and_delete_object,
293 with_mut_acq_and_delete_object,
294 |h_palette| {
295 unsafe { windows::Win32::Graphics::Gdi::DeleteObject(h_palette) };
296 }
297);
298
299#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
300impl_with_acq_and_free_fn!(
301 windows::Win32::Graphics::Gdi::HPEN,
302 with_res_and_delete_object,
303 with_acq_and_delete_object,
304 with_mut_acq_and_delete_object,
305 |h_pen| {
306 unsafe { windows::Win32::Graphics::Gdi::DeleteObject(h_pen) };
307 }
308);
309
310#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_System_Power"))]
311impl_with_acq_and_free_fn!(
312 windows::Win32::System::Power::HPOWERNOTIFY,
313 with_res_and_unregister_power_setting_notification,
314 with_acq_and_unregister_power_setting_notification,
315 with_mut_acq_and_unregister_power_setting_notification,
316 |h_power_notify| {
317 let _ = unsafe {
318 windows::Win32::System::Power::UnregisterPowerSettingNotification(h_power_notify)
319 };
320 }
321);
322
323#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_Graphics_Gdi"))]
324impl_with_acq_and_free_fn!(
325 windows::Win32::Graphics::Gdi::HRGN,
326 with_res_and_delete_object,
327 with_acq_and_delete_object,
328 with_mut_acq_and_delete_object,
329 |h_rgn| {
330 unsafe { windows::Win32::Graphics::Gdi::DeleteObject(h_rgn) };
331 }
332);
333
334#[cfg(feature = "windows_v0_48")]
335#[cfg(all(feature = "f_Win32_Foundation", feature = "f_Win32_System_Memory"))]
336impl_with_acq_and_free_fn!(
337 windows::core::PWSTR,
338 with_res_and_local_free,
339 with_acq_and_local_free,
340 with_mut_acq_and_local_free,
341 |pwstr| {
342 let _ = unsafe {
343 windows::Win32::System::Memory::LocalFree(windows::Win32::Foundation::HLOCAL(
344 pwstr.0 as _,
345 ))
346 };
347 }
348);
349
350#[cfg(not(feature = "windows_v0_48"))]
352#[cfg(all(feature = "f_Win32_Foundation"))]
353impl_with_acq_and_free_fn!(
354 windows::core::PWSTR,
355 with_res_and_local_free,
356 with_acq_and_local_free,
357 with_mut_acq_and_local_free,
358 |pwstr| {
359 let _ = unsafe {
360 windows::Win32::Foundation::LocalFree(windows::Win32::Foundation::HLOCAL(
361 pwstr.0.cast(),
362 ))
363 };
364 }
365);
366
367#[cfg(feature = "f_Win32_Foundation")]
368impl ResGuard<windows::Win32::Foundation::HANDLE> {
369 pub fn two_with_mut_acq_and_close_handle<A, T, E>(acquire_both: A) -> Result<(Self, Self), E>
372 where
373 A: FnOnce(
374 &mut windows::Win32::Foundation::HANDLE,
375 &mut windows::Win32::Foundation::HANDLE,
376 ) -> Result<T, E>,
377 {
378 Self::two_with_mut_acquisition(acquire_both, Self::FREE_FN, Self::FREE_FN)
383 }
384}
385
386impl<R: Copy> Deref for ResGuard<R> {
387 type Target = R;
388
389 fn deref(&self) -> &Self::Target {
390 &self.resource
391 }
392}
393
394impl<R: Copy> Drop for ResGuard<R> {
395 fn drop(&mut self) {
396 (self.free_fn)(self.resource);
397 }
398}
399
400#[cfg(all(test, feature = "windows_latest_compatible_all"))]
401mod tests {
402 use super::ResGuard;
403 use crate::{
404 core::{CheckNullError, CheckNumberError},
405 windows, Null,
406 };
407 use std::{mem, ptr};
408 use windows::{
409 core::PCWSTR,
410 Win32::{
411 Foundation::{CloseHandle, COLORREF},
412 Graphics::Gdi::{CreateSolidBrush, GetObjectW, HBRUSH, LOGBRUSH},
413 Storage::FileSystem::{ReadFile, WriteFile},
414 System::{
415 Pipes::CreatePipe,
416 Threading::{CreateEventW, SetEvent},
417 },
418 },
419 };
420
421 #[test]
422 fn new() {
423 let event_handle = unsafe { CreateEventW(None, true, false, PCWSTR::NULL) }
424 .expect("should be able to create event handle");
425 let event_handle = ResGuard::new(event_handle, |handle| {
426 let _ = unsafe { CloseHandle(handle) };
427 });
428
429 assert_eq!(unsafe { SetEvent(*event_handle) }, Ok(()));
430 }
431
432 #[test]
433 fn with_acq_and_close_handle() {
434 let event_handle = ResGuard::with_acq_and_close_handle(|| unsafe {
435 CreateEventW(None, true, false, PCWSTR::NULL)
436 })
437 .expect("should be able to create event handle");
438
439 assert_eq!(unsafe { SetEvent(*event_handle) }, Ok(()));
440 }
441
442 #[test]
443 fn two_with_mut_acq_and_close_handle() {
444 let (read_handle, write_handle) =
446 ResGuard::two_with_mut_acq_and_close_handle(|read_handle, write_handle| unsafe {
447 CreatePipe(read_handle, write_handle, None, 0)
448 })
449 .expect("should be able to create pipe handles");
450
451 let bytes = [123, 45, 67];
453 let mut bytes_written = 0;
454 assert_eq!(
455 unsafe { WriteFile(*write_handle, Some(&bytes), Some(&mut bytes_written), None,) },
456 Ok(())
457 );
458 assert_eq!(bytes_written as usize, bytes.len());
459
460 let mut buffer = Vec::new();
462 buffer.resize(bytes.len(), 0);
463 let mut bytes_read = 0;
464 assert_eq!(
465 unsafe { ReadFile(*read_handle, Some(&mut buffer), Some(&mut bytes_read), None) },
466 Ok(())
467 );
468 assert_eq!(bytes_read as usize, buffer.len());
469 assert_eq!(buffer, bytes);
470 }
471
472 #[test]
473 fn with_acq_and_delete_object() -> windows::core::Result<()> {
474 const BGR: u32 = 0x123456;
477
478 let h_brush = ResGuard::<HBRUSH>::with_acq_and_delete_object(|| {
479 unsafe { CreateSolidBrush(COLORREF(BGR)) }.nonnull_or_e_handle()
480 })?;
481
482 let mut log_brush = LOGBRUSH::default();
483 unsafe {
484 GetObjectW(
485 *h_brush,
486 mem::size_of::<LOGBRUSH>() as _,
487 Some(ptr::addr_of_mut!(log_brush).cast()),
488 )
489 }
490 .nonzero_or_e_fail()?;
491
492 assert_eq!(log_brush.lbColor.0, BGR);
493
494 Ok(())
495 }
496}