[glib: 2/3] GWin32AppInfo: Support verbs other than "open"
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 2/3] GWin32AppInfo: Support verbs other than "open"
- Date: Thu, 1 Oct 2020 16:37:37 +0000 (UTC)
commit 106e78af97ef368d29d8037a6b8c10dcce64cbd5
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date: Sun Dec 29 11:35:49 2019 +0000
GWin32AppInfo: Support verbs other than "open"
This combines a massive code re-folding with functionlity expansion
that allows us to track multiple verbs per handler or per application.
Also fixes a few issues and removes a function that made no sense.
gio/gwin32appinfo.c | 3883 +++++++++++++++++++++++------------------------
gio/gwin32registrykey.c | 4 +-
gio/gwin32registrykey.h | 4 +-
3 files changed, 1876 insertions(+), 2015 deletions(-)
---
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index ec3e2b70c..612373a6a 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -35,6 +35,9 @@
#include <windows.h>
+#include <glib/gstdioprivate.h>
+#include "glib-private.h"
+
/* We need to watch 8 places:
* 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
* (anything below that key)
@@ -55,15 +58,73 @@
* 7) HKEY_CLASSES_ROOT (only its subkeys)
* On change: re-enumerate subkeys, try to filter out wrong names.
*
+ *
+ * About verbs. A registry key (the name of that key is known as ProgID)
+ * can contain a "shell" subkey, which can then contain a number of verb
+ * subkeys (the most common being the "open" verb), and each of these
+ * contains a "command" subkey, which has a default string value that
+ * is the command to be run.
+ * Most ProgIDs are in HKEY_CLASSES_ROOT, but some are nested deeper in
+ * the registry (such as HKEY_CURRENT_USER\\Software\\<softwarename>).
+ *
+ * Verb selection works like this (according to https://docs.microsoft.com/en-us/windows/win32/shell/context
):
+ * 1) If "open" verb is available, that verb is used.
+ * 2) If the Shell subkey has a default string value, and if a verb subkey
+ * with that name exists, that verb is used.
+ * 3) The first subkey found in the list of verb subkeys is used.
+ * 4) The "openwith" verb is used
+ *
+ * Testing suggests that Windows never reaches the point 4 in any realistic
+ * circumstances. If a "command" subkey is missing for a verb, or if it has
+ * an empty string as its default value, the app launch fails
+ * (the "openwith" verb is not used, even if it's present).
+ * If the command is present, but is not valid (runs nonexisting executable,
+ * for example), then other verbs are not checked.
+ * It seems that when the documentation said "openwith verb", it meant
+ * that Windows invokes the default "Open with..." dialog (it does not
+ * look at the "openwith" verb subkey, even if it's there).
+ * If a verb subkey that is supposed to be used is present, but it lacks
+ * a command subkey, an error message is shown and nothing else happens.
*/
+#define _verb_idx(array,index) ((GWin32AppInfoShellVerb *) g_ptr_array_index (array, index))
+
+#define _lookup_by_verb(array, verb, dst, itemtype) do { \
+ gsize _index; \
+ itemtype *_v; \
+ for (_index = 0; array && _index < array->len; _index++) \
+ { \
+ _v = (itemtype *) g_ptr_array_index (array, _index); \
+ if (_wcsicmp (_v->verb_name, (verb)) == 0) \
+ { \
+ *(dst) = _v; \
+ break; \
+ } \
+ } \
+ if (array == NULL || _index >= array->len) \
+ *(dst) = NULL; \
+} while (0)
+
+#define _verb_lookup(array, verb, dst) _lookup_by_verb (array, verb, dst, GWin32AppInfoShellVerb)
+
+/* Because with subcommands a verb would have
+ * a name like "foo\\bar", but the key its command
+ * should be looked for is "shell\\foo\\shell\\bar\\command"
+ */
+typedef struct _reg_verb {
+ gunichar2 *name;
+ gunichar2 *shellpath;
+} reg_verb;
+
typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
+typedef struct _GWin32AppInfoShellVerb GWin32AppInfoShellVerb;
typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
+typedef struct _GWin32AppInfoShellVerbClass GWin32AppInfoShellVerbClass;
typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
@@ -87,6 +148,11 @@ struct _GWin32AppInfoApplicationClass
GObjectClass parent_class;
};
+struct _GWin32AppInfoShellVerbClass
+{
+ GObjectClass parent_class;
+};
+
struct _GWin32AppInfoURLSchema {
GObject parent_instance;
@@ -97,45 +163,57 @@ struct _GWin32AppInfoURLSchema {
gchar *schema_u8;
/* url schema (stuff before ':'), in UTF-8, folded */
- gchar *schema_folded;
+ gchar *schema_u8_folded;
- /* Handler currently selected for this schema */
+ /* Handler currently selected for this schema. Can be NULL. */
GWin32AppInfoHandler *chosen_handler;
- /* Maps folded handler IDs -> to other handlers for this schema */
+ /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this schema.
+ * Includes the chosen handler, if any.
+ */
GHashTable *handlers;
};
struct _GWin32AppInfoHandler {
GObject parent_instance;
- /* Class name in HKCR */
+ /* Usually a class name in HKCR */
gunichar2 *handler_id;
- /* Handler registry key (HKCR\\handler_id). Can be used to watch this handler. */
+ /* Registry object obtained by opening @handler_id. Can be used to watch this handler. */
GWin32RegistryKey *key;
- /* Class name in HKCR, UTF-8, folded */
+ /* @handler_id, in UTF-8, folded */
gchar *handler_id_folded;
- /* shell/open/command default value for the class named by class_id */
- gunichar2 *handler_command;
+ /* Icon of the application for this handler */
+ GIcon *icon;
+
+ /* Verbs that this handler supports */
+ GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
+};
- /* If handler_id class has no command, it might point to another class */
- gunichar2 *proxy_id;
+struct _GWin32AppInfoShellVerb {
+ GObject parent_instance;
- /* Proxy registry key (HKCR\\proxy_id). Can be used to watch handler's proxy. */
- GWin32RegistryKey *proxy_key;
+ /* The verb that is used to invoke this handler. */
+ gunichar2 *verb_name;
- /* shell/open/command default value for the class named by proxy_id */
- gunichar2 *proxy_command;
+ /* User-friendly (localized) verb name. */
+ gchar *verb_displayname;
- /* Executable of the program (for matching, in folded form; UTF-8) */
- gchar *executable_folded;
+ /* shell/verb/command */
+ gunichar2 *command;
+
+ /* Same as @command, but in UTF-8 */
+ gchar *command_utf8;
/* Executable of the program (UTF-8) */
gchar *executable;
+ /* Executable of the program (for matching, in folded form; UTF-8) */
+ gchar *executable_folded;
+
/* Pointer to a location within @executable */
gchar *executable_basename;
@@ -146,10 +224,7 @@ struct _GWin32AppInfoHandler {
*/
gchar *dll_function;
- /* Icon of the application for this handler */
- GIcon *icon;
-
- /* The application that is linked to this handler. */
+ /* The application that is linked to this verb. */
GWin32AppInfoApplication *app;
};
@@ -162,28 +237,34 @@ struct _GWin32AppInfoFileExtension {
/* File extension (with leading '.'), in UTF-8 */
gchar *extension_u8;
- /* handler currently selected for this extension */
+ /* handler currently selected for this extension. Can be NULL. */
GWin32AppInfoHandler *chosen_handler;
- /* Maps folded handler IDs -> to other handlers for this extension */
+ /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this extension.
+ * Includes the chosen handler, if any.
+ */
GHashTable *handlers;
-
- /* Maps folded app exename -> to apps that support this extension.
- * ONLY for apps that are not reachable via handlers (i.e. apps from
- * the HKCR/Applications, which have no handlers). */
- GHashTable *other_apps;
};
struct _GWin32AppInfoApplication {
GObject parent_instance;
- /* Canonical name (used for key names). Can be NULL. */
+ /* Canonical name (used for key names).
+ * For applications tracked by id this is the root registry
+ * key path for the application.
+ * For applications tracked by executable name this is the
+ * basename of the executable.
+ * For fake applications this is the full filename of the
+ * executable (as far as it can be inferred from a command line,
+ * meaning that it can also be a basename, if that's
+ * all that a commandline happen to give us).
+ */
gunichar2 *canonical_name;
- /* Canonical name (used for key names), in UTF-8. Can be NULL. */
+ /* @canonical_name, in UTF-8 */
gchar *canonical_name_u8;
- /* Canonical name (used for key names), in UTF-8, folded. Can be NULL. */
+ /* @canonical_name, in UTF-8, folded */
gchar *canonical_name_folded;
/* Human-readable name in English. Can be NULL */
@@ -204,37 +285,17 @@ struct _GWin32AppInfoApplication {
/* Description, could be in user's language, UTF-8. Can be NULL */
gchar *description_u8;
- /* shell/open/command for the application. Can be NULL (see executable). */
- gunichar2 *command;
-
- /* shell/open/command for the application. Can be NULL (see executable). */
- gchar *command_u8;
-
- /* Executable of the program (for matching, in folded form;
- * UTF-8). Never NULL. */
- gchar *executable_folded;
-
- /* Executable of the program (UTF-8). Never NULL. */
- gchar *executable;
-
- /* Pointer to a location within @executable */
- gchar *executable_basename;
-
- /* If not NULL, then @executable and its derived fields contain the name
- * of a DLL file (without the name of the function that rundll32.exe should
- * invoke), and this field contains the name of the function to be invoked.
- * The application is then invoked as 'rundll32.exe "dll_path",dll_function other_arguments...'.
- */
- gchar *dll_function;
+ /* Verbs that this application supports */
+ GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
/* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
- * UTF-8, folded) -> a GWin32AppInfoHandler
+ * UTF-8, folded) -> to a GWin32AppInfoHandler
* Schema can be used as a key in the urls hashmap.
*/
GHashTable *supported_urls;
/* Explicitly supported extensions, hashmap from map-owned gchar ptr
- * (.extension, UTF-8, folded) -> a GWin32AppInfoHandler
+ * (.extension, UTF-8, folded) -> to a GWin32AppInfoHandler
* Extension can be used as a key in the extensions hashmap.
*/
GHashTable *supported_exts;
@@ -272,13 +333,18 @@ struct _GWin32AppInfoApplication {
#define G_TYPE_WIN32_APPINFO_APPLICATION (g_win32_appinfo_application_get_type ())
#define G_WIN32_APPINFO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication))
+#define G_TYPE_WIN32_APPINFO_SHELL_VERB (g_win32_appinfo_shell_verb_get_type ())
+#define G_WIN32_APPINFO_SHELL_VERB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
G_TYPE_WIN32_APPINFO_SHELL_VERB, GWin32AppInfoShellVerb))
+
GType g_win32_appinfo_url_schema_get_type (void) G_GNUC_CONST;
GType g_win32_appinfo_file_extension_get_type (void) G_GNUC_CONST;
+GType g_win32_appinfo_shell_verb_get_type (void) G_GNUC_CONST;
GType g_win32_appinfo_handler_get_type (void) G_GNUC_CONST;
GType g_win32_appinfo_application_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE (GWin32AppInfoURLSchema, g_win32_appinfo_url_schema, G_TYPE_OBJECT)
G_DEFINE_TYPE (GWin32AppInfoFileExtension, g_win32_appinfo_file_extension, G_TYPE_OBJECT)
+G_DEFINE_TYPE (GWin32AppInfoShellVerb, g_win32_appinfo_shell_verb, G_TYPE_OBJECT)
G_DEFINE_TYPE (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT)
G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT)
@@ -289,7 +355,7 @@ g_win32_appinfo_url_schema_dispose (GObject *object)
g_clear_pointer (&url->schema, g_free);
g_clear_pointer (&url->schema_u8, g_free);
- g_clear_pointer (&url->schema_folded, g_free);
+ g_clear_pointer (&url->schema_u8_folded, g_free);
g_clear_object (&url->chosen_handler);
g_clear_pointer (&url->handlers, g_hash_table_destroy);
G_OBJECT_CLASS (g_win32_appinfo_url_schema_parent_class)->dispose (object);
@@ -303,16 +369,9 @@ g_win32_appinfo_handler_dispose (GObject *object)
g_clear_pointer (&handler->handler_id, g_free);
g_clear_pointer (&handler->handler_id_folded, g_free);
- g_clear_pointer (&handler->handler_command, g_free);
- g_clear_pointer (&handler->proxy_id, g_free);
- g_clear_pointer (&handler->proxy_command, g_free);
- g_clear_pointer (&handler->executable_folded, g_free);
- g_clear_pointer (&handler->executable, g_free);
- g_clear_pointer (&handler->dll_function, g_free);
g_clear_object (&handler->key);
- g_clear_object (&handler->proxy_key);
g_clear_object (&handler->icon);
- g_clear_object (&handler->app);
+ g_clear_pointer (&handler->verbs, g_ptr_array_unref);
G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
}
@@ -325,10 +384,25 @@ g_win32_appinfo_file_extension_dispose (GObject *object)
g_clear_pointer (&ext->extension_u8, g_free);
g_clear_object (&ext->chosen_handler);
g_clear_pointer (&ext->handlers, g_hash_table_destroy);
- g_clear_pointer (&ext->other_apps, g_hash_table_destroy);
G_OBJECT_CLASS (g_win32_appinfo_file_extension_parent_class)->dispose (object);
}
+static void
+g_win32_appinfo_shell_verb_dispose (GObject *object)
+{
+ GWin32AppInfoShellVerb *shverb = G_WIN32_APPINFO_SHELL_VERB (object);
+
+ g_clear_pointer (&shverb->verb_name, g_free);
+ g_clear_pointer (&shverb->verb_displayname, g_free);
+ g_clear_pointer (&shverb->command, g_free);
+ g_clear_pointer (&shverb->command_utf8, g_free);
+ g_clear_pointer (&shverb->executable_folded, g_free);
+ g_clear_pointer (&shverb->executable, g_free);
+ g_clear_pointer (&shverb->dll_function, g_free);
+ g_clear_object (&shverb->app);
+ G_OBJECT_CLASS (g_win32_appinfo_shell_verb_parent_class)->dispose (object);
+}
+
static void
g_win32_appinfo_application_dispose (GObject *object)
{
@@ -340,20 +414,28 @@ g_win32_appinfo_application_dispose (GObject *object)
g_clear_pointer (&app->pretty_name, g_free);
g_clear_pointer (&app->localized_pretty_name, g_free);
g_clear_pointer (&app->description, g_free);
- g_clear_pointer (&app->command, g_free);
g_clear_pointer (&app->pretty_name_u8, g_free);
g_clear_pointer (&app->localized_pretty_name_u8, g_free);
g_clear_pointer (&app->description_u8, g_free);
- g_clear_pointer (&app->command_u8, g_free);
- g_clear_pointer (&app->executable_folded, g_free);
- g_clear_pointer (&app->executable, g_free);
- g_clear_pointer (&app->dll_function, g_free);
g_clear_pointer (&app->supported_urls, g_hash_table_destroy);
g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
g_clear_object (&app->icon);
+ g_clear_pointer (&app->verbs, g_ptr_array_unref);
G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
}
+static const gchar *
+g_win32_appinfo_application_get_some_name (GWin32AppInfoApplication *app)
+{
+ if (app->localized_pretty_name_u8)
+ return app->localized_pretty_name_u8;
+
+ if (app->pretty_name_u8)
+ return app->pretty_name_u8;
+
+ return app->canonical_name_u8;
+}
+
static void
g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
{
@@ -370,6 +452,14 @@ g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klas
gobject_class->dispose = g_win32_appinfo_file_extension_dispose;
}
+static void
+g_win32_appinfo_shell_verb_class_init (GWin32AppInfoShellVerbClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = g_win32_appinfo_shell_verb_dispose;
+}
+
static void
g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
{
@@ -395,6 +485,11 @@ g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
g_object_unref);
}
+static void
+g_win32_appinfo_shell_verb_init (GWin32AppInfoShellVerb *self)
+{
+}
+
static void
g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
{
@@ -402,15 +497,12 @@ g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
g_str_equal,
g_free,
g_object_unref);
- self->other_apps = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
}
static void
g_win32_appinfo_handler_init (GWin32AppInfoHandler *self)
{
+ self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
}
static void
@@ -424,6 +516,7 @@ g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
g_str_equal,
g_free,
g_object_unref);
+ self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
}
G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
@@ -439,20 +532,27 @@ static GHashTable *extensions = NULL;
static GHashTable *urls = NULL;
/* Map of owned "appID" (UTF-8, folded) to
- * GWin32AppInfoApplication ptr
+ * a GWin32AppInfoApplication
*/
static GHashTable *apps_by_id = NULL;
/* Map of owned "app.exe" (UTF-8, folded) to
- * GWin32AppInfoApplication ptr.
+ * a GWin32AppInfoApplication.
* This map and its values are separate from apps_by_id. The fact that an app
* with known ID has the same executable [base]name as an app in this map does
* not mean that they are the same application.
*/
static GHashTable *apps_by_exe = NULL;
+/* Map of owned "path:\to\app.exe" (UTF-8, folded) to
+ * a GWin32AppInfoApplication.
+ * The app objects in this map are fake - they are linked to
+ * handlers that do not have any apps associated with them.
+ */
+static GHashTable *fake_apps = NULL;
+
/* Map of owned "handler id" (UTF-8, folded)
- * to GWin32AppInfoHandler ptr
+ * to a GWin32AppInfoHandler
*/
static GHashTable *handlers = NULL;
@@ -487,7 +587,6 @@ static GWin32RegistryKey *classes_root_key;
#define HKCR L"HKEY_CLASSES_ROOT\\"
#define HKCU L"HKEY_CURRENT_USER\\"
#define HKLM L"HKEY_LOCAL_MACHINE\\"
-#define SHELL_OPEN_COMMAND L"\\shell\\open\\command"
#define REG_PATH_MAX 256
#define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
@@ -498,56 +597,92 @@ static GWin32RegistryKey *classes_root_key;
#include "giowin32-private.c"
static void
-read_handler_icon (GWin32RegistryKey *proxy_key,
- GWin32RegistryKey *program_key,
+read_handler_icon (GWin32RegistryKey *key,
GIcon **icon_out)
{
- gint counter;
- GWin32RegistryKey *key;
+ GWin32RegistryKey *icon_key;
+ GWin32RegistryValueType default_type;
+ gchar *default_value;
+
+ g_assert (icon_out);
*icon_out = NULL;
- for (counter = 0; counter < 2; counter++)
- {
- GWin32RegistryKey *icon_key;
+ icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
- if (counter == 0)
- key = proxy_key;
- else
- key = program_key;
+ if (icon_key == NULL)
+ return;
- if (!key)
- continue;
+ if (g_win32_registry_key_get_value (icon_key,
+ NULL,
+ TRUE,
+ "",
+ &default_type,
+ (gpointer *) &default_value,
+ NULL,
+ NULL))
+ {
+ if (default_type == G_WIN32_REGISTRY_VALUE_STR &&
+ default_value[0] != '\0')
+ *icon_out = g_themed_icon_new (default_value);
- icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
+ g_clear_pointer (&default_value, g_free);
+ }
- if (icon_key != NULL)
- {
- GWin32RegistryValueType default_type;
- gchar *default_value;
+ g_object_unref (icon_key);
+}
- if (g_win32_registry_key_get_value (icon_key,
- NULL,
- TRUE,
- "",
- &default_type,
- (gpointer *) &default_value,
- NULL,
- NULL))
- {
- if (default_type == G_WIN32_REGISTRY_VALUE_STR ||
- default_value[0] != '\0')
- *icon_out = g_themed_icon_new (default_value);
+static void
+reg_verb_free (gpointer p)
+{
+ if (p == NULL)
+ return;
- g_clear_pointer (&default_value, g_free);
- }
+ g_free (((reg_verb *) p)->name);
+ g_free (((reg_verb *) p)->shellpath);
+ g_free (p);
+}
- g_object_unref (icon_key);
- }
+#define is_open(x) ( \
+ ((x)[0] == L'o' || (x)[0] == L'O') && \
+ ((x)[1] == L'p' || (x)[1] == L'P') && \
+ ((x)[2] == L'e' || (x)[2] == L'E') && \
+ ((x)[3] == L'n' || (x)[3] == L'N') && \
+ ((x)[4] == L'\0') \
+)
+
+/* default verb (if any) comes first,
+ * then "open", then the rest of the verbs
+ * are sorted alphabetically
+ */
+static gint
+compare_verbs (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ const reg_verb *ca = (const reg_verb *) a;
+ const reg_verb *cb = (const reg_verb *) b;
+ const gunichar2 *def = (const gunichar2 *) user_data;
+ gboolean is_open_ca;
+ gboolean is_open_cb;
- if (*icon_out)
- break;
+ if (def != NULL)
+ {
+ if (_wcsicmp (ca->name, def) == 0)
+ return 1;
+ else if (_wcsicmp (cb->name, def) == 0)
+ return -1;
}
+
+ is_open_ca = is_open (ca->name);
+ is_open_cb = is_open (cb->name);
+
+ if (is_open_ca && !is_open_cb)
+ return 1;
+ else if (is_open_ca && !is_open_cb)
+ return -1;
+
+ return _wcsicmp (ca->name, cb->name);
}
static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
@@ -555,6 +690,64 @@ static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_l
static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
+/* Called by process_verbs_commands.
+ * @verb is a verb name
+ * @command_line is the commandline of that verb
+ * @command_line_utf8 is the UTF-8 version of @command_line
+ * @verb_displayname is the prettier display name of the verb (might be NULL)
+ * @verb_is_preferred is TRUE if the verb is the preferred one
+ * @invent_new_verb_name is TRUE when the verb should be added
+ * even if a verb with such
+ * name already exists (in which case
+ * a new name is invented), unless
+ * the existing verb runs exactly the same
+ * commandline.
+ */
+typedef void (*verb_command_func) (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name);
+
+static gunichar2 * decide_which_id_to_use (const gunichar2 *program_id,
+ GWin32RegistryKey **return_key,
+ gchar **return_handler_id_u8,
+ gchar **return_handler_id_u8_folded);
+
+static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *schema,
+ const gchar *schema_u8,
+ const gchar *schema_u8_folded);
+
+static GWin32AppInfoHandler * get_handler_object (const gchar *handler_id_u8_folded,
+ GWin32RegistryKey *handler_key,
+ const gunichar2 *handler_id);
+
+static GWin32AppInfoFileExtension *get_ext_object (const gunichar2 *ext,
+ const gchar *ext_u8,
+ const gchar *ext_u8_folded);
+
+
+static void process_verbs_commands (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ verb_command_func handler,
+ gpointer handler_data1,
+ gpointer handler_data2);
+
+static void handler_add_verb (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name);
+
/* output_size is in *bytes*, not gunichar2s! */
static gboolean
build_registry_path (gunichar2 *output, gsize output_size, ...)
@@ -632,589 +825,830 @@ _g_win32_registry_key_build_and_new_w (GError **error, ...)
return key;
}
-/* Slow and dirty validator for UTF-16 strings */
-static gboolean
-g_utf16_validate (const gunichar2 *str,
- glong len)
-{
- gchar *tmp;
-
- if (str == NULL)
- return FALSE;
-
- tmp = g_utf16_to_utf8 (str, len, NULL, NULL, NULL);
-
- if (tmp == NULL)
- return FALSE;
-
- g_free (tmp);
-
- return TRUE;
-}
-
-/* Does a UTF-16 validity check on *proxy_command and/or *program_command.
- * Fails if that check doesn't pass.
+/* Gets the list of shell verbs (a GList of reg_verb, put into @verbs)
+ * from the @program_id_key.
+ * If one of the verbs should be preferred,
+ * a pointer to this verb (in the GList) will be
+ * put into @preferred_verb.
+ * Does not automatically assume that the first verb
+ * is preferred (when no other preferences exist).
+ * @verbname_prefix is prefixed to the name of the verb
+ * (this is used for subcommands) and is initially an
+ * empty string.
+ * @verbshell_prefix is the subkey of @program_id_key
+ * that contains the verbs. It is "Shell" initially,
+ * but grows with recursive invocations (for subcommands).
+ * Returns TRUE on success, FALSE on failure.
*/
static gboolean
-follow_class_chain_to_handler (const gunichar2 *program_id,
- gsize program_id_size,
- gunichar2 **program_command,
- GWin32RegistryKey **program_key,
- gunichar2 **proxy_id,
- gunichar2 **proxy_command,
- GWin32RegistryKey **proxy_key,
- gchar **program_id_u8,
- gchar **program_id_folded)
+get_verbs (GWin32RegistryKey *program_id_key,
+ const reg_verb **preferred_verb,
+ GList **verbs,
+ const gunichar2 *verbname_prefix,
+ const gunichar2 *verbshell_prefix)
{
+ GWin32RegistrySubkeyIter iter;
GWin32RegistryKey *key;
GWin32RegistryValueType val_type;
- gsize proxy_id_size;
- gboolean got_value;
-
- g_assert (program_id && program_command && proxy_id && proxy_command);
-
- *program_command = NULL;
- *proxy_id = NULL;
- *proxy_command = NULL;
+ gunichar2 *default_verb;
+ gsize verbshell_prefix_len;
+ gsize verbname_prefix_len;
+ GList *i;
- if (program_key)
- *program_key = NULL;
+ g_assert (program_id_key && verbs && preferred_verb);
- if (proxy_key)
- *proxy_key = NULL;
+ *verbs = NULL;
+ *preferred_verb = NULL;
+ key = g_win32_registry_key_get_child_w (program_id_key,
+ verbshell_prefix,
+ NULL);
- key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id,
- SHELL_OPEN_COMMAND, NULL);
+ if (key == NULL)
+ return FALSE;
- if (key != NULL)
+ if (!g_win32_registry_subkey_iter_init (&iter, key, NULL))
{
- got_value = g_win32_registry_key_get_value_w (key,
- NULL,
- TRUE,
- L"",
- &val_type,
- (void **) program_command,
- NULL,
- NULL);
- if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
- {
- if (((program_id_u8 != NULL || program_id_folded != NULL) &&
- !g_utf16_to_utf8_and_fold (program_id, -1, program_id_u8, program_id_folded)) ||
- !g_utf16_validate (*program_command, -1))
- {
- g_object_unref (key);
- g_free (program_command);
-
- return FALSE;
- }
- if (program_key == NULL)
- g_object_unref (key);
- else
- *program_key = key;
-
- return TRUE;
- }
- else if (got_value)
- g_clear_pointer (program_command, g_free);
-
g_object_unref (key);
+
+ return FALSE;
}
- key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, NULL);
+ verbshell_prefix_len = g_utf16_len (verbshell_prefix);
+ verbname_prefix_len = g_utf16_len (verbname_prefix);
- if (key == NULL)
- return FALSE;
+ while (g_win32_registry_subkey_iter_next (&iter, TRUE, NULL))
+ {
+ const gunichar2 *name;
+ gsize name_len;
+ GWin32RegistryKey *subkey;
+ gboolean has_subcommands;
+ const reg_verb *tmp;
+ GWin32RegistryValueType subc_type;
+ reg_verb *rverb;
+ const gunichar2 *shell = L"Shell";
+ const gsize shell_len = g_utf16_len (shell);
+
+ if (!g_win32_registry_subkey_iter_get_name_w (&iter, &name, &name_len, NULL))
+ continue;
- got_value = g_win32_registry_key_get_value_w (key,
- NULL,
- TRUE,
- L"",
- &val_type,
- (void **) proxy_id,
- &proxy_id_size,
- NULL);
- g_object_unref (key);
+ subkey = g_win32_registry_key_get_child_w (key,
+ name,
+ NULL);
+
+ g_assert (subkey != NULL);
+ /* The key we're looking at is "<some_root>/Shell/<this_key>",
+ * where "Shell" is verbshell_prefix.
+ * If it has a value named 'Subcommands' (doesn't matter what its data is),
+ * it means that this key has its own Shell subkey, the subkeys
+ * of which are shell commands (i.e. <some_root>/Shell/<this_key>/Shell/<some_other_keys>).
+ * To handle that, create new, extended nameprefix and shellprefix,
+ * and call the function recursively.
+ * name prefix "" -> "<this_key_name>\\"
+ * shell prefix "Shell" -> "Shell\\<this_key_name>\\Shell"
+ * The root, program_id_key, remains the same in all invocations.
+ * Essentially, we're flattening the command tree into a list.
+ */
+ has_subcommands = FALSE;
+ if (g_win32_registry_key_get_value_w (subkey,
+ NULL,
+ TRUE,
+ L"Subcommands",
+ &subc_type,
+ NULL,
+ NULL,
+ NULL) &&
+ subc_type == G_WIN32_REGISTRY_VALUE_STR)
+ {
+ gunichar2 *new_nameprefix = g_malloc ((verbname_prefix_len + name_len + 1 + 1) * sizeof
(gunichar2));
+ gunichar2 *new_shellprefix = g_malloc ((verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1) *
sizeof (gunichar2));
+ memcpy (&new_shellprefix[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
+ new_shellprefix[verbshell_prefix_len] = L'\\';
+ memcpy (&new_shellprefix[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
+ new_shellprefix[verbshell_prefix_len + 1 + name_len] = L'\\';
+ memcpy (&new_shellprefix[verbshell_prefix_len + 1 + name_len + 1], shell, shell_len * sizeof
(gunichar2));
+ new_shellprefix[verbshell_prefix_len + 1 + name_len + 1 + shell_len] = 0;
+
+ memcpy (&new_nameprefix[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
+ memcpy (&new_nameprefix[verbname_prefix_len], name, (name_len) * sizeof (gunichar2));
+ new_nameprefix[verbname_prefix_len + name_len] = L'\\';
+ new_nameprefix[verbname_prefix_len + name_len + 1] = 0;
+ has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix);
+ g_free (new_shellprefix);
+ g_free (new_nameprefix);
+ }
- if (!got_value ||
- (val_type != G_WIN32_REGISTRY_VALUE_STR))
- {
- g_clear_pointer (proxy_id, g_free);
+ g_clear_object (&subkey);
- return FALSE;
+ /* Presence of subcommands means that this key itself is not a command-key */
+ if (has_subcommands)
+ continue;
+
+ /* We don't look at the command sub-key and its value (the actual command line) here.
+ * We save the registry path instead, and use it later in process_verbs_commands().
+ * The name of the verb is also saved.
+ * verbname_prefix is prefixed to the verb name (it's either an empty string
+ * or already ends with a '\\', so no extra separators needed).
+ * verbshell_prefix is prefixed to the verb key path (this one needs a separator,
+ * because it never has one - all verbshell prefixes end with "Shell", not "Shell\\")
+ */
+ rverb = g_new0 (reg_verb, 1);
+ rverb->name = g_malloc ((verbname_prefix_len + name_len + 1) * sizeof (gunichar2));
+ memcpy (&rverb->name[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
+ memcpy (&rverb->name[verbname_prefix_len], name, name_len * sizeof (gunichar2));
+ rverb->name[verbname_prefix_len + name_len] = 0;
+ rverb->shellpath = g_malloc ((verbshell_prefix_len + 1 + name_len + 1) * sizeof (gunichar2));
+ memcpy (&rverb->shellpath[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
+ memcpy (&rverb->shellpath[verbshell_prefix_len], L"\\", sizeof (gunichar2));
+ memcpy (&rverb->shellpath[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
+ rverb->shellpath[verbshell_prefix_len + 1 + name_len] = 0;
+ *verbs = g_list_append (*verbs, rverb);
}
- key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id,
- SHELL_OPEN_COMMAND, NULL);
+ g_win32_registry_subkey_iter_clear (&iter);
- if (key == NULL)
+ if (*verbs == NULL)
{
- g_clear_pointer (proxy_id, g_free);
+ g_object_unref (key);
return FALSE;
}
- got_value = g_win32_registry_key_get_value_w (key,
- NULL,
- TRUE,
- L"",
- &val_type,
- (void **) proxy_command,
- NULL,
- NULL);
+ default_verb = NULL;
- if (proxy_key)
- *proxy_key = key;
- else
- g_object_unref (key);
+ if (g_win32_registry_key_get_value_w (key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &default_verb,
+ NULL,
+ NULL) &&
+ (val_type != G_WIN32_REGISTRY_VALUE_STR ||
+ g_utf16_len (default_verb) <= 0))
+ g_clear_pointer (&default_verb, g_free);
+
+ g_object_unref (key);
- if (!got_value ||
- val_type != G_WIN32_REGISTRY_VALUE_STR ||
- ((program_id_u8 != NULL || program_id_folded != NULL) &&
- !g_utf16_to_utf8_and_fold (program_id, -1, program_id_u8, program_id_folded)) ||
- !g_utf16_validate (*proxy_command, -1))
+ /* Only sort at the top level */
+ if (verbname_prefix[0] == 0)
{
- g_clear_pointer (proxy_id, g_free);
- g_clear_pointer (proxy_command, g_free);
- if (proxy_key)
- g_clear_object (proxy_key);
- return FALSE;
+ *verbs = g_list_sort_with_data (*verbs, compare_verbs, default_verb);
+
+ for (i = *verbs; default_verb && *preferred_verb == NULL && i; i = i->next)
+ if (_wcsicmp (default_verb, ((const reg_verb *) i->data)->name) == 0)
+ *preferred_verb = (const reg_verb *) i->data;
}
+ g_clear_pointer (&default_verb, g_free);
+
return TRUE;
}
+/* Grabs a URL association (from
HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
+ * or from an application with Capabilities, or just a schema subkey in HKCR).
+ * @program_id is a ProgID of the handler for the URL.
+ * @schema is the schema for the URL.
+ * @schema_u8 and @schema_u8_folded are UTF-8 and folded UTF-8
+ * respectively.
+ * @app is the app to which the URL handler belongs (can be NULL).
+ * @is_user_choice is TRUE if this association is clearly preferred
+ */
static void
-get_url_association (const gunichar2 *schema)
+get_url_association (const gunichar2 *program_id,
+ const gunichar2 *schema,
+ const gchar *schema_u8,
+ const gchar *schema_u8_folded,
+ GWin32AppInfoApplication *app,
+ gboolean is_user_choice)
{
GWin32AppInfoURLSchema *schema_rec;
GWin32AppInfoHandler *handler_rec;
- GWin32AppInfoHandler *handler_rec_in_url;
- gchar *schema_u8;
- gchar *schema_folded;
- GWin32RegistryKey *user_choice;
- GWin32RegistryValueType val_type;
- gunichar2 *program_id;
- gsize program_id_size;
- gunichar2 *program_command;
- gunichar2 *proxy_id;
- gunichar2 *proxy_command;
- gchar *program_id_u8;
- gchar *program_id_folded;
- GWin32RegistryKey *program_key;
- GWin32RegistryKey *proxy_key;
-
- user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
- schema, USER_CHOICE,
- NULL);
-
- if (user_choice == NULL)
+ gunichar2 *handler_id;
+ GList *verbs;
+ const reg_verb *preferred_verb;
+ gchar *handler_id_u8;
+ gchar *handler_id_u8_folded;
+ GWin32RegistryKey *handler_key;
+
+ if ((handler_id = decide_which_id_to_use (program_id,
+ &handler_key,
+ &handler_id_u8,
+ &handler_id_u8_folded)) == NULL)
return;
- if (!g_utf16_to_utf8_and_fold (schema, -1, &schema_u8, &schema_folded))
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
{
- g_object_unref (user_choice);
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_object (&handler_key);
+
return;
}
- schema_rec = g_hash_table_lookup (urls, schema_folded);
+ schema_rec = get_schema_object (schema,
+ schema_u8,
+ schema_u8_folded);
- if (!g_win32_registry_key_get_value_w (user_choice,
- NULL,
- TRUE,
- L"Progid",
- &val_type,
- (void **) &program_id,
- &program_id_size,
- NULL))
- {
- g_free (schema_u8);
- g_free (schema_folded);
- g_object_unref (user_choice);
- return;
- }
+ handler_rec = get_handler_object (handler_id_u8_folded,
+ handler_key,
+ handler_id);
+
+ if (is_user_choice || schema_rec->chosen_handler == NULL)
+ g_set_object (&schema_rec->chosen_handler, handler_rec);
+
+ g_hash_table_insert (schema_rec->handlers,
+ g_strdup (handler_id_u8_folded),
+ g_object_ref (handler_rec));
+
+ g_clear_object (&handler_key);
+
+ if (app)
+ g_hash_table_insert (app->supported_urls,
+ g_strdup (schema_rec->schema_u8_folded),
+ g_object_ref (handler_rec));
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_add_verb,
+ handler_rec,
+ app);
+
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_pointer (&handler_id, g_free);
+}
+
+/* Grabs a file extension association (from HKCR\.ext or similar).
+ * @program_id is a ProgID of the handler for the extension.
+ * @file_extension is the extension (with the leading '.')
+ * @app is the app to which the extension handler belongs (can be NULL).
+ * @is_user_choice is TRUE if this is clearly the preferred association
+ */
+static void
+get_file_ext (const gunichar2 *program_id,
+ const gunichar2 *file_extension,
+ GWin32AppInfoApplication *app,
+ gboolean is_user_choice)
+{
+ GWin32AppInfoHandler *handler_rec;
+ gunichar2 *handler_id;
+ const reg_verb *preferred_verb;
+ GList *verbs;
+ gchar *handler_id_u8;
+ gchar *handler_id_u8_folded;
+ GWin32RegistryKey *handler_key;
+ GWin32AppInfoFileExtension *file_extn;
+ gchar *file_extension_u8;
+ gchar *file_extension_u8_folded;
- if (val_type != G_WIN32_REGISTRY_VALUE_STR)
+ if ((handler_id = decide_which_id_to_use (program_id,
+ &handler_key,
+ &handler_id_u8,
+ &handler_id_u8_folded)) == NULL)
+ return;
+
+ if (!g_utf16_to_utf8_and_fold (file_extension,
+ -1,
+ &file_extension_u8,
+ &file_extension_u8_folded))
{
- g_free (schema_u8);
- g_free (schema_folded);
- g_free (program_id);
- g_object_unref (user_choice);
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_object (&handler_key);
+
return;
}
- program_key = proxy_key = NULL;
- program_command = proxy_id = proxy_command = NULL;
-
- if (!follow_class_chain_to_handler (program_id,
- program_id_size,
- &program_command,
- &program_key,
- &proxy_id,
- &proxy_command,
- &proxy_key,
- &program_id_u8,
- &program_id_folded))
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
{
- g_free (schema_u8);
- g_free (schema_folded);
- g_free (program_id);
- g_object_unref (user_choice);
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_object (&handler_key);
+ g_clear_pointer (&file_extension_u8, g_free);
+ g_clear_pointer (&file_extension_u8_folded, g_free);
+
return;
}
- handler_rec = g_hash_table_lookup (handlers, program_id_folded);
-
- if (handler_rec == NULL)
- {
- handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
-
- handler_rec->proxy_key = proxy_key;
- handler_rec->key = program_key;
- handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
- handler_rec->handler_id_folded =
- g_strdup (program_id_folded);
- handler_rec->handler_command =
- program_command ? g_wcsdup (program_command, -1) : NULL;
- handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
- handler_rec->proxy_command =
- proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ?
handler_rec->handler_command : handler_rec->proxy_command);
- read_handler_icon (proxy_key, program_key, &handler_rec->icon);
- g_hash_table_insert (handlers,
- g_strdup (program_id_folded),
- handler_rec);
- }
- else
- {
- g_clear_object (&program_key);
- g_clear_object (&proxy_key);
- }
+ file_extn = get_ext_object (file_extension, file_extension_u8, file_extension_u8_folded);
- if (schema_rec == NULL)
- {
- schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
- schema_rec->schema = g_wcsdup (schema, -1);
- schema_rec->schema_u8 = g_strdup (schema_u8);
- schema_rec->schema_folded = g_strdup (schema_folded);
- schema_rec->chosen_handler = g_object_ref (handler_rec);
- g_hash_table_insert (urls, g_strdup (schema_folded), schema_rec);
- }
+ handler_rec = get_handler_object (handler_id_u8_folded,
+ handler_key,
+ handler_id);
- if (schema_rec->chosen_handler == NULL)
- schema_rec->chosen_handler = g_object_ref (handler_rec);
+ if (is_user_choice || file_extn->chosen_handler == NULL)
+ g_set_object (&file_extn->chosen_handler, handler_rec);
- handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
- program_id_folded);
+ g_hash_table_insert (file_extn->handlers,
+ g_strdup (handler_id_u8_folded),
+ g_object_ref (handler_rec));
- if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
- g_hash_table_insert (schema_rec->handlers,
- g_strdup (program_id_folded),
+ if (app)
+ g_hash_table_insert (app->supported_exts,
+ g_strdup (file_extension_u8_folded),
g_object_ref (handler_rec));
- g_free (schema_u8);
- g_free (schema_folded);
- g_free (program_id);
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
- g_object_unref (user_choice);
+ g_clear_pointer (&file_extension_u8, g_free);
+ g_clear_pointer (&file_extension_u8_folded, g_free);
+ g_clear_object (&handler_key);
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_add_verb,
+ handler_rec,
+ app);
+
+ g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&handler_id_u8, g_free);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
}
-static void
-get_file_ext (const gunichar2 *ext)
+/* Returns either a @program_id or the string from
+ * the default value of the program_id key (which is a name
+ * of a proxy class), or NULL.
+ * Does not check that proxy represents a valid
+ * record, just checks that it exists.
+ * Can return the class key (HKCR/program_id or HKCR/proxy_id).
+ * Can convert returned value to UTF-8 and fold it.
+ */
+static gunichar2 *
+decide_which_id_to_use (const gunichar2 *program_id,
+ GWin32RegistryKey **return_key,
+ gchar **return_handler_id_u8,
+ gchar **return_handler_id_u8_folded)
{
- GWin32AppInfoFileExtension *file_extn;
- gboolean file_ext_known;
- GWin32AppInfoHandler *handler_rec;
- GWin32AppInfoHandler *handler_rec_in_ext;
- gchar *ext_u8;
- gchar *ext_folded;
- GWin32RegistryKey *user_choice;
- GWin32RegistryKey *open_with_progids;
+ GWin32RegistryKey *key;
GWin32RegistryValueType val_type;
- gsize program_id_size;
- gboolean found_handler;
- gunichar2 *program_id;
gunichar2 *proxy_id;
- gchar *program_id_u8;
- gchar *program_id_folded;
- GWin32RegistryKey *program_key;
- GWin32RegistryKey *proxy_key;
- gunichar2 *program_command;
- gunichar2 *proxy_command;
-
- open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
- ext,
- OPEN_WITH_PROGIDS,
- NULL);
-
- user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, ext,
- USER_CHOICE, NULL);
+ gunichar2 *return_id;
+ gboolean got_value;
+ gchar *handler_id_u8;
+ gchar *handler_id_u8_folded;
+ g_assert (program_id);
- if (user_choice == NULL && open_with_progids == NULL)
- return;
+ if (return_key)
+ *return_key = NULL;
- if (!g_utf16_to_utf8_and_fold (ext, -1, &ext_u8, &ext_folded))
- {
- g_clear_object (&user_choice);
- g_clear_object (&open_with_progids);
- return;
- }
+ /* Check the proxy first */
+ key = g_win32_registry_key_get_child_w (classes_root_key, program_id, NULL);
- file_extn = NULL;
- file_ext_known = g_hash_table_lookup_extended (extensions,
- ext_folded,
- NULL,
- (void **) &file_extn);
+ if (key == NULL)
+ return NULL;
- if (!file_ext_known)
- file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
+ proxy_id = NULL;
+ got_value = g_win32_registry_key_get_value_w (key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &proxy_id,
+ NULL,
+ NULL);
+ if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&proxy_id, g_free);
- found_handler = FALSE;
+ return_id = NULL;
- if (user_choice != NULL)
+ if (proxy_id)
{
- if (g_win32_registry_key_get_value_w (user_choice,
- NULL,
- TRUE,
- L"Progid",
- &val_type,
- (void **) &program_id,
- &program_id_size,
- NULL))
+ GWin32RegistryKey *proxy_key;
+ proxy_key = g_win32_registry_key_get_child_w (classes_root_key, proxy_id, NULL);
+
+ if (proxy_key)
{
- program_key = proxy_key = NULL;
-
- if (val_type == G_WIN32_REGISTRY_VALUE_STR &&
- follow_class_chain_to_handler (program_id,
- program_id_size,
- &program_command,
- &program_key,
- &proxy_id,
- &proxy_command,
- &proxy_key,
- &program_id_u8,
- &program_id_folded))
- {
- handler_rec = g_hash_table_lookup (handlers,
- program_id_folded);
+ if (return_key)
+ *return_key = g_steal_pointer (&proxy_key);
+ g_clear_object (&proxy_key);
- if (handler_rec == NULL)
- {
- handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER,
- NULL);
- handler_rec->proxy_key = proxy_key;
- handler_rec->key = program_key;
- handler_rec->handler_id =
- g_wcsdup (program_id, program_id_size);
- handler_rec->handler_id_folded =
- g_strdup (program_id_folded);
- handler_rec->handler_command =
- program_command ? g_wcsdup (program_command, -1) : NULL;
- handler_rec->proxy_id =
- proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
- handler_rec->proxy_command =
- proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ?
handler_rec->handler_command : handler_rec->proxy_command);
- read_handler_icon (proxy_key,
- program_key,
- &handler_rec->icon);
- g_hash_table_insert (handlers,
- g_strdup (program_id_folded),
- handler_rec);
- }
- else
- {
- g_clear_object (&program_key);
- g_clear_object (&proxy_key);
- }
+ return_id = g_steal_pointer (&proxy_id);
+ }
- found_handler = TRUE;
+ g_clear_pointer (&proxy_id, g_free);
+ }
- handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
- program_id_folded);
+ if ((return_handler_id_u8 ||
+ return_handler_id_u8_folded) &&
+ !g_utf16_to_utf8_and_fold (return_id == NULL ? program_id : return_id,
+ -1,
+ &handler_id_u8,
+ &handler_id_u8_folded))
+ {
+ g_clear_object (&key);
+ if (return_key)
+ g_clear_object (return_key);
+ g_clear_pointer (&return_id, g_free);
- if (file_extn->chosen_handler == NULL)
- {
- g_hash_table_insert (file_extn->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
- }
- else if (handler_rec_in_ext == NULL)
- {
- if (file_extn->chosen_handler->handler_id_folded &&
- strcmp (file_extn->chosen_handler->handler_id_folded,
- program_id_folded) != 0)
- g_hash_table_insert (file_extn->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
- }
+ return NULL;
+ }
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
- }
+ if (return_handler_id_u8)
+ *return_handler_id_u8 = g_steal_pointer (&handler_id_u8);
+ if (return_handler_id_u8_folded)
+ *return_handler_id_u8_folded = g_steal_pointer (&handler_id_u8_folded);
- g_free (program_id);
- }
+ if (return_id == NULL && return_key)
+ *return_key = g_steal_pointer (&key);
+ g_clear_object (&key);
- g_object_unref (user_choice);
- }
+ if (return_id == NULL)
+ return g_wcsdup (program_id, -1);
- if (open_with_progids != NULL)
- {
- GWin32RegistryValueIter iter;
+ return return_id;
+}
+
+/* Grabs the command for each verb from @verbs,
+ * and invokes @handler for it. Consumes @verbs.
+ * @path_to_progid and @progid are concatenated to
+ * produce a path to the key where Shell/verb/command
+ * subkeys are looked up.
+ * @preferred_verb, if not NULL, will be used to inform
+ * the @handler that a verb is preferred.
+ * @autoprefer_first_verb will automatically make the first
+ * verb to be preferred, if @preferred_verb is NULL.
+ * @handler_data1 and @handler_data2 are passed to @handler as-is.
+ */
+static void
+process_verbs_commands (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ verb_command_func handler,
+ gpointer handler_data1,
+ gpointer handler_data2)
+{
+ GList *i;
+ gboolean got_value;
+
+ g_assert (handler != NULL);
+ g_assert (verbs != NULL);
+ g_assert (progid != NULL);
- if (g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
+ for (i = verbs; i; i = i->next)
+ {
+ const reg_verb *verb = (const reg_verb *) i->data;
+ GWin32RegistryKey *key;
+ GWin32RegistryKey *verb_key;
+ gunichar2 *command_value;
+ gchar *command_value_utf8;
+ GWin32RegistryValueType val_type;
+ gunichar2 *verb_displayname;
+ gchar *verb_displayname_u8;
+
+ key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
+ L"\\", verb->shellpath, L"\\command", NULL);
+
+ if (key == NULL)
{
- gunichar2 *value_name;
- gunichar2 *value_data;
- gsize value_name_len;
- gsize value_data_size;
- GWin32RegistryValueType value_type;
+ g_debug ("%S%S\\shell\\%S does not have a \"command\" subkey",
+ path_to_progid, progid, verb->shellpath);
+ continue;
+ }
- while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
- {
- gsize value_name_size;
- program_key = proxy_key = NULL;
+ command_value = NULL;
+ got_value = g_win32_registry_key_get_value_w (key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &command_value,
+ NULL,
+ NULL);
+ g_clear_object (&key);
- if ((!g_win32_registry_value_iter_get_value_type (&iter,
- &value_type,
- NULL)) ||
- ((val_type != G_WIN32_REGISTRY_VALUE_STR) &&
- (val_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
- (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
- &value_name_len,
- NULL)) ||
- (value_name_len <= 0) ||
- (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
- (void **) &value_data,
- &value_data_size,
- NULL)) ||
- (value_data_size < sizeof (gunichar2)) ||
- (value_data[0] == L'\0'))
- continue;
+ if (!got_value ||
+ val_type != G_WIN32_REGISTRY_VALUE_STR ||
+ (command_value_utf8 = g_utf16_to_utf8 (command_value,
+ -1,
+ NULL,
+ NULL,
+ NULL)) == NULL)
+ {
+ g_clear_pointer (&command_value, g_free);
+ continue;
+ }
- value_name_size = sizeof (gunichar2) * (value_name_len + 1);
-
- if (!follow_class_chain_to_handler (value_name,
- value_name_size,
- &program_command,
- &program_key,
- &proxy_id,
- &proxy_command,
- &proxy_key,
- &program_id_u8,
- &program_id_folded))
- continue;
+ verb_displayname = NULL;
+ verb_displayname_u8 = NULL;
+ verb_key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
+ L"\\", verb->shellpath, NULL);
- handler_rec = g_hash_table_lookup (handlers,
- program_id_folded);
+ if (verb_key)
+ {
+ gsize verb_displayname_len;
- if (handler_rec == NULL)
- {
- handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
-
- handler_rec->proxy_key = proxy_key;
- handler_rec->key = program_key;
- handler_rec->handler_id =
- g_wcsdup (value_name, value_name_size);
- handler_rec->handler_id_folded =
- g_strdup (program_id_folded);
- handler_rec->handler_command =
- program_command ? g_wcsdup (program_command, -1) : NULL;
- handler_rec->proxy_id =
- proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
- handler_rec->proxy_command =
- proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ?
handler_rec->handler_command : handler_rec->proxy_command);
- read_handler_icon (proxy_key,
- program_key,
- &handler_rec->icon);
- g_hash_table_insert (handlers,
- g_strdup (program_id_folded),
- handler_rec);
- }
- else
- {
- g_clear_object (&program_key);
- g_clear_object (&proxy_key);
- }
+ got_value = g_win32_registry_key_get_value_w (verb_key,
+ g_win32_registry_get_os_dirs_w (),
+ TRUE,
+ L"MUIVerb",
+ &val_type,
+ (void **) &verb_displayname,
+ &verb_displayname_len,
+ NULL);
- found_handler = TRUE;
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR &&
+ verb_displayname_len > sizeof (gunichar2))
+ verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
- handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
- program_id_folded);
+ g_clear_pointer (&verb_displayname, g_free);
- if (handler_rec_in_ext == NULL)
- {
- if (file_extn->chosen_handler == NULL)
- g_hash_table_insert (file_extn->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
- else if (file_extn->chosen_handler->handler_id_folded &&
- strcmp (file_extn->chosen_handler->handler_id_folded,
- program_id_folded) != 0)
- g_hash_table_insert (file_extn->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
- }
+ if (verb_displayname_u8 == NULL)
+ {
+ got_value = g_win32_registry_key_get_value_w (verb_key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &verb_displayname,
+ &verb_displayname_len,
+ NULL);
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR &&
+ verb_displayname_len > sizeof (gunichar2))
+ verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
}
- g_win32_registry_value_iter_clear (&iter);
+ g_clear_pointer (&verb_displayname, g_free);
+ g_clear_object (&verb_key);
}
- g_object_unref (open_with_progids);
- }
+ handler (handler_data1, handler_data2, verb->name, command_value, command_value_utf8,
+ verb_displayname_u8,
+ (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) ||
+ (!preferred_verb && autoprefer_first_verb && i == verbs),
+ FALSE);
- if (!found_handler)
- {
- if (!file_ext_known)
- g_object_unref (file_extn);
+ g_clear_pointer (&command_value, g_free);
+ g_clear_pointer (&command_value_utf8, g_free);
+ g_clear_pointer (&verb_displayname_u8, g_free);
}
- else if (!file_ext_known)
+
+ g_list_free_full (verbs, reg_verb_free);
+}
+
+/* Looks up a schema object identified by
+ * @schema_u8_folded in the urls hash table.
+ * If such object doesn't exist,
+ * creates it and puts it into the urls hash table.
+ * Returns the object.
+ */
+static GWin32AppInfoURLSchema *
+get_schema_object (const gunichar2 *schema,
+ const gchar *schema_u8,
+ const gchar *schema_u8_folded)
+{
+ GWin32AppInfoURLSchema *schema_rec;
+
+ schema_rec = g_hash_table_lookup (urls, schema_u8_folded);
+
+ if (schema_rec != NULL)
+ return schema_rec;
+
+ schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
+ schema_rec->schema = g_wcsdup (schema, -1);
+ schema_rec->schema_u8 = g_strdup (schema_u8);
+ schema_rec->schema_u8_folded = g_strdup (schema_u8_folded);
+ g_hash_table_insert (urls, g_strdup (schema_rec->schema_u8_folded), schema_rec);
+
+ return schema_rec;
+}
+
+/* Looks up a handler object identified by
+ * @handler_id_u8_folded in the handlers hash table.
+ * If such object doesn't exist,
+ * creates it and puts it into the handlers hash table.
+ * Returns the object.
+ */
+static GWin32AppInfoHandler *
+get_handler_object (const gchar *handler_id_u8_folded,
+ GWin32RegistryKey *handler_key,
+ const gunichar2 *handler_id)
+{
+ GWin32AppInfoHandler *handler_rec;
+
+ handler_rec = g_hash_table_lookup (handlers, handler_id_u8_folded);
+
+ if (handler_rec != NULL)
+ return handler_rec;
+
+ handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
+ handler_rec->key = g_object_ref (handler_key);
+ handler_rec->handler_id = g_wcsdup (handler_id, -1);
+ handler_rec->handler_id_folded = g_strdup (handler_id_u8_folded);
+ read_handler_icon (handler_key, &handler_rec->icon);
+ g_hash_table_insert (handlers, g_strdup (handler_id_u8_folded), handler_rec);
+
+ return handler_rec;
+}
+
+static void
+handler_add_verb (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name)
+{
+ GWin32AppInfoHandler *handler_rec = (GWin32AppInfoHandler *) handler_data1;
+ GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
+ GWin32AppInfoShellVerb *shverb;
+
+ _verb_lookup (handler_rec->verbs, verb, &shverb);
+
+ if (shverb != NULL)
+ return;
+
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ shverb->verb_name = g_wcsdup (verb, -1);
+ shverb->verb_displayname = g_strdup (verb_displayname);
+ shverb->command = g_wcsdup (command_line, -1);
+ shverb->command_utf8 = g_strdup (command_line_utf8);
+ if (app_rec)
+ shverb->app = g_object_ref (app_rec);
+
+ _g_win32_extract_executable (shverb->command,
+ &shverb->executable,
+ &shverb->executable_basename,
+ &shverb->executable_folded,
+ NULL,
+ &shverb->dll_function);
+
+ if (shverb->dll_function != NULL)
+ _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
+
+ if (!verb_is_preferred)
+ g_ptr_array_add (handler_rec->verbs, shverb);
+ else
+ g_ptr_array_insert (handler_rec->verbs, 0, shverb);
+}
+
+/* Tries to generate a new name for a verb that looks
+ * like "verb (%x)", where %x is an integer in range of [0;255).
+ * On success puts new verb (and new verb displayname) into
+ * @new_verb and @new_displayname and return TRUE.
+ * On failure puts NULL into both and returns FALSE.
+ */
+static gboolean
+generate_new_verb_name (GPtrArray *verbs,
+ const gunichar2 *verb,
+ const gchar *verb_displayname,
+ gunichar2 **new_verb,
+ gchar **new_displayname)
+{
+ gsize counter;
+ GWin32AppInfoShellVerb *shverb;
+ gsize orig_len = g_utf16_len (verb);
+ gsize new_verb_name_len = orig_len + strlen (" ()") + 2 + 1;
+ gunichar2 *new_verb_name = g_malloc (new_verb_name_len * sizeof (gunichar2));
+
+ *new_verb = NULL;
+ *new_displayname = NULL;
+
+ memcpy (new_verb_name, verb, orig_len * sizeof (gunichar2));
+ for (counter = 0; counter < 255; counter++)
+ {
+ _snwprintf (&new_verb_name[orig_len], new_verb_name_len, L" (%x)", counter);
+ _verb_lookup (verbs, new_verb_name, &shverb);
+
+ if (shverb == NULL)
+ {
+ *new_verb = new_verb_name;
+ if (verb_displayname != NULL)
+ *new_displayname = g_strdup_printf ("%s (%zx)", verb_displayname, counter);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+app_add_verb (gpointer handler_data1,
+ gpointer handler_data2,
+ const gunichar2 *verb,
+ const gunichar2 *command_line,
+ const gchar *command_line_utf8,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred,
+ gboolean invent_new_verb_name)
+{
+ gunichar2 *new_verb = NULL;
+ gchar *new_displayname = NULL;
+ GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
+ GWin32AppInfoShellVerb *shverb;
+
+ _verb_lookup (app_rec->verbs, verb, &shverb);
+
+ /* Special logic for fake apps - do our best to
+ * collate all possible verbs in the app,
+ * including the verbs that have the same name but
+ * different commandlines, in which case a new
+ * verb name has to be invented.
+ */
+ if (shverb != NULL)
{
- file_extn->extension = g_wcsdup (ext, -1);
- file_extn->extension_u8 = g_strdup (ext_u8);
- g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
+ gsize vi;
+
+ if (!invent_new_verb_name)
+ return;
+
+ for (vi = 0; vi < app_rec->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *app_verb;
+
+ app_verb = _verb_idx (app_rec->verbs, vi);
+
+ if (_wcsicmp (command_line, app_verb->command) == 0)
+ break;
+ }
+
+ if (vi < app_rec->verbs->len ||
+ !generate_new_verb_name (app_rec->verbs,
+ verb,
+ verb_displayname,
+ &new_verb,
+ &new_displayname))
+ return;
}
- g_free (ext_u8);
- g_free (ext_folded);
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ if (new_verb == NULL)
+ shverb->verb_name = g_wcsdup (verb, -1);
+ else
+ shverb->verb_name = g_steal_pointer (&new_verb);
+ if (new_displayname == NULL)
+ shverb->verb_displayname = g_strdup (verb_displayname);
+ else
+ shverb->verb_displayname = g_steal_pointer (&new_displayname);
+
+ shverb->command = g_wcsdup (command_line, -1);
+ shverb->command_utf8 = g_strdup (command_line_utf8);
+ shverb->app = g_object_ref (app_rec);
+
+ _g_win32_extract_executable (shverb->command,
+ &shverb->executable,
+ &shverb->executable_basename,
+ &shverb->executable_folded,
+ NULL,
+ &shverb->dll_function);
+
+ if (shverb->dll_function != NULL)
+ _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
+
+ if (!verb_is_preferred)
+ g_ptr_array_add (app_rec->verbs, shverb);
+ else
+ g_ptr_array_insert (app_rec->verbs, 0, shverb);
+}
+
+/* Looks up a file extension object identified by
+ * @ext_u8_folded in the extensions hash table.
+ * If such object doesn't exist,
+ * creates it and puts it into the extensions hash table.
+ * Returns the object.
+ */
+static GWin32AppInfoFileExtension *
+get_ext_object (const gunichar2 *ext,
+ const gchar *ext_u8,
+ const gchar *ext_u8_folded)
+{
+ GWin32AppInfoFileExtension *file_extn;
+
+ if (g_hash_table_lookup_extended (extensions,
+ ext_u8_folded,
+ NULL,
+ (void **) &file_extn))
+ return file_extn;
+
+ file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
+ file_extn->extension = g_wcsdup (ext, -1);
+ file_extn->extension_u8 = g_strdup (ext_u8);
+ g_hash_table_insert (extensions, g_strdup (ext_u8_folded), file_extn);
+
+ return file_extn;
}
+/* Iterates over HKCU\\Software\\Clients or HKLM\\Software\\Clients,
+ * (depending on @user_registry being TRUE or FALSE),
+ * collecting applications listed there.
+ * Puts the path to the client key for each client into @priority_capable_apps
+ * (only for clients with file or URL associations).
+ */
static void
collect_capable_apps_from_clients (GPtrArray *capable_apps,
GPtrArray *priority_capable_apps,
@@ -1223,7 +1657,7 @@ collect_capable_apps_from_clients (GPtrArray *capable_apps,
GWin32RegistryKey *clients;
GWin32RegistrySubkeyIter clients_iter;
- gunichar2 *client_type_name;
+ const gunichar2 *client_type_name;
gsize client_type_name_len;
@@ -1251,7 +1685,7 @@ collect_capable_apps_from_clients (GPtrArray *capable_apps,
GWin32RegistryKey *system_client_type;
GWin32RegistryValueType default_type;
gunichar2 *default_value = NULL;
- gunichar2 *client_name;
+ const gunichar2 *client_name;
gsize client_name_len;
if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
@@ -1356,11 +1790,17 @@ collect_capable_apps_from_clients (GPtrArray *capable_apps,
g_object_unref (clients);
}
+/* Iterates over HKCU\\Software\\RegisteredApplications or HKLM\\Software\\RegisteredApplications,
+ * (depending on @user_registry being TRUE or FALSE),
+ * collecting applications listed there.
+ * Puts the path to the app key for each app into @capable_apps.
+ */
static void
collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
gboolean user_registry)
{
GWin32RegistryValueIter iter;
+ const gunichar2 *reg_path;
gunichar2 *value_data;
gsize value_data_size;
@@ -1368,13 +1808,12 @@ collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
GWin32RegistryKey *registered_apps;
if (user_registry)
- registered_apps =
- g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
- NULL);
+ reg_path = L"HKEY_CURRENT_USER\\Software\\RegisteredApplications";
else
- registered_apps =
- g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
- NULL);
+ reg_path = L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications";
+
+ registered_apps =
+ g_win32_registry_key_new_w (reg_path, NULL);
if (!registered_apps)
return;
@@ -1382,13 +1821,15 @@ collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
if (!g_win32_registry_value_iter_init (&iter, registered_apps, NULL))
{
g_object_unref (registered_apps);
+
return;
}
while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
{
gunichar2 possible_location[REG_PATH_MAX_SIZE + 1];
- GWin32RegistryKey *location = NULL;
+ GWin32RegistryKey *location;
+ gunichar2 *p;
if ((!g_win32_registry_value_iter_get_value_type (&iter,
&value_type,
@@ -1402,51 +1843,81 @@ collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
(value_data[0] == L'\0'))
continue;
- if (build_registry_path (possible_location, sizeof (possible_location),
- HKCU, value_data, NULL))
- location = g_win32_registry_key_new_w (possible_location, NULL);
-
- if (location)
- {
- gunichar2 *p = wcsrchr (possible_location, L'\\');
-
- if (p)
- *p = L'\0';
-
- g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
- g_object_unref (location);
- continue;
- }
-
if (!build_registry_path (possible_location, sizeof (possible_location),
user_registry ? HKCU : HKLM, value_data, NULL))
continue;
location = g_win32_registry_key_new_w (possible_location, NULL);
- if (location)
+ if (location == NULL)
+ continue;
+
+ p = wcsrchr (possible_location, L'\\');
+
+ if (p)
{
- gunichar2 *p = wcsrchr (possible_location, L'\\');
- if (p)
- *p = L'\0';
+ *p = L'\0';
g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
- g_object_unref (location);
}
+
+ g_object_unref (location);
}
g_win32_registry_value_iter_clear (&iter);
g_object_unref (registered_apps);
}
+/* Looks up an app object identified by
+ * @canonical_name_folded in the @app_hashmap.
+ * If such object doesn't exist,
+ * creates it and puts it into the @app_hashmap.
+ * Returns the object.
+ */
+static GWin32AppInfoApplication *
+get_app_object (GHashTable *app_hashmap,
+ const gunichar2 *canonical_name,
+ const gchar *canonical_name_u8,
+ const gchar *canonical_name_folded,
+ gboolean user_specific,
+ gboolean default_app)
+{
+ GWin32AppInfoApplication *app;
+
+ app = g_hash_table_lookup (app_hashmap, canonical_name_folded);
+
+ if (app != NULL)
+ return app;
+
+ app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
+ app->canonical_name = g_wcsdup (canonical_name, -1);
+ app->canonical_name_u8 = g_strdup (canonical_name_u8);
+ app->canonical_name_folded = g_strdup (canonical_name_folded);
+ app->no_open_with = FALSE;
+ app->user_specific = user_specific;
+ app->default_app = default_app;
+ g_hash_table_insert (app_hashmap,
+ g_strdup (canonical_name_folded),
+ app);
+
+ return app;
+}
+
+/* Grabs an application that has Capabilities.
+ * @app_key_path is the path to the application key
+ * (which must have a "Capabilities" subkey).
+ * @default_app is TRUE if the app has priority
+ */
static void
-read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolean default_app)
+read_capable_app (const gunichar2 *app_key_path,
+ gboolean user_specific,
+ gboolean default_app)
{
GWin32AppInfoApplication *app;
- gunichar2 *app_key_path;
- gunichar2 *canonical_name;
- gchar *canonical_name_u8;
- gchar *canonical_name_folded;
- GWin32RegistryKey *appkey;
+ gchar *canonical_name_u8 = NULL;
+ gchar *canonical_name_folded = NULL;
+ gchar *app_key_path_u8 = NULL;
+ gchar *app_key_path_u8_folded = NULL;
+ GWin32RegistryKey *appkey = NULL;
gunichar2 *fallback_friendly_name;
GWin32RegistryValueType vtype;
gboolean success;
@@ -1456,136 +1927,49 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
gunichar2 *icon_source;
GWin32RegistryKey *capabilities;
GWin32RegistryKey *default_icon_key;
- GWin32RegistryKey *shell_open_command_key;
- gunichar2 *shell_open_command;
- gchar *app_executable;
- gchar *app_executable_basename;
- gchar *app_executable_folded;
- gchar *app_executable_folded_basename;
- gchar *app_dll_function;
GWin32RegistryKey *associations;
-
- app_key_path = g_wcsdup (input_app_key_path, -1);
-
- canonical_name = wcsrchr (app_key_path, L'\\');
-
- if (canonical_name == NULL)
+ const reg_verb *preferred_verb;
+ GList *verbs = NULL;
+
+ appkey = NULL;
+ capabilities = NULL;
+
+ if (!g_utf16_to_utf8_and_fold (app_key_path,
+ -1,
+ &canonical_name_u8,
+ &canonical_name_folded) ||
+ !g_utf16_to_utf8_and_fold (app_key_path,
+ -1,
+ &app_key_path_u8,
+ &app_key_path_u8_folded) ||
+ (appkey = g_win32_registry_key_new_w (app_key_path, NULL)) == NULL ||
+ (capabilities = g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL)) == NULL ||
+ !get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell"))
{
- /* The key must have at least one '\\' */
- g_free (app_key_path);
- return;
- }
-
- canonical_name += 1;
-
- if (!g_utf16_to_utf8_and_fold (canonical_name, -1, &canonical_name_u8, &canonical_name_folded))
- {
- g_free (app_key_path);
- return;
- }
-
- appkey = g_win32_registry_key_new_w (app_key_path, NULL);
-
- if (appkey == NULL)
- {
- g_free (canonical_name_u8);
- g_free (canonical_name_folded);
- g_free (app_key_path);
- return;
- }
-
- capabilities =
- g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL);
-
- if (capabilities == NULL)
- {
- /* Must have capabilities */
- g_free (canonical_name_u8);
- g_free (canonical_name_folded);
- g_free (app_key_path);
- return;
- }
-
- shell_open_command_key =
- g_win32_registry_key_get_child_w (appkey,
- L"shell\\open\\command",
- NULL);
-
- if (shell_open_command_key == NULL)
- {
- g_object_unref (capabilities);
- g_free (canonical_name_u8);
- g_free (canonical_name_folded);
- g_free (app_key_path);
- g_object_unref (appkey);
- return ;
- }
-
- shell_open_command = NULL;
-
- success = g_win32_registry_key_get_value_w (shell_open_command_key,
- NULL,
- TRUE,
- L"",
- &vtype,
- (gpointer *) &shell_open_command,
- NULL,
- NULL);
+ g_clear_pointer (&canonical_name_u8, g_free);
+ g_clear_pointer (&canonical_name_folded, g_free);
+ g_clear_object (&appkey);
+ g_clear_pointer (&app_key_path_u8, g_free);
+ g_clear_pointer (&app_key_path_u8_folded, g_free);
- if (success &&
- (vtype != G_WIN32_REGISTRY_VALUE_STR ||
- !g_utf16_validate (shell_open_command, -1)))
- {
- /* Must have a command */
- g_clear_pointer (&shell_open_command, g_free);
- g_object_unref (capabilities);
- g_free (canonical_name_u8);
- g_free (canonical_name_folded);
- g_free (app_key_path);
- g_object_unref (appkey);
return;
}
- _g_win32_extract_executable (shell_open_command,
- &app_executable,
- &app_executable_basename,
- &app_executable_folded,
- &app_executable_folded_basename,
- &app_dll_function);
- if (app_dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (shell_open_command);
-
- app = g_hash_table_lookup (apps_by_id, canonical_name_folded);
-
- if (app == NULL)
- {
- app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
-
- app->canonical_name = g_wcsdup (canonical_name, -1);
- app->canonical_name_u8 = g_strdup (canonical_name_u8);
- app->canonical_name_folded =
- g_strdup (canonical_name_folded);
-
- app->command = g_wcsdup (shell_open_command, -1);
- app->command_u8 =
- g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
- app->executable = g_strdup (app_executable);
- app->executable_basename =
- &app->executable[app_executable_basename - app_executable];
- app->executable_folded =
- g_strdup (app_executable_folded);
-
- app->no_open_with = FALSE;
-
- app->user_specific = user_specific;
- app->default_app = default_app;
-
- app->dll_function = g_strdup (app_dll_function);
-
- g_hash_table_insert (apps_by_id,
- g_strdup (canonical_name_folded),
- app);
- }
+ app = get_app_object (apps_by_id,
+ app_key_path,
+ canonical_name_u8,
+ canonical_name_folded,
+ user_specific,
+ default_app);
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ L"", /* [ab]use the fact that two strings are simply concatenated */
+ g_win32_registry_key_get_path_w (capabilities),
+ FALSE,
+ app_add_verb,
+ app,
+ app);
fallback_friendly_name = NULL;
success = g_win32_registry_key_get_value_w (appkey,
@@ -1600,7 +1984,8 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&fallback_friendly_name, g_free);
- if (fallback_friendly_name && app->pretty_name == NULL)
+ if (fallback_friendly_name &&
+ app->pretty_name == NULL)
{
app->pretty_name = g_wcsdup (fallback_friendly_name, -1);
g_clear_pointer (&app->pretty_name_u8, g_free);
@@ -1621,10 +2006,12 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ if (success &&
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&friendly_name, g_free);
- if (friendly_name && app->localized_pretty_name == NULL)
+ if (friendly_name &&
+ app->localized_pretty_name == NULL)
{
app->localized_pretty_name = g_wcsdup (friendly_name, -1);
g_clear_pointer (&app->localized_pretty_name_u8, g_free);
@@ -1672,7 +2059,8 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ if (success &&
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&icon_source, g_free);
g_object_unref (default_icon_key);
@@ -1689,11 +2077,13 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ if (success &&
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&icon_source, g_free);
}
- if (icon_source && app->icon == NULL)
+ if (icon_source &&
+ app->icon == NULL)
{
gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
app->icon = g_themed_icon_new (name);
@@ -1713,13 +2103,17 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
g_clear_pointer (&narrow_application_name, g_free);
- /* TODO: do something with the narrow name. Maybe make a kind of sub-app?
- * Narrow name is a more precise name of the application in given context.
- * I.e. Thunderbird's name is "Thunderbird", whereas its narrow name is
- * "Thunderbird (news)" when registering it as a news client.
- * Maybe we should consider applications with different narrow names as
- * different applications altogether?
- */
+ if (narrow_application_name &&
+ app->localized_pretty_name == NULL)
+ {
+ app->localized_pretty_name = g_wcsdup (narrow_application_name, -1);
+ g_clear_pointer (&app->localized_pretty_name_u8, g_free);
+ app->localized_pretty_name_u8 = g_utf16_to_utf8 (narrow_application_name,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+ }
associations = g_win32_registry_key_get_child_w (capabilities,
L"FileAssociations",
@@ -1739,19 +2133,6 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
{
- GWin32AppInfoHandler *handler_rec;
- GWin32AppInfoHandler *handler_rec_in_ext;
- GWin32AppInfoFileExtension *ext;
- gunichar2 *program_command;
- gunichar2 *proxy_id;
- gunichar2 *proxy_command;
- GWin32RegistryKey *program_key;
- GWin32RegistryKey *proxy_key;
- gchar *program_id_u8;
- gchar *program_id_folded;
- gchar *file_extension_u8;
- gchar *file_extension_folded;
-
if ((!g_win32_registry_value_iter_get_value_type (&iter,
&value_type,
NULL)) ||
@@ -1770,110 +2151,7 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
(extension_handler[0] == L'\0'))
continue;
- if (!follow_class_chain_to_handler (extension_handler,
- extension_handler_size,
- &program_command,
- &program_key,
- &proxy_id,
- &proxy_command,
- &proxy_key,
- &program_id_u8,
- &program_id_folded))
- continue;
-
- handler_rec = g_hash_table_lookup (handlers,
- program_id_folded);
-
- if (handler_rec == NULL)
- {
- handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
-
- handler_rec->proxy_key = proxy_key;
- handler_rec->key = program_key;
- handler_rec->handler_id =
- g_wcsdup (extension_handler,extension_handler_size);
- handler_rec->handler_id_folded =
- g_strdup (program_id_folded);
- handler_rec->handler_command =
- program_command ? g_wcsdup (program_command, -1) : NULL;
- handler_rec->proxy_id =
- proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
- handler_rec->proxy_command =
- proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ?
handler_rec->handler_command : handler_rec->proxy_command);
- read_handler_icon (proxy_key,
- program_key,
- &handler_rec->icon);
- g_hash_table_insert (handlers,
- g_strdup (program_id_folded),
- handler_rec);
- }
- else
- {
- g_clear_object (&program_key);
- g_clear_object (&proxy_key);
- }
-
- if (g_utf16_to_utf8_and_fold (file_extension,
- -1,
- &file_extension_u8,
- &file_extension_folded))
- {
- ext = g_hash_table_lookup (extensions,
- file_extension_folded);
-
- if (ext == NULL)
- {
- ext = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
-
- ext->extension = g_wcsdup (file_extension, -1);
- ext->extension_u8 = g_strdup (file_extension_u8);
- g_hash_table_insert (extensions, g_strdup (file_extension_folded), ext);
- }
-
- handler_rec_in_ext =
- g_hash_table_lookup (ext->handlers,
- program_id_folded);
-
- if (handler_rec_in_ext == NULL)
- {
- if (ext->chosen_handler == NULL)
- g_hash_table_insert (ext->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
- else if (ext->chosen_handler->handler_id_folded &&
- strcmp (ext->chosen_handler->handler_id_folded,
- program_id_folded) != 0)
- g_hash_table_insert (ext->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
- }
-
- handler_rec_in_ext =
- g_hash_table_lookup (app->supported_exts,
- file_extension_folded);
-
- if (handler_rec_in_ext == NULL)
- g_hash_table_insert (app->supported_exts,
- g_strdup (file_extension_folded),
- g_object_ref (handler_rec));
-
- g_free (file_extension_u8);
- g_free (file_extension_folded);
- }
-
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
+ get_file_ext (extension_handler, file_extension, app, FALSE);
}
g_win32_registry_value_iter_clear (&iter);
@@ -1898,18 +2176,8 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
{
- GWin32AppInfoHandler *handler_rec;
- GWin32AppInfoHandler *handler_rec_in_url;
- GWin32AppInfoURLSchema *schema;
- gunichar2 *program_command;
- gunichar2 *proxy_id;
- gunichar2 *proxy_command;
- GWin32RegistryKey *program_key;
- GWin32RegistryKey *proxy_key;
- gchar *program_id_u8;
- gchar *program_id_folded;
gchar *schema_u8;
- gchar *schema_folded;
+ gchar *schema_u8_folded;
if ((!g_win32_registry_value_iter_get_value_type (&iter,
&value_type,
@@ -1930,105 +2198,16 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
(schema_handler[0] == L'\0'))
continue;
- if (!follow_class_chain_to_handler (schema_handler,
- schema_handler_size,
- &program_command,
- &program_key,
- &proxy_id,
- &proxy_command,
- &proxy_key,
- &program_id_u8,
- &program_id_folded))
- continue;
-
- handler_rec = g_hash_table_lookup (handlers, program_id_folded);
- if (handler_rec == NULL)
- {
- handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
-
- handler_rec->proxy_key = proxy_key;
- handler_rec->key = program_key;
- handler_rec->handler_id =
- g_wcsdup (schema_handler, schema_handler_size);
- handler_rec->handler_id_folded =
- g_strdup (program_id_folded);
- handler_rec->handler_command = program_command ?
- g_wcsdup (program_command, -1) : NULL;
- handler_rec->proxy_id =
- proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
- handler_rec->proxy_command =
- proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ?
handler_rec->handler_command : handler_rec->proxy_command);
- read_handler_icon (proxy_key,
- program_key,
- &handler_rec->icon);
- g_hash_table_insert (handlers,
- g_strdup (program_id_folded),
- handler_rec);
- }
- else
- {
- g_clear_object (&program_key);
- g_clear_object (&proxy_key);
- }
+ if (g_utf16_to_utf8_and_fold (url_schema,
+ url_schema_len,
+ &schema_u8,
+ &schema_u8_folded))
+ get_url_association (schema_handler, url_schema, schema_u8, schema_u8_folded, app, FALSE);
- if (g_utf16_to_utf8_and_fold (url_schema,
- -1,
- &schema_u8,
- &schema_folded))
- {
- schema = g_hash_table_lookup (urls,
- schema_folded);
-
- if (schema == NULL)
- {
- schema = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
-
- schema->schema = g_wcsdup (url_schema, -1);
- schema->schema_u8 = g_strdup (schema_u8);
- schema->schema_folded =
- g_strdup (schema_folded);
- g_hash_table_insert (urls,
- g_strdup (schema_folded),
- schema);
- }
-
- handler_rec_in_url =
- g_hash_table_lookup (schema->handlers,
- program_id_folded);
-
- if (handler_rec_in_url == NULL)
- g_hash_table_insert (schema->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
-
- handler_rec_in_url =
- g_hash_table_lookup (app->supported_urls,
- schema_folded);
-
- if (handler_rec_in_url == NULL)
- g_hash_table_insert (app->supported_urls,
- g_strdup (schema_folded),
- g_object_ref (handler_rec));
-
- g_free (schema_u8);
- g_free (schema_folded);
- }
-
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
+ g_clear_pointer (&schema_u8, g_free);
+ g_clear_pointer (&schema_u8_folded, g_free);
}
g_win32_registry_value_iter_clear (&iter);
@@ -2037,29 +2216,26 @@ read_capable_app (gunichar2 *input_app_key_path, gboolean user_specific, gboolea
g_object_unref (associations);
}
- g_clear_pointer (&app_executable, g_free);
- g_clear_pointer (&app_executable_folded, g_free);
- g_clear_pointer (&app_dll_function, g_free);
g_clear_pointer (&fallback_friendly_name, g_free);
g_clear_pointer (&description, g_free);
g_clear_pointer (&icon_source, g_free);
g_clear_pointer (&narrow_application_name, g_free);
- g_clear_pointer (&shell_open_command, g_free);
g_object_unref (appkey);
- g_object_unref (shell_open_command_key);
g_object_unref (capabilities);
- g_free (canonical_name_u8);
- g_free (canonical_name_folded);
- g_free (app_key_path);
+ g_clear_pointer (&app_key_path_u8, g_free);
+ g_clear_pointer (&app_key_path_u8_folded, g_free);
+ g_clear_pointer (&canonical_name_u8, g_free);
+ g_clear_pointer (&canonical_name_folded, g_free);
}
+/* Iterates over subkeys in
HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
+ * and calls get_url_association() for each one that has a user-chosen handler.
+ */
static void
read_urls (GWin32RegistryKey *url_associations)
{
GWin32RegistrySubkeyIter url_iter;
- gunichar2 *url_schema;
- gsize url_schema_len;
if (url_associations == NULL)
return;
@@ -2069,302 +2245,259 @@ read_urls (GWin32RegistryKey *url_associations)
while (g_win32_registry_subkey_iter_next (&url_iter, TRUE, NULL))
{
- if (!g_win32_registry_subkey_iter_get_name_w (&url_iter,
- &url_schema,
- &url_schema_len,
- NULL))
- continue;
-
- get_url_association (url_schema);
+ gchar *schema_u8 = NULL;
+ gchar *schema_u8_folded = NULL;
+ const gunichar2 *url_schema = NULL;
+ gunichar2 *program_id = NULL;
+ GWin32RegistryKey *user_choice = NULL;
+ gsize url_schema_len;
+ GWin32RegistryValueType val_type;
+
+ if (g_win32_registry_subkey_iter_get_name_w (&url_iter,
+ &url_schema,
+ &url_schema_len,
+ NULL) &&
+ g_utf16_to_utf8_and_fold (url_schema,
+ url_schema_len,
+ &schema_u8,
+ &schema_u8_folded) &&
+ (user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
+ url_schema, USER_CHOICE,
+ NULL)) != NULL &&
+ g_win32_registry_key_get_value_w (user_choice,
+ NULL,
+ TRUE,
+ L"Progid",
+ &val_type,
+ (void **) &program_id,
+ NULL,
+ NULL) &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR)
+ get_url_association (program_id, url_schema, schema_u8, schema_u8_folded, NULL, TRUE);
+
+ g_clear_pointer (&program_id, g_free);
+ g_clear_pointer (&user_choice, g_object_unref);
+ g_clear_pointer (&schema_u8, g_free);
+ g_clear_pointer (&schema_u8_folded, g_free);
}
g_win32_registry_subkey_iter_clear (&url_iter);
}
+/* Reads an application that is only registered by the basename of its
+ * executable (and doesn't have Capabilities subkey).
+ * @incapable_app is the registry key for the app.
+ * @app_exe_basename is the basename of its executable.
+ */
static void
-read_exeapps (void)
+read_incapable_app (GWin32RegistryKey *incapable_app,
+ const gunichar2 *app_exe_basename,
+ const gchar *app_exe_basename_u8,
+ const gchar *app_exe_basename_u8_folded)
{
- GWin32RegistryKey *applications_key;
- GWin32RegistrySubkeyIter app_iter;
-
- applications_key =
- g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
+ GWin32RegistryValueIter sup_iter;
+ GWin32AppInfoApplication *app;
+ GList *verbs;
+ const reg_verb *preferred_verb;
+ gunichar2 *friendly_app_name;
+ gboolean success;
+ GWin32RegistryValueType vtype;
+ gboolean no_open_with;
+ GWin32RegistryKey *default_icon_key;
+ gunichar2 *icon_source;
+ GIcon *icon = NULL;
+ GWin32RegistryKey *supported_key;
- if (applications_key == NULL)
+ if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell"))
return;
- if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
- {
- g_object_unref (applications_key);
- return;
- }
-
- while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
- {
- gunichar2 *app_exe_basename;
- gsize app_exe_basename_len;
- GWin32RegistryKey *incapable_app;
- gunichar2 *friendly_app_name;
- gboolean success;
- gboolean no_open_with;
- GWin32RegistryValueType vtype;
- GWin32RegistryKey *default_icon_key;
- gunichar2 *icon_source;
- GIcon *icon = NULL;
- gchar *appexe;
- gchar *appexe_basename;
- gchar *appexe_folded;
- gchar *appexe_folded_basename;
- GWin32AppInfoApplication *app;
- GWin32RegistryKey *shell_open_command_key;
- gunichar2 *shell_open_command;
- GWin32RegistryKey *supported_key;
-
- if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
- &app_exe_basename,
- &app_exe_basename_len,
- NULL) ||
- !g_utf16_validate (app_exe_basename, app_exe_basename_len))
- continue;
-
- incapable_app =
- g_win32_registry_key_get_child_w (applications_key,
- app_exe_basename,
- NULL);
-
- if (incapable_app == NULL)
- continue;
-
- _g_win32_extract_executable (app_exe_basename,
- &appexe,
- &appexe_basename,
- &appexe_folded,
- &appexe_folded_basename,
- NULL);
-
- shell_open_command_key =
- g_win32_registry_key_get_child_w (incapable_app,
- L"shell\\open\\command",
- NULL);
-
- shell_open_command = NULL;
-
- if (shell_open_command_key != NULL)
- {
- success = g_win32_registry_key_get_value_w (shell_open_command_key,
- NULL,
- TRUE,
- L"",
- &vtype,
- (gpointer *) &shell_open_command,
- NULL,
- NULL);
-
- if (success &&
- (vtype != G_WIN32_REGISTRY_VALUE_STR ||
- !g_utf16_validate (shell_open_command, -1)))
- {
- g_clear_pointer (&shell_open_command, g_free);
- }
-
- g_object_unref (shell_open_command_key);
- }
-
- friendly_app_name = NULL;
- success = g_win32_registry_key_get_value_w (incapable_app,
- g_win32_registry_get_os_dirs_w (),
- TRUE,
- L"FriendlyAppName",
- &vtype,
- (void **) &friendly_app_name,
- NULL,
- NULL);
-
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
- g_clear_pointer (&friendly_app_name, g_free);
-
- no_open_with = FALSE;
- success = g_win32_registry_key_get_value_w (incapable_app,
- NULL,
- TRUE,
- L"NoOpenWith",
- &vtype,
- NULL,
- NULL,
- NULL);
-
- if (success)
- no_open_with = TRUE;
-
- default_icon_key =
- g_win32_registry_key_get_child_w (incapable_app,
- L"DefaultIcon",
- NULL);
-
- icon_source = NULL;
-
- if (default_icon_key != NULL)
- {
- success =
- g_win32_registry_key_get_value_w (default_icon_key,
- NULL,
+ app = get_app_object (apps_by_exe,
+ app_exe_basename,
+ app_exe_basename_u8,
+ app_exe_basename_u8_folded,
+ FALSE,
+ FALSE);
+
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ L"HKEY_CLASSES_ROOT\\Applications\\",
+ app_exe_basename,
+ TRUE,
+ app_add_verb,
+ app,
+ app);
+
+ friendly_app_name = NULL;
+ success = g_win32_registry_key_get_value_w (incapable_app,
+ g_win32_registry_get_os_dirs_w (),
TRUE,
- L"",
+ L"FriendlyAppName",
&vtype,
- (void **) &icon_source,
+ (void **) &friendly_app_name,
NULL,
NULL);
- if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
- g_clear_pointer (&icon_source, g_free);
+ if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&friendly_app_name, g_free);
+
+ no_open_with = g_win32_registry_key_get_value_w (incapable_app,
+ NULL,
+ TRUE,
+ L"NoOpenWith",
+ &vtype,
+ NULL,
+ NULL,
+ NULL);
- g_object_unref (default_icon_key);
- }
+ default_icon_key =
+ g_win32_registry_key_get_child_w (incapable_app,
+ L"DefaultIcon",
+ NULL);
- if (icon_source)
- {
- gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
- icon = g_themed_icon_new (name);
- g_free (name);
- }
+ icon_source = NULL;
- app = g_hash_table_lookup (apps_by_exe, appexe_folded_basename);
+ if (default_icon_key != NULL)
+ {
+ success =
+ g_win32_registry_key_get_value_w (default_icon_key,
+ NULL,
+ TRUE,
+ L"",
+ &vtype,
+ (void **) &icon_source,
+ NULL,
+ NULL);
- if (app == NULL)
- {
- app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
+ if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&icon_source, g_free);
- if (shell_open_command)
- {
- gchar *dll_function;
-
- _g_win32_extract_executable (shell_open_command,
- NULL,
- NULL,
- NULL,
- NULL,
- &dll_function);
- if (dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (shell_open_command);
- g_clear_pointer (&dll_function, g_free);
- }
+ g_object_unref (default_icon_key);
+ }
- app->command =
- shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL;
+ if (icon_source)
+ {
+ gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
+ if (name != NULL)
+ icon = g_themed_icon_new (name);
+ g_free (name);
+ }
- if (shell_open_command)
- app->command_u8 = g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
+ app->no_open_with = no_open_with;
- app->executable = g_strdup (appexe);
- app->executable_basename = &app->executable[appexe_basename - appexe];
- app->executable_folded = g_strdup (appexe_folded);
+ if (friendly_app_name &&
+ app->localized_pretty_name == NULL)
+ {
+ app->localized_pretty_name = g_wcsdup (friendly_app_name, -1);
+ g_clear_pointer (&app->localized_pretty_name_u8, g_free);
+ app->localized_pretty_name_u8 =
+ g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL);
+ }
- app->no_open_with = no_open_with;
+ if (icon && app->icon == NULL)
+ app->icon = g_object_ref (icon);
- if (friendly_app_name)
- {
- app->localized_pretty_name = g_wcsdup (friendly_app_name, -1);
- g_clear_pointer (&app->localized_pretty_name_u8, g_free);
- app->localized_pretty_name_u8 =
- g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL);
- }
+ supported_key =
+ g_win32_registry_key_get_child_w (incapable_app,
+ L"SupportedTypes",
+ NULL);
- if (icon)
- app->icon = g_object_ref (icon);
+ if (supported_key &&
+ g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
+ {
+ gunichar2 *ext_name;
+ gsize ext_name_len;
- app->user_specific = FALSE;
- app->default_app = FALSE;
+ while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
+ {
+ if ((!g_win32_registry_value_iter_get_name_w (&sup_iter,
+ &ext_name,
+ &ext_name_len,
+ NULL)) ||
+ (ext_name_len <= 0) ||
+ (ext_name[0] != L'.'))
+ continue;
- g_hash_table_insert (apps_by_exe,
- g_strdup (appexe_folded_basename),
- app);
+ get_file_ext (ext_name, ext_name, app, FALSE);
}
- supported_key =
- g_win32_registry_key_get_child_w (incapable_app,
- L"SupportedTypes",
- NULL);
+ g_win32_registry_value_iter_clear (&sup_iter);
+ }
- if (supported_key)
- {
- GWin32RegistryValueIter sup_iter;
- if (g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
- {
- gunichar2 *ext_name;
- gsize ext_name_len;
+ g_clear_object (&supported_key);
+ g_free (friendly_app_name);
+ g_free (icon_source);
- while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
- {
- gchar *ext_u8;
- gchar *ext_folded;
- GWin32AppInfoFileExtension *file_extn;
- gboolean file_ext_known;
-
- if ((!g_win32_registry_value_iter_get_name_w (&sup_iter,
- &ext_name,
- &ext_name_len,
- NULL)) ||
- (ext_name_len <= 0) ||
- (ext_name[0] != L'.') ||
- (!g_utf16_to_utf8_and_fold (ext_name,
- -1,
- &ext_u8,
- &ext_folded)))
- continue;
-
- file_extn = NULL;
- file_ext_known =
- g_hash_table_lookup_extended (extensions,
- ext_folded,
- NULL,
- (void **) &file_extn);
+ g_clear_object (&icon);
+}
- if (!file_ext_known)
- {
- file_extn =
- g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
- file_extn->extension = g_wcsdup (ext_name, -1);
- file_extn->extension_u8 = g_strdup (ext_u8);
- g_hash_table_insert (extensions,
- g_strdup (ext_folded),
- file_extn);
- }
+/* Iterates over subkeys of HKEY_CLASSES_ROOT\\Applications
+ * and calls read_incapable_app() for each one.
+ */
+static void
+read_exeapps (void)
+{
+ GWin32RegistryKey *applications_key;
+ GWin32RegistrySubkeyIter app_iter;
- g_hash_table_insert (file_extn->other_apps,
- g_strdup (appexe_folded),
- g_object_ref (app));
+ applications_key =
+ g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
- g_free (ext_u8);
- g_free (ext_folded);
- }
+ if (applications_key == NULL)
+ return;
- g_win32_registry_value_iter_clear (&sup_iter);
- }
+ if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
+ {
+ g_object_unref (applications_key);
+ return;
+ }
- g_object_unref (supported_key);
- }
+ while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
+ {
+ const gunichar2 *app_exe_basename;
+ gsize app_exe_basename_len;
+ GWin32RegistryKey *incapable_app;
+ gchar *app_exe_basename_u8;
+ gchar *app_exe_basename_u8_folded;
+
+ if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
+ &app_exe_basename,
+ &app_exe_basename_len,
+ NULL) ||
+ !g_utf16_to_utf8_and_fold (app_exe_basename,
+ app_exe_basename_len,
+ &app_exe_basename_u8,
+ &app_exe_basename_u8_folded))
+ continue;
+ incapable_app =
+ g_win32_registry_key_get_child_w (applications_key,
+ app_exe_basename,
+ NULL);
- g_free (appexe);
- g_free (appexe_folded);
- g_free (shell_open_command);
- g_free (friendly_app_name);
- g_free (icon_source);
+ if (incapable_app != NULL)
+ read_incapable_app (incapable_app,
+ app_exe_basename,
+ app_exe_basename_u8,
+ app_exe_basename_u8_folded);
- g_clear_object (&icon);
g_clear_object (&incapable_app);
+ g_clear_pointer (&app_exe_basename_u8, g_free);
+ g_clear_pointer (&app_exe_basename_u8_folded, g_free);
}
g_win32_registry_subkey_iter_clear (&app_iter);
g_object_unref (applications_key);
}
-
+/* Iterates over subkeys of
HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\
+ * and calls get_file_ext() for each associated handler
+ * (starting with user-chosen handler, if any)
+ */
static void
read_exts (GWin32RegistryKey *file_exts)
{
GWin32RegistrySubkeyIter ext_iter;
- gunichar2 *file_extension;
+ const gunichar2 *file_extension;
gsize file_extension_len;
if (file_exts == NULL)
@@ -2375,684 +2508,427 @@ read_exts (GWin32RegistryKey *file_exts)
while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
{
+ GWin32RegistryKey *open_with_progids;
+ gunichar2 *program_id;
+ GWin32RegistryValueIter iter;
+ gunichar2 *value_name;
+ gsize value_name_len;
+ GWin32RegistryValueType value_type;
+ GWin32RegistryKey *user_choice;
+
if (!g_win32_registry_subkey_iter_get_name_w (&ext_iter,
&file_extension,
&file_extension_len,
NULL))
continue;
- get_file_ext (file_extension);
- }
-
- g_win32_registry_subkey_iter_clear (&ext_iter);
-}
-
-static void
-read_class_extension (GWin32RegistryKey *classes_root,
- gunichar2 *class_name,
- gsize class_name_len)
-{
- gchar *ext_u8;
- gchar *ext_folded;
- GWin32AppInfoFileExtension *file_extn;
- GWin32AppInfoHandler *handler_rec;
- GWin32AppInfoHandler *handler_rec_in_ext;
- GWin32RegistryKey *class_key;
- gsize program_id_size;
- gunichar2 *program_id;
- gunichar2 *proxy_id;
- GWin32RegistryKey *program_key;
- GWin32RegistryKey *proxy_key;
- gunichar2 *program_command;
- gunichar2 *proxy_command;
-
- class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
-
- if (class_key == NULL)
- return;
-
- program_id = class_name;
- program_id_size = (class_name_len + 1) * sizeof (gunichar2);
- program_key = proxy_key = NULL;
- program_command = proxy_command = NULL;
-
- if (!follow_class_chain_to_handler (program_id,
- program_id_size,
- &program_command,
- &program_key,
- &proxy_id,
- &proxy_command,
- &proxy_key,
- &ext_u8,
- &ext_folded))
- {
- g_object_unref (class_key);
- return;
- }
-
-
- file_extn = g_hash_table_lookup (extensions, ext_folded);
- handler_rec = g_hash_table_lookup (handlers, ext_folded);
-
- if (file_extn == NULL)
- {
- file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
- file_extn->extension = g_wcsdup (class_name, -1);
- file_extn->extension_u8 = g_strdup (ext_u8);
- g_hash_table_insert (extensions, g_strdup (ext_folded), file_extn);
- }
-
- if (handler_rec == NULL)
- {
- handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
-
- handler_rec->proxy_key = proxy_key;
- handler_rec->key = program_key;
- handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
- handler_rec->handler_id_folded = g_strdup (ext_folded);
- handler_rec->handler_command =
- program_command ? g_wcsdup (program_command, -1) : NULL;
- handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
- handler_rec->proxy_command =
- proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ?
handler_rec->handler_command : handler_rec->proxy_command);
- read_handler_icon (proxy_key, program_key, &handler_rec->icon);
- g_hash_table_insert (handlers,
- g_strdup (ext_folded),
- handler_rec);
- }
- else
- {
- g_clear_object (&program_key);
- g_clear_object (&proxy_key);
- }
-
- handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
- ext_folded);
-
- if (file_extn->chosen_handler == NULL)
- g_hash_table_insert (file_extn->handlers,
- g_strdup (ext_folded),
- g_object_ref (handler_rec));
- else if (handler_rec_in_ext == NULL)
- {
- if (file_extn->chosen_handler->handler_id_folded &&
- strcmp (file_extn->chosen_handler->handler_id_folded,
- ext_folded) != 0)
- g_hash_table_insert (file_extn->handlers,
- g_strdup (ext_folded),
- g_object_ref (handler_rec));
- }
-
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
- g_free (ext_u8);
- g_free (ext_folded);
- g_object_unref (class_key);
-}
-
-static void
-read_class_url (GWin32RegistryKey *classes_root,
- gunichar2 *class_name,
- gsize class_name_len)
-{
- GWin32RegistryKey *class_key;
- gboolean success;
- GWin32RegistryValueType vtype;
- GWin32AppInfoURLSchema *schema_rec;
- GWin32AppInfoHandler *handler_rec;
- GWin32AppInfoHandler *handler_rec_in_url;
- gunichar2 *program_id;
- gsize program_id_size;
- gunichar2 *program_command;
- gunichar2 *proxy_id;
- gunichar2 *proxy_command;
- gchar *program_id_u8;
- gchar *program_id_folded;
- GWin32RegistryKey *program_key;
- GWin32RegistryKey *proxy_key;
-
- class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
-
- if (class_key == NULL)
- return;
-
- success = g_win32_registry_key_get_value_w (class_key,
- NULL,
- TRUE,
- L"URL Protocol",
- &vtype,
- NULL,
- NULL,
- NULL);
-
- if (!success ||
- vtype != G_WIN32_REGISTRY_VALUE_STR)
- {
- g_object_unref (class_key);
- return;
- }
-
- program_id = class_name;
- program_id_size = (class_name_len + 1) * sizeof (gunichar2);
- proxy_key = program_key = NULL;
- program_command = proxy_id = proxy_command = NULL;
-
- if (!follow_class_chain_to_handler (program_id,
- program_id_size,
- &program_command,
- &program_key,
- &proxy_id,
- &proxy_command,
- &proxy_key,
- &program_id_u8,
- &program_id_folded))
- {
- g_object_unref (class_key);
- return;
- }
-
- schema_rec = g_hash_table_lookup (urls, program_id_folded);
- handler_rec = g_hash_table_lookup (handlers, program_id_folded);
-
- if (handler_rec == NULL)
- {
- handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
-
- handler_rec->proxy_key = proxy_key;
- handler_rec->key = program_key;
- handler_rec->handler_id = g_wcsdup (program_id, program_id_size);
- handler_rec->handler_id_folded =
- g_strdup (program_id_folded);
- handler_rec->handler_command =
- program_command ? g_wcsdup (program_command, -1) : NULL;
- handler_rec->proxy_id = proxy_id ? g_wcsdup (proxy_id, -1) : NULL;
- handler_rec->proxy_command =
- proxy_command ? g_wcsdup (proxy_command, -1) : NULL;
- _g_win32_extract_executable (proxy_command ? proxy_command : program_command,
- &handler_rec->executable,
- &handler_rec->executable_basename,
- &handler_rec->executable_folded,
- NULL,
- &handler_rec->dll_function);
- if (handler_rec->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (handler_rec->handler_command ?
handler_rec->handler_command : handler_rec->proxy_command);
- read_handler_icon (proxy_key, program_key, &handler_rec->icon);
- g_hash_table_insert (handlers,
- g_strdup (program_id_folded),
- handler_rec);
- }
- else
- {
- g_clear_object (&program_key);
- g_clear_object (&proxy_key);
- }
-
- if (schema_rec == NULL)
- {
- schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
- schema_rec->schema = g_wcsdup (class_name, -1);
- schema_rec->schema_u8 = g_strdup (program_id_u8);
- schema_rec->schema_folded = g_strdup (program_id_folded);
- schema_rec->chosen_handler = g_object_ref (handler_rec);
- g_hash_table_insert (urls,
- g_strdup (program_id_folded),
- schema_rec);
- }
-
- if (schema_rec->chosen_handler == NULL)
- schema_rec->chosen_handler = g_object_ref (handler_rec);
-
- handler_rec_in_url = g_hash_table_lookup (schema_rec->handlers,
- program_id_folded);
-
- if (handler_rec_in_url == NULL && schema_rec->chosen_handler != handler_rec)
- g_hash_table_insert (schema_rec->handlers,
- g_strdup (program_id_folded),
- g_object_ref (handler_rec));
-
- g_free (program_id_u8);
- g_free (program_id_folded);
- g_free (program_command);
- g_free (proxy_id);
- g_free (proxy_command);
- g_object_unref (class_key);
-}
-
-static void
-read_classes (GWin32RegistryKey *classes_root)
-{
- GWin32RegistrySubkeyIter class_iter;
- gunichar2 *class_name;
- gsize class_name_len;
+ program_id = NULL;
+ user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, file_extension,
+ USER_CHOICE, NULL);
+ if (user_choice &&
+ g_win32_registry_key_get_value_w (user_choice,
+ NULL,
+ TRUE,
+ L"Progid",
+ &value_type,
+ (void **) &program_id,
+ NULL,
+ NULL) &&
+ value_type == G_WIN32_REGISTRY_VALUE_STR)
+ {
+ /* Note: program_id could be "ProgramID" or "Applications\\program.exe".
+ * The code still works, but handler_id might have a backslash
+ * in it - that might trip us up later on.
+ * Even though in that case this is logically an "application"
+ * registry entry, we don't treat it in any special way.
+ * We do scan that registry branch anyway, just not here.
+ */
+ get_file_ext (program_id, file_extension, NULL, TRUE);
+ }
- if (classes_root == NULL)
- return;
+ g_clear_object (&user_choice);
+ g_clear_pointer (&program_id, g_free);
- if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL))
- return;
+ open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
+ file_extension,
+ OPEN_WITH_PROGIDS,
+ NULL);
- while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL))
- {
- if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
- &class_name,
- &class_name_len,
- NULL)) ||
- (class_name_len <= 1))
+ if (open_with_progids == NULL)
continue;
- if (class_name[0] == L'.')
- read_class_extension (classes_root, class_name, class_name_len);
- else
+ if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
{
- gsize i;
+ g_clear_object (&open_with_progids);
+ continue;
+ }
- for (i = 0; i < class_name_len; i++)
- if (!iswalpha (class_name[i]))
- break;
+ while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
+ {
+ if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
+ &value_name_len,
+ NULL) ||
+ (value_name_len == 0))
+ continue;
- if (i == class_name_len)
- read_class_url (classes_root, class_name, class_name_len);
+ get_file_ext (value_name, file_extension, NULL, FALSE);
}
+
+ g_win32_registry_value_iter_clear (&iter);
+ g_clear_object (&open_with_progids);
}
- g_win32_registry_subkey_iter_clear (&class_iter);
+ g_win32_registry_subkey_iter_clear (&ext_iter);
}
+/* Iterates over subkeys in HKCR, calls
+ * get_file_ext() for any subkey that starts with ".",
+ * or get_url_association() for any subkey that could
+ * be a URL schema and has a "URL Protocol" value.
+ */
static void
-link_chosen_handlers (void)
+read_classes (GWin32RegistryKey *classes_root)
{
- GHashTableIter iter;
- GHashTableIter handler_iter;
- gchar *schema_folded;
- GWin32AppInfoURLSchema *schema;
- gchar *handler_id_folded;
- GWin32AppInfoHandler *handler;
- gchar *ext_folded;
- GWin32AppInfoFileExtension *ext;
+ GWin32RegistrySubkeyIter class_iter;
+ const gunichar2 *class_name;
+ gsize class_name_len;
- g_hash_table_iter_init (&iter, urls);
+ if (classes_root == NULL)
+ return;
- while (g_hash_table_iter_next (&iter,
- (gpointer *) &schema_folded,
- (gpointer *) &schema))
+ if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL))
+ return;
+
+ while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL))
{
- if (schema->chosen_handler != NULL)
+ if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
+ &class_name,
+ &class_name_len,
+ NULL)) ||
+ (class_name_len <= 1))
continue;
- g_hash_table_iter_init (&handler_iter, schema->handlers);
-
- while (g_hash_table_iter_next (&handler_iter,
- (gpointer *) &handler_id_folded,
- (gpointer *) &handler))
+ if (class_name[0] == L'.')
{
- gchar *proxy_id_folded;
+ GWin32RegistryKey *class_key;
+ GWin32RegistryValueIter iter;
+ GWin32RegistryKey *open_with_progids;
+ gunichar2 *value_name;
+ gsize value_name_len;
- if (schema->chosen_handler != NULL)
- break;
+ /* Read the data from the HKCR\\.ext (usually proxied
+ * to another HKCR subkey)
+ */
+ get_file_ext (class_name, class_name, NULL, FALSE);
- if (strcmp (handler_id_folded, schema_folded) != 0)
- continue;
+ class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
- if (handler->proxy_command &&
- handler->proxy_id &&
- g_utf16_to_utf8_and_fold (handler->proxy_id,
- -1,
- NULL,
- &proxy_id_folded))
- {
- GWin32AppInfoHandler *proxy;
+ if (class_key == NULL)
+ continue;
- proxy = g_hash_table_lookup (handlers, proxy_id_folded);
+ open_with_progids = g_win32_registry_key_get_child_w (class_key, L"OpenWithProgids", NULL);
+ g_clear_object (&class_key);
- if (proxy)
- {
- schema->chosen_handler = g_object_ref (proxy);
- g_debug ("Linking schema %s to proxy handler %c ? \"%S\" : %S\n",
- schema->schema_u8,
- schema->chosen_handler->proxy_id ? 'P' : 'T',
- schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id :
schema->chosen_handler->handler_id,
- schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command :
schema->chosen_handler->handler_command);
- }
+ if (open_with_progids == NULL)
+ continue;
- g_free (proxy_id_folded);
+ if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
+ {
+ g_clear_object (&open_with_progids);
+ continue;
}
- if (schema->chosen_handler == NULL)
+ /* Read the data for other handlers for this extension */
+ while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
{
- schema->chosen_handler = g_object_ref (handler);
- g_debug ("Linking schema %s to handler %c ? \"%S\" : %S\n",
- schema->schema_u8,
- schema->chosen_handler->proxy_id ? 'P' : 'T',
- schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id :
schema->chosen_handler->handler_id,
- schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command :
schema->chosen_handler->handler_command);
- }
- }
- }
+ if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
+ &value_name_len,
+ NULL) ||
+ (value_name_len == 0))
+ continue;
- g_hash_table_iter_init (&iter, extensions);
+ get_file_ext (value_name, class_name, NULL, FALSE);
+ }
- while (g_hash_table_iter_next (&iter,
- (gpointer *) &ext_folded,
- (gpointer *) &ext))
- {
- if (ext->chosen_handler != NULL)
- continue;
+ g_win32_registry_value_iter_clear (&iter);
+ g_clear_object (&open_with_progids);
+ }
+ else
+ {
+ gsize i;
+ GWin32RegistryKey *class_key;
+ gboolean success;
+ GWin32RegistryValueType vtype;
+ gchar *schema_u8;
+ gchar *schema_u8_folded;
- g_hash_table_iter_init (&handler_iter, ext->handlers);
+ for (i = 0; i < class_name_len; i++)
+ if (!iswalpha (class_name[i]))
+ break;
- while (g_hash_table_iter_next (&handler_iter,
- (gpointer *) &handler_id_folded,
- (gpointer *) &handler))
- {
- gchar *proxy_id_folded;
+ if (i != class_name_len)
+ continue;
- if (ext->chosen_handler != NULL)
- break;
+ class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
- if (strcmp (handler_id_folded, ext_folded) != 0)
+ if (class_key == NULL)
continue;
- if (handler->proxy_command &&
- handler->proxy_id &&
- g_utf16_to_utf8_and_fold (handler->proxy_id,
- -1,
- NULL,
- &proxy_id_folded))
- {
- GWin32AppInfoHandler *proxy;
+ success = g_win32_registry_key_get_value_w (class_key,
+ NULL,
+ TRUE,
+ L"URL Protocol",
+ &vtype,
+ NULL,
+ NULL,
+ NULL);
+ g_clear_object (&class_key);
- proxy = g_hash_table_lookup (handlers, proxy_id_folded);
+ if (!success ||
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
+ continue;
- if (proxy)
- {
- ext->chosen_handler = g_object_ref (proxy);
- g_debug ("Linking ext %s to proxy handler %c ? \"%S\" : %S\n",
- ext->extension_u8,
- ext->chosen_handler->proxy_id ? 'P' : 'T',
- ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id :
ext->chosen_handler->handler_id,
- ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command :
ext->chosen_handler->handler_command);
- }
+ if (!g_utf16_to_utf8_and_fold (class_name, -1, &schema_u8, &schema_u8_folded))
+ continue;
- g_free (proxy_id_folded);
- }
+ get_url_association (class_name, class_name, schema_u8, schema_u8_folded, NULL, FALSE);
- if (ext->chosen_handler == NULL)
- {
- ext->chosen_handler = g_object_ref (handler);
- g_debug ("Linking ext %s to handler %c ? \"%S\" : %S\n",
- ext->extension_u8,
- ext->chosen_handler->proxy_id ? 'P' : 'T',
- ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id :
ext->chosen_handler->handler_id,
- ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command :
ext->chosen_handler->handler_command);
- }
+ g_clear_pointer (&schema_u8, g_free);
+ g_clear_pointer (&schema_u8_folded, g_free);
}
}
+
+ g_win32_registry_subkey_iter_clear (&class_iter);
}
+/* Iterates over all handlers and over all apps,
+ * and links handler verbs to apps if a handler
+ * runs the same executable as one of the app verbs.
+ */
static void
-link_handlers_to_registered_apps (void)
+link_handlers_to_unregistered_apps (void)
{
GHashTableIter iter;
- GHashTableIter sup_iter;
- gchar *app_id_folded;
+ GHashTableIter app_iter;
+ GWin32AppInfoHandler *handler;
+ gchar *handler_id_fld;
GWin32AppInfoApplication *app;
- gchar *schema_folded;
- GWin32AppInfoURLSchema *schema;
- gchar *ext_folded;
- GWin32AppInfoFileExtension *ext;
- gsize unhandled_exts;
-
- g_hash_table_iter_init (&sup_iter, urls);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &schema_folded,
- (gpointer *) &schema))
- {
- if (schema->chosen_handler == NULL)
- g_debug ("WARNING: schema %s has no chosen handler\n", schema->schema_u8);
- }
- unhandled_exts= 0;
- g_hash_table_iter_init (&sup_iter, extensions);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &ext_folded,
- (gpointer *) &ext))
- {
- if (ext->chosen_handler == NULL)
- {
- g_debug ("WARNING: extension %s has no chosen handler\n",
- ext->extension_u8);
- unhandled_exts += 1;
- }
- }
+ gchar *canonical_name_fld;
+ gchar *appexe_fld_basename;
- g_hash_table_iter_init (&iter, apps_by_id);
+ g_hash_table_iter_init (&iter, handlers);
while (g_hash_table_iter_next (&iter,
- (gpointer *) &app_id_folded,
- (gpointer *) &app))
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
{
- if (app->supported_urls)
- {
- GWin32AppInfoHandler *handler;
+ gsize vi;
- g_hash_table_iter_init (&sup_iter, app->supported_urls);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &schema_folded,
- (gpointer *) &handler))
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+ const gchar *handler_exe_basename;
+ enum
{
- schema = g_hash_table_lookup (urls, schema_folded);
+ SH_UNKNOWN,
+ GOT_SH_INFO,
+ ERROR_GETTING_SH_INFO,
+ } have_stat_handler = SH_UNKNOWN;
+ GWin32PrivateStat handler_verb_exec_info;
- g_assert (schema != NULL);
+ handler_verb = _verb_idx (handler->verbs, vi);
- if (schema->chosen_handler != NULL &&
- schema->chosen_handler->app == NULL)
- {
- schema->chosen_handler->app = g_object_ref (app);
- g_debug ("Linking %S", app->canonical_name);
-
- if (app->localized_pretty_name)
- g_debug (" '%S'", app->localized_pretty_name);
- else if (app->pretty_name)
- g_debug (" '%S'", app->pretty_name);
- else
- g_debug (" '%s'", app->executable);
-
- if (app->command)
- g_debug (" %S", app->command);
-
- g_debug ("\n to schema %s handler %c ? \"%S\" : %S\n",
- schema->schema_u8,
- schema->chosen_handler->proxy_id ? 'P' : 'T',
- schema->chosen_handler->proxy_id ? schema->chosen_handler->proxy_id :
schema->chosen_handler->handler_id,
- schema->chosen_handler->proxy_command ? schema->chosen_handler->proxy_command :
schema->chosen_handler->handler_command);
- }
- }
+ if (handler_verb->app != NULL)
+ continue;
+
+ handler_exe_basename = g_utf8_find_basename (handler_verb->executable_folded, -1);
+ g_hash_table_iter_init (&app_iter, apps_by_id);
- g_hash_table_iter_init (&sup_iter, app->supported_urls);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &schema_folded,
- (gpointer *) &handler))
+ while (g_hash_table_iter_next (&app_iter,
+ (gpointer *) &canonical_name_fld,
+ (gpointer *) &app))
{
- if (handler->app == NULL)
- {
- handler->app = g_object_ref (app);
- g_debug ("Linking %S", app->canonical_name);
-
- if (app->localized_pretty_name)
- g_debug (" '%S'", app->localized_pretty_name);
- else if (app->pretty_name)
- g_debug (" '%S'", app->pretty_name);
- else
- g_debug (" '%s'", app->executable);
-
- if (app->command)
- g_debug (" %S", app->command);
-
- g_debug ("\n directly to schema handler to %c ? \"%S\" : %S\n",
- handler->proxy_id ? 'P' : 'T',
- handler->proxy_id ? handler->proxy_id : handler->handler_id,
- handler->proxy_command ? handler->proxy_command : handler->handler_command);
- }
- }
- }
+ GWin32AppInfoShellVerb *app_verb;
+ gsize ai;
- if (app->supported_exts)
- {
- GWin32AppInfoHandler *handler;
+ for (ai = 0; ai < app->verbs->len; ai++)
+ {
+ GWin32PrivateStat app_verb_exec_info;
+ const gchar *app_exe_basename;
+ app_verb = _verb_idx (app->verbs, ai);
- g_hash_table_iter_init (&sup_iter, app->supported_exts);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &ext_folded,
- (gpointer *) &handler))
- {
- ext = g_hash_table_lookup (extensions, ext_folded);
+ app_exe_basename = g_utf8_find_basename (app_verb->executable_folded, -1);
- g_assert (ext != NULL);
+ /* First check that the executable paths are identical */
+ if (g_strcmp0 (app_verb->executable_folded, handler_verb->executable_folded) != 0)
+ {
+ /* If not, check the basenames. If they are different, don't bother
+ * with further checks.
+ */
+ if (g_strcmp0 (app_exe_basename, handler_exe_basename) != 0)
+ continue;
+
+ /* Get filesystem IDs for both files.
+ * For the handler that is attempted only once.
+ */
+ if (have_stat_handler == SH_UNKNOWN)
+ {
+ if (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (handler_verb->executable_folded,
+ &handler_verb_exec_info) == 0)
+ have_stat_handler = GOT_SH_INFO;
+ else
+ have_stat_handler = ERROR_GETTING_SH_INFO;
+ }
+
+ if (have_stat_handler != GOT_SH_INFO ||
+ (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (app_verb->executable_folded,
+ &app_verb_exec_info) != 0) ||
+ app_verb_exec_info.file_index != handler_verb_exec_info.file_index)
+ continue;
+ }
- if (ext->chosen_handler != NULL &&
- ext->chosen_handler->app == NULL)
- {
- ext->chosen_handler->app = g_object_ref (app);
- g_debug ("Linking %S", app->canonical_name);
-
- if (app->localized_pretty_name)
- g_debug (" '%S'", app->localized_pretty_name);
- else if (app->pretty_name)
- g_debug (" '%S'", app->pretty_name);
- else
- g_debug (" '%s'", app->executable);
-
- if (app->command)
- g_debug (" %S", app->command);
-
- g_debug ("\n to ext %s handler %c ? \"%S\" : %S\n",
- ext->extension_u8,
- ext->chosen_handler->proxy_id ? 'P' : 'T',
- ext->chosen_handler->proxy_id ? ext->chosen_handler->proxy_id :
ext->chosen_handler->handler_id,
- ext->chosen_handler->proxy_command ? ext->chosen_handler->proxy_command :
ext->chosen_handler->handler_command);
+ handler_verb->app = g_object_ref (app);
+ break;
}
}
- g_hash_table_iter_init (&sup_iter, app->supported_exts);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &ext_folded,
- (gpointer *) &handler))
+ if (handler_verb->app != NULL)
+ continue;
+
+ g_hash_table_iter_init (&app_iter, apps_by_exe);
+
+ while (g_hash_table_iter_next (&app_iter,
+ (gpointer *) &appexe_fld_basename,
+ (gpointer *) &app))
{
- if (handler->app == NULL)
- {
- handler->app = g_object_ref (app);
- g_debug ("Linking %S", app->canonical_name);
-
- if (app->localized_pretty_name)
- g_debug (" '%S'", app->localized_pretty_name);
- else if (app->pretty_name)
- g_debug (" '%S'", app->pretty_name);
- else
- g_debug (" '%s'", app->executable);
-
- if (app->command)
- g_debug (" %S", app->command);
-
- g_debug ("\n directly to ext handler %c ? \"%S\" : %S\n",
- handler->proxy_id ? 'P' : 'T',
- handler->proxy_id ? handler->proxy_id : handler->handler_id,
- handler->proxy_command ? handler->proxy_command : handler->handler_command);
- }
- }
- }
- }
+ /* Use basename because apps_by_exe only has basenames */
+ if (g_strcmp0 (handler_exe_basename, appexe_fld_basename) != 0)
+ continue;
- g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
- unhandled_exts= 0;
- g_hash_table_iter_init (&sup_iter, extensions);
- while (g_hash_table_iter_next (&sup_iter,
- (gpointer *) &ext_folded,
- (gpointer *) &ext))
- {
- if (ext->chosen_handler == NULL)
- {
- g_debug ("WARNING: extension %s has no chosen handler\n",
- ext->extension_u8);
- unhandled_exts += 1;
+ handler_verb->app = g_object_ref (app);
+ break;
+ }
}
}
- g_debug ("%" G_GSIZE_FORMAT "undefhandled extensions\n", unhandled_exts);
}
+/* Finds all .ext and schema: handler verbs that have no app linked to them,
+ * creates a "fake app" object and links these verbs to these
+ * objects. Objects are identified by the full path to
+ * the executable being run, thus multiple different invocations
+ * get grouped in a more-or-less natural way.
+ * The iteration goes separately over .ext and schema: handlers
+ * (instead of the global handlers hashmap) to allow us to
+ * put the handlers into supported_urls or supported_exts as
+ * needed (handler objects themselves have no knowledge of extensions
+ * and/or URLs they are associated with).
+ */
static void
-link_handlers_to_unregistered_apps (void)
+link_handlers_to_fake_apps (void)
{
GHashTableIter iter;
- GHashTableIter app_iter;
+ GHashTableIter handler_iter;
+ gchar *extension_utf8_folded;
+ GWin32AppInfoFileExtension *file_extn;
+ gchar *handler_id_fld;
GWin32AppInfoHandler *handler;
- gchar *handler_id_fc;
- GWin32AppInfoApplication *app;
- gchar *canonical_name_fc;
- gchar *appexe_fc_basename;
+ gchar *url_utf8_folded;
+ GWin32AppInfoURLSchema *schema;
- g_hash_table_iter_init (&iter, handlers);
+ g_hash_table_iter_init (&iter, extensions);
while (g_hash_table_iter_next (&iter,
- (gpointer *) &handler_id_fc,
- (gpointer *) &handler))
+ (gpointer *) &extension_utf8_folded,
+ (gpointer *) &file_extn))
{
- gchar *hndexe_fc_basename;
-
- if ((handler->app != NULL) ||
- (handler->executable_folded == NULL))
- continue;
-
- g_hash_table_iter_init (&app_iter, apps_by_id);
-
- while (g_hash_table_iter_next (&app_iter,
- (gpointer *) &canonical_name_fc,
- (gpointer *) &app))
+ g_hash_table_iter_init (&handler_iter, file_extn->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
{
- if (app->executable_folded == NULL)
- continue;
-
- if (strcmp (app->executable_folded,
- handler->executable_folded) != 0)
- continue;
-
- handler->app = app;
- break;
- }
+ gsize vi;
- if (handler->app != NULL)
- continue;
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+ GWin32AppInfoApplication *app;
+ gunichar2 *exename_utf16;
+ handler_verb = _verb_idx (handler->verbs, vi);
- hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
+ if (handler_verb->app != NULL)
+ continue;
- if (hndexe_fc_basename == NULL)
- continue;
+ exename_utf16 = g_utf8_to_utf16 (handler_verb->executable, -1, NULL, NULL, NULL);
+ if (exename_utf16 == NULL)
+ continue;
- g_hash_table_iter_init (&app_iter, apps_by_exe);
+ app = get_app_object (fake_apps,
+ exename_utf16,
+ handler_verb->executable,
+ handler_verb->executable_folded,
+ FALSE,
+ FALSE);
+ g_clear_pointer (&exename_utf16, g_free);
+ handler_verb->app = g_object_ref (app);
+
+ app_add_verb (app,
+ app,
+ handler_verb->verb_name,
+ handler_verb->command,
+ handler_verb->command_utf8,
+ handler_verb->verb_displayname,
+ TRUE,
+ TRUE);
+ g_hash_table_insert (app->supported_exts,
+ g_strdup (extension_utf8_folded),
+ g_object_ref (handler));
+ }
+ }
+ }
- while ((hndexe_fc_basename != NULL) &&
- (g_hash_table_iter_next (&app_iter,
- (gpointer *) &appexe_fc_basename,
- (gpointer *) &app)))
+ g_hash_table_iter_init (&iter, urls);
+ while (g_hash_table_iter_next (&iter,
+ (gpointer *) &url_utf8_folded,
+ (gpointer *) &schema))
+ {
+ g_hash_table_iter_init (&handler_iter, schema->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
{
- /* Use basename because apps_by_exe only has basenames */
- if (strcmp (hndexe_fc_basename, appexe_fc_basename) != 0)
- continue;
+ gsize vi;
- handler->app = app;
- break;
- }
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+ GWin32AppInfoApplication *app;
+ gchar *command_utf8_folded;
+ handler_verb = _verb_idx (handler->verbs, vi);
- g_free (hndexe_fc_basename);
+ if (handler_verb->app != NULL)
+ continue;
- if (handler->app == NULL)
- g_debug ("WARNING: handler that runs %s has no corresponding app\n",
- handler->executable);
+ command_utf8_folded = g_utf8_casefold (handler_verb->command_utf8, -1);
+ app = get_app_object (fake_apps,
+ handler_verb->command,
+ handler_verb->command_utf8,
+ command_utf8_folded,
+ FALSE,
+ FALSE);
+ g_clear_pointer (&command_utf8_folded, g_free);
+ handler_verb->app = g_object_ref (app);
+
+ app_add_verb (app,
+ app,
+ handler_verb->verb_name,
+ handler_verb->command,
+ handler_verb->command_utf8,
+ handler_verb->verb_displayname,
+ TRUE,
+ TRUE);
+ g_hash_table_insert (app->supported_urls,
+ g_strdup (url_utf8_folded),
+ g_object_ref (handler));
+ }
+ }
}
}
@@ -3083,6 +2959,7 @@ update_registry_data (void)
g_clear_pointer (&apps_by_id, g_hash_table_destroy);
g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
+ g_clear_pointer (&fake_apps, g_hash_table_destroy);
g_clear_pointer (&urls, g_hash_table_destroy);
g_clear_pointer (&extensions, g_hash_table_destroy);
g_clear_pointer (&handlers, g_hash_table_destroy);
@@ -3104,6 +2981,8 @@ update_registry_data (void)
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
apps_by_exe =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ fake_apps =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
urls =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
extensions =
@@ -3134,9 +3013,8 @@ update_registry_data (void)
exeapp_end = GetTickCount ();
read_classes (classes_root);
classes_end = GetTickCount ();
- link_chosen_handlers ();
- link_handlers_to_registered_apps ();
link_handlers_to_unregistered_apps ();
+ link_handlers_to_fake_apps ();
postproc_end = GetTickCount ();
g_debug ("Collecting capable appnames: %lums\n"
@@ -3147,7 +3025,7 @@ update_registry_data (void)
"Reading exe-only apps:...... %lums\n"
"Reading classes: %lums\n"
"Postprocessing:..............%lums\n"
- "TOTAL: %lums\n",
+ "TOTAL: %lums",
collect_end - collect_start,
alloc_end - collect_end,
capable_end - alloc_end,
@@ -3337,7 +3215,7 @@ g_win32_app_info_finalize (GObject *object)
info = G_WIN32_APP_INFO (object);
- g_clear_pointer (&info->supported_types, g_free);
+ g_clear_pointer (&info->supported_types, g_strfreev);
g_clear_object (&info->app);
g_clear_object (&info->handler);
@@ -3392,7 +3270,7 @@ g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
if (!ext)
continue;
- new_info->supported_types[i] = (gchar *) ext;
+ new_info->supported_types[i] = g_strdup ((gchar *) ext);
i += 1;
}
@@ -3444,30 +3322,41 @@ static gboolean
g_win32_app_info_equal (GAppInfo *appinfo1,
GAppInfo *appinfo2)
{
+ GWin32AppInfoShellVerb *shverb1 = NULL;
+ GWin32AppInfoShellVerb *shverb2 = NULL;
GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
+ GWin32AppInfoApplication *app1 = info1->app;
+ GWin32AppInfoApplication *app2 = info2->app;
- if (info1->app == NULL ||
- info2->app == NULL)
+ if (app1 == NULL ||
+ app2 == NULL)
return info1 == info2;
- if (info1->app->canonical_name_folded != NULL &&
- info2->app->canonical_name_folded != NULL)
- return (strcmp (info1->app->canonical_name_folded,
- info2->app->canonical_name_folded)) == 0;
+ if (app1->canonical_name_folded != NULL &&
+ app2->canonical_name_folded != NULL)
+ return (g_strcmp0 (app1->canonical_name_folded,
+ app2->canonical_name_folded)) == 0;
- if (info1->app->executable_folded != NULL &&
- info2->app->executable_folded != NULL)
- return (strcmp (info1->app->executable_folded,
- info2->app->executable_folded)) == 0;
+ if (app1->verbs->len > 0 &&
+ app2->verbs->len > 0)
+ {
+ shverb1 = _verb_idx (app1->verbs, 0);
+ shverb2 = _verb_idx (app2->verbs, 0);
+ if (shverb1->executable_folded != NULL &&
+ shverb2->executable_folded != NULL)
+ return (g_strcmp0 (shverb1->executable_folded,
+ shverb2->executable_folded)) == 0;
+ }
- return info1->app == info2->app;
+ return app1 == app2;
}
static const char *
g_win32_app_info_get_id (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+ GWin32AppInfoShellVerb *shverb;
if (info->app == NULL)
return NULL;
@@ -3475,8 +3364,9 @@ g_win32_app_info_get_id (GAppInfo *appinfo)
if (info->app->canonical_name_u8)
return info->app->canonical_name_u8;
- if (info->app->executable_basename)
- return info->app->executable_basename;
+ if (info->app->verbs->len > 0 &&
+ (shverb = _verb_idx (info->app->verbs, 0))->executable_basename != NULL)
+ return shverb->executable_basename;
return NULL;
}
@@ -3527,7 +3417,10 @@ g_win32_app_info_get_executable (GAppInfo *appinfo)
if (info->app == NULL)
return NULL;
- return info->app->executable;
+ if (info->app->verbs->len > 0)
+ return _verb_idx (info->app->verbs, 0)->executable;
+
+ return NULL;
}
static const char *
@@ -3538,7 +3431,10 @@ g_win32_app_info_get_commandline (GAppInfo *appinfo)
if (info->app == NULL)
return NULL;
- return info->app->command_u8;
+ if (info->app->verbs->len > 0)
+ return _verb_idx (info->app->verbs, 0)->command_utf8;
+
+ return NULL;
}
static GIcon *
@@ -3842,14 +3738,6 @@ expand_application_parameters (GWin32AppInfo *info,
gboolean res;
gchar *a_char;
- if (exec_line == NULL)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- P_("Application registry did not specify"
- " a shell\\open\\command"));
- return FALSE;
- }
-
expanded_exec = g_string_new (NULL);
res = FALSE;
@@ -3897,42 +3785,44 @@ expand_application_parameters (GWin32AppInfo *info,
static gchar *
-get_appath_for_exe (gunichar2 *exe_basename)
+get_appath_for_exe (const gchar *exe_basename)
{
GWin32RegistryKey *apppath_key = NULL;
GWin32RegistryValueType val_type;
- gunichar2 *appath = NULL;
+ gchar *appath = NULL;
gboolean got_value;
- gchar *result = NULL;
+ gchar *key_path = g_strdup_printf ("HKEY_LOCAL_MACHINE\\"
+ "SOFTWARE\\"
+ "Microsoft\\"
+ "Windows\\"
+ "CurrentVersion\\"
+ "App Paths\\"
+ "%s", exe_basename);
- apppath_key = _g_win32_registry_key_build_and_new_w (NULL, L"HKEY_LOCAL_MACHINE\\"
- L"\\SOFTWARE"
- L"\\Microsoft"
- L"\\Windows"
- L"\\CurrentVersion"
- L"\\App Paths\\",
- exe_basename, NULL);
+ apppath_key = g_win32_registry_key_new (key_path, NULL);
+ g_clear_pointer (&key_path, g_free);
if (apppath_key == NULL)
return NULL;
- got_value = g_win32_registry_key_get_value_w (apppath_key,
- NULL,
- TRUE,
- L"Path",
- &val_type,
- (void **) &appath,
- NULL,
- NULL);
+ got_value = g_win32_registry_key_get_value (apppath_key,
+ NULL,
+ TRUE,
+ "Path",
+ &val_type,
+ (void **) &appath,
+ NULL,
+ NULL);
g_object_unref (apppath_key);
- if (got_value && val_type == G_WIN32_REGISTRY_VALUE_STR)
- result = g_utf16_to_utf8 (appath, -1, NULL,NULL, NULL);
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR)
+ return appath;
g_clear_pointer (&appath, g_free);
- return result;
+ return appath;
}
@@ -3946,9 +3836,9 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
gboolean completed = FALSE;
char **argv, **envp;
int argc;
- gchar *command;
+ const gchar *command;
gchar *apppath;
- gunichar2 *exe_basename;
+ GWin32AppInfoShellVerb *shverb;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (info->app != NULL, FALSE);
@@ -3960,52 +3850,32 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
else
envp = g_get_environ ();
- command = NULL;
- exe_basename = NULL;
+ shverb = NULL;
- if (info->handler)
- {
- if (info->handler->handler_command)
- {
- command = g_utf16_to_utf8 (info->handler->handler_command,
- -1,
- NULL,
- NULL,
- NULL);
- exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
- -1,
- NULL,
- NULL,
- NULL);
- }
- else if (info->handler->proxy_command)
- {
- command = g_utf16_to_utf8 (info->handler->proxy_command,
- -1,
- NULL,
- NULL,
- NULL);
- exe_basename = g_utf8_to_utf16 (info->handler->executable_basename,
- -1,
- NULL,
- NULL,
- NULL);
- }
- }
+ if (info->handler != NULL &&
+ info->handler->verbs->len > 0)
+ shverb = _verb_idx (info->handler->verbs, 0);
+ else if (info->app->verbs->len > 0)
+ shverb = _verb_idx (info->app->verbs, 0);
- if (command == NULL)
+ if (shverb == NULL)
{
- command = g_strdup (info->app->command_u8);
- exe_basename = g_utf8_to_utf16 (info->app->executable_basename,
- -1,
- NULL,
- NULL,
- NULL);
- }
+ if (info->handler == NULL)
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ P_("The app ‘%s’ in the application object has no verbs"),
+ g_win32_appinfo_application_get_some_name (info->app));
+ else if (info->handler->verbs->len == 0)
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ P_("The app ‘%s’ and the handler ‘%s’ in the application object have no verbs"),
+ g_win32_appinfo_application_get_some_name (info->app),
+ info->handler->handler_id_folded);
- apppath = get_appath_for_exe (exe_basename);
+ return FALSE;
+ }
- g_free (exe_basename);
+ g_assert (shverb->command_utf8 != NULL);
+ command = shverb->command_utf8;
+ apppath = get_appath_for_exe (shverb->executable_basename);
if (apppath)
{
@@ -4095,7 +3965,6 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
out:
g_strfreev (argv);
g_strfreev (envp);
- g_free (command);
return completed;
}
@@ -4266,7 +4135,9 @@ g_app_info_create_from_commandline (const char *commandline,
info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
- app->command = g_steal_pointer (&app_command);
+ app->no_open_with = FALSE;
+ app->user_specific = FALSE;
+ app->default_app = FALSE;
if (application_name)
{
@@ -4279,21 +4150,16 @@ g_app_info_create_from_commandline (const char *commandline,
app->canonical_name_folded = g_utf8_casefold (application_name, -1);
}
- _g_win32_extract_executable (app->command,
- &app->executable,
- &app->executable_basename,
- &app->executable_folded,
- NULL,
- &app->dll_function);
- if (app->dll_function != NULL)
- _g_win32_fixup_broken_microsoft_rundll_commandline (app->command);
-
- app->command_u8 = g_utf16_to_utf8 (app->command, -1, NULL, NULL, NULL);
-
- app->no_open_with = FALSE;
- app->user_specific = FALSE;
- app->default_app = FALSE;
+ app_add_verb (app,
+ app,
+ L"open",
+ app_command,
+ commandline,
+ "open",
+ TRUE,
+ FALSE);
+ g_clear_pointer (&app_command, g_free);
info->app = app;
info->handler = NULL;
@@ -4333,9 +4199,10 @@ g_win32_app_info_iface_init (GAppInfoIface *iface)
GAppInfo *
g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
{
- GWin32AppInfoURLSchema *scheme;
+ GWin32AppInfoURLSchema *scheme = NULL;
char *scheme_down;
GAppInfo *result;
+ GWin32AppInfoShellVerb *shverb;
scheme_down = g_utf8_casefold (uri_scheme, -1);
@@ -4345,30 +4212,28 @@ g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
if (strcmp (scheme_down, "file") == 0)
{
g_free (scheme_down);
+
return NULL;
}
g_win32_appinfo_init ();
G_LOCK (gio_win32_appinfo);
- scheme = g_hash_table_lookup (urls, scheme_down);
+ g_set_object (&scheme, g_hash_table_lookup (urls, scheme_down));
g_free (scheme_down);
- if (scheme)
- g_object_ref (scheme);
-
G_UNLOCK (gio_win32_appinfo);
result = NULL;
if (scheme != NULL &&
scheme->chosen_handler != NULL &&
- scheme->chosen_handler->app != NULL)
- result = g_win32_app_info_new_from_app (scheme->chosen_handler->app,
+ scheme->chosen_handler->verbs->len > 0 &&
+ (shverb = _verb_idx (scheme->chosen_handler->verbs, 0))->app != NULL)
+ result = g_win32_app_info_new_from_app (shverb->app,
scheme->chosen_handler);
- if (scheme)
- g_object_unref (scheme);
+ g_clear_object (&scheme);
return result;
}
@@ -4377,12 +4242,10 @@ GAppInfo *
g_app_info_get_default_for_type (const char *content_type,
gboolean must_support_uris)
{
- GWin32AppInfoFileExtension *ext;
+ GWin32AppInfoFileExtension *ext = NULL;
char *ext_down;
- GWin32AppInfoHandler *handler;
GAppInfo *result;
- GWin32AppInfoApplication *app;
- GHashTableIter iter;
+ GWin32AppInfoShellVerb *shverb;
ext_down = g_utf8_casefold (content_type, -1);
@@ -4393,56 +4256,47 @@ g_app_info_get_default_for_type (const char *content_type,
G_LOCK (gio_win32_appinfo);
/* Assuming that "content_type" is a file extension, not a MIME type */
- ext = g_hash_table_lookup (extensions, ext_down);
+ g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
g_free (ext_down);
- result = NULL;
+ G_UNLOCK (gio_win32_appinfo);
- if (ext != NULL)
- g_object_ref (ext);
+ if (ext == NULL)
+ return NULL;
- G_UNLOCK (gio_win32_appinfo);
+ result = NULL;
- if (ext != NULL)
+ if (ext->chosen_handler != NULL &&
+ ext->chosen_handler->verbs->len > 0 &&
+ (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL &&
+ (!must_support_uris ||
+ g_win32_app_supports_uris (shverb->app)))
+ result = g_win32_app_info_new_from_app (shverb->app,
+ ext->chosen_handler);
+ else
{
- if (ext->chosen_handler != NULL &&
- ext->chosen_handler->app != NULL &&
- (!must_support_uris ||
- g_win32_app_supports_uris (ext->chosen_handler->app)))
- result = g_win32_app_info_new_from_app (ext->chosen_handler->app,
- ext->chosen_handler);
- else
+ GHashTableIter iter;
+ GWin32AppInfoHandler *handler;
+
+ g_hash_table_iter_init (&iter, ext->handlers);
+
+ while (result == NULL &&
+ g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
{
- g_hash_table_iter_init (&iter, ext->handlers);
+ if (handler->verbs->len == 0)
+ continue;
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
- {
- if (handler->app &&
- (!must_support_uris ||
- g_win32_app_supports_uris (ext->chosen_handler->app)))
- {
- result = g_win32_app_info_new_from_app (handler->app, handler);
- break;
- }
- }
+ shverb = _verb_idx (handler->verbs, 0);
- if (result == NULL)
- {
- g_hash_table_iter_init (&iter, ext->other_apps);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
- {
- if (!must_support_uris ||
- g_win32_app_supports_uris (ext->chosen_handler->app))
- {
- result = g_win32_app_info_new_from_app (app, NULL);
- break;
- }
- }
- }
+ if (shverb->app &&
+ (!must_support_uris ||
+ g_win32_app_supports_uris (shverb->app)))
+ result = g_win32_app_info_new_from_app (shverb->app, handler);
}
- g_object_unref (ext);
}
+ g_clear_object (&ext);
+
return result;
}
@@ -4478,12 +4332,13 @@ g_app_info_get_all (void)
GList *
g_app_info_get_all_for_type (const char *content_type)
{
- GWin32AppInfoFileExtension *ext;
+ GWin32AppInfoFileExtension *ext = NULL;
char *ext_down;
GWin32AppInfoHandler *handler;
- GWin32AppInfoApplication *app;
GHashTableIter iter;
+ GHashTable *apps = NULL;
GList *result;
+ GWin32AppInfoShellVerb *shverb;
ext_down = g_utf8_casefold (content_type, -1);
@@ -4494,46 +4349,52 @@ g_app_info_get_all_for_type (const char *content_type)
G_LOCK (gio_win32_appinfo);
/* Assuming that "content_type" is a file extension, not a MIME type */
- ext = g_hash_table_lookup (extensions, ext_down);
+ g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
g_free (ext_down);
- result = NULL;
-
- if (ext != NULL)
- g_object_ref (ext);
-
G_UNLOCK (gio_win32_appinfo);
if (ext == NULL)
return NULL;
+ result = NULL;
+ /* Used as a set to ensure uniqueness */
+ apps = g_hash_table_new (g_direct_hash, g_direct_equal);
+
if (ext->chosen_handler != NULL &&
- ext->chosen_handler->app != NULL)
- result = g_list_prepend (result,
- g_win32_app_info_new_from_app (ext->chosen_handler->app,
- ext->chosen_handler));
+ ext->chosen_handler->verbs->len > 0 &&
+ (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL)
+ {
+ g_hash_table_add (apps, shverb->app);
+ result = g_list_prepend (result,
+ g_win32_app_info_new_from_app (shverb->app,
+ ext->chosen_handler));
+ }
g_hash_table_iter_init (&iter, ext->handlers);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
{
- if (handler->app &&
- (ext->chosen_handler == NULL || ext->chosen_handler->app != handler->app))
- result = g_list_prepend (result,
- g_win32_app_info_new_from_app (handler->app,
- handler));
- }
+ gsize vi;
- g_hash_table_iter_init (&iter, ext->other_apps);
+ for (vi = 0; vi < handler->verbs->len; vi++)
+ {
+ shverb = _verb_idx (handler->verbs, vi);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app))
- {
- result = g_list_prepend (result, g_win32_app_info_new_from_app (app, NULL));
- }
+ if (shverb->app == NULL ||
+ g_hash_table_contains (apps, shverb->app))
+ continue;
- g_object_unref (ext);
+ g_hash_table_add (apps, shverb->app);
+ result = g_list_prepend (result,
+ g_win32_app_info_new_from_app (shverb->app,
+ handler));
+ }
+ }
+ g_clear_object (&ext);
result = g_list_reverse (result);
+ g_hash_table_unref (apps);
return result;
}
diff --git a/gio/gwin32registrykey.c b/gio/gwin32registrykey.c
index 7b4905347..6b24fdd90 100644
--- a/gio/gwin32registrykey.c
+++ b/gio/gwin32registrykey.c
@@ -946,7 +946,7 @@ g_win32_registry_subkey_iter_next (GWin32RegistrySubkeyIter *iter,
**/
gboolean
g_win32_registry_subkey_iter_get_name_w (GWin32RegistrySubkeyIter *iter,
- gunichar2 **subkey_name,
+ const gunichar2 **subkey_name,
gsize *subkey_name_len,
GError **error)
{
@@ -988,7 +988,7 @@ g_win32_registry_subkey_iter_get_name_w (GWin32RegistrySubkeyIter *iter,
**/
gboolean
g_win32_registry_subkey_iter_get_name (GWin32RegistrySubkeyIter *iter,
- gchar **subkey_name,
+ const gchar **subkey_name,
gsize *subkey_name_len,
GError **error)
{
diff --git a/gio/gwin32registrykey.h b/gio/gwin32registrykey.h
index 28b57a843..f92a10caf 100644
--- a/gio/gwin32registrykey.h
+++ b/gio/gwin32registrykey.h
@@ -191,12 +191,12 @@ gboolean g_win32_registry_subkey_iter_next (GWin32RegistrySubk
GError **error);
GLIB_AVAILABLE_IN_2_46
gboolean g_win32_registry_subkey_iter_get_name (GWin32RegistrySubkeyIter *iter,
- gchar **subkey_name,
+ const gchar **subkey_name,
gsize
*subkey_name_len,
GError **error);
GLIB_AVAILABLE_IN_2_46
gboolean g_win32_registry_subkey_iter_get_name_w (GWin32RegistrySubkeyIter *iter,
- gunichar2 **subkey_name,
+ const gunichar2 **subkey_name,
gsize
*subkey_name_len,
GError **error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]