native_windows_gui2/resources/
embed.rs

1use super::{Bitmap, Cursor, Icon};
2use crate::NwgError;
3use crate::win32::base_helper::{from_utf16, to_utf16};
4use std::{ptr, slice};
5use winapi::ctypes::c_void;
6use winapi::shared::minwindef::{HGLOBAL, HINSTANCE, HRSRC};
7use winapi::um::winuser::{LR_CREATEDIBSECTION, LR_DEFAULTSIZE, LoadImageW};
8
9/// Raw resource type that can be stored into an embedded resource.
10#[derive(Copy, Clone, Debug)]
11pub enum RawResourceType {
12    Cursor,
13    Bitmap,
14    Icon,
15    Menu,
16    Dialog,
17    String,
18    FontDir,
19    Font,
20    Accelerator,
21    RawData,
22    MessageTable,
23    Version,
24    DlgInclude,
25    PlugPlay,
26    Vxd,
27    AnimatedCursor,
28    AnimatedIcon,
29    Html,
30    Manifest,
31    Other(&'static str),
32}
33
34/**
35    Represents a raw handle to a embed resource. Manipulating raw resources is inherently unsafe.
36    `RawResources` are loaded using `EmbedResource::raw` and `EmbedResource::raw_str`
37
38    In order to access the raw resource data use `as_mut_ptr()` or `as_mut_slice()` and cast the pointer to your data type.
39*/
40pub struct RawResource {
41    module: HINSTANCE,
42    handle: HRSRC,
43    data_handle: HGLOBAL,
44    ty: RawResourceType,
45}
46
47impl RawResource {
48    /// Returns the system handle for the resource
49    pub fn handle(&self) -> HRSRC {
50        self.handle
51    }
52
53    /// Returns the system handle for the resource data
54    pub fn data_handle(&self) -> HGLOBAL {
55        self.data_handle
56    }
57
58    /// Returns the resource type set during texture loading
59    pub fn resource_type(&self) -> RawResourceType {
60        self.ty
61    }
62
63    /// Returns the size in bytes of the resource
64    pub fn len(&self) -> usize {
65        use winapi::um::libloaderapi::SizeofResource;
66
67        unsafe { SizeofResource(self.module, self.handle) as usize }
68    }
69
70    /// Return a const pointer to the resource.
71    pub unsafe fn as_mut_ptr(&mut self) -> *mut c_void {
72        self.lock()
73    }
74
75    /// Return the resource data as a byte slice. This is equivalent to using `slice::from_raw_parts_mut`
76    pub fn as_mut_slice(&self) -> &mut [u8] {
77        unsafe { std::slice::from_raw_parts_mut(self.lock() as *mut u8, self.len()) }
78    }
79
80    fn lock(&self) -> *mut c_void {
81        use winapi::um::libloaderapi::LockResource;
82        unsafe { LockResource(self.data_handle) }
83    }
84}
85
86/**
87EmbedResource represent an embed resource file (".rc") inside on the executable module.
88
89By default (without any arguments), the embed resources wraps the executable. If the embed resources
90are in a dll, it's also possible to load them by setting the "module" parameter to the dll name.
91
92**Builder parameters:**
93    * `module`:  The name of the module that owns the embed resources. If `None`, use the executable name.
94
95```rust
96use native_windows_gui2 as nwg;
97fn build_embed1() -> nwg::EmbedResource {
98    nwg::EmbedResource::load(None).unwrap()
99}
100
101fn build_embed2() -> nwg::EmbedResource {
102    nwg::EmbedResource::load(Some("external.dll")).unwrap()
103}
104```
105*/
106pub struct EmbedResource {
107    pub hinst: HINSTANCE,
108}
109
110impl EmbedResource {
111    /// Returns an embed resource that wraps the current executable. Shortcut for the builder API.
112    pub fn load(name: Option<&str>) -> Result<EmbedResource, NwgError> {
113        let mut embed = EmbedResource::default();
114        EmbedResource::builder().module(name).build(&mut embed)?;
115
116        Ok(embed)
117    }
118
119    /// Creates a `EmbedResourceBuilder`. `EmbedResource::load` can also be used to skip the builder api
120    pub fn builder() -> EmbedResourceBuilder {
121        EmbedResourceBuilder { module: None }
122    }
123
124    /// Load a string the the RC file STRINGTABLE. Returns `None` if `id` does not map to a string.
125    pub fn string(&self, id: u32) -> Option<String> {
126        use winapi::um::libloaderapi::LoadStringW;
127        unsafe {
128            let mut str_ptr = ptr::null_mut();
129            let ccount = LoadStringW(self.hinst, id, (&mut str_ptr) as *mut *mut u16 as _, 0);
130            match ccount {
131                0 => None,
132                count => {
133                    let str_slice = slice::from_raw_parts(str_ptr, count as usize);
134                    Some(from_utf16(str_slice))
135                }
136            }
137        }
138    }
139
140    /// Load an icon from the rc file. Returns `None` if `id` does not map to a icon.
141    /// For more feature, use the `Icon::builder` with the `embed` parameter.
142    pub fn icon(&self, id: usize, size: Option<(u32, u32)>) -> Option<Icon> {
143        use winapi::um::winuser::IMAGE_ICON;
144
145        unsafe {
146            let id_rc = id as _;
147            let icon = match size {
148                None => LoadImageW(self.hinst, id_rc, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE),
149                Some((w, h)) => LoadImageW(self.hinst, id_rc, IMAGE_ICON, w as _, h as _, 0),
150            };
151
152            if icon.is_null() {
153                None
154            } else {
155                Some(Icon {
156                    handle: icon as _,
157                    owned: true,
158                })
159            }
160        }
161    }
162
163    /// Load an icon identified by a string in a resource file. Returns `None` if `id` does not map to a icon.
164    pub fn icon_str(&self, id: &str, size: Option<(u32, u32)>) -> Option<Icon> {
165        let name = to_utf16(id);
166        self.icon(name.as_ptr() as usize, size)
167    }
168
169    /// Load a bitmap file from the rc file. Returns `None` if `id` does not map to a bitmap.
170    pub fn bitmap(&self, id: usize, size: Option<(u32, u32)>) -> Option<Bitmap> {
171        use winapi::um::winuser::IMAGE_BITMAP;
172
173        unsafe {
174            let id_rc = id as _;
175            let bitmap = match size {
176                None => LoadImageW(self.hinst, id_rc, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE),
177                Some((w, h)) => LoadImageW(
178                    self.hinst,
179                    id_rc,
180                    IMAGE_BITMAP,
181                    w as _,
182                    h as _,
183                    LR_CREATEDIBSECTION,
184                ),
185            };
186
187            if bitmap.is_null() {
188                None
189            } else {
190                Some(Bitmap {
191                    handle: bitmap as _,
192                    owned: true,
193                })
194            }
195        }
196    }
197
198    /// Load a bitmap file from the rc file. Returns `None` if `id` does not map to a bitmap.
199    pub fn bitmap_str(&self, id: &str, size: Option<(u32, u32)>) -> Option<Bitmap> {
200        let name = to_utf16(id);
201        self.bitmap(name.as_ptr() as usize, size)
202    }
203
204    #[cfg(feature = "image-decoder")]
205    /// Load an image from the embed files and returns a bitmap. An image is defined this way: `IMAGE_NAME IMAGE "../path/my_image.bmp"`
206    /// This method can load any image type supported by the image decoder.
207    pub fn image(&self, id: usize, size: Option<(u32, u32)>) -> Option<Bitmap> {
208        use crate::win32::resources_helper as rh;
209
210        match self.raw(id, RawResourceType::Other("Image")) {
211            None => None,
212            Some(raw) => {
213                let src = raw.as_mut_slice();
214                let handle = unsafe { rh::build_image_decoder_from_memory(src, size) };
215                match handle {
216                    Ok(handle) => Some(Bitmap {
217                        handle,
218                        owned: true,
219                    }),
220                    Err(e) => {
221                        println!("{:?}", e);
222                        None
223                    }
224                }
225            }
226        }
227    }
228
229    #[cfg(feature = "image-decoder")]
230    /// Load a image using a string name. See `EmbedResource::image`
231    pub fn image_str(&self, id: &str, size: Option<(u32, u32)>) -> Option<Bitmap> {
232        let name = to_utf16(id);
233        self.image(name.as_ptr() as usize, size)
234    }
235
236    /// Load a cursor file from the rc file. Returns `None` if `id` does not map to a cursor.
237    pub fn cursor(&self, id: usize) -> Option<Cursor> {
238        use winapi::um::winuser::IMAGE_CURSOR;
239
240        unsafe {
241            let id_rc = id as _;
242            let cursor = LoadImageW(self.hinst, id_rc, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
243            if cursor.is_null() {
244                None
245            } else {
246                Some(Cursor {
247                    handle: cursor as _,
248                    owned: true,
249                })
250            }
251        }
252    }
253
254    /// Load a cursor file from the rc file. Returns `None` if `id` does not map to a cursor.
255    pub fn cursor_str(&self, id: &str) -> Option<Cursor> {
256        let name = to_utf16(id);
257        self.cursor(name.as_ptr() as usize)
258    }
259
260    /// Return a wrapper over the data of an embed resource. Return `None` `id` does not map to a resource.
261    pub fn raw(&self, id: usize, ty: RawResourceType) -> Option<RawResource> {
262        use RawResourceType::*;
263        use winapi::um::libloaderapi::{FindResourceW, LoadResource};
264
265        unsafe {
266            let data_u16;
267            let ty_value = match ty {
268                Cursor => 1,
269                Bitmap => 2,
270                Icon => 3,
271                Menu => 4,
272                Dialog => 5,
273                String => 6,
274                FontDir => 7,
275                Font => 8,
276                Accelerator => 9,
277                RawData => 10,
278                MessageTable => 11,
279                Version => 16,
280                DlgInclude => 17,
281                PlugPlay => 19,
282                Vxd => 20,
283                AnimatedCursor => 21,
284                AnimatedIcon => 22,
285                Html => 23,
286                Manifest => 24,
287                Other(value) => {
288                    data_u16 = Some(to_utf16(value));
289                    data_u16.as_ref().map(|v| v.as_ptr() as usize).unwrap()
290                }
291            };
292
293            let handle = FindResourceW(self.hinst as _, id as _, ty_value as _);
294            if handle.is_null() {
295                return None;
296            }
297
298            let data_handle = LoadResource(self.hinst as _, handle);
299
300            Some(RawResource {
301                module: self.hinst,
302                handle,
303                data_handle,
304                ty,
305            })
306        }
307    }
308
309    /// Return a wrapper over the data of an embed resource. Return `None` `id` does not map to a resource.
310    pub fn raw_str(&self, id: &str, ty: RawResourceType) -> Option<RawResource> {
311        let name = to_utf16(id);
312        self.raw(name.as_ptr() as usize, ty)
313    }
314}
315
316impl Default for EmbedResource {
317    fn default() -> EmbedResource {
318        EmbedResource {
319            hinst: ptr::null_mut(),
320        }
321    }
322}
323
324/**
325    The EmbedResource builder. See `EmbedResource` docs.
326*/
327pub struct EmbedResourceBuilder {
328    module: Option<String>,
329}
330
331impl EmbedResourceBuilder {
332    pub fn module(mut self, module: Option<&str>) -> EmbedResourceBuilder {
333        self.module = module.map(|s| s.to_string());
334        self
335    }
336
337    pub fn build(self, out: &mut EmbedResource) -> Result<(), NwgError> {
338        use winapi::um::libloaderapi::GetModuleHandleW;
339
340        let hinst = match self.module.as_ref() {
341            Some(name) => {
342                let name = to_utf16(name);
343                unsafe { GetModuleHandleW(name.as_ptr()) as HINSTANCE }
344            }
345            None => unsafe { GetModuleHandleW(ptr::null_mut()) as HINSTANCE },
346        };
347
348        if hinst.is_null() {
349            let name = self.module.as_ref().map(|name| name as &str).unwrap_or("");
350            return Err(NwgError::resource_create(format!(
351                "No module named \"{}\" in application",
352                name
353            )));
354        }
355
356        *out = EmbedResource { hinst };
357
358        Ok(())
359    }
360}