native_windows_gui/resources/
embed.rs

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