glib_win32/functions.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2#[cfg(any(windows, docsrs))]
3use glib::translate::*;
4#[cfg(any(windows, docsrs))]
5use std::path::PathBuf;
6
7#[cfg(any(windows, docsrs))]
8use crate::ffi;
9
10#[cfg(windows)]
11use std::os::windows::raw::HANDLE;
12#[cfg(all(unix, docsrs))]
13pub type HANDLE = *mut std::os::raw::c_void;
14
15/// This function tries to determine the installation directory of a
16/// software package based on the location of a DLL of the software
17/// package.
18///
19/// @hmodule should be the handle of a loaded DLL or [`None`]. The
20/// function looks up the directory that DLL was loaded from. If
21/// @hmodule is NULL, the directory the main executable of the current
22/// process is looked up. If that directory's last component is "bin"
23/// or "lib", its parent directory is returned, otherwise the directory
24/// itself.
25///
26/// It thus makes sense to pass only the handle to a "public" DLL of a
27/// software package to this function, as such DLLs typically are known
28/// to be installed in a "bin" or occasionally "lib" subfolder of the
29/// installation folder. DLLs that are of the dynamically loaded module
30/// or plugin variety are often located in more private locations
31/// deeper down in the tree, from which it is impossible for GLib to
32/// deduce the root of the package installation.
33///
34/// The typical use case for this function is to have a DllMain() that
35/// saves the handle for the DLL. Then when code in the DLL needs to
36/// construct names of files in the installation tree it calls this
37/// function passing the DLL handle.
38/// ## `hmodule`
39/// The Win32 handle for a DLL loaded into the current process, or [`None`]
40///
41/// # Returns
42///
43/// a string containing the guessed installation directory for
44/// the software package @hmodule is from. The string is in the GLib
45/// file name encoding, i.e. UTF-8. The return value should be freed
46/// with g_free() when not needed any longer. If the function fails
47/// [`None`] is returned.
48#[doc(alias = "g_win32_get_package_installation_directory_of_module")]
49#[doc(alias = "get_package_installation_directory_of_module")]
50#[cfg(any(windows, docsrs))]
51pub fn package_installation_directory_of_module(
52 hmodule: HANDLE,
53) -> Result<PathBuf, std::io::Error> {
54 // # Safety
55 // The underlying `GetModuleFilenameW` function has three possible
56 // outcomes when a raw pointer get passed to it:
57 // - When the pointer is a valid HINSTANCE of a DLL (e.g. acquired
58 // through the `GetModuleHandleW`), it sets a file path to the
59 // assigned "out" buffer and sets the return value to be the length
60 // of said path string
61 // - When the pointer is null, it sets the full path of the process'
62 // executable binary to the assigned buffer and sets the return value
63 // to be the length of said string
64 // - Whenever the provided buffer size is too small, it will set a
65 // truncated version of the path and return the length of said string
66 // while also setting the thread-local last-error code to
67 // `ERROR_INSUFFICIENT_BUFFER` (evaluates to 0x7A)
68 // - When the pointer is not a valid HINSTANCE that isn't NULL (e.g.
69 // a pointer to some GKeyFile), it will return 0 and set the last-error
70 // code to `ERROR_MOD_NOT_FOUND` (evaluates to 0x7E)
71 //
72 // The `g_win32_get_package_installation_directory_of_module` already
73 // handles all of the outcomes gracefully by:
74 // - Preallocating a MAX_PATH-long array of wchar_t for the out buffer,
75 // so that outcome #3 can be safely assumed to never happen
76 // - Returning NULL when outcome #4 happens
77 match unsafe {
78 from_glib_full::<_, Option<PathBuf>>(
79 ffi::g_win32_get_package_installation_directory_of_module(hmodule),
80 )
81 } {
82 Some(pb) => Ok(pb),
83 None => Err(std::io::Error::last_os_error()),
84 }
85}