From 5a88294fc39579d763770f31b8e6d48a4c133c89 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Fri, 8 Apr 2005 12:03:16 +0000 Subject: [PATCH] Improve g_get_system_data_dirs() on Windows. A library that calls 2005-04-08 Tor Lillqvist Improve g_get_system_data_dirs() on Windows. A library that calls g_get_system_data_dirs() might be installed in a different top-level prefix than GLib or the application being run. * glib/gutils.h (g_win32_get_system_data_dirs): New static function defined in this header. Calls g_win32_get_system_data_dirs_for_module() passing the address of itself as parameter. g_get_system_data_dirs() is #defined as this function. * glib/gutils.c (g_win32_get_system_data_dirs_for_module): New function. If the address parameter is non-NULL, the corresponding module's installation location is used for one of the returned path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS, glib top-level and application top-level folders. (g_get_system_data_dirs): Now just for backward compatibility on Win32. Just call g_win32_get_system_data_dirs_for_module(NULL). * glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module. --- ChangeLog | 20 ++++ ChangeLog.pre-2-10 | 20 ++++ ChangeLog.pre-2-12 | 20 ++++ ChangeLog.pre-2-8 | 20 ++++ glib/glib.symbols | 3 + glib/gutils.c | 262 ++++++++++++++++++++++++++++++++++++++++------------- glib/gutils.h | 11 +++ 7 files changed, 291 insertions(+), 65 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5acd78f..6432d59 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,25 @@ 2005-04-08 Tor Lillqvist + Improve g_get_system_data_dirs() on Windows. A library that calls + g_get_system_data_dirs() might be installed in a different + top-level prefix than GLib or the application being run. + + * glib/gutils.h (g_win32_get_system_data_dirs): New static + function defined in this header. Calls + g_win32_get_system_data_dirs_for_module() passing the address of + itself as parameter. g_get_system_data_dirs() is #defined as this + function. + + * glib/gutils.c (g_win32_get_system_data_dirs_for_module): New + function. If the address parameter is non-NULL, the corresponding + module's installation location is used for one of the returned + path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS, + glib top-level and application top-level folders. + (g_get_system_data_dirs): Now just for backward compatibility on + Win32. Just call g_win32_get_system_data_dirs_for_module(NULL). + + * glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module. + * glib/gutils.c (read_aliases): Not used on Windows. (unalias_lang): Don't do anything on Windows, there is no /usr/share/locale/locale.alias file.. diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 5acd78f..6432d59 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,5 +1,25 @@ 2005-04-08 Tor Lillqvist + Improve g_get_system_data_dirs() on Windows. A library that calls + g_get_system_data_dirs() might be installed in a different + top-level prefix than GLib or the application being run. + + * glib/gutils.h (g_win32_get_system_data_dirs): New static + function defined in this header. Calls + g_win32_get_system_data_dirs_for_module() passing the address of + itself as parameter. g_get_system_data_dirs() is #defined as this + function. + + * glib/gutils.c (g_win32_get_system_data_dirs_for_module): New + function. If the address parameter is non-NULL, the corresponding + module's installation location is used for one of the returned + path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS, + glib top-level and application top-level folders. + (g_get_system_data_dirs): Now just for backward compatibility on + Win32. Just call g_win32_get_system_data_dirs_for_module(NULL). + + * glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module. + * glib/gutils.c (read_aliases): Not used on Windows. (unalias_lang): Don't do anything on Windows, there is no /usr/share/locale/locale.alias file.. diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 5acd78f..6432d59 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,5 +1,25 @@ 2005-04-08 Tor Lillqvist + Improve g_get_system_data_dirs() on Windows. A library that calls + g_get_system_data_dirs() might be installed in a different + top-level prefix than GLib or the application being run. + + * glib/gutils.h (g_win32_get_system_data_dirs): New static + function defined in this header. Calls + g_win32_get_system_data_dirs_for_module() passing the address of + itself as parameter. g_get_system_data_dirs() is #defined as this + function. + + * glib/gutils.c (g_win32_get_system_data_dirs_for_module): New + function. If the address parameter is non-NULL, the corresponding + module's installation location is used for one of the returned + path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS, + glib top-level and application top-level folders. + (g_get_system_data_dirs): Now just for backward compatibility on + Win32. Just call g_win32_get_system_data_dirs_for_module(NULL). + + * glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module. + * glib/gutils.c (read_aliases): Not used on Windows. (unalias_lang): Don't do anything on Windows, there is no /usr/share/locale/locale.alias file.. diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 5acd78f..6432d59 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,5 +1,25 @@ 2005-04-08 Tor Lillqvist + Improve g_get_system_data_dirs() on Windows. A library that calls + g_get_system_data_dirs() might be installed in a different + top-level prefix than GLib or the application being run. + + * glib/gutils.h (g_win32_get_system_data_dirs): New static + function defined in this header. Calls + g_win32_get_system_data_dirs_for_module() passing the address of + itself as parameter. g_get_system_data_dirs() is #defined as this + function. + + * glib/gutils.c (g_win32_get_system_data_dirs_for_module): New + function. If the address parameter is non-NULL, the corresponding + module's installation location is used for one of the returned + path names, in addition to the COMMON_APPDATA, COMMON_DOCUMENTS, + glib top-level and application top-level folders. + (g_get_system_data_dirs): Now just for backward compatibility on + Win32. Just call g_win32_get_system_data_dirs_for_module(NULL). + + * glib/glib.symbols: Add g_win32_get_system_data_dirs_for_module. + * glib/gutils.c (read_aliases): Not used on Windows. (unalias_lang): Don't do anything on Windows, there is no /usr/share/locale/locale.alias file.. diff --git a/glib/glib.symbols b/glib/glib.symbols index 458b5a4..492b238 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -1174,6 +1174,9 @@ g_get_real_name_utf8 #endif g_get_system_config_dirs g_get_system_data_dirs +#ifdef G_OS_WIN32 +g_win32_get_system_data_dirs_for_module +#endif g_get_tmp_dir PRIVATE #ifdef G_OS_WIN32 g_get_tmp_dir_utf8 diff --git a/glib/gutils.c b/glib/gutils.c index 282a0e5..aa44499 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -70,6 +70,10 @@ # define STRICT /* Strict typing, please */ # include # undef STRICT +# ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS +# define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2 +# define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4 +# endif # include /* For UNLEN */ #endif /* G_PLATFORM_WIN32 */ @@ -2043,6 +2047,174 @@ g_get_user_cache_dir (void) return cache_dir; } +#ifdef G_OS_WIN32 + +#undef g_get_system_data_dirs + +static HMODULE +get_module_for_address (gconstpointer address) +{ + /* Holds the g_utils_global lock */ + + static gboolean beenhere = FALSE; + typedef BOOL (WINAPI *t_GetModuleHandleExA) (DWORD, LPCTSTR, HMODULE *); + static t_GetModuleHandleExA p_GetModuleHandleExA = NULL; + HMODULE hmodule; + + if (!address) + return NULL; + + if (!beenhere) + { + p_GetModuleHandleExA = + (t_GetModuleHandleExA) GetProcAddress (LoadLibrary ("kernel32.dll"), + "GetModuleHandleExA"); + beenhere = TRUE; + } + + if (p_GetModuleHandleExA == NULL || + !(*p_GetModuleHandleExA) (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + address, &hmodule)) + { + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery (address, &mbi, sizeof (mbi)); + hmodule = (HMODULE) mbi.AllocationBase; + } + + return hmodule; +} + +static gchar * +get_module_share_dir (gconstpointer address) +{ + HMODULE hmodule; + gchar *filename = NULL; + gchar *p, *retval; + + hmodule = get_module_for_address (address); + if (hmodule == NULL) + return NULL; + + if (G_WIN32_IS_NT_BASED ()) + { + wchar_t wfilename[MAX_PATH]; + if (GetModuleFileNameW (hmodule, wfilename, G_N_ELEMENTS (wfilename))) + filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL); + } + else + { + char cpfilename[MAX_PATH]; + if (GetModuleFileNameA (hmodule, cpfilename, G_N_ELEMENTS (cpfilename))) + filename = g_locale_to_utf8 (cpfilename, -1, NULL, NULL, NULL); + } + + if (filename == NULL) + return NULL; + + if ((p = strrchr (filename, G_DIR_SEPARATOR)) != NULL) + *p = '\0'; + + p = strrchr (filename, G_DIR_SEPARATOR); + if (p && (g_ascii_strcasecmp (p + 1, "bin") == 0)) + *p = '\0'; + + retval = g_build_filename (filename, "share", NULL); + g_free (filename); + + return retval; +} + +G_CONST_RETURN gchar * G_CONST_RETURN * +g_win32_get_system_data_dirs_for_module (gconstpointer address) +{ + GArray *data_dirs; + HMODULE hmodule; + static GHashTable *per_module_data_dirs = NULL; + gchar **retval; + gchar *p; + + if (address) + { + G_LOCK (g_utils_global); + hmodule = get_module_for_address (address); + if (hmodule != NULL) + { + if (per_module_data_dirs == NULL) + per_module_data_dirs = g_hash_table_new (NULL, NULL); + else + { + retval = g_hash_table_lookup (per_module_data_dirs, hmodule); + + if (retval != NULL) + { + G_UNLOCK (g_utils_global); + return (G_CONST_RETURN gchar * G_CONST_RETURN *) retval; + } + } + } + } + + data_dirs = g_array_new (TRUE, TRUE, sizeof (char *)); + + /* Documents and Settings\All Users\Application Data */ + p = get_special_folder (CSIDL_COMMON_APPDATA); + if (p) + g_array_append_val (data_dirs, p); + + /* Documents and Settings\All Users\Documents */ + p = get_special_folder (CSIDL_COMMON_DOCUMENTS); + if (p) + g_array_append_val (data_dirs, p); + + /* Using the above subfolders of Documents and Settings perhaps + * makes sense from a Windows perspective. + * + * But looking at the actual use cases of this function in GTK+ + * and GNOME software, what we really want is the "share" + * subdirectory of the installation directory for the package + * our caller is a part of. + * + * The address parameter, if non-NULL, points to a function in the + * calling module. Use that to determine that module's installation + * folder, and use its "share" subfolder. + * + * Additionally, also use the "share" subfolder of the installation + * locations of GLib and the .exe file being run. + * + * To guard against none of the above being what is really wanted, + * callers of this function should have Win32-specific code to look + * up their installation folder themselves, and handle a subfolder + * "share" of it in the same way as the folders returned from this + * function. + */ + + p = get_module_share_dir (address); + if (p) + g_array_append_val (data_dirs, p); + + p = g_win32_get_package_installation_subdirectory (NULL, dll_name, "share"); + if (p) + g_array_append_val (data_dirs, p); + + p = g_win32_get_package_installation_subdirectory (NULL, NULL, "share"); + if (p) + g_array_append_val (data_dirs, p); + + retval = (gchar **) g_array_free (data_dirs, FALSE); + + if (address) + { + if (hmodule != NULL) + g_hash_table_insert (per_module_data_dirs, hmodule, retval); + G_UNLOCK (g_utils_global); + } + + return (G_CONST_RETURN gchar * G_CONST_RETURN *) retval; +} + +#endif + /** * g_get_system_data_dirs: * @@ -2053,6 +2225,28 @@ g_get_user_cache_dir (void) * the * XDG Base Directory Specification * + * On Windows the first elements in the list are the Application Data + * and Documents folders for All Users. (These can be determined only + * on Windows 2000 or later and are not present in the list on other + * Windows versions.) See documentation for CSIDL_COMMON_APPDATA and + * CSIDL_COMMON_DOCUMENTS. + * + * Then follows the "share" subfolder in the installation folder for + * the package containing the DLL that calls this function, if it can + * be determined. + * + * Finally the list contains the "share" subfolder in the installation + * folder for GLib, and in the installation folder for the package the + * application's .exe file belongs to. + * + * The installation folders above are determined by looking up the + * folder where the module (DLL or EXE) in question is located. If the + * folder's name is "bin", its parent is used, otherwise the folder + * itself. + * + * Note that on Windows the returned list can vary depending on where + * this function is called. + * * Return value: a %NULL-terminated array of strings owned by GLib that must * not be modified or freed. * Since: 2.6 @@ -2060,78 +2254,16 @@ g_get_user_cache_dir (void) G_CONST_RETURN gchar * G_CONST_RETURN * g_get_system_data_dirs (void) { - gchar *data_dirs, **data_dir_vector; + gchar **data_dir_vector; G_LOCK (g_utils_global); if (!g_system_data_dirs) { #ifdef G_OS_WIN32 - gchar *glib_top_share_dir, *exe_top_share_dir; - - /* Documents and Settings\All Users\Application Data */ - char *appdata = get_special_folder (CSIDL_COMMON_APPDATA); - /* Documents and Settings\All Users\Documents */ - char *docs = get_special_folder (CSIDL_COMMON_DOCUMENTS); - - if (appdata && docs) - { - data_dirs = g_strconcat (appdata, - G_SEARCHPATH_SEPARATOR_S, - docs, - NULL); - g_free (appdata); - g_free (docs); - } - else if (appdata) - data_dirs = appdata; - else if (docs) - data_dirs = docs; - else - data_dirs = g_strdup (""); - - /* Using the above subfolders of Documents and Settings perhaps - * makes sense from a Windows perspective. - * - * But looking at the actual use cases of this function in GTK+ - * and GNOME software, what we really want is the "share" - * subdirectory of the installation directory for the package - * our caller is a part of. - * - * As we don't know who calls us, punt, and use the installation - * location of GLib, and of the .exe file being run. To guard - * against neither of those being what we really want, callers - * of this function should have Win32-specific code to look up - * their installation folder themselves, and handle a subfolder - * "share" of it in the same way as the folders returned from - * this function. - */ - glib_top_share_dir = g_win32_get_package_installation_subdirectory (NULL, dll_name, "share"); - - if (glib_top_share_dir) - { - gchar *tem = data_dirs; - data_dirs = g_strconcat (data_dirs, G_SEARCHPATH_SEPARATOR_S, - glib_top_share_dir, NULL); - g_free (tem); - g_free (glib_top_share_dir); - } - - exe_top_share_dir = g_win32_get_package_installation_subdirectory (NULL, NULL, "share"); - - if (exe_top_share_dir) - { - gchar *tem = data_dirs; - data_dirs = g_strconcat (data_dirs, G_SEARCHPATH_SEPARATOR_S, - exe_top_share_dir, NULL); - g_free (tem); - g_free (exe_top_share_dir); - } - - data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); - g_free (data_dirs); + data_dir_vector = (gchar **) g_win32_get_system_data_dirs_for_module (NULL); #else - data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS"); + gchar *data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS"); if (!data_dirs || !data_dirs[0]) data_dirs = "/usr/local/share/:/usr/share/"; diff --git a/glib/gutils.h b/glib/gutils.h index 27de963..020ccd3 100644 --- a/glib/gutils.h +++ b/glib/gutils.h @@ -125,6 +125,17 @@ G_CONST_RETURN gchar* g_get_user_data_dir (void); G_CONST_RETURN gchar* g_get_user_config_dir (void); G_CONST_RETURN gchar* g_get_user_cache_dir (void); G_CONST_RETURN gchar* G_CONST_RETURN * g_get_system_data_dirs (void); + +#ifdef G_OS_WIN32 +G_CONST_RETURN gchar* G_CONST_RETURN * g_win32_get_system_data_dirs_for_module (gconstpointer address); +static G_CONST_RETURN gchar * G_CONST_RETURN * +g_win32_get_system_data_dirs (void) +{ + return g_win32_get_system_data_dirs_for_module (&g_win32_get_system_data_dirs); +} +#define g_get_system_data_dirs g_win32_get_system_data_dirs +#endif + G_CONST_RETURN gchar* G_CONST_RETURN * g_get_system_config_dirs (void); G_CONST_RETURN gchar* G_CONST_RETURN * g_get_language_names (void); -- 2.7.4