Branch data Line data Source code
1 : : /* GLIB - Library of useful routines for C programming
2 : : * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
3 : : * Copyright (C) 1998-1999 Tor Lillqvist
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
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 for the unix part, FIXME: make the win32 part MT safe as well.
30 : : */
31 : :
32 : : #include "config.h"
33 : :
34 : : #include "glibconfig.h"
35 : :
36 : : #include <glib/gstdio.h>
37 : : #include <stdlib.h>
38 : : #include <stdio.h>
39 : : #include <string.h>
40 : : #include <wchar.h>
41 : : #include <errno.h>
42 : : #include <fcntl.h>
43 : :
44 : : #include <winsock2.h>
45 : : #include <windows.h>
46 : : #ifndef G_WITH_CYGWIN
47 : : #include <direct.h>
48 : : #endif
49 : : #include <errno.h>
50 : : #include <ctype.h>
51 : : #if defined(_MSC_VER) || defined(__DMC__)
52 : : # include <io.h>
53 : : #endif /* _MSC_VER || __DMC__ */
54 : :
55 : : #define MODERN_API_FAMILY 2
56 : :
57 : : #if WINAPI_FAMILY == MODERN_API_FAMILY
58 : : /* This is for modern UI Builds, where we can't use LoadLibraryW()/GetProcAddress() */
59 : : /* ntddk.h is found in the WDK, and MinGW */
60 : : #include <ntddk.h>
61 : :
62 : : #ifdef _MSC_VER
63 : : #pragma comment (lib, "ntoskrnl.lib")
64 : : #endif
65 : : #elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
66 : : /* mingw-w64 must use winternl.h, but not MinGW */
67 : : #include <ntdef.h>
68 : : #else
69 : : #include <winternl.h>
70 : : #endif
71 : :
72 : : #include "glib.h"
73 : : #include "gwin32.h"
74 : : #include "gwin32private.h"
75 : : #include "gthreadprivate.h"
76 : : #include "gwin32private.h"
77 : : #include "glib-init.h"
78 : :
79 : : #ifdef G_WITH_CYGWIN
80 : : #include <sys/cygwin.h>
81 : : #endif
82 : :
83 : : #ifndef G_WITH_CYGWIN
84 : :
85 : : gint
86 : 28 : g_win32_ftruncate (gint fd,
87 : : guint size)
88 : : {
89 : 28 : return _chsize (fd, size);
90 : : }
91 : :
92 : : #endif
93 : :
94 : : /**
95 : : * g_win32_getlocale:
96 : : *
97 : : * The setlocale() function in the Microsoft C library uses locale
98 : : * names of the form "English_United States.1252" etc. We want the
99 : : * UNIXish standard form "en_US", "zh_TW" etc. This function gets the
100 : : * current thread locale from Windows - without any encoding info -
101 : : * and returns it as a string of the above form for use in forming
102 : : * file names etc. The returned string should be deallocated with
103 : : * g_free().
104 : : *
105 : : * Returns: newly-allocated locale name.
106 : : **/
107 : :
108 : : #ifndef SUBLANG_SERBIAN_LATIN_BA
109 : : #define SUBLANG_SERBIAN_LATIN_BA 0x06
110 : : #endif
111 : :
112 : : gchar *
113 : 8592 : g_win32_getlocale (void)
114 : : {
115 : : gchar *result;
116 : : LCID lcid;
117 : : LANGID langid;
118 : : const gchar *ev;
119 : : gint primary, sub;
120 : : WCHAR iso639[10];
121 : : gchar *iso639_utf8;
122 : : WCHAR iso3166[10];
123 : : gchar *iso3166_utf8;
124 : 8592 : const gchar *script = NULL;
125 : :
126 : : /* Let the user override the system settings through environment
127 : : * variables, as on POSIX systems. Note that in GTK applications
128 : : * since GTK 2.10.7 setting either LC_ALL or LANG also sets the
129 : : * Win32 locale and C library locale through code in gtkmain.c.
130 : : */
131 : 8592 : if (((ev = g_getenv ("LC_ALL")) != NULL && ev[0] != '\0')
132 : 8592 : || ((ev = g_getenv ("LC_MESSAGES")) != NULL && ev[0] != '\0')
133 : 8574 : || ((ev = g_getenv ("LANG")) != NULL && ev[0] != '\0'))
134 : 111 : return g_strdup (ev);
135 : :
136 : 8481 : lcid = GetThreadLocale ();
137 : :
138 : 8481 : if (!GetLocaleInfoW (lcid, LOCALE_SISO639LANGNAME, iso639, sizeof (iso639)) ||
139 : 8481 : !GetLocaleInfoW (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166)))
140 : 0 : return g_strdup ("C");
141 : :
142 : : /* Strip off the sorting rules, keep only the language part. */
143 : 8481 : langid = LANGIDFROMLCID (lcid);
144 : :
145 : : /* Split into language and territory part. */
146 : 8481 : primary = PRIMARYLANGID (langid);
147 : 8481 : sub = SUBLANGID (langid);
148 : :
149 : : /* Handle special cases */
150 : 8481 : switch (primary)
151 : : {
152 : : case LANG_AZERI:
153 : 12 : switch (sub)
154 : : {
155 : : case SUBLANG_AZERI_LATIN:
156 : 12 : script = "@Latn";
157 : 12 : break;
158 : : case SUBLANG_AZERI_CYRILLIC:
159 : 0 : script = "@Cyrl";
160 : 0 : break;
161 : : }
162 : 12 : break;
163 : : case LANG_SERBIAN: /* LANG_CROATIAN == LANG_SERBIAN */
164 : 0 : switch (sub)
165 : : {
166 : : case SUBLANG_SERBIAN_LATIN:
167 : : case 0x06: /* Serbian (Latin) - Bosnia and Herzegovina */
168 : 0 : script = "@Latn";
169 : 0 : break;
170 : : }
171 : 0 : break;
172 : : case LANG_UZBEK:
173 : 0 : switch (sub)
174 : : {
175 : : case SUBLANG_UZBEK_LATIN:
176 : 0 : script = "@Latn";
177 : 0 : break;
178 : : case SUBLANG_UZBEK_CYRILLIC:
179 : 0 : script = "@Cyrl";
180 : 0 : break;
181 : : }
182 : 0 : break;
183 : : }
184 : :
185 : 8481 : iso639_utf8 = g_utf16_to_utf8 (iso639, -1, NULL, NULL, NULL);
186 : 8481 : iso3166_utf8 = g_utf16_to_utf8 (iso3166, -1, NULL, NULL, NULL);
187 : :
188 : 8481 : result = g_strconcat (iso639_utf8, "_", iso3166_utf8, script, NULL);
189 : :
190 : 8481 : g_free (iso3166_utf8);
191 : 8481 : g_free (iso639_utf8);
192 : :
193 : 8481 : return result;
194 : 8592 : }
195 : :
196 : : /**
197 : : * g_win32_error_message:
198 : : * @error: Win32 error code
199 : : *
200 : : * Translate a Win32 error code into a human readable message.
201 : : *
202 : : * The error code could be as returned by
203 : : * [`GetLastError()`](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror)
204 : : * or [`WSAGetLastError()`](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsagetlasterror).
205 : : *
206 : : * The message is either language neutral, or in the thread’s language, or the
207 : : * user’s language, the system’s language, or US English (see documentation for
208 : : * [`FormatMessage()`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagew)).
209 : : * The returned string is in UTF-8.
210 : : *
211 : : * If a human readable message cannot be found for the given @error, an empty
212 : : * string is returned.
213 : : *
214 : : * Returns: (transfer full) (not nullable): newly-allocated error message
215 : : **/
216 : : gchar *
217 : 161 : g_win32_error_message (gint error)
218 : : {
219 : : gchar *retval;
220 : 161 : wchar_t *msg = NULL;
221 : : size_t nchars;
222 : :
223 : 161 : FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER
224 : : |FORMAT_MESSAGE_IGNORE_INSERTS
225 : : |FORMAT_MESSAGE_FROM_SYSTEM,
226 : 161 : NULL, error, 0,
227 : : (LPWSTR) &msg, 0, NULL);
228 : 161 : if (msg != NULL)
229 : : {
230 : 161 : nchars = wcslen (msg);
231 : :
232 : 161 : if (nchars >= 2 && msg[nchars-1] == L'\n' && msg[nchars-2] == L'\r')
233 : 161 : msg[nchars-2] = L'\0';
234 : :
235 : 161 : retval = g_utf16_to_utf8 (msg, -1, NULL, NULL, NULL);
236 : :
237 : 161 : LocalFree (msg);
238 : 161 : }
239 : : else
240 : 0 : retval = g_strdup ("");
241 : :
242 : 161 : return retval;
243 : : }
244 : :
245 : : /**
246 : : * g_win32_get_package_installation_directory_of_module:
247 : : * @hmodule: (nullable): The Win32 handle for a DLL loaded into the current process, or %NULL
248 : : *
249 : : * This function tries to determine the installation directory of a
250 : : * software package based on the location of a DLL of the software
251 : : * package.
252 : : *
253 : : * @hmodule should be the handle of a loaded DLL or %NULL. The
254 : : * function looks up the directory that DLL was loaded from. If
255 : : * @hmodule is NULL, the directory the main executable of the current
256 : : * process is looked up. If that directory's last component is "bin"
257 : : * or "lib", its parent directory is returned, otherwise the directory
258 : : * itself.
259 : : *
260 : : * It thus makes sense to pass only the handle to a "public" DLL of a
261 : : * software package to this function, as such DLLs typically are known
262 : : * to be installed in a "bin" or occasionally "lib" subfolder of the
263 : : * installation folder. DLLs that are of the dynamically loaded module
264 : : * or plugin variety are often located in more private locations
265 : : * deeper down in the tree, from which it is impossible for GLib to
266 : : * deduce the root of the package installation.
267 : : *
268 : : * The typical use case for this function is to have a DllMain() that
269 : : * saves the handle for the DLL. Then when code in the DLL needs to
270 : : * construct names of files in the installation tree it calls this
271 : : * function passing the DLL handle.
272 : : *
273 : : * Returns: a string containing the guessed installation directory for
274 : : * the software package @hmodule is from. The string is in the GLib
275 : : * file name encoding, i.e. UTF-8. The return value should be freed
276 : : * with g_free() when not needed any longer. If the function fails
277 : : * %NULL is returned.
278 : : *
279 : : * Since: 2.16
280 : : */
281 : : gchar *
282 : 151 : g_win32_get_package_installation_directory_of_module (gpointer hmodule)
283 : : {
284 : : gchar *filename;
285 : : gchar *retval;
286 : : gchar *p;
287 : : wchar_t wc_fn[MAX_PATH];
288 : :
289 : : /* NOTE: it relies that GetModuleFileNameW returns only canonical paths */
290 : 151 : if (!GetModuleFileNameW (hmodule, wc_fn, MAX_PATH))
291 : 0 : return NULL;
292 : :
293 : 151 : filename = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);
294 : :
295 : 151 : if ((p = strrchr (filename, G_DIR_SEPARATOR)) != NULL)
296 : 151 : *p = '\0';
297 : :
298 : 151 : retval = g_strdup (filename);
299 : :
300 : 151 : do
301 : : {
302 : 1359 : p = strrchr (retval, G_DIR_SEPARATOR);
303 : 1359 : if (p == NULL)
304 : 151 : break;
305 : :
306 : 1208 : *p = '\0';
307 : :
308 : 1208 : if (g_ascii_strcasecmp (p + 1, "bin") == 0 ||
309 : 1208 : g_ascii_strcasecmp (p + 1, "lib") == 0)
310 : 0 : break;
311 : 1208 : }
312 : 1208 : while (p != NULL);
313 : :
314 : 151 : if (p == NULL)
315 : : {
316 : 151 : g_free (retval);
317 : 151 : retval = filename;
318 : 151 : }
319 : : else
320 : 0 : g_free (filename);
321 : :
322 : : #ifdef G_WITH_CYGWIN
323 : : /* In Cygwin we need to have POSIX paths */
324 : : {
325 : : gchar tmp[MAX_PATH];
326 : :
327 : : cygwin_conv_to_posix_path (retval, tmp);
328 : : g_free (retval);
329 : : retval = g_strdup (tmp);
330 : : }
331 : : #endif
332 : :
333 : 151 : return retval;
334 : 151 : }
335 : :
336 : : static gchar *
337 : 0 : get_package_directory_from_module (const gchar *module_name)
338 : : {
339 : : static GHashTable *module_dirs = NULL;
340 : : G_LOCK_DEFINE_STATIC (module_dirs);
341 : 0 : HMODULE hmodule = NULL;
342 : : gchar *fn;
343 : :
344 : 0 : G_LOCK (module_dirs);
345 : :
346 : 0 : if (module_dirs == NULL)
347 : 0 : module_dirs = g_hash_table_new (g_str_hash, g_str_equal);
348 : :
349 : 0 : fn = g_hash_table_lookup (module_dirs, module_name ? module_name : "");
350 : :
351 : 0 : if (fn)
352 : : {
353 : 0 : G_UNLOCK (module_dirs);
354 : 0 : return g_strdup (fn);
355 : : }
356 : :
357 : 0 : if (module_name)
358 : : {
359 : 0 : wchar_t *wc_module_name = g_utf8_to_utf16 (module_name, -1, NULL, NULL, NULL);
360 : 0 : hmodule = GetModuleHandleW (wc_module_name);
361 : 0 : g_free (wc_module_name);
362 : :
363 : 0 : if (!hmodule)
364 : : {
365 : 0 : G_UNLOCK (module_dirs);
366 : 0 : return NULL;
367 : : }
368 : 0 : }
369 : :
370 : 0 : fn = g_win32_get_package_installation_directory_of_module (hmodule);
371 : :
372 : 0 : if (fn == NULL)
373 : : {
374 : 0 : G_UNLOCK (module_dirs);
375 : 0 : return NULL;
376 : : }
377 : :
378 : 0 : g_hash_table_insert (module_dirs, module_name ? g_strdup (module_name) : "", fn);
379 : :
380 : 0 : G_UNLOCK (module_dirs);
381 : :
382 : 0 : return g_strdup (fn);
383 : 0 : }
384 : :
385 : : /**
386 : : * g_win32_get_package_installation_directory:
387 : : * @package: (nullable): You should pass %NULL for this.
388 : : * @dll_name: (nullable): The name of a DLL that a package provides in UTF-8, or %NULL.
389 : : *
390 : : * Try to determine the installation directory for a software package.
391 : : *
392 : : * This function is deprecated. Use
393 : : * g_win32_get_package_installation_directory_of_module() instead.
394 : : *
395 : : * The use of @package is deprecated. You should always pass %NULL. A
396 : : * warning is printed if non-NULL is passed as @package.
397 : : *
398 : : * The original intended use of @package was for a short identifier of
399 : : * the package, typically the same identifier as used for
400 : : * `GETTEXT_PACKAGE` in software configured using GNU
401 : : * autotools. The function first looks in the Windows Registry for the
402 : : * value `#InstallationDirectory` in the key
403 : : * `#HKLM\Software\@package`, and if that value
404 : : * exists and is a string, returns that.
405 : : *
406 : : * It is strongly recommended that packagers of GLib-using libraries
407 : : * for Windows do not store installation paths in the Registry to be
408 : : * used by this function as that interferes with having several
409 : : * parallel installations of the library. Enabling multiple
410 : : * installations of different versions of some GLib-using library, or
411 : : * GLib itself, is desirable for various reasons.
412 : : *
413 : : * For this reason it is recommended to always pass %NULL as
414 : : * @package to this function, to avoid the temptation to use the
415 : : * Registry. In version 2.20 of GLib the @package parameter
416 : : * will be ignored and this function won't look in the Registry at all.
417 : : *
418 : : * If @package is %NULL, or the above value isn't found in the
419 : : * Registry, but @dll_name is non-%NULL, it should name a DLL loaded
420 : : * into the current process. Typically that would be the name of the
421 : : * DLL calling this function, looking for its installation
422 : : * directory. The function then asks Windows what directory that DLL
423 : : * was loaded from. If that directory's last component is "bin" or
424 : : * "lib", the parent directory is returned, otherwise the directory
425 : : * itself. If that DLL isn't loaded, the function proceeds as if
426 : : * @dll_name was %NULL.
427 : : *
428 : : * If both @package and @dll_name are %NULL, the directory from where
429 : : * the main executable of the process was loaded is used instead in
430 : : * the same way as above.
431 : : *
432 : : * Returns: a string containing the installation directory for
433 : : * @package. The string is in the GLib file name encoding,
434 : : * i.e. UTF-8. The return value should be freed with g_free() when not
435 : : * needed any longer. If the function fails %NULL is returned.
436 : : *
437 : : * Deprecated: 2.18: Pass the HMODULE of a DLL or EXE to
438 : : * g_win32_get_package_installation_directory_of_module() instead.
439 : : **/
440 : :
441 : : gchar *
442 : 0 : g_win32_get_package_installation_directory (const gchar *package,
443 : : const gchar *dll_name)
444 : : {
445 : 0 : gchar *result = NULL;
446 : :
447 : 0 : if (package != NULL)
448 : 0 : g_warning ("Passing a non-NULL package to g_win32_get_package_installation_directory() is deprecated and it is ignored.");
449 : :
450 : 0 : if (dll_name != NULL)
451 : 0 : result = get_package_directory_from_module (dll_name);
452 : :
453 : 0 : if (result == NULL)
454 : 0 : result = get_package_directory_from_module (NULL);
455 : :
456 : 0 : return result;
457 : : }
458 : :
459 : : /**
460 : : * g_win32_get_package_installation_subdirectory:
461 : : * @package: (nullable): You should pass %NULL for this.
462 : : * @dll_name: (nullable): The name of a DLL that a package provides, in UTF-8, or %NULL.
463 : : * @subdir: A subdirectory of the package installation directory, also in UTF-8
464 : : *
465 : : * This function is deprecated. Use
466 : : * g_win32_get_package_installation_directory_of_module() and
467 : : * g_build_filename() instead.
468 : : *
469 : : * Returns a newly-allocated string containing the path of the
470 : : * subdirectory @subdir in the return value from calling
471 : : * g_win32_get_package_installation_directory() with the @package and
472 : : * @dll_name parameters. See the documentation for
473 : : * g_win32_get_package_installation_directory() for more details. In
474 : : * particular, note that it is deprecated to pass anything except NULL
475 : : * as @package.
476 : : *
477 : : * Returns: a string containing the complete path to @subdir inside
478 : : * the installation directory of @package. The returned string is in
479 : : * the GLib file name encoding, i.e. UTF-8. The return value should be
480 : : * freed with g_free() when no longer needed. If something goes wrong,
481 : : * %NULL is returned.
482 : : *
483 : : * Deprecated: 2.18: Pass the HMODULE of a DLL or EXE to
484 : : * g_win32_get_package_installation_directory_of_module() instead, and
485 : : * then construct a subdirectory pathname with g_build_filename().
486 : : **/
487 : :
488 : : gchar *
489 : 0 : g_win32_get_package_installation_subdirectory (const gchar *package,
490 : : const gchar *dll_name,
491 : : const gchar *subdir)
492 : : {
493 : : gchar *prefix;
494 : : gchar *dirname;
495 : :
496 : : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
497 : 0 : prefix = g_win32_get_package_installation_directory (package, dll_name);
498 : : G_GNUC_END_IGNORE_DEPRECATIONS
499 : :
500 : 0 : dirname = g_build_filename (prefix, subdir, NULL);
501 : 0 : g_free (prefix);
502 : :
503 : 0 : return dirname;
504 : : }
505 : :
506 : : /*
507 : : * private API to call Windows's RtlGetVersion(), which may need to be called
508 : : * via GetProcAddress()
509 : : */
510 : : gboolean
511 : 147 : _g_win32_call_rtl_version (OSVERSIONINFOEXW *info)
512 : : {
513 : : static OSVERSIONINFOEXW result;
514 : : static gsize inited = 0;
515 : :
516 : 147 : g_return_val_if_fail (info != NULL, FALSE);
517 : :
518 : 147 : if (g_once_init_enter (&inited))
519 : : {
520 : : #if WINAPI_FAMILY != MODERN_API_FAMILY
521 : : /* For non-modern UI Apps, use the LoadLibraryW()/GetProcAddress() thing */
522 : : typedef NTSTATUS (WINAPI fRtlGetVersion) (PRTL_OSVERSIONINFOEXW);
523 : :
524 : : fRtlGetVersion *RtlGetVersion;
525 : 147 : HMODULE hmodule = LoadLibraryW (L"ntdll.dll");
526 : 147 : g_return_val_if_fail (hmodule != NULL, FALSE);
527 : :
528 : 147 : RtlGetVersion = (fRtlGetVersion *) GetProcAddress (hmodule, "RtlGetVersion");
529 : 147 : g_return_val_if_fail (RtlGetVersion != NULL, FALSE);
530 : : #endif
531 : :
532 : 147 : memset (&result, 0, sizeof (OSVERSIONINFOEXW));
533 : 147 : result.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEXW);
534 : :
535 : 147 : RtlGetVersion (&result);
536 : :
537 : : #if WINAPI_FAMILY != MODERN_API_FAMILY
538 : 147 : FreeLibrary (hmodule);
539 : : #endif
540 : 147 : g_once_init_leave (&inited, TRUE);
541 : 147 : }
542 : :
543 : 147 : *info = result;
544 : :
545 : 147 : return TRUE;
546 : 147 : }
547 : :
548 : : /**
549 : : * g_win32_check_windows_version:
550 : : * @major: major version of Windows
551 : : * @minor: minor version of Windows
552 : : * @spver: Windows Service Pack Level, 0 if none
553 : : * @os_type: Type of Windows OS
554 : : *
555 : : * Returns whether the version of the Windows operating system the
556 : : * code is running on is at least the specified major, minor and
557 : : * service pack versions. See MSDN documentation for the Operating
558 : : * System Version. Software that needs even more detailed version and
559 : : * feature information should use the Win32 API VerifyVersionInfo()
560 : : * directly.
561 : : *
562 : : * Successive calls of this function can be used for enabling or
563 : : * disabling features at run-time for a range of Windows versions,
564 : : * as per the VerifyVersionInfo() API documentation.
565 : : *
566 : : * Returns: %TRUE if the Windows Version is the same or greater than
567 : : * the specified major, minor and service pack versions, and
568 : : * whether the running Windows is a workstation or server edition
569 : : * of Windows, if specifically specified.
570 : : *
571 : : * Since: 2.44
572 : : **/
573 : : gboolean
574 : 147 : g_win32_check_windows_version (const gint major,
575 : : const gint minor,
576 : : const gint spver,
577 : : const GWin32OSType os_type)
578 : : {
579 : : OSVERSIONINFOEXW osverinfo;
580 : 147 : gboolean is_ver_checked = FALSE;
581 : 147 : gboolean is_type_checked = FALSE;
582 : :
583 : : /* We Only Support Checking for XP or later */
584 : 147 : g_return_val_if_fail (major >= 5 && (major <= 6 || major == 10), FALSE);
585 : 147 : g_return_val_if_fail ((major >= 5 && minor >= 1) || major >= 6, FALSE);
586 : :
587 : : /* Check for Service Pack Version >= 0 */
588 : 147 : g_return_val_if_fail (spver >= 0, FALSE);
589 : :
590 : 147 : if (!_g_win32_call_rtl_version (&osverinfo))
591 : 0 : return FALSE;
592 : :
593 : : /* check the OS and Service Pack Versions */
594 : 147 : if (osverinfo.dwMajorVersion > (DWORD) major)
595 : 0 : is_ver_checked = TRUE;
596 : 147 : else if (osverinfo.dwMajorVersion == (DWORD) major)
597 : : {
598 : 147 : if (osverinfo.dwMinorVersion > (DWORD) minor)
599 : 0 : is_ver_checked = TRUE;
600 : 147 : else if (osverinfo.dwMinorVersion == (DWORD) minor)
601 : 294 : if (osverinfo.wServicePackMajor >= (DWORD) spver)
602 : 147 : is_ver_checked = TRUE;
603 : 147 : }
604 : :
605 : : /* Check OS Type */
606 : 147 : if (is_ver_checked)
607 : : {
608 : 147 : switch (os_type)
609 : : {
610 : : case G_WIN32_OS_ANY:
611 : 147 : is_type_checked = TRUE;
612 : 147 : break;
613 : : case G_WIN32_OS_WORKSTATION:
614 : 0 : if (osverinfo.wProductType == VER_NT_WORKSTATION)
615 : 0 : is_type_checked = TRUE;
616 : 0 : break;
617 : : case G_WIN32_OS_SERVER:
618 : 0 : if (osverinfo.wProductType == VER_NT_SERVER ||
619 : 0 : osverinfo.wProductType == VER_NT_DOMAIN_CONTROLLER)
620 : 0 : is_type_checked = TRUE;
621 : 0 : break;
622 : : default:
623 : : /* shouldn't get here normally */
624 : 0 : g_warning ("Invalid os_type specified");
625 : 0 : break;
626 : : }
627 : 147 : }
628 : :
629 : 147 : return is_ver_checked && is_type_checked;
630 : 147 : }
631 : :
632 : : /**
633 : : * g_win32_get_windows_version:
634 : : *
635 : : * This function is deprecated. Use
636 : : * g_win32_check_windows_version() instead.
637 : : *
638 : : * Returns version information for the Windows operating system the
639 : : * code is running on. See MSDN documentation for the GetVersion()
640 : : * function. To summarize, the most significant bit is one on Win9x,
641 : : * and zero on NT-based systems. Since version 2.14, GLib works only
642 : : * on NT-based systems, so checking whether your are running on Win9x
643 : : * in your own software is moot. The least significant byte is 4 on
644 : : * Windows NT 4, and 5 on Windows XP. Software that needs really
645 : : * detailed version and feature information should use Win32 API like
646 : : * GetVersionEx() and VerifyVersionInfo().
647 : : *
648 : : * Returns: The version information.
649 : : *
650 : : * Deprecated: 2.44: Be aware that for Windows 8.1 and Windows Server
651 : : * 2012 R2 and later, this will return 62 unless the application is
652 : : * manifested for Windows 8.1/Windows Server 2012 R2, for example.
653 : : * MSDN stated that GetVersion(), which is used here, is subject to
654 : : * further change or removal after Windows 8.1.
655 : : **/
656 : : guint
657 : 0 : g_win32_get_windows_version (void)
658 : : {
659 : : static gsize windows_version;
660 : :
661 : 0 : if (g_once_init_enter (&windows_version))
662 : 0 : g_once_init_leave (&windows_version, GetVersion ());
663 : :
664 : 0 : return windows_version;
665 : : }
666 : :
667 : : /*
668 : : * Doesn't use gettext (and gconv), preventing recursive calls when
669 : : * g_win32_locale_filename_from_utf8() is called during
670 : : * gettext initialization.
671 : : */
672 : : static gchar *
673 : 117 : special_wchar_to_locale_encoding (wchar_t *wstring)
674 : : {
675 : : int sizeof_output;
676 : : int wctmb_result;
677 : : char *result;
678 : 117 : BOOL not_representable = FALSE;
679 : :
680 : 117 : sizeof_output = WideCharToMultiByte (CP_ACP,
681 : : WC_NO_BEST_FIT_CHARS,
682 : 117 : wstring, -1,
683 : : NULL, 0,
684 : : NULL,
685 : : ¬_representable);
686 : :
687 : 117 : if (not_representable ||
688 : 117 : sizeof_output == 0 ||
689 : 117 : sizeof_output > MAX_PATH)
690 : 0 : return NULL;
691 : :
692 : 117 : result = g_malloc0 (sizeof_output + 1);
693 : :
694 : 117 : wctmb_result = WideCharToMultiByte (CP_ACP,
695 : : WC_NO_BEST_FIT_CHARS,
696 : 117 : wstring, -1,
697 : 117 : result, sizeof_output + 1,
698 : : NULL,
699 : : ¬_representable);
700 : :
701 : 117 : if (wctmb_result == sizeof_output &&
702 : 117 : not_representable == FALSE)
703 : 117 : return result;
704 : :
705 : 0 : g_free (result);
706 : :
707 : 0 : return NULL;
708 : 117 : }
709 : :
710 : : /**
711 : : * g_win32_locale_filename_from_utf8:
712 : : * @utf8filename: a UTF-8 encoded filename.
713 : : *
714 : : * Converts a filename from UTF-8 to the system codepage.
715 : : *
716 : : * On NT-based Windows, on NTFS file systems, file names are in
717 : : * Unicode. It is quite possible that Unicode file names contain
718 : : * characters not representable in the system codepage. (For instance,
719 : : * Greek or Cyrillic characters on Western European or US Windows
720 : : * installations, or various less common CJK characters on CJK Windows
721 : : * installations.)
722 : : *
723 : : * In such a case, and if the filename refers to an existing file, and
724 : : * the file system stores alternate short (8.3) names for directory
725 : : * entries, the short form of the filename is returned. Note that the
726 : : * "short" name might in fact be longer than the Unicode name if the
727 : : * Unicode name has very short pathname components containing
728 : : * non-ASCII characters. If no system codepage name for the file is
729 : : * possible, %NULL is returned.
730 : : *
731 : : * The return value is dynamically allocated and should be freed with
732 : : * g_free() when no longer needed.
733 : : *
734 : : * Returns: The converted filename, or %NULL on conversion
735 : : * failure and lack of short names.
736 : : *
737 : : * Since: 2.8
738 : : */
739 : : gchar *
740 : 117 : g_win32_locale_filename_from_utf8 (const gchar *utf8filename)
741 : : {
742 : : gchar *retval;
743 : : wchar_t *wname;
744 : :
745 : 117 : wname = g_utf8_to_utf16 (utf8filename, -1, NULL, NULL, NULL);
746 : :
747 : 117 : if (wname == NULL)
748 : 0 : return NULL;
749 : :
750 : 117 : retval = special_wchar_to_locale_encoding (wname);
751 : :
752 : 117 : if (retval == NULL)
753 : : {
754 : : /* Conversion failed, so check if there is a 8.3 version, and use that. */
755 : : wchar_t wshortname[MAX_PATH + 1];
756 : :
757 : 0 : if (GetShortPathNameW (wname, wshortname, G_N_ELEMENTS (wshortname)))
758 : 0 : retval = special_wchar_to_locale_encoding (wshortname);
759 : 0 : }
760 : :
761 : 117 : g_free (wname);
762 : :
763 : 117 : return retval;
764 : 117 : }
765 : :
766 : : /**
767 : : * g_win32_get_command_line:
768 : : *
769 : : * Gets the command line arguments, on Windows, in the GLib filename
770 : : * encoding (ie: UTF-8).
771 : : *
772 : : * Normally, on Windows, the command line arguments are passed to main()
773 : : * in the system codepage encoding. This prevents passing filenames as
774 : : * arguments if the filenames contain characters that fall outside of
775 : : * this codepage. If such filenames are passed, then substitutions
776 : : * will occur (such as replacing some characters with '?').
777 : : *
778 : : * GLib's policy of using UTF-8 as a filename encoding on Windows was
779 : : * designed to localise the pain of dealing with filenames outside of
780 : : * the system codepage to one area: dealing with commandline arguments
781 : : * in main().
782 : : *
783 : : * As such, most GLib programs should ignore the value of argv passed to
784 : : * their main() function and call g_win32_get_command_line() instead.
785 : : * This will get the "full Unicode" commandline arguments using
786 : : * GetCommandLineW() and convert it to the GLib filename encoding (which
787 : : * is UTF-8 on Windows).
788 : : *
789 : : * The strings returned by this function are suitable for use with
790 : : * functions such as g_open() and g_file_new_for_commandline_arg() but
791 : : * are not suitable for use with g_option_context_parse(), which assumes
792 : : * that its input will be in the system codepage. The return value is
793 : : * suitable for use with g_option_context_parse_strv(), however, which
794 : : * is a better match anyway because it won't leak memory.
795 : : *
796 : : * Unlike argv, the returned value is a normal strv and can (and should)
797 : : * be freed with g_strfreev() when no longer needed.
798 : : *
799 : : * Returns: (transfer full): the commandline arguments in the GLib
800 : : * filename encoding (ie: UTF-8)
801 : : *
802 : : * Since: 2.40
803 : : **/
804 : : gchar **
805 : 8 : g_win32_get_command_line (void)
806 : : {
807 : : gchar **result;
808 : : LPWSTR *args;
809 : : gint i, n;
810 : :
811 : 8 : args = CommandLineToArgvW (GetCommandLineW(), &n);
812 : :
813 : 8 : result = g_new (gchar *, n + 1);
814 : 78 : for (i = 0; i < n; i++)
815 : 70 : result[i] = g_utf16_to_utf8 (args[i], -1, NULL, NULL, NULL);
816 : 8 : result[i] = NULL;
817 : :
818 : 8 : LocalFree (args);
819 : 8 : return result;
820 : : }
821 : :
822 : : /* Binary compatibility versions. Not for newly compiled code. */
823 : :
824 : : _GLIB_EXTERN gchar *g_win32_get_package_installation_directory_utf8 (const gchar *package,
825 : : const gchar *dll_name);
826 : :
827 : : _GLIB_EXTERN gchar *g_win32_get_package_installation_subdirectory_utf8 (const gchar *package,
828 : : const gchar *dll_name,
829 : : const gchar *subdir);
830 : :
831 : : gchar *
832 : 0 : g_win32_get_package_installation_directory_utf8 (const gchar *package,
833 : : const gchar *dll_name)
834 : : {
835 : : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
836 : 0 : return g_win32_get_package_installation_directory (package, dll_name);
837 : : G_GNUC_END_IGNORE_DEPRECATIONS
838 : : }
839 : :
840 : : gchar *
841 : 0 : g_win32_get_package_installation_subdirectory_utf8 (const gchar *package,
842 : : const gchar *dll_name,
843 : : const gchar *subdir)
844 : : {
845 : : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
846 : 0 : return g_win32_get_package_installation_subdirectory (package,
847 : 0 : dll_name,
848 : 0 : subdir);
849 : : G_GNUC_END_IGNORE_DEPRECATIONS
850 : : }
851 : :
852 : : /* This function looks up two environment
853 : : * variables, G_WIN32_ALLOC_CONSOLE and G_WIN32_ATTACH_CONSOLE.
854 : : * G_WIN32_ALLOC_CONSOLE, if set to 1, makes the process
855 : : * call AllocConsole(). This is useful for binaries that
856 : : * are compiled to run without automatically-allocated console
857 : : * (like most GUI applications).
858 : : * G_WIN32_ATTACH_CONSOLE, if set to a comma-separated list
859 : : * of one or more strings "stdout", "stdin" and "stderr",
860 : : * makes the process reopen the corresponding standard streams
861 : : * to ensure that they are attached to the files that
862 : : * GetStdHandle() returns, which, hopefully, would be
863 : : * either a file handle or a console handle.
864 : : *
865 : : * This function is called automatically when glib DLL is
866 : : * attached to a process, from DllMain().
867 : : */
868 : : void
869 : 1217 : g_console_win32_init (void)
870 : : {
871 : : struct
872 : : {
873 : : gboolean redirect;
874 : : FILE *stream;
875 : : const gchar *stream_name;
876 : : DWORD std_handle_type;
877 : : int flags;
878 : : const gchar *mode;
879 : : }
880 : : streams[] =
881 : 2434 : {
882 : 1217 : { FALSE, stdin, "stdin", STD_INPUT_HANDLE, _O_RDONLY, "rb" },
883 : 1217 : { FALSE, stdout, "stdout", STD_OUTPUT_HANDLE, 0, "wb" },
884 : 1217 : { FALSE, stderr, "stderr", STD_ERROR_HANDLE, 0, "wb" },
885 : : };
886 : :
887 : : const gchar *attach_envvar;
888 : : guint i;
889 : : gchar **attach_strs;
890 : :
891 : : /* Note: it's not a very good practice to use DllMain()
892 : : * to call any functions not in Kernel32.dll.
893 : : * The following only works if there are no weird
894 : : * circular DLL dependencies that could cause glib DllMain()
895 : : * to be called before CRT DllMain().
896 : : */
897 : :
898 : 1217 : if (g_strcmp0 (g_getenv ("G_WIN32_ALLOC_CONSOLE"), "1") == 0)
899 : 0 : AllocConsole (); /* no error handling, fails if console already exists */
900 : :
901 : 1217 : attach_envvar = g_getenv ("G_WIN32_ATTACH_CONSOLE");
902 : :
903 : 1217 : if (attach_envvar == NULL)
904 : 1217 : return;
905 : :
906 : : /* Re-use parent console, if we don't have our own.
907 : : * If we do, it will fail, so just ignore the error.
908 : : */
909 : 0 : AttachConsole (ATTACH_PARENT_PROCESS);
910 : :
911 : 0 : attach_strs = g_strsplit (attach_envvar, ",", -1);
912 : :
913 : 0 : for (i = 0; attach_strs[i]; i++)
914 : : {
915 : 0 : if (g_strcmp0 (attach_strs[i], "stdout") == 0)
916 : 0 : streams[1].redirect = TRUE;
917 : 0 : else if (g_strcmp0 (attach_strs[i], "stderr") == 0)
918 : 0 : streams[2].redirect = TRUE;
919 : 0 : else if (g_strcmp0 (attach_strs[i], "stdin") == 0)
920 : 0 : streams[0].redirect = TRUE;
921 : : else
922 : 0 : g_warning ("Unrecognized stream name %s", attach_strs[i]);
923 : 0 : }
924 : :
925 : 0 : g_strfreev (attach_strs);
926 : :
927 : 0 : for (i = 0; i < G_N_ELEMENTS (streams); i++)
928 : : {
929 : : int old_fd;
930 : : int backup_fd;
931 : : int new_fd;
932 : 0 : int preferred_fd = i;
933 : : HANDLE std_handle;
934 : 0 : errno_t errsv = 0;
935 : :
936 : 0 : if (!streams[i].redirect)
937 : 0 : continue;
938 : :
939 : 0 : if (ferror (streams[i].stream) != 0)
940 : : {
941 : 0 : g_warning ("Stream %s is in error state", streams[i].stream_name);
942 : 0 : continue;
943 : : }
944 : :
945 : 0 : std_handle = GetStdHandle (streams[i].std_handle_type);
946 : :
947 : 0 : if (std_handle == INVALID_HANDLE_VALUE)
948 : : {
949 : 0 : DWORD gle = GetLastError ();
950 : 0 : g_warning ("Standard handle for %s can't be obtained: %lu",
951 : 0 : streams[i].stream_name, gle);
952 : 0 : continue;
953 : : }
954 : :
955 : 0 : old_fd = fileno (streams[i].stream);
956 : :
957 : : /* We need the stream object to be associated with
958 : : * any valid integer fd for the code to work.
959 : : * If it isn't, reopen it with NUL (/dev/null) to
960 : : * ensure that it is.
961 : : */
962 : 0 : if (old_fd < 0)
963 : : {
964 : 0 : if (freopen ("NUL", streams[i].mode, streams[i].stream) == NULL)
965 : : {
966 : 0 : errsv = errno;
967 : 0 : g_warning ("Failed to redirect %s: %d - %s",
968 : 0 : streams[i].stream_name,
969 : 0 : errsv,
970 : 0 : strerror (errsv));
971 : 0 : continue;
972 : : }
973 : :
974 : 0 : old_fd = fileno (streams[i].stream);
975 : :
976 : 0 : if (old_fd < 0)
977 : : {
978 : 0 : g_warning ("Stream %s does not have a valid fd",
979 : 0 : streams[i].stream_name);
980 : 0 : continue;
981 : : }
982 : 0 : }
983 : :
984 : 0 : new_fd = _open_osfhandle ((intptr_t) std_handle, streams[i].flags);
985 : :
986 : 0 : if (new_fd < 0)
987 : : {
988 : 0 : g_warning ("Failed to create new fd for stream %s",
989 : 0 : streams[i].stream_name);
990 : 0 : continue;
991 : : }
992 : :
993 : 0 : backup_fd = dup (old_fd);
994 : :
995 : 0 : if (backup_fd < 0)
996 : 0 : g_warning ("Failed to backup old fd %d for stream %s",
997 : 0 : old_fd, streams[i].stream_name);
998 : :
999 : 0 : errno = 0;
1000 : :
1001 : : /* Force old_fd to be associated with the same file
1002 : : * as new_fd, i.e with the standard handle we need
1003 : : * (or, rather, with the same kernel object; handle
1004 : : * value will be different, but the kernel object
1005 : : * won't be).
1006 : : */
1007 : : /* NOTE: MSDN claims that _dup2() returns 0 on success and -1 on error,
1008 : : * POSIX claims that dup2() reurns new FD on success and -1 on error.
1009 : : * The "< 0" check satisfies the error condition for either implementation.
1010 : : */
1011 : 0 : if (_dup2 (new_fd, old_fd) < 0)
1012 : : {
1013 : 0 : errsv = errno;
1014 : 0 : g_warning ("Failed to substitute fd %d for stream %s: %d : %s",
1015 : 0 : old_fd, streams[i].stream_name, errsv, strerror (errsv));
1016 : :
1017 : 0 : _close (new_fd);
1018 : :
1019 : 0 : if (backup_fd < 0)
1020 : 0 : continue;
1021 : :
1022 : 0 : errno = 0;
1023 : :
1024 : : /* Try to restore old_fd back to its previous
1025 : : * handle, in case the _dup2() call above succeeded partially.
1026 : : */
1027 : 0 : if (_dup2 (backup_fd, old_fd) < 0)
1028 : : {
1029 : 0 : errsv = errno;
1030 : 0 : g_warning ("Failed to restore fd %d for stream %s: %d : %s",
1031 : 0 : old_fd, streams[i].stream_name, errsv, strerror (errsv));
1032 : 0 : }
1033 : :
1034 : 0 : _close (backup_fd);
1035 : :
1036 : 0 : continue;
1037 : : }
1038 : :
1039 : : /* Success, drop the backup */
1040 : 0 : if (backup_fd >= 0)
1041 : 0 : _close (backup_fd);
1042 : :
1043 : : /* Sadly, there's no way to check that preferred_fd
1044 : : * is currently valid, so we can't back it up.
1045 : : * Doing operations on invalid FDs invokes invalid
1046 : : * parameter handler, which is bad for us.
1047 : : */
1048 : 0 : if (old_fd != preferred_fd)
1049 : : /* This extra code will also try to ensure that
1050 : : * the expected file descriptors 0, 1 and 2 are
1051 : : * associated with the appropriate standard
1052 : : * handles.
1053 : : */
1054 : 0 : if (_dup2 (new_fd, preferred_fd) < 0)
1055 : 0 : g_warning ("Failed to dup fd %d into fd %d", new_fd, preferred_fd);
1056 : :
1057 : 0 : _close (new_fd);
1058 : 0 : }
1059 : 1217 : }
1060 : :
1061 : : /* This is a handle to the Vectored Exception Handler that
1062 : : * we install on library initialization. If installed correctly,
1063 : : * it will be non-NULL. Only used to later de-install the handler
1064 : : * on library de-initialization.
1065 : : */
1066 : : static void *WinVEH_handle = NULL;
1067 : :
1068 : : #define DEBUGGER_BUFFER_SIZE (MAX_PATH + 1)
1069 : : /* This is the debugger that we'll run on crash */
1070 : : static wchar_t debugger[DEBUGGER_BUFFER_SIZE];
1071 : :
1072 : : static gsize number_of_exceptions_to_catch = 0;
1073 : : static DWORD *exceptions_to_catch = NULL;
1074 : :
1075 : : static HANDLE debugger_wakeup_event = 0;
1076 : : static DWORD debugger_spawn_flags = 0;
1077 : :
1078 : : /* Copy @cmdline into @debugger, and substitute @pid for `%p`
1079 : : * and @event for `%e`.
1080 : : * If @debugger_size (in wchar_ts) is overflowed, return %FALSE.
1081 : : * Also returns %FALSE when `%` is followed by anything other
1082 : : * than `e` or `p`.
1083 : : */
1084 : : bool
1085 : 1218 : g_win32_substitute_pid_and_event (wchar_t *local_debugger,
1086 : : gsize debugger_size,
1087 : : const wchar_t *cmdline,
1088 : : DWORD pid,
1089 : : guintptr event)
1090 : : {
1091 : 1218 : gsize i = 0, dbg_i = 0;
1092 : : /* These are integers, and they can't be longer than 20 characters
1093 : : * even when they are 64-bit and in decimal notation.
1094 : : * Use 30 just to be sure.
1095 : : */
1096 : : #define STR_BUFFER_SIZE 30
1097 : 1218 : wchar_t pid_str[STR_BUFFER_SIZE] = {0};
1098 : : gsize pid_str_len;
1099 : 1218 : wchar_t event_str[STR_BUFFER_SIZE] = {0};
1100 : : gsize event_str_len;
1101 : :
1102 : 1218 : _snwprintf_s (pid_str, STR_BUFFER_SIZE, G_N_ELEMENTS (pid_str), L"%lu", pid);
1103 : 1218 : pid_str[G_N_ELEMENTS (pid_str) - 1] = 0;
1104 : 1218 : pid_str_len = wcslen (pid_str);
1105 : 1218 : _snwprintf_s (event_str, STR_BUFFER_SIZE, G_N_ELEMENTS (pid_str), L"%Iu", event);
1106 : 1218 : event_str[G_N_ELEMENTS (pid_str) - 1] = 0;
1107 : 1218 : event_str_len = wcslen (event_str);
1108 : : #undef STR_BUFFER_SIZE
1109 : :
1110 : 154066 : while (cmdline[i] != 0 && dbg_i < debugger_size)
1111 : : {
1112 : 152849 : if (cmdline[i] != L'%')
1113 : 150413 : local_debugger[dbg_i++] = cmdline[i++];
1114 : 2436 : else if (cmdline[i + 1] == L'p')
1115 : : {
1116 : 1217 : gsize j = 0;
1117 : 7242 : while (j < pid_str_len && dbg_i < debugger_size)
1118 : 6025 : local_debugger[dbg_i++] = pid_str[j++];
1119 : 1217 : i += 2;
1120 : 1217 : }
1121 : 1219 : else if (cmdline[i + 1] == L'e')
1122 : : {
1123 : 1218 : gsize j = 0;
1124 : 4892 : while (j < event_str_len && dbg_i < debugger_size)
1125 : 3674 : local_debugger[dbg_i++] = event_str[j++];
1126 : 1218 : i += 2;
1127 : 1218 : }
1128 : : else
1129 : 1 : return FALSE;
1130 : : }
1131 : 1217 : if (dbg_i < debugger_size)
1132 : 1215 : local_debugger[dbg_i] = 0;
1133 : : else
1134 : 2 : return FALSE;
1135 : :
1136 : 1215 : return TRUE;
1137 : 1218 : }
1138 : :
1139 : : static char *
1140 : 0 : copy_chars (char *buffer,
1141 : : gsize *buffer_size,
1142 : : const char *to_copy)
1143 : : {
1144 : 0 : gsize copy_count = MIN (strlen (to_copy), *buffer_size - 1);
1145 : 0 : memset (buffer, 0x20, copy_count);
1146 : 0 : strncpy_s (buffer, *buffer_size, to_copy, _TRUNCATE);
1147 : 0 : *buffer_size -= copy_count;
1148 : 0 : return &buffer[copy_count];
1149 : : }
1150 : :
1151 : : /* Handles exceptions (useful for debugging).
1152 : : * Issues a DebugBreak() call if the process is being debugged (not really
1153 : : * useful - if the process is being debugged, this handler won't be invoked
1154 : : * anyway). If it is not, runs a debugger from G_DEBUGGER env var,
1155 : : * substituting first %p in it for PID, and the first %e for the event handle -
1156 : : * that event should be set once the debugger attaches itself (otherwise the
1157 : : * only way out of WaitForSingleObject() is to time out after 1 minute).
1158 : : * For example, G_DEBUGGER can be set to the following command:
1159 : : * ```
1160 : : * gdb.exe -ex "attach %p" -ex "signal-event %e" -ex "bt" -ex "c"
1161 : : * ```
1162 : : * This will make GDB attach to the process, signal the event (GDB must be
1163 : : * recent enough for the signal-event command to be available),
1164 : : * show the backtrace and resume execution, which should make it catch
1165 : : * the exception when Windows re-raises it again.
1166 : : * The command line can't be longer than MAX_PATH (260 characters).
1167 : : *
1168 : : * This function will only stop (and run a debugger) on the following exceptions:
1169 : : * * EXCEPTION_ACCESS_VIOLATION
1170 : : * * EXCEPTION_STACK_OVERFLOW
1171 : : * * EXCEPTION_ILLEGAL_INSTRUCTION
1172 : : * To make it stop at other exceptions one should set the G_VEH_CATCH
1173 : : * environment variable to a list of comma-separated hexadecimal numbers,
1174 : : * where each number is the code of an exception that should be caught.
1175 : : * This is done to prevent GLib from breaking when Windows uses
1176 : : * exceptions to shuttle information (SetThreadName(), OutputDebugString())
1177 : : * or for control flow.
1178 : : *
1179 : : * This function deliberately avoids calling any GLib code.
1180 : : * This is done on purpose. This function can be called when the program
1181 : : * is in a bad state (crashing). It can also be called very early, as soon
1182 : : * as the handler is installed. Therefore, it's imperative that
1183 : : * it does as little as possible. Preferably, all the work that can be
1184 : : * done in advance (when the program is not crashing yet) should be done
1185 : : * in advance.
1186 : : */
1187 : : static LONG __stdcall
1188 : 0 : g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo)
1189 : : {
1190 : : EXCEPTION_RECORD *er;
1191 : : gsize i;
1192 : : STARTUPINFOW si;
1193 : : PROCESS_INFORMATION pi;
1194 : : #define ITOA_BUFFER_SIZE 100
1195 : : char itoa_buffer[ITOA_BUFFER_SIZE];
1196 : : #define DEBUG_STRING_SIZE 1024
1197 : 0 : gsize dbgs = DEBUG_STRING_SIZE;
1198 : : char debug_string[DEBUG_STRING_SIZE];
1199 : : char *dbgp;
1200 : :
1201 : 0 : if (ExceptionInfo == NULL ||
1202 : 0 : ExceptionInfo->ExceptionRecord == NULL ||
1203 : 0 : IsDebuggerPresent () ||
1204 : 0 : debugger[0] == 0)
1205 : 0 : return EXCEPTION_CONTINUE_SEARCH;
1206 : :
1207 : 0 : er = ExceptionInfo->ExceptionRecord;
1208 : :
1209 : 0 : switch (er->ExceptionCode)
1210 : : {
1211 : : case EXCEPTION_ACCESS_VIOLATION:
1212 : : case EXCEPTION_STACK_OVERFLOW:
1213 : : case EXCEPTION_ILLEGAL_INSTRUCTION:
1214 : 0 : break;
1215 : : default:
1216 : 0 : for (i = 0; i < number_of_exceptions_to_catch; i++)
1217 : 0 : if (exceptions_to_catch[i] == er->ExceptionCode)
1218 : 0 : break;
1219 : :
1220 : 0 : if (i == number_of_exceptions_to_catch)
1221 : 0 : return EXCEPTION_CONTINUE_SEARCH;
1222 : :
1223 : 0 : break;
1224 : : }
1225 : :
1226 : 0 : memset (&si, 0, sizeof (si));
1227 : 0 : memset (&pi, 0, sizeof (pi));
1228 : 0 : si.cb = sizeof (si);
1229 : :
1230 : : /* Run the debugger */
1231 : 0 : if (0 != CreateProcessW (NULL, debugger, NULL, NULL, TRUE, debugger_spawn_flags, NULL, NULL, &si, &pi))
1232 : : {
1233 : 0 : CloseHandle (pi.hProcess);
1234 : 0 : CloseHandle (pi.hThread);
1235 : : /* If successful, wait for 60 seconds on the event
1236 : : * we passed. The debugger should signal that event.
1237 : : * 60 second limit is here to prevent us from hanging
1238 : : * up forever in case the debugger does not support
1239 : : * event signalling.
1240 : : */
1241 : 0 : WaitForSingleObject (debugger_wakeup_event, 60000);
1242 : :
1243 : 0 : dbgp = &debug_string[0];
1244 : :
1245 : 0 : dbgp = copy_chars (dbgp, &dbgs, "Exception code=0x");
1246 : 0 : itoa_buffer[0] = 0;
1247 : 0 : _ui64toa_s (er->ExceptionCode, itoa_buffer, ITOA_BUFFER_SIZE, 16);
1248 : 0 : dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
1249 : 0 : dbgp = copy_chars (dbgp, &dbgs, " flags=0x");
1250 : 0 : itoa_buffer[0] = 0;
1251 : 0 : _ui64toa_s (er->ExceptionFlags, itoa_buffer, ITOA_BUFFER_SIZE, 16);
1252 : 0 : dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
1253 : 0 : dbgp = copy_chars (dbgp, &dbgs, " at 0x");
1254 : 0 : itoa_buffer[0] = 0;
1255 : 0 : _ui64toa_s ((guintptr) er->ExceptionAddress, itoa_buffer, ITOA_BUFFER_SIZE, 16);
1256 : 0 : dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
1257 : :
1258 : 0 : switch (er->ExceptionCode)
1259 : : {
1260 : : case EXCEPTION_ACCESS_VIOLATION:
1261 : 0 : dbgp = copy_chars (dbgp, &dbgs, ". Access violation - attempting to ");
1262 : 0 : if (er->ExceptionInformation[0] == 0)
1263 : 0 : dbgp = copy_chars (dbgp, &dbgs, "read data");
1264 : 0 : else if (er->ExceptionInformation[0] == 1)
1265 : 0 : dbgp = copy_chars (dbgp, &dbgs, "write data");
1266 : 0 : else if (er->ExceptionInformation[0] == 8)
1267 : 0 : dbgp = copy_chars (dbgp, &dbgs, "execute data");
1268 : : else
1269 : 0 : dbgp = copy_chars (dbgp, &dbgs, "do something bad");
1270 : 0 : dbgp = copy_chars (dbgp, &dbgs, " at address 0x");
1271 : 0 : itoa_buffer[0] = 0;
1272 : 0 : _ui64toa_s (er->ExceptionInformation[1], itoa_buffer, ITOA_BUFFER_SIZE, 16);
1273 : 0 : dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
1274 : 0 : break;
1275 : : case EXCEPTION_IN_PAGE_ERROR:
1276 : 0 : dbgp = copy_chars (dbgp, &dbgs, ". Page access violation - attempting to ");
1277 : 0 : if (er->ExceptionInformation[0] == 0)
1278 : 0 : dbgp = copy_chars (dbgp, &dbgs, "read from an inaccessible page");
1279 : 0 : else if (er->ExceptionInformation[0] == 1)
1280 : 0 : dbgp = copy_chars (dbgp, &dbgs, "write to an inaccessible page");
1281 : 0 : else if (er->ExceptionInformation[0] == 8)
1282 : 0 : dbgp = copy_chars (dbgp, &dbgs, "execute data in page");
1283 : : else
1284 : 0 : dbgp = copy_chars (dbgp, &dbgs, "do something bad with a page");
1285 : 0 : dbgp = copy_chars (dbgp, &dbgs, " at address 0x");
1286 : 0 : itoa_buffer[0] = 0;
1287 : 0 : _ui64toa_s (er->ExceptionInformation[1], itoa_buffer, ITOA_BUFFER_SIZE, 16);
1288 : 0 : dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
1289 : 0 : dbgp = copy_chars (dbgp, &dbgs, " with status ");
1290 : 0 : itoa_buffer[0] = 0;
1291 : 0 : _ui64toa_s (er->ExceptionInformation[2], itoa_buffer, ITOA_BUFFER_SIZE, 16);
1292 : 0 : dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
1293 : 0 : break;
1294 : : default:
1295 : 0 : break;
1296 : : }
1297 : :
1298 : 0 : dbgp = copy_chars (dbgp, &dbgs, "\n");
1299 : 0 : OutputDebugStringA (debug_string);
1300 : 0 : }
1301 : :
1302 : : /* Now the debugger is present, and we can try
1303 : : * resuming execution, re-triggering the exception,
1304 : : * which will be caught by debugger this time around.
1305 : : */
1306 : 0 : if (IsDebuggerPresent ())
1307 : 0 : return EXCEPTION_CONTINUE_EXECUTION;
1308 : :
1309 : 0 : return EXCEPTION_CONTINUE_SEARCH;
1310 : 0 : }
1311 : :
1312 : : static gsize
1313 : 0 : parse_catch_list (const wchar_t *catch_buffer,
1314 : : DWORD *exceptions,
1315 : : gsize num_exceptions)
1316 : : {
1317 : 0 : const wchar_t *catch_list = catch_buffer;
1318 : 0 : gsize result = 0;
1319 : 0 : gsize i = 0;
1320 : :
1321 : 0 : while (catch_list != NULL &&
1322 : 0 : catch_list[0] != 0)
1323 : : {
1324 : : unsigned long catch_code;
1325 : : wchar_t *end;
1326 : 0 : errno = 0;
1327 : 0 : catch_code = wcstoul (catch_list, &end, 16);
1328 : 0 : if (errno != NO_ERROR)
1329 : 0 : break;
1330 : 0 : catch_list = end;
1331 : 0 : if (catch_list != NULL && catch_list[0] == L',')
1332 : 0 : catch_list++;
1333 : 0 : if (exceptions && i < num_exceptions)
1334 : 0 : exceptions[i++] = catch_code;
1335 : : }
1336 : :
1337 : 0 : return result;
1338 : : }
1339 : :
1340 : : void
1341 : 1217 : g_crash_handler_win32_init (void)
1342 : : {
1343 : : wchar_t debugger_env[DEBUGGER_BUFFER_SIZE];
1344 : : #define CATCH_BUFFER_SIZE 1024
1345 : : wchar_t catch_buffer[CATCH_BUFFER_SIZE];
1346 : : SECURITY_ATTRIBUTES sa;
1347 : :
1348 : 1217 : if (WinVEH_handle != NULL)
1349 : 0 : return;
1350 : :
1351 : : /* Do not register an exception handler if we're not supposed to catch any
1352 : : * exceptions. Exception handlers are considered dangerous to use, and can
1353 : : * break advanced exception handling such as in CLRs like C# or other managed
1354 : : * code. See: http://www.windows-tech.info/13/785f590867bd6316.php
1355 : : */
1356 : 1217 : debugger_env[0] = 0;
1357 : 1217 : if (!GetEnvironmentVariableW (L"G_DEBUGGER", debugger_env, DEBUGGER_BUFFER_SIZE))
1358 : 4 : return;
1359 : :
1360 : : /* Create an inheritable event */
1361 : 1213 : memset (&sa, 0, sizeof (sa));
1362 : 1213 : sa.nLength = sizeof (sa);
1363 : 1213 : sa.bInheritHandle = TRUE;
1364 : 1213 : debugger_wakeup_event = CreateEvent (&sa, FALSE, FALSE, NULL);
1365 : :
1366 : : /* Put process ID and event handle into debugger commandline */
1367 : 1213 : if (!g_win32_substitute_pid_and_event (debugger, G_N_ELEMENTS (debugger),
1368 : 1213 : debugger_env, GetCurrentProcessId (),
1369 : 1213 : (guintptr) debugger_wakeup_event))
1370 : : {
1371 : 0 : CloseHandle (debugger_wakeup_event);
1372 : 0 : debugger_wakeup_event = 0;
1373 : 0 : debugger[0] = 0;
1374 : 0 : return;
1375 : : }
1376 : 1213 : debugger[MAX_PATH] = L'\0';
1377 : :
1378 : 1213 : catch_buffer[0] = 0;
1379 : 1213 : if (GetEnvironmentVariableW (L"G_VEH_CATCH", catch_buffer, CATCH_BUFFER_SIZE))
1380 : : {
1381 : 0 : number_of_exceptions_to_catch = parse_catch_list (catch_buffer, NULL, 0);
1382 : 0 : if (number_of_exceptions_to_catch > 0)
1383 : : {
1384 : 0 : exceptions_to_catch = g_new0 (DWORD, number_of_exceptions_to_catch);
1385 : 0 : parse_catch_list (catch_buffer, exceptions_to_catch, number_of_exceptions_to_catch);
1386 : 0 : }
1387 : 0 : }
1388 : :
1389 : 1213 : if (GetEnvironmentVariableW (L"G_DEBUGGER_OLD_CONSOLE", (wchar_t *) &debugger_spawn_flags, 1))
1390 : 2 : debugger_spawn_flags = 0;
1391 : : else
1392 : 1211 : debugger_spawn_flags = CREATE_NEW_CONSOLE;
1393 : :
1394 : 1213 : WinVEH_handle = AddVectoredExceptionHandler (0, &g_win32_veh_handler);
1395 : 1217 : }
1396 : :
1397 : : void
1398 : 1214 : g_crash_handler_win32_deinit (void)
1399 : : {
1400 : 1214 : if (WinVEH_handle != NULL)
1401 : 1210 : RemoveVectoredExceptionHandler (WinVEH_handle);
1402 : :
1403 : 1214 : WinVEH_handle = NULL;
1404 : 1214 : }
1405 : :
1406 : : /**
1407 : : * g_win32_find_helper_executable_path:
1408 : : * @executable_name: (transfer none): name of the helper executable to find
1409 : : * (something like gspawn-win64-helper.exe or gdbus.exe for example).
1410 : : * @dll_handle: handle of the DLL to use as searching base path. Pass NULL
1411 : : * to take current process executable as searching base path.
1412 : : *
1413 : : * Find an external executable path and name starting in the same folder
1414 : : * as a specified DLL or current process executable path. Helper executables
1415 : : * (like gspawn-win64-helper.exe, gspawn-win64-helper-console.exe or
1416 : : * gdbus.exe for example) are generally installed in the same folder as the
1417 : : * corresponding DLL file.
1418 : : *
1419 : : * So, if package has been correctly installed, with a dynamic build of GLib,
1420 : : * the helper executable should be in the same directory as the corresponding
1421 : : * DLL file and searching should be straightforward.
1422 : : *
1423 : : * But if built statically, DLL handle is not available and we have to start
1424 : : * searching from the directory holding current executable. It may be very
1425 : : * different from the directory containing the helper program. In order to
1426 : : * find the right helper program automatically in all common situations, we
1427 : : * use this pattern:
1428 : : *
1429 : : * current directory
1430 : : * |-- ???
1431 : : * |-- bin
1432 : : * | |-- ???
1433 : : * |-- lib
1434 : : * | |-- ???
1435 : : * |-- glib
1436 : : * | |-- ???
1437 : : * |-- gio
1438 : : * |-- ???
1439 : : *
1440 : : * starting at base searching path (DLL or current executable directory) and
1441 : : * getting up until the root path. If we cannot still find the helper program,
1442 : : * we'll rely on PATH as the last resort.
1443 : : *
1444 : : * Returns: (transfer full) (type filename) (nullable): the helper executable
1445 : : * path and name in the GLib filename encoding or NULL in case of error. It
1446 : : * should be deallocated with g_free().
1447 : : */
1448 : : gchar *
1449 : 437 : _g_win32_find_helper_walk_up (gchar *base_searching_path,
1450 : : const gchar *executable_name)
1451 : : {
1452 : : static const gchar *const subdirs[] = { "", "bin", "lib", "glib", "gio" };
1453 : : static const gsize nb_subdirs = G_N_ELEMENTS (subdirs);
1454 : :
1455 : : gchar *p;
1456 : 437 : gchar *executable_path = NULL;
1457 : : gsize i;
1458 : :
1459 : 451 : for (;;)
1460 : : {
1461 : : /* Search in subdirectories */
1462 : 562 : for (i = 0; i < nb_subdirs; ++i)
1463 : : {
1464 : : /* As this function is exclusively used on Windows, the
1465 : : * executable_path is always an absolute path. At worse, when
1466 : : * reaching the root of the filesystem, base_searching_path may
1467 : : * equal something like "[Drive letter]:" but never "/" like on
1468 : : * Linux or Mac.
1469 : : * For the peace of mind we still assert this, just in case that
1470 : : * one day someone tries to use this function on Linux or Mac.
1471 : : */
1472 : 540 : executable_path = g_build_filename (base_searching_path, subdirs[i], executable_name, NULL);
1473 : 540 : g_assert (g_path_is_absolute (executable_path));
1474 : 540 : if (g_file_test (executable_path, G_FILE_TEST_IS_REGULAR))
1475 : 429 : break;
1476 : :
1477 : 111 : g_free (executable_path);
1478 : 111 : executable_path = NULL;
1479 : 111 : }
1480 : :
1481 : 451 : if (executable_path != NULL)
1482 : 429 : break;
1483 : :
1484 : : /* Let's get one directory level up */
1485 : 22 : p = strrchr (base_searching_path, G_DIR_SEPARATOR);
1486 : 22 : if (p == NULL)
1487 : 4 : break;
1488 : :
1489 : : /* Don't strip past the root of a UNC path ("\\\\server\\share").
1490 : : * For drive-letter paths ("C:") the strrchr() == NULL check above
1491 : : * already prevents over-stripping. UNC paths start with a
1492 : : * separator, so without this guard the loop strips past
1493 : : * "\\server\\share" down to "\\server", "\", and finally "" (empty),
1494 : : * which makes g_build_filename() below produce a non-absolute path
1495 : : * and trips the g_assert() above. The separator we're about to strip
1496 : : * is the first one after the server name, i.e. the one preceding the
1497 : : * share name.
1498 : : */
1499 : 18 : if (G_IS_DIR_SEPARATOR (base_searching_path[0]) &&
1500 : 18 : G_IS_DIR_SEPARATOR (base_searching_path[1]) &&
1501 : 18 : strchr (base_searching_path + 2, G_DIR_SEPARATOR) == p)
1502 : 4 : break;
1503 : :
1504 : 14 : *p = '\0';
1505 : : }
1506 : :
1507 : 437 : g_free (base_searching_path);
1508 : 437 : return executable_path;
1509 : : }
1510 : :
1511 : : gchar *
1512 : 428 : g_win32_find_helper_executable_path (const gchar *executable_name, void *dll_handle)
1513 : : {
1514 : : DWORD module_path_len;
1515 : 428 : wchar_t module_path[MAX_PATH + 2] = { 0 };
1516 : : gchar *base_searching_path;
1517 : : gchar *p;
1518 : : gchar *executable_path;
1519 : :
1520 : 428 : g_return_val_if_fail (executable_name && *executable_name, NULL);
1521 : :
1522 : 428 : module_path_len = GetModuleFileNameW (dll_handle, module_path, MAX_PATH + 1);
1523 : : /* The > MAX_PATH check prevents truncated module path usage */
1524 : 428 : if (module_path_len == 0 || module_path_len > MAX_PATH)
1525 : 0 : return NULL;
1526 : :
1527 : 428 : base_searching_path = g_utf16_to_utf8 (module_path, -1, NULL, NULL, NULL);
1528 : 428 : if (base_searching_path == NULL)
1529 : 0 : return NULL;
1530 : :
1531 : 428 : p = strrchr (base_searching_path, G_DIR_SEPARATOR);
1532 : 428 : if (p == NULL)
1533 : : {
1534 : 0 : g_free (base_searching_path);
1535 : 0 : return NULL;
1536 : : }
1537 : 428 : *p = '\0';
1538 : :
1539 : 428 : executable_path = _g_win32_find_helper_walk_up (base_searching_path, executable_name);
1540 : :
1541 : 428 : if (executable_path == NULL)
1542 : : {
1543 : : /* Search in system PATH */
1544 : 0 : executable_path = g_find_program_in_path (executable_name);
1545 : 0 : if (executable_path == NULL)
1546 : 0 : executable_path = g_strdup (executable_name);
1547 : 0 : }
1548 : :
1549 : 428 : return executable_path;
1550 : 428 : }
1551 : :
1552 : : /** < private >
1553 : : *
1554 : : * g_win32_file_stream_is_console_output:
1555 : : *
1556 : : * @stream: a FILE stream
1557 : : *
1558 : : * Checks if the given FILE stream refers to a Win32 console
1559 : : * screen buffer.
1560 : : */
1561 : : bool
1562 : 27656 : g_win32_file_stream_is_console_output (FILE *stream)
1563 : : {
1564 : 27656 : int fd = _fileno (stream);
1565 : :
1566 : 27656 : if (fd < 0)
1567 : : {
1568 : : /* On Windows, FILE streams can be 'open' but not associated
1569 : : * with any file descriptor. As far as I know, that can only
1570 : : * happen for the standard streams stdin, stdout, and stderr.
1571 : : * Window processes can have NULL standard HANDLEs, but the
1572 : : * C standard states that standard streams must be 'open'
1573 : : * when main is called. So, on Windows, _fileno() is expected
1574 : : * to return values < 0 even without errors.
1575 : : */
1576 : 0 : return false;
1577 : : }
1578 : :
1579 : : /* We call _isatty() first because it's a very fast check (just
1580 : : * checking an internal flag). However, the _isatty check comprehends
1581 : : * output devices like serial ports, so we check if the output is
1582 : : * really a win32 console with g_win32_handle_is_console_output.
1583 : : */
1584 : 27723 : return _isatty (fd) &&
1585 : 67 : g_win32_handle_is_console_output ((HANDLE)_get_osfhandle (fd));
1586 : 27656 : }
1587 : :
1588 : : /** < private >
1589 : : *
1590 : : * g_win32_handle_is_console_output:
1591 : : *
1592 : : * @handle: the given HANDLE
1593 : : *
1594 : : * Checks if the given HANDLE refers to a Win32 console screen
1595 : : * buffer (output HANDLE).
1596 : : */
1597 : : bool
1598 : 72 : g_win32_handle_is_console_output (HANDLE handle)
1599 : : {
1600 : : /* MSDN suggests using GetConsoleMode() to check if a HANDLE refers to
1601 : : * the console. However GetConsoleMode() requires read access rights
1602 : : * (FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA on Windows 10),
1603 : : * and output HANDLEs may have been opened with write rights only. To
1604 : : * overcome that, we use WriteConsole() with a zero characters count.
1605 : : */
1606 : 72 : const wchar_t *dummy = L"";
1607 : 72 : if (!WriteConsole (handle, dummy, 0, NULL, NULL))
1608 : : {
1609 : 70 : DWORD code = GetLastError ();
1610 : :
1611 : 70 : if (code != ERROR_INVALID_FUNCTION && code != ERROR_INVALID_HANDLE)
1612 : : {
1613 : 0 : WIN32_API_FAILED ("WriteConsole");
1614 : 0 : }
1615 : :
1616 : 70 : return false;
1617 : : }
1618 : :
1619 : 2 : return true;
1620 : 72 : }
1621 : :
1622 : : /*
1623 : : * g_win32_handle_is_socket:
1624 : : * @h: a win32 HANDLE
1625 : : *
1626 : : * Returns: %TRUE if the handle is a `SOCKET`.
1627 : : */
1628 : : gboolean
1629 : 2164 : g_win32_handle_is_socket (HANDLE h)
1630 : : {
1631 : 2164 : int option = 0;
1632 : 2164 : int optlen = sizeof (option);
1633 : :
1634 : : /* according to: https://stackoverflow.com/a/50981652/1277510, this is reasonable */
1635 : 2164 : if (getsockopt ((SOCKET) h, SOL_SOCKET, SO_DEBUG, (char *) &option, &optlen) == SOCKET_ERROR)
1636 : 2142 : return FALSE;
1637 : :
1638 : 22 : return TRUE;
1639 : 2164 : }
1640 : :
1641 : : /*
1642 : : * g_win32_reopen_noninherited:
1643 : : * @fd: (transfer full): A file descriptor
1644 : : * @mode: _open_osfhandle flags
1645 : : * @error: A location to return an error of type %G_FILE_ERROR
1646 : : *
1647 : : * Reopen the given @fd with `_O_NOINHERIT`.
1648 : : *
1649 : : * The @fd is closed on success.
1650 : : *
1651 : : * Returns: (transfer full): The new file-descriptor, or -1 on error.
1652 : : */
1653 : : int
1654 : 2138 : g_win32_reopen_noninherited (int fd,
1655 : : int mode,
1656 : : GError **error)
1657 : : {
1658 : : HANDLE h;
1659 : : HANDLE duph;
1660 : : int dupfd, errsv;
1661 : :
1662 : 2138 : h = (HANDLE) _get_osfhandle (fd);
1663 : 2138 : errsv = errno;
1664 : :
1665 : 2138 : if (h == INVALID_HANDLE_VALUE)
1666 : : {
1667 : 0 : const char *emsg = g_strerror (errsv);
1668 : 0 : g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
1669 : 0 : "_get_osfhandle() failed: %s", emsg);
1670 : 0 : return -1;
1671 : : }
1672 : :
1673 : 2138 : if (g_win32_handle_is_socket (h))
1674 : : {
1675 : : WSAPROTOCOL_INFO info;
1676 : :
1677 : 0 : if (WSADuplicateSocket ((SOCKET) h,
1678 : 0 : GetCurrentProcessId (),
1679 : : &info))
1680 : : {
1681 : 0 : gchar *emsg = g_win32_error_message (WSAGetLastError ());
1682 : 0 : g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
1683 : 0 : "WSADuplicateSocket() failed: %s", emsg);
1684 : 0 : g_free (emsg);
1685 : 0 : return -1;
1686 : : }
1687 : :
1688 : 0 : duph = (HANDLE) WSASocket (FROM_PROTOCOL_INFO,
1689 : : FROM_PROTOCOL_INFO,
1690 : : FROM_PROTOCOL_INFO,
1691 : : &info, 0, 0);
1692 : 0 : if (duph == (HANDLE) INVALID_SOCKET)
1693 : : {
1694 : 0 : gchar *emsg = g_win32_error_message (WSAGetLastError ());
1695 : 0 : g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
1696 : 0 : "WSASocket() failed: %s", emsg);
1697 : 0 : g_free (emsg);
1698 : 0 : return -1;
1699 : : }
1700 : 0 : }
1701 : 4276 : else if (DuplicateHandle (GetCurrentProcess (), h,
1702 : 2138 : GetCurrentProcess (), &duph,
1703 : 2138 : 0, FALSE, DUPLICATE_SAME_ACCESS) == 0)
1704 : : {
1705 : 0 : char *emsg = g_win32_error_message (GetLastError ());
1706 : 0 : g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
1707 : 0 : "DuplicateHandle() failed: %s", emsg);
1708 : 0 : g_free (emsg);
1709 : 0 : return -1;
1710 : : }
1711 : :
1712 : : /* the duph ownership is transferred to dupfd */
1713 : 2138 : dupfd = _open_osfhandle ((gintptr) duph, mode | _O_NOINHERIT);
1714 : 2138 : if (dupfd < 0)
1715 : : {
1716 : 0 : g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
1717 : : "_open_osfhandle() failed");
1718 : 0 : CloseHandle (duph);
1719 : 0 : return -1;
1720 : : }
1721 : :
1722 : 2138 : if (!g_close (fd, error))
1723 : : {
1724 : : /* ignore extra errors in this case */
1725 : 0 : g_close (dupfd, NULL);
1726 : 0 : return -1;
1727 : : }
1728 : :
1729 : 2138 : return dupfd;
1730 : 2138 : }
1731 : :
1732 : : bool
1733 : 2 : g_win32_error_message_in_place (DWORD code,
1734 : : wchar_t *buffer,
1735 : : size_t wchars_count)
1736 : : {
1737 : 2 : const DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM |
1738 : : FORMAT_MESSAGE_IGNORE_INSERTS;
1739 : : DWORD wchars_written;
1740 : :
1741 : 2 : g_assert (wchars_count > 0);
1742 : :
1743 : 2 : wchars_written = FormatMessage (flags, NULL, code, 0, buffer, wchars_count - 1, NULL);
1744 : 2 : if (wchars_written == 0)
1745 : 1 : return false;
1746 : :
1747 : 1 : g_assert (wchars_written < wchars_count);
1748 : :
1749 : 1 : if (wchars_written >= 2)
1750 : : {
1751 : 2 : wchars_written -= (buffer[wchars_written - 1] == L'\n') +
1752 : 1 : (buffer[wchars_written - 2] == L'\r');
1753 : 1 : }
1754 : :
1755 : 1 : buffer[wchars_written] = L'\0';
1756 : :
1757 : 1 : return true;
1758 : 2 : }
1759 : :
1760 : : /** < private >
1761 : : *
1762 : : * g_win32_api_failed:
1763 : : *
1764 : : * @where: location in the source code
1765 : : * @api: name of the failing API
1766 : : * @code: a Windows error code or a failing HRESULT
1767 : : *
1768 : : * Prints a warning about the failing API with an extended
1769 : : * error description (see g_win32_error_message).
1770 : : */
1771 : : void
1772 : 0 : g_win32_api_failed_with_code (const char *where,
1773 : : const char *api,
1774 : : DWORD code)
1775 : : {
1776 : : wchar_t description[500];
1777 : :
1778 : 0 : if (g_win32_error_message_in_place (code, description, G_N_ELEMENTS (description)))
1779 : 0 : g_warning ("%s failed: %S", api, description);
1780 : : else
1781 : 0 : g_warning ("%s failed with error code %u", api, (unsigned int) code);
1782 : 0 : }
1783 : :
1784 : : /** < private >
1785 : : *
1786 : : * g_win32_api_failed:
1787 : : *
1788 : : * @where: location in the source code
1789 : : * @api: name of the failing API
1790 : : *
1791 : : * Prints a warning about the failing API with an extended
1792 : : * error description (see g_win32_error_message).
1793 : : */
1794 : : void
1795 : 0 : g_win32_api_failed (const char *where,
1796 : : const char *api)
1797 : : {
1798 : 0 : g_win32_api_failed_with_code (where, api, GetLastError ());
1799 : 0 : }
|