1 /* GMODULE - GLIB wrapper code for dynamic module loading
2 * Copyright (C) 1998, 2000 Tim Janik
3 *
4 * Win32 GMODULE implementation
5 * Copyright (C) 1998 Tor Lillqvist
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22 * Modified by the GLib Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GLib Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GLib at ftp://ftp.gtk.org/pub/gtk/.
26 */
27
28 /*
29 * MT safe
30 */
31 #include "config.h"
32
33 #include <stdio.h>
34 #include <windows.h>
35
36 #include <tlhelp32.h>
37
38 #ifdef G_WITH_CYGWIN
39 #include <sys/cygwin.h>
40 #endif
41
42 static void G_GNUC_PRINTF (1, 2)
set_error(const gchar * format,...)43 set_error (const gchar *format,
44 ...)
45 {
46 gchar *error;
47 gchar *detail;
48 gchar *message;
49 va_list args;
50
51 error = g_win32_error_message (GetLastError ());
52
53 va_start (args, format);
54 detail = g_strdup_vprintf (format, args);
55 va_end (args);
56
57 message = g_strconcat (detail, error, NULL);
58
59 g_module_set_error (message);
60 g_free (message);
61 g_free (detail);
62 g_free (error);
63 }
64
65 /* --- functions --- */
66 static gpointer
_g_module_open(const gchar * file_name,gboolean bind_lazy,gboolean bind_local)67 _g_module_open (const gchar *file_name,
68 gboolean bind_lazy,
69 gboolean bind_local)
70 {
71 HINSTANCE handle;
72 wchar_t *wfilename;
73 DWORD old_mode;
74 BOOL success;
75 #ifdef G_WITH_CYGWIN
76 gchar tmp[MAX_PATH];
77
78 cygwin_conv_to_win32_path(file_name, tmp);
79 file_name = tmp;
80 #endif
81 wfilename = g_utf8_to_utf16 (file_name, -1, NULL, NULL, NULL);
82
83 /* suppress error dialog */
84 success = SetThreadErrorMode (SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS, &old_mode);
85 if (!success)
86 set_error ("");
87
88 /* When building for UWP, load app asset DLLs instead of filesystem DLLs.
89 * Needs MSVC, Windows 8 and newer, and is only usable from apps. */
90 #if _WIN32_WINNT >= 0x0602 && defined(G_WINAPI_ONLY_APP)
91 handle = LoadPackagedLibrary (wfilename, 0);
92 #else
93 handle = LoadLibraryW (wfilename);
94 #endif
95
96 if (success)
97 SetThreadErrorMode (old_mode, NULL);
98 g_free (wfilename);
99
100 if (!handle)
101 set_error ("'%s': ", file_name);
102
103 return handle;
104 }
105
106 static gint dummy;
107 static gpointer null_module_handle = &dummy;
108
109 static gpointer
_g_module_self(void)110 _g_module_self (void)
111 {
112 return null_module_handle;
113 }
114
115 static void
_g_module_close(gpointer handle)116 _g_module_close (gpointer handle)
117 {
118 if (handle != null_module_handle)
119 if (!FreeLibrary (handle))
120 set_error ("");
121 }
122
123 static gpointer
find_in_any_module_using_toolhelp(const gchar * symbol_name)124 find_in_any_module_using_toolhelp (const gchar *symbol_name)
125 {
126 HANDLE snapshot;
127 MODULEENTRY32 me32;
128
129 gpointer p = NULL;
130
131 /* Under UWP, Module32Next and Module32First are not available since we're
132 * not allowed to search in the address space of arbitrary loaded DLLs */
133 #if !defined(G_WINAPI_ONLY_APP)
134 /* https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot#remarks
135 * If the function fails with ERROR_BAD_LENGTH, retry the function until it succeeds. */
136 while (TRUE)
137 {
138 snapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
139 if (snapshot == INVALID_HANDLE_VALUE && GetLastError () == ERROR_BAD_LENGTH)
140 {
141 g_thread_yield ();
142 continue;
143 }
144 break;
145 }
146
147 if (snapshot == INVALID_HANDLE_VALUE)
148 return NULL;
149
150 me32.dwSize = sizeof (me32);
151 p = NULL;
152 if (Module32First (snapshot, &me32))
153 {
154 do {
155 if ((p = GetProcAddress (me32.hModule, symbol_name)) != NULL)
156 break;
157 } while (Module32Next (snapshot, &me32));
158 }
159
160 CloseHandle (snapshot);
161 #endif
162
163 return p;
164 }
165
166 static gpointer
find_in_any_module(const gchar * symbol_name)167 find_in_any_module (const gchar *symbol_name)
168 {
169 gpointer result;
170
171 if ((result = find_in_any_module_using_toolhelp (symbol_name)) == NULL)
172 return NULL;
173 else
174 return result;
175 }
176
177 static gpointer
_g_module_symbol(gpointer handle,const gchar * symbol_name)178 _g_module_symbol (gpointer handle,
179 const gchar *symbol_name)
180 {
181 gpointer p;
182
183 if (handle == null_module_handle)
184 {
185 if ((p = GetProcAddress (GetModuleHandle (NULL), symbol_name)) == NULL)
186 p = find_in_any_module (symbol_name);
187 }
188 else
189 p = GetProcAddress (handle, symbol_name);
190
191 if (!p)
192 set_error ("");
193
194 return p;
195 }
196
197 static gchar*
_g_module_build_path(const gchar * directory,const gchar * module_name)198 _g_module_build_path (const gchar *directory,
199 const gchar *module_name)
200 {
201 gint k;
202
203 k = strlen (module_name);
204
205 if (directory && *directory)
206 if (k > 4 && g_ascii_strcasecmp (module_name + k - 4, ".dll") == 0)
207 return g_strconcat (directory, G_DIR_SEPARATOR_S, module_name, NULL);
208 #ifdef G_WITH_CYGWIN
209 else if (strncmp (module_name, "lib", 3) == 0 || strncmp (module_name, "cyg", 3) == 0)
210 return g_strconcat (directory, G_DIR_SEPARATOR_S, module_name, ".dll", NULL);
211 else
212 return g_strconcat (directory, G_DIR_SEPARATOR_S, "cyg", module_name, ".dll", NULL);
213 #else
214 else if (strncmp (module_name, "lib", 3) == 0)
215 return g_strconcat (directory, G_DIR_SEPARATOR_S, module_name, ".dll", NULL);
216 else
217 return g_strconcat (directory, G_DIR_SEPARATOR_S, "lib", module_name, ".dll", NULL);
218 #endif
219 else if (k > 4 && g_ascii_strcasecmp (module_name + k - 4, ".dll") == 0)
220 return g_strdup (module_name);
221 #ifdef G_WITH_CYGWIN
222 else if (strncmp (module_name, "lib", 3) == 0 || strncmp (module_name, "cyg", 3) == 0)
223 return g_strconcat (module_name, ".dll", NULL);
224 else
225 return g_strconcat ("cyg", module_name, ".dll", NULL);
226 #else
227 else if (strncmp (module_name, "lib", 3) == 0)
228 return g_strconcat (module_name, ".dll", NULL);
229 else
230 return g_strconcat ("lib", module_name, ".dll", NULL);
231 #endif
232 }
233