[glib] GWin32AppInfo rewrite



commit 4d800e4d86db6825dd3c508c83352b9a4cd24350
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Fri Aug 29 08:53:35 2014 +0000

    GWin32AppInfo rewrite
    
    - On first call scan the registry, collect information about URI protocols,
      file extensions, applications and handlers, store that as a set of
      interconnected structures in several hash tables
    - Watch the registry keys, re-scan the registry when any one of them changes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=666831

 gio/gwin32appinfo.c | 4875 +++++++++++++++++++++++++++++++++++++++++++++------
 gio/gwin32appinfo.h |    4 +-
 2 files changed, 4376 insertions(+), 503 deletions(-)
---
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index f1697c8..5b94157 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -1,6 +1,7 @@
 /* GIO - GLib Input, Output and Streaming Library
  * 
  * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2014 Руслан Ижбулатов
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,7 +16,8 @@
  * You should have received a copy of the GNU Lesser General
  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
- * Author: Alexander Larsson <alexl redhat com>
+ * Authors: Alexander Larsson <alexl redhat com>
+ *          Руслан Ижбулатов  <lrn1986 gmail com>
  */
 
 #include "config.h"
@@ -29,41 +31,3429 @@
 #include "gfile.h"
 #include <glib/gstdio.h>
 #include "glibintl.h"
+#include <gio/gwin32registrykey.h>
 
 #include <windows.h>
-#include <shlwapi.h>
 
+/* We need to watch 8 places:
+ * 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
+ *    (anything below that key)
+ *    On change: re-enumerate subkeys, read their values.
+ * 1) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
+ *    (anything below that key)
+ *    On change: re-enumerate subkeys
+ * 2) HKEY_CURRENT_USER\\Software\\Clients (anything below that key)
+ *    On change: re-read the whole hierarchy of handlers
+ * 3) HKEY_LOCAL_MACHINE\\Software\\Clients (anything below that key)
+ *    On change: re-read the whole hierarchy of handlers
+ * 4) HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications (values of that key)
+ *    On change: re-read the value list of registered applications
+ * 5) HKEY_CURRENT_USER\\Software\\RegisteredApplications (values of that key)
+ *    On change: re-read the value list of registered applications
+ * 6) HKEY_CLASSES_ROOT\\Applications (anything below that key)
+ *    On change: re-read the whole hierarchy of apps
+ * 7) HKEY_CLASSES_ROOT (only its subkeys)
+ *    On change: re-enumerate subkeys, try to filter out wrong names.
+ *
+ */
+
+typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
+typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
+typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
+typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
+
+typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
+typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
+typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
+typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
+
+struct _GWin32AppInfoURLSchemaClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GWin32AppInfoFileExtensionClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GWin32AppInfoHandlerClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GWin32AppInfoApplicationClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GWin32AppInfoURLSchema {
+  GObject parent_instance;
+
+  /* url schema (stuff before ':') */
+  gunichar2 *schema;
+
+  /* url schema (stuff before ':'), in UTF-8 */
+  gchar *schema_u8;
+
+  /* url schema (stuff before ':'), in UTF-8, folded */
+  gchar *schema_folded;
+
+  /* Handler currently selected for this schema */
+  GWin32AppInfoHandler *chosen_handler;
+
+  /* Maps folded handler IDs -> to other handlers for this schema */
+  GHashTable *handlers;
+};
+
+struct _GWin32AppInfoHandler {
+  GObject parent_instance;
+
+  /* Class name in HKCR */
+  gunichar2 *handler_id;
+
+  /* Handler registry key (HKCR\\handler_id). Can be used to watch this handler. */
+  GWin32RegistryKey *key;
+
+  /* Class name in HKCR, UTF-8, folded */
+  gchar *handler_id_folded;
+
+  /* shell/open/command default value for the class named by class_id */
+  gunichar2 *handler_command;
+
+  /* If handler_id class has no command, it might point to another class */
+  gunichar2 *proxy_id;
+
+  /* Proxy registry key (HKCR\\proxy_id). Can be used to watch handler's proxy. */
+  GWin32RegistryKey *proxy_key;
+
+  /* shell/open/command default value for the class named by proxy_id */
+  gunichar2 *proxy_command;
+
+  /* Executable of the program (for matching, in folded form; UTF-8) */
+  gchar *executable_folded;
+
+  /* Executable of the program (UTF-8) */
+  gchar *executable;
+
+  /* Pointer to a location within @executable */
+  gchar *executable_basename;
+
+  /* Icon of the application for this handler */
+  GIcon *icon;
+
+  /* The application that is linked to this handler. */
+  GWin32AppInfoApplication *app;
+};
+
+struct _GWin32AppInfoFileExtension {
+  GObject parent_instance;
+
+  /* File extension (with leading '.') */
+  gunichar2 *extension;
+
+  /* File extension (with leading '.'), in UTF-8 */
+  gchar *extension_u8;
+
+  /* handler currently selected for this extension */
+  GWin32AppInfoHandler *chosen_handler;
+
+  /* Maps folded handler IDs -> to other handlers for this extension */
+  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. */
+  gunichar2 *canonical_name;
+
+  /* Canonical name (used for key names), in UTF-8. Can be NULL. */
+  gchar *canonical_name_u8;
+
+  /* Canonical name (used for key names), in UTF-8, folded. Can be NULL. */
+  gchar *canonical_name_folded;
+
+  /* Human-readable name in English. Can be NULL */
+  gunichar2 *pretty_name;
+
+  /* Human-readable name in English, UTF-8. Can be NULL */
+  gchar *pretty_name_u8;
+
+  /* Human-readable name in user's language. Can be NULL  */
+  gunichar2 *localized_pretty_name;
+
+  /* Human-readable name in user's language, UTF-8. Can be NULL  */
+  gchar *localized_pretty_name_u8;
+
+  /* Description, could be in user's language. Can be NULL */
+  gunichar2 *description;
+
+  /* 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;
+
+  /* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
+   * UTF-8, folded) -> 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 can be used as a key in the extensions hashmap.
+   */
+  GHashTable *supported_exts;
+
+  /* Icon of the application (remember, handler can have its own icon too) */
+  GIcon *icon;
+
+  /* Set to TRUE to prevent this app from appearing in lists of apps for
+   * opening files. This will not prevent it from appearing in lists of apps
+   * just for running, or lists of apps for opening exts/urls for which this
+   * app reports explicit support.
+   */
+  gboolean no_open_with;
+
+  /* Set to TRUE for applications from HKEY_CURRENT_USER.
+   * Give them priority over applications from HKEY_LOCAL_MACHINE, when all
+   * other things are equal.
+   */
+  gboolean user_specific;
+
+  /* Set to TRUE for applications that are machine-wide defaults (i.e. default
+   * browser) */
+  gboolean default_app;
+};
+
+#define G_TYPE_WIN32_APPINFO_URL_SCHEMA           (g_win32_appinfo_url_schema_get_type ())
+#define G_WIN32_APPINFO_URL_SCHEMA(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema))
+
+#define G_TYPE_WIN32_APPINFO_FILE_EXTENSION       (g_win32_appinfo_file_extension_get_type ())
+#define G_WIN32_APPINFO_FILE_EXTENSION(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension))
+
+#define G_TYPE_WIN32_APPINFO_HANDLER              (g_win32_appinfo_handler_get_type ())
+#define G_WIN32_APPINFO_HANDLER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler))
+
+#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))
+
+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_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 (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT)
+G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT)
+
+static void
+g_win32_appinfo_url_schema_dispose (GObject *object)
+{
+  GWin32AppInfoURLSchema *url = G_WIN32_APPINFO_URL_SCHEMA (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_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);
+}
+
+
+static void
+g_win32_appinfo_handler_dispose (GObject *object)
+{
+  GWin32AppInfoHandler *handler = G_WIN32_APPINFO_HANDLER (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_object (&handler->key);
+  g_clear_object (&handler->proxy_key);
+  g_clear_object (&handler->icon);
+  g_clear_object (&handler->app);
+  G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
+}
+
+static void
+g_win32_appinfo_file_extension_dispose (GObject *object)
+{
+  GWin32AppInfoFileExtension *ext = G_WIN32_APPINFO_FILE_EXTENSION (object);
+
+  g_clear_pointer (&ext->extension, g_free);
+  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_application_dispose (GObject *object)
+{
+  GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object);
+
+  g_clear_pointer (&app->canonical_name_u8, g_free);
+  g_clear_pointer (&app->canonical_name_folded, g_free);
+  g_clear_pointer (&app->canonical_name, g_free);
+  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->supported_urls, g_hash_table_destroy);
+  g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
+  g_clear_object (&app->icon);
+  G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
+}
+
+static void
+g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = g_win32_appinfo_url_schema_dispose;
+}
+
+static void
+g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = g_win32_appinfo_file_extension_dispose;
+}
+
+static void
+g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = g_win32_appinfo_handler_dispose;
+}
+
+static void
+g_win32_appinfo_application_class_init (GWin32AppInfoApplicationClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = g_win32_appinfo_application_dispose;
+}
+
+static void
+g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
+{
+  self->handlers = g_hash_table_new_full (g_str_hash,
+                                          g_str_equal,
+                                          g_free,
+                                          g_object_unref);
+}
+
+static void
+g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
+{
+  self->handlers = g_hash_table_new_full (g_str_hash,
+                                          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)
+{
+}
+
+static void
+g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
+{
+  self->supported_urls = g_hash_table_new_full (g_str_hash,
+                                                g_str_equal,
+                                                g_free,
+                                                g_object_unref);
+  self->supported_exts = g_hash_table_new_full (g_str_hash,
+                                                g_str_equal,
+                                                g_free,
+                                                g_object_unref);
+}
+
+G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
+
+/* Map of owned ".ext" (with '.', UTF-8, folded)
+ * to GWin32AppInfoFileExtension ptr
+ */
+static GHashTable *extensions = NULL;
+
+/* Map of owned "schema" (without ':', UTF-8, folded)
+ * to GWin32AppInfoURLSchema ptr
+ */
+static GHashTable *urls = NULL;
+
+/* Map of owned "appID" (UTF-8, folded) to
+ * GWin32AppInfoApplication ptr
+ */
+static GHashTable *apps_by_id = NULL;
+
+/* Map of owned "app.exe" (UTF-8, folded) to
+ * GWin32AppInfoApplication ptr.
+ * 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 "handler id" (UTF-8, folded)
+ * to GWin32AppInfoHandler ptr
+ */
+static GHashTable *handlers = NULL;
+
+/* Watch this whole subtree */
+static GWin32RegistryKey *url_associations_key;
+
+/* Watch this whole subtree */
+static GWin32RegistryKey *file_exts_key;
+
+/* Watch this whole subtree */
+static GWin32RegistryKey *user_clients_key;
+
+/* Watch this whole subtree */
+static GWin32RegistryKey *system_clients_key;
+
+/* Watch this key */
+static GWin32RegistryKey *user_registered_apps_key;
+
+/* Watch this key */
+static GWin32RegistryKey *system_registered_apps_key;
+
+/* Watch this whole subtree */
+static GWin32RegistryKey *applications_key;
+
+/* Watch this key */
+static GWin32RegistryKey *classes_root_key;
+
+static gunichar2 *
+g_wcsdup (const gunichar2 *str, gssize str_size)
+{
+  if (str_size == -1)
+    {
+      str_size = wcslen (str) + 1;
+      str_size *= sizeof (gunichar2);
+    }
+  return g_memdup (str, str_size);
+}
+
+#define URL_ASSOCIATIONS 
L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
+#define USER_CHOICE L"\\UserChoice"
+#define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
+#define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"
+#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))
+
+static gunichar2 *
+read_resource_string (gunichar2 *res)
+{
+  gunichar2 *id_str;
+  gunichar2 *id_str_end;
+  gunichar2 *filename_str;
+  unsigned long id;
+  HMODULE resource_module;
+  gunichar2 *buffer;
+  int string_length;
+  int buffer_length;
+
+  if (res == NULL || res[0] != L'@')
+    return res;
+
+  id_str = wcsrchr (res, L'-');
+
+  if (id_str == NULL || id_str[-1] != L',')
+    return res;
+
+  id_str += 1;
+
+  id = wcstoul (id_str, &id_str_end, 10);
+
+  if (id_str_end == id_str || id_str_end[0] != L'\0' || id == ULONG_MAX)
+    return res;
+
+  filename_str = &res[1];
+  id_str[-2] = L'\0';
+
+  resource_module = LoadLibraryExW (filename_str,
+                                    0,
+                                    LOAD_LIBRARY_AS_DATAFILE |
+                                    LOAD_LIBRARY_AS_IMAGE_RESOURCE);
+
+  g_free (res);
+
+  if (resource_module == NULL)
+    return NULL;
+
+  buffer_length = 256;
+  string_length = buffer_length - 1;
+
+  while (TRUE)
+    {
+      buffer = g_malloc (buffer_length * sizeof (gunichar2));
+      string_length = LoadStringW (resource_module, id, buffer, buffer_length);
+
+      if (string_length != 0 && string_length == buffer_length - 1)
+        {
+          g_free (buffer);
+          buffer_length *= 2;
+        }
+      else
+        {
+          if (string_length == 0)
+            g_clear_pointer (&buffer, g_free);
+
+          break;
+        }
+    }
+
+  FreeLibrary (resource_module);
+
+  if (buffer)
+    {
+      gunichar2 *result = g_wcsdup (buffer, -1);
+      g_free (buffer);
+      return result;
+    }
+
+  return NULL;
+}
+
+static void
+read_handler_icon (GWin32RegistryKey  *proxy_key,
+                   GWin32RegistryKey  *program_key,
+                   GIcon             **icon_out)
+{
+  gint counter;
+  GWin32RegistryKey *key;
+
+  *icon_out = NULL;
+
+  for (counter = 0; counter < 2; counter++)
+    {
+      GWin32RegistryKey *icon_key;
+
+      if (counter == 0)
+        key = proxy_key;
+      else
+        key = program_key;
+
+      if (!key)
+        continue;
+
+      icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
+
+      if (icon_key != NULL)
+        {
+          GWin32RegistryValueType default_type;
+          gchar *default_value;
+
+          if (g_win32_registry_key_get_value (icon_key,
+                                              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);
+
+              g_clear_pointer (&default_value, g_free);
+            }
+
+          g_object_unref (icon_key);
+        }
+
+      if (*icon_out)
+        break;
+    }
+}
+
+static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
+static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_list components);
+
+static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
+
+/* output_size is in *bytes*, not gunichar2s! */
+static gboolean
+build_registry_path (gunichar2 *output, gsize output_size, ...)
+{
+  va_list ap;
+  gboolean result;
+
+  va_start (ap, output_size);
+
+  result = build_registry_pathv (output, output_size, ap);
+
+  va_end (ap);
+
+  return result;
+}
+
+/* output_size is in *bytes*, not gunichar2s! */
+static gboolean
+build_registry_pathv (gunichar2 *output, gsize output_size, va_list components)
+{
+  va_list lentest;
+  gunichar2 *p;
+  gunichar2 *component;
+  gsize length;
+
+  if (output == NULL)
+    return FALSE;
+
+  va_copy (lentest, components);
+
+  for (length = 0, component = va_arg (lentest, gunichar2 *);
+       component != NULL;
+       component = va_arg (lentest, gunichar2 *))
+    {
+      length += wcslen (component);
+    }
+
+  va_end (lentest);
+
+  if ((length >= REG_PATH_MAX_SIZE) ||
+      (length * sizeof (gunichar2) >= output_size))
+    return FALSE;
+
+  output[0] = L'\0';
+
+  for (p = output, component = va_arg (components, gunichar2 *);
+       component != NULL;
+       component = va_arg (components, gunichar2 *))
+    {
+      length = wcslen (component);
+      wcscat (p, component);
+      p += length;
+    }
+
+  return TRUE;
+}
+
+
+static GWin32RegistryKey *
+_g_win32_registry_key_build_and_new_w (GError **error, ...)
+{
+  va_list ap;
+  gunichar2 key_path[REG_PATH_MAX_SIZE + 1];
+  GWin32RegistryKey *key;
+
+  va_start (ap, error);
+
+  key = NULL;
+
+  if (build_registry_pathv (key_path, sizeof (key_path), ap))
+    key = g_win32_registry_key_new_w (key_path, error);
+
+  va_end (ap);
+
+  return key;
+}
+
+
+static gboolean
+utf8_and_fold (const gunichar2  *str,
+               gchar           **str_u8,
+               gchar           **str_u8_folded)
+{
+  gchar *u8;
+  gchar *folded;
+  u8 = g_utf16_to_utf8 (str, -1, NULL, NULL, NULL);
+
+  if (u8 == NULL)
+    return FALSE;
+
+  folded = g_utf8_casefold (u8, -1);
+
+  if (folded == NULL)
+    {
+      g_free (u8);
+      return FALSE;
+    }
+
+  if (str_u8)
+    *str_u8 = u8;
+  else
+    g_free (u8);
+
+  if (str_u8_folded)
+    *str_u8_folded = folded;
+  else
+    g_free (folded);
+
+  return TRUE;
+}
+
+
+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)
+{
+  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;
+
+  if (program_key)
+    *program_key = NULL;
+
+  if (proxy_key)
+    *proxy_key = NULL;
+
+
+  key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id,
+                                               SHELL_OPEN_COMMAND, NULL);
+
+  if (key != NULL)
+    {
+      got_value = g_win32_registry_key_get_value_w (key,
+                                                    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) &&
+              !utf8_and_fold (program_id, program_id_u8, program_id_folded))
+            {
+              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);
+    }
+
+  key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, program_id, NULL);
+
+  if (key == NULL)
+    return FALSE;
+
+  got_value = g_win32_registry_key_get_value_w (key,
+                                                TRUE,
+                                                L"",
+                                                &val_type,
+                                                (void **) proxy_id,
+                                                &proxy_id_size,
+                                                NULL);
+  if (!got_value ||
+      (val_type != G_WIN32_REGISTRY_VALUE_STR))
+    {
+      g_object_unref (key);
+      g_clear_pointer (proxy_id, g_free);
+      return FALSE;
+    }
+
+  if (proxy_key)
+    *proxy_key = key;
+  else
+    g_object_unref (key);
+
+  key = _g_win32_registry_key_build_and_new_w (NULL, HKCR, *proxy_id,
+                                               SHELL_OPEN_COMMAND, NULL);
+
+  if (key == NULL)
+    {
+      g_clear_pointer (proxy_id, g_free);
+      if (proxy_key)
+        g_clear_object (proxy_key);
+      return FALSE;
+    }
+
+  got_value = g_win32_registry_key_get_value_w (key,
+                                                TRUE,
+                                                L"",
+                                                &val_type,
+                                                (void **) proxy_command,
+                                                NULL,
+                                                NULL);
+  g_object_unref (key);
+
+  if (!got_value ||
+      val_type != G_WIN32_REGISTRY_VALUE_STR ||
+      ((program_id_u8 != NULL || program_id_folded != NULL) &&
+       !utf8_and_fold (program_id, program_id_u8, program_id_folded)))
+    {
+      g_clear_pointer (proxy_id, g_free);
+      g_clear_pointer (proxy_command, g_free);
+      if (proxy_key)
+        g_clear_object (proxy_key);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+static void
+extract_executable (gunichar2  *commandline,
+                    gchar     **ex_out,
+                    gchar     **ex_basename_out,
+                    gchar     **ex_folded_out,
+                    gchar     **ex_folded_basename_out)
+{
+  gchar *ex;
+  gchar *ex_folded;
+  gunichar2 *p;
+  gboolean quoted;
+  size_t len;
+  size_t execlen;
+  gunichar2 *exepart;
+  gboolean found;
+
+  quoted = FALSE;
+  execlen = 0;
+  found = FALSE;
+  len = wcslen (commandline);
+  p = commandline;
+
+  while (p < &commandline[len])
+    {
+      switch (p[0])
+        {
+        case L'"':
+          quoted = !quoted;
+          break;
+        case L' ':
+          if (!quoted)
+            {
+              execlen = p - commandline;
+              p = &commandline[len];
+              found = TRUE;
+            }
+          break;
+        default:
+          break;
+        }
+      p += 1;
+    }
+
+  if (!found)
+    execlen = len;
+
+  exepart = g_wcsdup (commandline, (execlen + 1) * sizeof (gunichar2));
+  exepart[execlen] = L'\0';
+
+  p = &exepart[0];
+
+  while (execlen > 0 && exepart[0] == L'"' && exepart[execlen - 1] == L'"')
+    {
+      p = &exepart[1];
+      exepart[execlen - 1] = L'\0';
+      execlen -= 2;
+    }
+
+  if (!utf8_and_fold (p, &ex, &ex_folded))
+    /* Currently no code to handle this case. It shouldn't happen though... */
+    g_assert_not_reached ();
+
+  g_free (exepart);
+
+  if (ex_out)
+    {
+      *ex_out = ex;
+
+      if (ex_basename_out)
+        {
+          *ex_basename_out = &ex[strlen (ex) - 1];
+
+          while (*ex_basename_out > ex)
+            {
+              if ((*ex_basename_out)[0] == '/' ||
+                  (*ex_basename_out)[0] == '\\')
+                {
+                  *ex_basename_out += 1;
+                  break;
+                }
+
+              *ex_basename_out -= 1;
+            }
+        }
+    }
+  else
+    {
+      g_free (ex);
+    }
+
+  if (ex_folded_out)
+    {
+      *ex_folded_out = ex_folded;
+
+      if (ex_folded_basename_out)
+        {
+          *ex_folded_basename_out = &ex_folded[strlen (ex_folded) - 1];
+
+          while (*ex_folded_basename_out > ex_folded)
+            {
+              if ((*ex_folded_basename_out)[0] == '/' ||
+                  (*ex_folded_basename_out)[0] == '\\')
+                {
+                  *ex_folded_basename_out += 1;
+                  break;
+                }
+
+              *ex_folded_basename_out -= 1;
+            }
+        }
+    }
+  else
+    {
+      g_free (ex_folded);
+    }
+}
+
+static void
+get_url_association (const gunichar2 *schema)
+{
+  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)
+    return;
+
+  if (!utf8_and_fold (schema, &schema_u8, &schema_folded))
+    {
+      g_object_unref (user_choice);
+      return;
+    }
+
+  schema_rec = g_hash_table_lookup (urls, schema_folded);
+
+  if (!g_win32_registry_key_get_value_w (user_choice,
+                                         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;
+    }
+
+  if (val_type != G_WIN32_REGISTRY_VALUE_STR)
+    {
+      g_free (schema_u8);
+      g_free (schema_folded);
+      g_free (program_id);
+      g_object_unref (user_choice);
+      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))
+    {
+      g_free (schema_u8);
+      g_free (schema_folded);
+      g_free (program_id);
+      g_object_unref (user_choice);
+      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;
+      extract_executable (proxy_command ? proxy_command : program_command,
+                          &handler_rec->executable,
+                          &handler_rec->executable_basename,
+                          &handler_rec->executable_folded,
+                          NULL);
+      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 (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);
+    }
+
+  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 (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);
+}
+
+static void
+get_file_ext (const gunichar2 *ext)
+{
+  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;
+  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);
+
+  if (user_choice == NULL && open_with_progids == NULL)
+    return;
+
+  if (!utf8_and_fold (ext, &ext_u8, &ext_folded))
+    {
+      g_clear_object (&user_choice);
+      g_clear_object (&open_with_progids);
+      return;
+    }
+
+  file_extn = NULL;
+  file_ext_known = g_hash_table_lookup_extended (extensions,
+                                                 ext_folded,
+                                                 NULL,
+                                                 (void **) &file_extn);
+
+  if (!file_ext_known)
+    file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
+
+  found_handler = FALSE;
+
+  if (user_choice != NULL)
+    {
+      if (g_win32_registry_key_get_value_w (user_choice,
+                                            TRUE,
+                                            L"Progid",
+                                            &val_type,
+                                            (void **) &program_id,
+                                            &program_id_size,
+                                            NULL))
+        {
+          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 (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;
+                  extract_executable (proxy_command ? proxy_command : program_command,
+                                      &handler_rec->executable,
+                                      &handler_rec->executable_basename,
+                                      &handler_rec->executable_folded,
+                                      NULL);
+                  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);
+                }
+
+              found_handler = TRUE;
+
+              handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
+                                                        program_id_folded);
+
+              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));
+                }
+
+              g_free (program_id_u8);
+              g_free (program_id_folded);
+              g_free (program_command);
+              g_free (proxy_id);
+              g_free (proxy_command);
+            }
+
+          g_free (program_id);
+        }
+
+      g_object_unref (user_choice);
+    }
+
+  if (open_with_progids != NULL)
+    {
+      GWin32RegistryValueIter iter;
+
+      if (g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
+        {
+          gunichar2 *value_name;
+          gunichar2 *value_data;
+          gsize      value_name_len;
+          gsize      value_data_size;
+          GWin32RegistryValueType value_type;
+
+          while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
+            {
+              gsize value_name_size;
+              program_key = proxy_key = NULL;
+
+              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;
+
+              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;
+
+              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 (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;
+                  extract_executable (proxy_command ? proxy_command : program_command,
+                                      &handler_rec->executable,
+                                      &handler_rec->executable_basename,
+                                      &handler_rec->executable_folded,
+                                      NULL);
+                  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);
+                }
+
+              found_handler = TRUE;
+
+              handler_rec_in_ext = g_hash_table_lookup (file_extn->handlers,
+                                                        program_id_folded);
+
+              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));
+                }
+
+              g_free (program_id_u8);
+              g_free (program_id_folded);
+              g_free (program_command);
+              g_free (proxy_id);
+              g_free (proxy_command);
+            }
+
+          g_win32_registry_value_iter_clear (&iter);
+        }
+
+      g_object_unref (open_with_progids);
+    }
+
+  if (!found_handler)
+    {
+      if (!file_ext_known)
+        g_object_unref (file_extn);
+    }
+  else if (!file_ext_known)
+    {
+      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);
+    }
+
+  g_free (ext_u8);
+  g_free (ext_folded);
+}
+
+static void
+collect_capable_apps_from_clients (GPtrArray *capable_apps,
+                                   GPtrArray *priority_capable_apps,
+                                   gboolean   user_registry)
+{
+  GWin32RegistryKey *clients;
+  GWin32RegistrySubkeyIter clients_iter;
+
+  gunichar2 *client_type_name;
+  gsize client_type_name_len;
+
+
+  if (user_registry)
+    clients =
+        g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
+                                     NULL);
+  else
+    clients =
+        g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
+                                     NULL);
+
+  if (clients == NULL)
+    return;
+
+  if (!g_win32_registry_subkey_iter_init (&clients_iter, clients, NULL))
+    {
+      g_object_unref (clients);
+      return;
+    }
+
+  while (g_win32_registry_subkey_iter_next (&clients_iter, TRUE, NULL))
+    {
+      GWin32RegistrySubkeyIter subkey_iter;
+      GWin32RegistryKey *system_client_type;
+      GWin32RegistryValueType default_type;
+      gunichar2 *default_value;
+      gunichar2 *client_name;
+      gsize client_name_len;
+
+      if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
+                                                    &client_type_name,
+                                                    &client_type_name_len,
+                                                    NULL))
+        continue;
+
+      system_client_type = g_win32_registry_key_get_child_w (clients,
+                                                             client_type_name,
+                                                             NULL);
+
+      if (system_client_type == NULL)
+        continue;
+
+      if (g_win32_registry_key_get_value_w (system_client_type,
+                                            TRUE,
+                                            L"",
+                                            &default_type,
+                                            (gpointer *) &default_value,
+                                            NULL,
+                                            NULL))
+        {
+          if (default_type != G_WIN32_REGISTRY_VALUE_STR ||
+              default_value[0] == L'\0')
+            g_clear_pointer (&default_value, g_free);
+        }
+
+      if (!g_win32_registry_subkey_iter_init (&subkey_iter,
+                                              system_client_type,
+                                              NULL))
+        {
+          g_clear_pointer (&default_value, g_free);
+          g_object_unref (system_client_type);
+          continue;
+        }
+
+      while (g_win32_registry_subkey_iter_next (&subkey_iter, TRUE, NULL))
+        {
+          GWin32RegistryKey *system_client;
+          GWin32RegistryKey *system_client_assoc;
+          gboolean add;
+          gunichar2 *keyname;
+
+          if (!g_win32_registry_subkey_iter_get_name_w (&subkey_iter,
+                                                        &client_name,
+                                                        &client_name_len,
+                                                        NULL))
+            continue;
+
+          system_client = g_win32_registry_key_get_child_w (system_client_type,
+                                                            client_name,
+                                                            NULL);
+
+          if (system_client == NULL)
+            continue;
+
+          add = FALSE;
+
+          system_client_assoc = g_win32_registry_key_get_child_w (system_client,
+                                                                  L"Capabilities\\FileAssociations",
+                                                                  NULL);
+
+          if (system_client_assoc != NULL)
+            {
+              add = TRUE;
+              g_object_unref (system_client_assoc);
+            }
+          else
+            {
+              system_client_assoc = g_win32_registry_key_get_child_w (system_client,
+                                                                      L"Capabilities\\UrlAssociations",
+                                                                      NULL);
+
+              if (system_client_assoc != NULL)
+                {
+                  add = TRUE;
+                  g_object_unref (system_client_assoc);
+                }
+            }
+
+          if (add)
+            {
+              keyname = g_wcsdup (g_win32_registry_key_get_path_w (system_client), -1);
+
+              if (default_value && wcscmp (default_value, client_name) == 0)
+                g_ptr_array_add (priority_capable_apps, keyname);
+              else
+                g_ptr_array_add (capable_apps, keyname);
+            }
+
+          g_object_unref (system_client);
+        }
+
+      g_win32_registry_subkey_iter_clear (&subkey_iter);
+      g_clear_pointer (&default_value, g_free);
+      g_object_unref (system_client_type);
+    }
+
+  g_win32_registry_subkey_iter_clear (&clients_iter);
+  g_object_unref (clients);
+}
+
+static void
+collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
+                                           gboolean   user_registry)
+{
+  GWin32RegistryValueIter iter;
+
+  gunichar2 *value_data;
+  gsize      value_data_size;
+  GWin32RegistryValueType value_type;
+  GWin32RegistryKey *registered_apps;
+
+  if (user_registry)
+    registered_apps =
+        g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
+                                    NULL);
+  else
+    registered_apps =
+        g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
+                                    NULL);
+
+  if (!registered_apps)
+    return;
+
+  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;
+
+      if ((!g_win32_registry_value_iter_get_value_type (&iter,
+                                                        &value_type,
+                                                        NULL)) ||
+          (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
+          (!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 (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)
+        {
+          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);
+        }
+    }
+
+  g_win32_registry_value_iter_clear (&iter);
+  g_object_unref (registered_apps);
+}
+
+static void
+read_capable_app (gunichar2 *input_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;
+  gunichar2 *fallback_friendly_name;
+  GWin32RegistryValueType vtype;
+  gboolean success;
+  gunichar2 *friendly_name;
+  gunichar2 *description;
+  gunichar2 *narrow_application_name;
+  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;
+  GWin32RegistryKey *associations;
+
+  app_key_path = g_wcsdup (input_app_key_path, -1);
+
+  canonical_name = wcsrchr (app_key_path, L'\\');
+
+  if (canonical_name == NULL)
+    {
+      /* The key must have at least one '\\' */
+      g_free (app_key_path);
+      return;
+    }
+
+  canonical_name += 1;
+
+  if (!utf8_and_fold (canonical_name, &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,
+                                              TRUE,
+                                              L"",
+                                              &vtype,
+                                              (gpointer *) &shell_open_command,
+                                              NULL,
+                                              NULL);
+
+  if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+    {
+      /* 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;
+    }
+
+  extract_executable (shell_open_command,
+                      &app_executable,
+                      &app_executable_basename,
+                      &app_executable_folded,
+                      &app_executable_folded_basename);
+
+  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;
+
+      g_hash_table_insert (apps_by_id,
+                           g_strdup (canonical_name_folded),
+                           app);
+    }
+
+  fallback_friendly_name = NULL;
+  success = g_win32_registry_key_get_value_w (appkey,
+                                              TRUE,
+                                              L"",
+                                              &vtype,
+                                              (void **) &fallback_friendly_name,
+                                              NULL,
+                                              NULL);
+
+  if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+    g_clear_pointer (&fallback_friendly_name, g_free);
+
+  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);
+      app->pretty_name_u8 = g_utf16_to_utf8 (fallback_friendly_name,
+                                             -1,
+                                             NULL,
+                                             NULL,
+                                             NULL);
+    }
+
+  friendly_name = NULL;
+  success = g_win32_registry_key_get_value_w (capabilities,
+                                              TRUE,
+                                              L"LocalizedString",
+                                              &vtype,
+                                              (void **) &friendly_name,
+                                              NULL,
+                                              NULL);
+
+  if (success && (vtype != G_WIN32_REGISTRY_VALUE_STR || friendly_name[0] != L'@'))
+    g_clear_pointer (&friendly_name, g_free);
+
+  friendly_name = read_resource_string (friendly_name);
+
+  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);
+      app->localized_pretty_name_u8 = g_utf16_to_utf8 (friendly_name,
+                                                       -1,
+                                                       NULL,
+                                                       NULL,
+                                                       NULL);
+    }
+
+  description = NULL;
+  success = g_win32_registry_key_get_value_w (capabilities,
+                                              TRUE,
+                                              L"ApplicationDescription",
+                                              &vtype,
+                                              (void **) &description,
+                                              NULL,
+                                              NULL);
+
+  if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+    g_clear_pointer (&description, g_free);
+
+  description = read_resource_string (description);
+
+  if (description && app->description == NULL)
+    {
+      app->description = g_wcsdup (description, -1);
+      g_clear_pointer (&app->description_u8, g_free);
+      app->description_u8 = g_utf16_to_utf8 (description, -1, NULL, NULL, NULL);
+    }
+
+  default_icon_key = g_win32_registry_key_get_child_w (appkey,
+                                                       L"DefaultIcon",
+                                                       NULL);
+
+  icon_source = NULL;
+
+  if (default_icon_key != NULL)
+    {
+      success = g_win32_registry_key_get_value_w (default_icon_key,
+                                                  TRUE,
+                                                  L"",
+                                                  &vtype,
+                                                  (void **) &icon_source,
+                                                  NULL,
+                                                  NULL);
+
+      if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+        g_clear_pointer (&icon_source, g_free);
+
+      g_object_unref (default_icon_key);
+    }
+
+  if (icon_source == NULL)
+    {
+      success = g_win32_registry_key_get_value_w (capabilities,
+                                                  TRUE,
+                                                  L"ApplicationIcon",
+                                                  &vtype,
+                                                  (void **) &icon_source,
+                                                  NULL,
+                                                  NULL);
+
+      if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+        g_clear_pointer (&icon_source, g_free);
+    }
+
+  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);
+      g_free (name);
+    }
+
+  narrow_application_name = NULL;
+  success = g_win32_registry_key_get_value_w (capabilities,
+                                              TRUE,
+                                              L"ApplicationName",
+                                              &vtype,
+                                              (void **) &narrow_application_name,
+                                              NULL,
+                                              NULL);
+
+  if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+    g_clear_pointer (&narrow_application_name, g_free);
+
+  narrow_application_name = read_resource_string (narrow_application_name);
+
+  /* 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?
+   */
+
+  associations = g_win32_registry_key_get_child_w (capabilities,
+                                                   L"FileAssociations",
+                                                   NULL);
+
+  if (associations != NULL)
+    {
+      GWin32RegistryValueIter iter;
+
+      if (g_win32_registry_value_iter_init (&iter, associations, NULL))
+        {
+          gunichar2 *file_extension;
+          gunichar2 *extension_handler;
+          gsize      file_extension_len;
+          gsize      extension_handler_size;
+          GWin32RegistryValueType value_type;
+
+          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)) ||
+                  (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
+                  (!g_win32_registry_value_iter_get_name_w (&iter,
+                                                            &file_extension,
+                                                            &file_extension_len,
+                                                            NULL)) ||
+                  (file_extension_len <= 0) ||
+                  (file_extension[0] != L'.') ||
+                  (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
+                                                            (void **) &extension_handler,
+                                                            &extension_handler_size,
+                                                            NULL)) ||
+                  (extension_handler_size < sizeof (gunichar2)) ||
+                  (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;
+                  extract_executable (proxy_command ? proxy_command : program_command,
+                                      &handler_rec->executable,
+                                      &handler_rec->executable_basename,
+                                      &handler_rec->executable_folded,
+                                      NULL);
+                  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 (utf8_and_fold (file_extension,
+                                   &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);
+            }
+
+          g_win32_registry_value_iter_clear (&iter);
+        }
+
+      g_object_unref (associations);
+    }
+
+  associations = g_win32_registry_key_get_child_w (capabilities, L"URLAssociations", NULL);
+
+  if (associations != NULL)
+    {
+      GWin32RegistryValueIter iter;
+
+      if (g_win32_registry_value_iter_init (&iter, associations, NULL))
+        {
+          gunichar2 *url_schema;
+          gunichar2 *schema_handler;
+          gsize      url_schema_len;
+          gsize      schema_handler_size;
+          GWin32RegistryValueType value_type;
+
+          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;
+
+              if ((!g_win32_registry_value_iter_get_value_type (&iter,
+                                                                &value_type,
+                                                                NULL)) ||
+                  ((value_type != G_WIN32_REGISTRY_VALUE_STR) &&
+                   (value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
+                  (!g_win32_registry_value_iter_get_name_w (&iter,
+                                                            &url_schema,
+                                                            &url_schema_len,
+                                                            NULL)) ||
+                  (url_schema_len <= 0) ||
+                  (url_schema[0] == L'\0') ||
+                  (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
+                                                            (void **) &schema_handler,
+                                                            &schema_handler_size,
+                                                            NULL)) ||
+                  (schema_handler_size < sizeof (gunichar2)) ||
+                  (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;
+                  extract_executable (proxy_command ? proxy_command : program_command,
+                                      &handler_rec->executable,
+                                      &handler_rec->executable_basename,
+                                      &handler_rec->executable_folded,
+                                      NULL);
+                  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 (utf8_and_fold (url_schema,
+                                   &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_win32_registry_value_iter_clear (&iter);
+        }
+
+      g_object_unref (associations);
+    }
+
+  g_clear_pointer (&app_executable, g_free);
+  g_clear_pointer (&app_executable_folded, 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);
+}
+
+static void
+read_urls (GWin32RegistryKey *url_associations)
+{
+  GWin32RegistrySubkeyIter url_iter;
+  gunichar2 *url_schema;
+  gsize url_schema_len;
+
+  if (url_associations == NULL)
+    return;
+
+  if (!g_win32_registry_subkey_iter_init (&url_iter, url_associations, NULL))
+    return;
+
+  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);
+    }
 
-#ifndef ASSOCF_INIT_BYEXENAME
-#define ASSOCF_INIT_BYEXENAME 0x00000002
-#endif
+  g_win32_registry_subkey_iter_clear (&url_iter);
+}
+
+static void
+read_exeapps (void)
+{
+  GWin32RegistryKey *applications_key;
+  GWin32RegistrySubkeyIter app_iter;
+  gunichar2 *app_exe_basename;
+  gsize app_exe_basename_len;
+
+  applications_key =
+      g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
+
+  if (applications_key == NULL)
+    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))
+    {
+      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))
+        continue;
+
+      incapable_app =
+          g_win32_registry_key_get_child_w (applications_key,
+                                            app_exe_basename,
+                                            NULL);
+
+      if (incapable_app == NULL)
+        continue;
+
+      extract_executable (app_exe_basename,
+                          &appexe,
+                          &appexe_basename,
+                          &appexe_folded,
+                          &appexe_folded_basename);
+
+      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,
+                                                      TRUE,
+                                                      L"",
+                                                      &vtype,
+                                                      (gpointer *) &shell_open_command,
+                                                      NULL,
+                                                      NULL);
+
+          if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+            {
+              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,
+                                                  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);
+
+      friendly_app_name = read_resource_string (friendly_app_name);
+
+      no_open_with = FALSE;
+      success = g_win32_registry_key_get_value_w (incapable_app,
+                                                  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,
+                                              TRUE,
+                                              L"",
+                                              &vtype,
+                                              (void **) &icon_source,
+                                              NULL,
+                                              NULL);
+
+        if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
+          g_clear_pointer (&icon_source, g_free);
+
+        g_object_unref (default_icon_key);
+      }
+
+      if (icon_source)
+        {
+          gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
+          icon = g_themed_icon_new (name);
+          g_free (name);
+        }
+
+      app = g_hash_table_lookup (apps_by_exe, appexe_folded_basename);
+
+      if (app == NULL)
+        {
+          app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
+
+          app->command =
+              shell_open_command ? g_wcsdup (shell_open_command, -1) : NULL;
+
+          if (shell_open_command)
+            app->command_u8 = g_utf16_to_utf8 (shell_open_command, -1, NULL, NULL, NULL);
+
+          app->executable = g_strdup (appexe);
+          app->executable_basename = &app->executable[appexe_basename - appexe];
+          app->executable_folded = g_strdup (appexe_folded);
+
+          app->no_open_with = no_open_with;
+
+          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);
+            }
 
-/* These were wrong in MingW */
-#define REAL_ASSOCSTR_COMMAND 1
-#define REAL_ASSOCSTR_EXECUTABLE 2
-#define REAL_ASSOCSTR_FRIENDLYDOCNAME 3
-#define REAL_ASSOCSTR_FRIENDLYAPPNAME 4
+          if (icon)
+            app->icon = g_object_ref (icon);
+
+          app->user_specific = FALSE;
+          app->default_app = FALSE;
+
+          g_hash_table_insert (apps_by_exe,
+                               g_strdup (appexe_folded_basename),
+                               app);
+        }
+
+      supported_key =
+          g_win32_registry_key_get_child_w (incapable_app,
+                                            L"SupportedTypes",
+                                            NULL);
+
+      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;
+
+              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'.') ||
+                      (!utf8_and_fold (ext_name,
+                                       &ext_u8,
+                                       &ext_folded)))
+                    continue;
+
+                  file_extn = NULL;
+                  file_ext_known =
+                      g_hash_table_lookup_extended (extensions,
+                                                    ext_folded,
+                                                    NULL,
+                                                    (void **) &file_extn);
+
+                  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);
+                    }
+
+                  g_hash_table_insert (file_extn->other_apps,
+                                       g_strdup (appexe_folded),
+                                       g_object_ref (app));
+
+                  g_free (ext_u8);
+                  g_free (ext_folded);
+                }
+
+              g_win32_registry_value_iter_clear (&sup_iter);
+            }
+
+          g_object_unref (supported_key);
+        }
+
+
+      g_free (appexe);
+      g_free (appexe_folded);
+      g_free (shell_open_command);
+      g_free (friendly_app_name);
+      g_free (icon_source);
+
+      g_clear_object (&icon);
+      g_clear_object (&incapable_app);
+    }
+
+  g_win32_registry_subkey_iter_clear (&app_iter);
+  g_object_unref (applications_key);
+}
+
+
+static void
+read_exts (GWin32RegistryKey *file_exts)
+{
+  GWin32RegistrySubkeyIter ext_iter;
+  gunichar2 *file_extension;
+  gsize file_extension_len;
+
+  if (file_exts == NULL)
+    return;
+
+  if (!g_win32_registry_subkey_iter_init (&ext_iter, file_exts, NULL))
+    return;
+
+  while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
+    {
+      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;
+      extract_executable (proxy_command ? proxy_command : program_command,
+                          &handler_rec->executable,
+                          &handler_rec->executable_basename,
+                          &handler_rec->executable_folded,
+                          NULL);
+      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,
+                                              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;
+      extract_executable (proxy_command ? proxy_command : program_command,
+                          &handler_rec->executable,
+                          &handler_rec->executable_basename,
+                          &handler_rec->executable_folded,
+                          NULL);
+      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;
+
+  if (classes_root == NULL)
+    return;
+
+  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 ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
+                                                     &class_name,
+                                                     &class_name_len,
+                                                     NULL)) ||
+          (class_name_len <= 1))
+        continue;
+
+      if (class_name[0] == L'.')
+        read_class_extension (classes_root, class_name, class_name_len);
+      else
+        {
+          gsize i;
+
+          for (i = 0; i < class_name_len; i++)
+            if (!iswalpha (class_name[i]))
+              break;
+
+          if (i == class_name_len)
+            read_class_url (classes_root, class_name, class_name_len);
+        }
+    }
+
+  g_win32_registry_subkey_iter_clear (&class_iter);
+}
+
+static void
+link_chosen_handlers (void)
+{
+  GHashTableIter iter;
+  GHashTableIter handler_iter;
+  gchar *schema_folded;
+  GWin32AppInfoURLSchema *schema;
+  gchar *handler_id_folded;
+  GWin32AppInfoHandler *handler;
+  gchar *ext_folded;
+  GWin32AppInfoFileExtension *ext;
+
+  g_hash_table_iter_init (&iter, urls);
+
+  while (g_hash_table_iter_next (&iter,
+                                (gpointer *) &schema_folded,
+                                (gpointer *) &schema))
+    {
+      if (schema->chosen_handler != NULL)
+        continue;
+
+      g_hash_table_iter_init (&handler_iter, schema->handlers);
+
+      while (g_hash_table_iter_next (&handler_iter,
+                                     (gpointer *) &handler_id_folded,
+                                     (gpointer *) &handler))
+        {
+          gchar *proxy_id_folded;
+
+          if (schema->chosen_handler != NULL)
+            break;
+
+          if (strcmp (handler_id_folded, schema_folded) != 0)
+            continue;
+
+          if (handler->proxy_command &&
+              handler->proxy_id &&
+              utf8_and_fold (handler->proxy_id,
+                             NULL,
+                             &proxy_id_folded))
+            {
+              GWin32AppInfoHandler *proxy;
+
+              proxy = g_hash_table_lookup (handlers, proxy_id_folded);
+
+              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);
+                }
+
+              g_free (proxy_id_folded);
+            }
+
+          if (schema->chosen_handler == 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);
+            }
+        }
+    }
+
+  g_hash_table_iter_init (&iter, extensions);
+
+  while (g_hash_table_iter_next (&iter,
+                                 (gpointer *) &ext_folded,
+                                 (gpointer *) &ext))
+    {
+      if (ext->chosen_handler != NULL)
+        continue;
+
+      g_hash_table_iter_init (&handler_iter, ext->handlers);
+
+      while (g_hash_table_iter_next (&handler_iter,
+                                     (gpointer *) &handler_id_folded,
+                                     (gpointer *) &handler))
+        {
+          gchar *proxy_id_folded;
+
+          if (ext->chosen_handler != NULL)
+            break;
+
+          if (strcmp (handler_id_folded, ext_folded) != 0)
+            continue;
+
+          if (handler->proxy_command &&
+              handler->proxy_id &&
+              utf8_and_fold (handler->proxy_id,
+                             NULL,
+                             &proxy_id_folded))
+            {
+              GWin32AppInfoHandler *proxy;
+
+              proxy = g_hash_table_lookup (handlers, proxy_id_folded);
+
+              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);
+                }
+
+              g_free (proxy_id_folded);
+            }
+
+          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);
+            }
+        }
+    }
+}
+
+static void
+link_handlers_to_registered_apps (void)
+{
+  GHashTableIter iter;
+  GHashTableIter sup_iter;
+  gchar *app_id_folded;
+  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;
+        }
+    }
+
+  g_hash_table_iter_init (&iter, apps_by_id);
+  while (g_hash_table_iter_next (&iter,
+                                 (gpointer *) &app_id_folded,
+                                 (gpointer *) &app))
+    {
+      if (app->supported_urls)
+        {
+          GWin32AppInfoHandler *handler;
+
+          g_hash_table_iter_init (&sup_iter, app->supported_urls);
+          while (g_hash_table_iter_next (&sup_iter,
+                                         (gpointer *) &schema_folded,
+                                         (gpointer *) &handler))
+            {
+              schema = g_hash_table_lookup (urls, schema_folded);
+
+              g_assert (schema != NULL);
+
+              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);
+                }
+            }
+
+          g_hash_table_iter_init (&sup_iter, app->supported_urls);
+          while (g_hash_table_iter_next (&sup_iter,
+                                         (gpointer *) &schema_folded,
+                                         (gpointer *) &handler))
+            {
+              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);
+                }
+            }
+        }
+
+      if (app->supported_exts)
+        {
+          GWin32AppInfoHandler *handler;
+
+          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);
+
+              g_assert (ext != NULL);
+
+              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);
+                }
+            }
+
+          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->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);
+                }
+            }
+        }
+    }
+
+  g_debug ("%u 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;
+        }
+    }
+  g_debug ("%u undefhandled extensions\n", unhandled_exts);
+}
+
+static void
+link_handlers_to_unregistered_apps (void)
+{
+  GHashTableIter iter;
+  GHashTableIter app_iter;
+  GWin32AppInfoHandler *handler;
+  gchar *handler_id_fc;
+  GWin32AppInfoApplication *app;
+  gchar *canonical_name_fc;
+  gchar *appexe_fc_basename;
+
+  g_hash_table_iter_init (&iter, handlers);
+  while (g_hash_table_iter_next (&iter,
+                                 (gpointer *) &handler_id_fc,
+                                 (gpointer *) &handler))
+    {
+      gchar *hndexe_fc_basename;
+
+      if ((handler->app != NULL) ||
+          (handler->executable_folded == NULL))
+        continue;
+
+      hndexe_fc_basename = g_utf8_casefold (handler->executable_basename, -1);
+
+      if (hndexe_fc_basename == 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))
+        {
+          if (app->executable_folded == NULL)
+            continue;
+
+          if (strcmp (app->executable_folded,
+                      handler->executable_folded) != 0)
+            continue;
+
+          handler->app = app;
+          break;
+        }
+
+      if (handler->app != NULL)
+        continue;
+
+      g_hash_table_iter_init (&app_iter, apps_by_exe);
+
+      while ((hndexe_fc_basename != NULL) &&
+             (g_hash_table_iter_next (&app_iter,
+                                      (gpointer *) &appexe_fc_basename,
+                                      (gpointer *) &app)))
+        {
+          /* Use basename because apps_by_exe only has basenames */
+          if (strcmp (hndexe_fc_basename, appexe_fc_basename) != 0)
+            continue;
+
+          handler->app = app;
+          break;
+        }
+
+      g_free (hndexe_fc_basename);
+
+      if (handler->app == NULL)
+        g_debug ("WARNING: handler that runs %s has no corresponding app\n",
+                 handler->executable);
+    }
+}
+
+
+static void
+update_registry_data (void)
+{
+  guint i;
+  GPtrArray *capable_apps_keys;
+  GPtrArray *user_capable_apps_keys;
+  GPtrArray *priority_capable_apps_keys;
+  GWin32RegistryKey *url_associations;
+  GWin32RegistryKey *file_exts;
+  GWin32RegistryKey *classes_root;
+  DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, 
postproc_end;
+
+  url_associations =
+      g_win32_registry_key_new_w 
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
+                                   NULL);
+  file_exts =
+      g_win32_registry_key_new_w 
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
+                                   NULL);
+  classes_root = g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT", NULL);
+
+  capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
+  user_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
+  priority_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
+
+  g_clear_pointer (&apps_by_id, g_hash_table_destroy);
+  g_clear_pointer (&apps_by_exe, 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);
+
+  collect_start = GetTickCount ();
+  collect_capable_apps_from_clients (capable_apps_keys,
+                                     priority_capable_apps_keys,
+                                     FALSE);
+  collect_capable_apps_from_clients (user_capable_apps_keys,
+                                     priority_capable_apps_keys,
+                                     TRUE);
+  collect_capable_apps_from_registered_apps (user_capable_apps_keys,
+                                             TRUE);
+  collect_capable_apps_from_registered_apps (capable_apps_keys,
+                                             FALSE);
+  collect_end = GetTickCount ();
+
+  apps_by_id =
+      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);
+  urls =
+      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  extensions =
+      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  handlers =
+      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  alloc_end = GetTickCount ();
+
+  for (i = 0; i < priority_capable_apps_keys->len; i++)
+    read_capable_app (g_ptr_array_index (priority_capable_apps_keys, i),
+                      TRUE,
+                      TRUE);
+  for (i = 0; i < user_capable_apps_keys->len; i++)
+    read_capable_app (g_ptr_array_index (user_capable_apps_keys, i),
+                      TRUE,
+                      FALSE);
+  for (i = 0; i < capable_apps_keys->len; i++)
+    read_capable_app (g_ptr_array_index (capable_apps_keys, i),
+                      FALSE,
+                      FALSE);
+  capable_end = GetTickCount ();
+
+  read_urls (url_associations);
+  url_end = GetTickCount ();
+  read_exts (file_exts);
+  ext_end = GetTickCount ();
+  read_exeapps ();
+  exeapp_end = GetTickCount ();
+  read_classes (classes_root);
+  classes_end = GetTickCount ();
+  link_chosen_handlers ();
+  link_handlers_to_registered_apps ();
+  link_handlers_to_unregistered_apps ();
+  postproc_end = GetTickCount ();
+
+  g_debug ("Collecting capable appnames: %lums\n"
+           "Allocating hashtables:...... %lums\n"
+           "Reading capable apps:        %lums\n"
+           "Reading URL associations:... %lums\n"
+           "Reading extension assocs:    %lums\n"
+           "Reading exe-only apps:...... %lums\n"
+           "Reading classes:             %lums\n"
+           "Postprocessing:..............%lums\n"
+           "TOTAL:                       %lums\n",
+           collect_end - collect_start,
+           alloc_end - collect_end,
+           capable_end - alloc_end,
+           url_end - capable_end,
+           ext_end - url_end,
+           exeapp_end - ext_end,
+           classes_end - exeapp_end,
+           postproc_end - classes_end,
+           postproc_end - collect_start);
+
+  g_clear_object (&classes_root);
+  g_clear_object (&url_associations);
+  g_clear_object (&file_exts);
+  g_ptr_array_free (capable_apps_keys, TRUE);
+  g_ptr_array_free (user_capable_apps_keys, TRUE);
+  g_ptr_array_free (priority_capable_apps_keys, TRUE);
+
+  return;
+}
+
+static void
+watch_keys (void)
+{
+  if (url_associations_key)
+    g_win32_registry_key_watch (url_associations_key,
+                                TRUE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+
+  if (file_exts_key)
+    g_win32_registry_key_watch (file_exts_key,
+                                TRUE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+
+  if (user_clients_key)
+    g_win32_registry_key_watch (user_clients_key,
+                                TRUE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+
+  if (system_clients_key)
+    g_win32_registry_key_watch (system_clients_key,
+                                TRUE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+
+  if (applications_key)
+    g_win32_registry_key_watch (applications_key,
+                                TRUE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+
+  if (user_registered_apps_key)
+    g_win32_registry_key_watch (user_registered_apps_key,
+                                TRUE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+
+  if (system_registered_apps_key)
+    g_win32_registry_key_watch (system_registered_apps_key,
+                                TRUE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+
+  if (classes_root_key)
+    g_win32_registry_key_watch (classes_root_key,
+                                FALSE,
+                                G_WIN32_REGISTRY_WATCH_NAME |
+                                G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
+                                G_WIN32_REGISTRY_WATCH_VALUES,
+                                NULL,
+                                NULL,
+                                NULL);
+}
+
+
+static void
+g_win32_appinfo_init (void)
+{
+  static gboolean initialized = FALSE;
+
+  if (g_once_init_enter (&initialized))
+    {
+      url_associations_key =
+          g_win32_registry_key_new_w 
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
+                                       NULL);
+      file_exts_key =
+          g_win32_registry_key_new_w 
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
+                                       NULL);
+      user_clients_key =
+          g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
+                                       NULL);
+      system_clients_key =
+          g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
+                                       NULL);
+      applications_key =
+          g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications",
+                                       NULL);
+      user_registered_apps_key =
+          g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
+                                       NULL);
+      system_registered_apps_key =
+          g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
+                                       NULL);
+      classes_root_key =
+          g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT",
+                                       NULL);
+
+      watch_keys ();
+
+      update_registry_data ();
+
+      g_once_init_leave (&initialized, TRUE);
+    }
+
+  if ((url_associations_key       && g_win32_registry_key_has_changed (url_associations_key))       ||
+      (file_exts_key              && g_win32_registry_key_has_changed (file_exts_key))              ||
+      (user_clients_key           && g_win32_registry_key_has_changed (user_clients_key))           ||
+      (system_clients_key         && g_win32_registry_key_has_changed (system_clients_key))         ||
+      (applications_key           && g_win32_registry_key_has_changed (applications_key))           ||
+      (user_registered_apps_key   && g_win32_registry_key_has_changed (user_registered_apps_key))   ||
+      (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
+      (classes_root_key           && g_win32_registry_key_has_changed (classes_root_key)))
+    {
+      G_LOCK (gio_win32_appinfo);
+      update_registry_data ();
+      watch_keys ();
+      G_UNLOCK (gio_win32_appinfo);
+    }
+}
 
-#ifndef AssocQueryString
-#pragma message("AssocQueryString not available with SDK used")
-#endif
 
 static void g_win32_app_info_iface_init (GAppInfoIface *iface);
 
 struct _GWin32AppInfo
 {
   GObject parent_instance;
-  wchar_t *id;
-  char *id_utf8;
-  gboolean id_is_exename;
-  char *executable;
-  char *name;
-  gboolean no_open_with;
+
+  /*<private>*/
+  gchar **supported_types;
+
+  GWin32AppInfoApplication *app;
+
+  GWin32AppInfoHandler *handler;
+
+  guint startup_notify : 1;
 };
 
 G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
-                        G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
-                                               g_win32_app_info_iface_init))
+                         G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
+                                                g_win32_app_info_iface_init))
 
 
 static void
@@ -73,10 +3463,9 @@ g_win32_app_info_finalize (GObject *object)
 
   info = G_WIN32_APP_INFO (object);
 
-  g_free (info->id);
-  g_free (info->id_utf8);
-  g_free (info->name);
-  g_free (info->executable);
+  g_clear_pointer (&info->supported_types, g_free);
+  g_clear_object (&info->app);
+  g_clear_object (&info->handler);
 
   G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
 }
@@ -85,7 +3474,7 @@ static void
 g_win32_app_info_class_init (GWin32AppInfoClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  
+
   gobject_class->finalize = g_win32_app_info_finalize;
 }
 
@@ -95,96 +3484,85 @@ g_win32_app_info_init (GWin32AppInfo *local)
 }
 
 static GAppInfo *
-g_desktop_app_info_new_from_id (wchar_t *id /* takes ownership */,
-                               gboolean id_is_exename)
-{
-#ifdef AssocQueryString
-  ASSOCF flags;
-#endif
-  wchar_t buffer[1024];
-  DWORD buffer_size;
-  GWin32AppInfo *info;
-  HKEY app_key;
-  
-  info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
-  info->id = id; /* Takes ownership */
-  info->id_utf8 = g_utf16_to_utf8 (id, -1, NULL, NULL, NULL);  
-  info->id_is_exename = id_is_exename;
-
-#ifdef AssocQueryString  
-  flags = 0;
-  if (id_is_exename)
-    flags |= ASSOCF_INIT_BYEXENAME;
-
-  buffer_size = 1024;
-  if (AssocQueryStringW(flags,
-                       REAL_ASSOCSTR_EXECUTABLE,
-                       id,
-                       NULL,
-                       buffer,
-                       &buffer_size) == S_OK)
-    info->executable = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
- 
-  buffer_size = 1024;
-  if (AssocQueryStringW(flags,
-                       REAL_ASSOCSTR_FRIENDLYAPPNAME,
-                       id,
-                       NULL,
-                       buffer,
-                       &buffer_size) == S_OK)
-    info->name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
-#endif
-
-  if (info->name == NULL)
-    {
-      /* TODO: Should look up name from executable resources */
-      if (info->executable)
-       info->name = g_path_get_basename (info->executable);
-      else
-       info->name = g_strdup (info->id_utf8);
+g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
+                               GWin32AppInfoHandler     *handler)
+{
+  GWin32AppInfo *new_info;
+  GHashTableIter iter;
+  gpointer ext;
+  int i;
+
+  new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
+
+  new_info->app = g_object_ref (app);
+
+  g_win32_appinfo_init ();
+  G_LOCK (gio_win32_appinfo);
+
+  i = 0;
+  g_hash_table_iter_init (&iter, new_info->app->supported_exts);
+
+  while (g_hash_table_iter_next (&iter, &ext, NULL))
+    {
+      if (ext)
+        i += 1;
     }
 
-#ifdef AssocQueryString
-  if (AssocQueryKeyW(flags,
-                    ASSOCKEY_APP,
-                    info->id,
-                    NULL,
-                    &app_key) == S_OK)
+  new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
+
+  i = 0;
+  g_hash_table_iter_init (&iter, new_info->app->supported_exts);
+
+  while (g_hash_table_iter_next (&iter, &ext, NULL))
     {
-      if (RegQueryValueExW (app_key, L"NoOpenWith", 0,
-                           NULL, NULL, NULL) == ERROR_SUCCESS)
-       info->no_open_with = TRUE;
-      RegCloseKey (app_key);
+      if (!ext)
+        continue;
+
+      new_info->supported_types[i] = (gchar *) ext;
+      i += 1;
     }
-#endif
-  
-  return G_APP_INFO (info);
-}
 
-static wchar_t *
-dup_wstring (wchar_t *str)
-{
-  gsize len;
-  for (len = 0; str[len] != 0; len++)
-    ;
-  return (wchar_t *)g_memdup (str, (len + 1) * 2);
+  G_UNLOCK (gio_win32_appinfo);
+
+  new_info->supported_types[i] = NULL;
+
+  new_info->handler = g_object_ref (handler);
+
+  return G_APP_INFO (new_info);
 }
 
+
 static GAppInfo *
 g_win32_app_info_dup (GAppInfo *appinfo)
 {
   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
   GWin32AppInfo *new_info;
-  
+
   new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
 
-  new_info->id = dup_wstring (info->id);
-  new_info->id_utf8 = g_strdup (info->id_utf8);
-  new_info->id_is_exename = info->id_is_exename;
-  new_info->name = g_strdup (info->name);
-  new_info->executable = g_strdup (info->executable);
-  new_info->no_open_with = info->no_open_with;
-  
+  if (info->app)
+    new_info->app = g_object_ref (info->app);
+
+  if (info->handler)
+    new_info->handler = g_object_ref (info->handler);
+
+  new_info->startup_notify = info->startup_notify;
+
+  if (info->supported_types)
+    {
+      int i;
+
+      for (i = 0; info->supported_types[i]; i++)
+        break;
+
+      new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
+
+      for (i = 0; info->supported_types[i]; i++)
+        new_info->supported_types[i] = g_strdup (info->supported_types[i]);
+
+      new_info->supported_types[i] = NULL;
+    }
+
   return G_APP_INFO (new_info);
 }
 
@@ -195,11 +3573,21 @@ g_win32_app_info_equal (GAppInfo *appinfo1,
   GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
   GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
 
-  if (info1->executable == NULL ||
-      info2->executable == NULL)
-    return FALSE;
-  
-  return strcmp (info1->executable, info2->executable) == 0;
+  if (info1->app == NULL ||
+      info2->app == 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 (info1->app->executable_folded != NULL &&
+      info2->app->executable_folded != NULL)
+    return (strcmp (info1->app->executable_folded,
+                    info2->app->executable_folded)) == 0;
+
+  return info1->app == info2->app;
 }
 
 static const char *
@@ -207,7 +3595,16 @@ g_win32_app_info_get_id (GAppInfo *appinfo)
 {
   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
 
-  return info->id_utf8;
+  if (info->app == NULL)
+    return NULL;
+
+  if (info->app->canonical_name_u8)
+    return info->app->canonical_name_u8;
+
+  if (info->app->executable_basename)
+    return info->app->executable_basename;
+
+  return NULL;
 }
 
 static const char *
@@ -215,182 +3612,811 @@ g_win32_app_info_get_name (GAppInfo *appinfo)
 {
   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
 
-  if (info->name == NULL)
-    return _("Unnamed");
-  
-  return info->name;
+  if (info->app && info->app->canonical_name_u8)
+    return info->app->canonical_name_u8;
+  else
+    return P_("Unnamed");
+}
+
+static const char *
+g_win32_app_info_get_display_name (GAppInfo *appinfo)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  if (info->app)
+    {
+      if (info->app->localized_pretty_name_u8)
+        return info->app->localized_pretty_name_u8;
+      else if (info->app->pretty_name_u8)
+        return info->app->pretty_name_u8;
+    }
+
+  return g_win32_app_info_get_name (appinfo);
 }
 
 static const char *
 g_win32_app_info_get_description (GAppInfo *appinfo)
 {
-  /* Win32 has no app descriptions */
-  return NULL;
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  if (info->app == NULL)
+    return NULL;
+
+  return info->app->description_u8;
 }
 
 static const char *
 g_win32_app_info_get_executable (GAppInfo *appinfo)
 {
   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
-  
-  return info->executable;
+
+  if (info->app == NULL)
+    return NULL;
+
+  return info->app->executable;
+}
+
+static const char *
+g_win32_app_info_get_commandline (GAppInfo *appinfo)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  if (info->app == NULL)
+    return NULL;
+
+  return info->app->command_u8;
 }
 
 static GIcon *
 g_win32_app_info_get_icon (GAppInfo *appinfo)
 {
-  /* GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo); */
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
 
-  /* TODO: How to handle icons */
-  return NULL;
+  if (info->app == NULL)
+    return NULL;
+
+  return info->app->icon;
+}
+
+typedef struct _file_or_uri {
+  gchar *uri;
+  gchar *file;
+} file_or_uri;
+
+static char *
+expand_macro_single (char macro, file_or_uri *obj)
+{
+  char *result = NULL;
+
+  switch (macro)
+    {
+    case '*':
+    case '0':
+    case '1':
+    case 'l':
+    case 'd':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      /* TODO: handle 'l' and 'd' differently (longname and desktop name) */
+      if (obj->uri)
+        result = g_strdup (obj->uri);
+      else if (obj->file)
+        result = g_strdup (obj->file);
+      break;
+    case 'u':
+    case 'U':
+      if (obj->uri)
+        result = g_shell_quote (obj->uri);
+      break;
+    case 'f':
+    case 'F':
+      if (obj->file)
+        result = g_shell_quote (obj->file);
+      break;
+    }
+
+  return result;
 }
 
 static gboolean
-g_win32_app_info_launch_locations (GAppInfo           *appinfo,
-                                   GList              *locations,
-                                   GAppLaunchContext  *launch_context,
-                                   GError            **error)
+expand_macro (char               macro,
+              GString           *exec,
+              GWin32AppInfo     *info,
+              GList            **stat_obj_list,
+              GList            **obj_list)
 {
-  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
-#ifdef AssocQueryString
-  ASSOCF flags;
-#endif
-  HKEY class_key;
-  SHELLEXECUTEINFOW exec_info = {0};
-  GList *l;
-
-  /* TODO:  What might startup_id mean on win32? */
-#ifdef AssocQueryString  
-  flags = 0;
-  if (info->id_is_exename)
-    flags |= ASSOCF_INIT_BYEXENAME;
-
-  if (AssocQueryKeyW (flags,
-                     ASSOCKEY_SHELLEXECCLASS,
-                     info->id,
-                     NULL,
-                     &class_key) != S_OK)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Can't find application"));
+  GList *objs = *obj_list;
+  char *expanded;
+  gboolean result = FALSE;
+
+  g_return_val_if_fail (exec != NULL, FALSE);
+
+/*
+Legend: (from http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
+%* - replace with all parameters
+%~ - replace with all parameters starting with and following the second parameter
+%0 or %1 the first file parameter. For example "C:\\Users\\Eric\\Destop\\New Text Document.txt". Generally 
this should be in quotes and the applications command line parsing should accept quotes to disambiguate files 
with spaces in the name and different command line parameters (this is a security best practice and I believe 
mentioned in MSDN).
+%<n> (where N is 2 - 9), replace with the nth parameter
+%s - show command
+%h - hotkey value
+%i - IDList stored in a shared memory handle is passed here.
+%l - long file name form of the first parameter. Note win32 applications will be passed the long file name, 
win16 applications get the short file name. Specifying %L is preferred as it avoids the need to probe for the 
application type.
+%d - desktop absolute parsing name of the first parameter (for items that don't have file system paths)
+%v - for verbs that are none implies all, if there is no parameter passed this is the working directory
+%w - the working directory
+*/
+
+  switch (macro)
+    {
+    case '*':
+    case '~':
+      if (*stat_obj_list)
+        {
+          gint i;
+          GList *o;
+
+          for (o = *stat_obj_list, i = 0;
+               macro == '~' && o && i < 2;
+               o = o->next, i++);
+
+          for (; o; o = o->next)
+            {
+              expanded = expand_macro_single (macro, o->data);
+
+              if (expanded)
+                {
+                  if (o != *stat_obj_list)
+                    g_string_append (exec, " ");
+
+                  g_string_append (exec, expanded);
+                  g_free (expanded);
+                }
+            }
+
+          objs = NULL;
+          result = TRUE;
+        }
+      break;
+    case '0':
+    case '1':
+    case 'l':
+    case 'd':
+      if (*stat_obj_list)
+        {
+          GList *o;
+
+          o = *stat_obj_list;
+
+          if (o)
+            {
+              expanded = expand_macro_single (macro, o->data);
+
+              if (expanded)
+                {
+                  if (o != *stat_obj_list)
+                    g_string_append (exec, " ");
+
+                  g_string_append (exec, expanded);
+                  g_free (expanded);
+                }
+            }
+
+          if (objs)
+            objs = objs->next;
+
+          result = TRUE;
+        }
+      break;
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      if (*stat_obj_list)
+        {
+          gint i;
+          GList *o;
+          gint n;
+
+          switch (macro)
+            {
+            case '2':
+              n = 2;
+              break;
+            case '3':
+              n = 3;
+              break;
+            case '4':
+              n = 4;
+              break;
+            case '5':
+              n = 5;
+              break;
+            case '6':
+              n = 6;
+              break;
+            case '7':
+              n = 7;
+              break;
+            case '8':
+              n = 8;
+              break;
+            case '9':
+              n = 9;
+              break;
+            }
+
+          for (o = *stat_obj_list, i = 0; o && i < n; o = o->next, i++);
+
+          if (o)
+            {
+              expanded = expand_macro_single (macro, o->data);
+
+              if (expanded)
+                {
+                  if (o != *stat_obj_list)
+                    g_string_append (exec, " ");
+
+                  g_string_append (exec, expanded);
+                  g_free (expanded);
+                }
+            }
+          result = TRUE;
+
+          if (objs)
+            objs = NULL;
+        }
+      break;
+    case 's':
+      break;
+    case 'h':
+      break;
+    case 'i':
+      break;
+    case 'v':
+      break;
+    case 'w':
+      expanded = g_get_current_dir ();
+      g_string_append (exec, expanded);
+      g_free (expanded);
+      break;
+    case 'u':
+    case 'f':
+      if (objs)
+        {
+          expanded = expand_macro_single (macro, objs->data);
+
+          if (expanded)
+            {
+              g_string_append (exec, expanded);
+              g_free (expanded);
+            }
+          objs = objs->next;
+          result = TRUE;
+        }
+
+      break;
+
+    case 'U':
+    case 'F':
+      while (objs)
+        {
+          expanded = expand_macro_single (macro, objs->data);
+
+          if (expanded)
+            {
+              g_string_append (exec, expanded);
+              g_free (expanded);
+            }
+
+          objs = objs->next;
+          result = TRUE;
+
+          if (objs != NULL && expanded)
+            g_string_append_c (exec, ' ');
+        }
+
+      break;
+
+    case 'c':
+      if (info->app && info->app->localized_pretty_name_u8)
+        {
+          expanded = g_shell_quote (info->app->localized_pretty_name_u8);
+          g_string_append (exec, expanded);
+          g_free (expanded);
+        }
+      break;
+
+    case 'm': /* deprecated */
+    case 'n': /* deprecated */
+    case 'N': /* deprecated */
+    /*case 'd': *//* deprecated */
+    case 'D': /* deprecated */
+      break;
+
+    case '%':
+      g_string_append_c (exec, '%');
+      break;
+    }
+
+  *obj_list = objs;
+
+  return result;
+}
+
+static gboolean
+expand_application_parameters (GWin32AppInfo   *info,
+                               const gchar     *exec_line,
+                               GList          **objs,
+                               int             *argc,
+                               char          ***argv,
+                               GError         **error)
+{
+  GList *obj_list = *objs;
+  GList **stat_obj_list = objs;
+  const char *p = exec_line;
+  GString *expanded_exec;
+  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;
     }
-#endif
 
-  /* FIXME: Need to do something with
-   * g_app_launch_context_get_environment()... ShellExecuteExW()
-   * doesn't have any way to pass an environment though. We need to
-   * either (a) update environment, ShellExecuteExW(), revert
-   * environment; or (b) find an API to figure out what app
-   * ShellExecuteExW() would launch, and then use g_spawn_async()
-   * instead.
+  expanded_exec = g_string_new (NULL);
+  res = FALSE;
+
+  while (*p)
+    {
+      if (p[0] == '%' && p[1] != '\0')
+        {
+          if (expand_macro (p[1],
+                            expanded_exec,
+                            info, stat_obj_list,
+                            objs))
+            res = TRUE;
+
+          p++;
+        }
+      else
+        g_string_append_c (expanded_exec, *p);
+
+      p++;
+    }
+
+  /* No file substitutions */
+  if (obj_list == *objs && obj_list != NULL && !res)
+    {
+      /* If there is no macro default to %f. This is also what KDE does */
+      g_string_append_c (expanded_exec, ' ');
+      expand_macro ('f', expanded_exec, info, stat_obj_list, objs);
+    }
+
+  /* Replace '\\' with '/', because g_shell_parse_argv considers them
+   * to be escape sequences.
    */
+  for (a_char = expanded_exec->str;
+       a_char <= &expanded_exec->str[expanded_exec->len];
+       a_char++)
+    {
+      if (*a_char == '\\')
+        *a_char = '/';
+    }
 
-  for (l = locations; l != NULL; l = l->next)
-    {
-      wchar_t *wloc = g_utf8_to_utf16 (l->data, -1, NULL, NULL, NULL);
-      
-      memset (&exec_info, 0, sizeof (exec_info));
-      exec_info.cbSize = sizeof (exec_info);
-      exec_info.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_CLASSKEY;
-      exec_info.lpFile = wloc;
-      exec_info.nShow = SW_SHOWNORMAL;
-      exec_info.hkeyClass = class_key;
-      
-      if (!ShellExecuteExW (&exec_info))
-       {
-         char *message_utf8 = g_win32_error_message (GetLastError ());
-
-         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Error launching application: %s"), 
message_utf8);
-         g_free (message_utf8);
-         
-         g_free (wloc);
-         RegCloseKey (class_key);
-         return FALSE;
-       }
-      
-      g_free (wloc);
-    }
-  
-  RegCloseKey (class_key);
-  
-  return TRUE;
+  res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
+  g_string_free (expanded_exec, TRUE);
+  return res;
 }
 
-static gboolean
-g_win32_app_info_supports_uris (GAppInfo *appinfo)
+
+static gchar *
+get_appath_for_exe (gunichar2 *exe_basename)
 {
-  /* TODO: is there some way to determine this on windows? */
-  return TRUE;
+  GWin32RegistryKey *apppath_key = NULL;
+  GWin32RegistryValueType val_type;
+  gunichar2 *appath = NULL;
+  gboolean got_value;
+  gchar *result = NULL;
+
+  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);
+
+  if (apppath_key == NULL)
+    return NULL;
+
+  got_value = g_win32_registry_key_get_value_w (apppath_key,
+                                                TRUE,
+                                                L"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);
+
+  g_clear_pointer (&appath, g_free);
+
+  return result;
 }
 
+
 static gboolean
-g_win32_app_info_supports_files (GAppInfo *appinfo)
+g_win32_app_info_launch_internal (GWin32AppInfo      *info,
+                                  GList              *objs,
+                                  GAppLaunchContext  *launch_context,
+                                  GSpawnFlags         spawn_flags,
+                                  GError            **error)
 {
-  return TRUE;
+  gboolean completed = FALSE;
+  char **argv, **envp;
+  int argc;
+  gchar *command;
+  gchar *apppath;
+  gunichar2 *exe_basename;
+
+  g_return_val_if_fail (info != NULL, FALSE);
+  g_return_val_if_fail (info->app != NULL, FALSE);
+
+  argv = NULL;
+
+  if (launch_context)
+    envp = g_app_launch_context_get_environment (launch_context);
+  else
+    envp = g_get_environ ();
+
+  command = NULL;
+  exe_basename = 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 (command == NULL)
+    {
+      command = g_strdup (info->app->command_u8);
+      exe_basename = g_utf8_to_utf16 (info->app->executable_basename,
+                                      -1,
+                                      NULL,
+                                      NULL,
+                                      NULL);
+    }
+
+  apppath = get_appath_for_exe (exe_basename);
+
+  g_free (exe_basename);
+
+  if (apppath)
+    {
+      gchar **p;
+      gint p_index;
+
+      for (p = envp, p_index = 0; p[0]; p++, p_index++)
+        if ((p[0][0] == 'p' || p[0][0] == 'P') &&
+            (p[0][1] == 'a' || p[0][1] == 'A') &&
+            (p[0][2] == 't' || p[0][2] == 'T') &&
+            (p[0][3] == 'h' || p[0][3] == 'H') &&
+            (p[0][4] == '='))
+          break;
+
+      if (p[0] == NULL)
+        {
+          gchar **new_envp;
+          new_envp = g_new (char *, g_strv_length (envp) + 2);
+          new_envp[0] = g_strdup_printf ("PATH=%s", apppath);
+
+          for (p_index = 0; p_index <= g_strv_length (envp); p_index++)
+            new_envp[1 + p_index] = envp[p_index];
+
+          g_free (envp);
+          envp = new_envp;
+        }
+      else
+        {
+          gchar *p_path;
+
+          p_path = &p[0][5];
+
+          if (p_path[0] != '\0')
+            envp[p_index] = g_strdup_printf ("PATH=%s%c%s",
+                                             apppath,
+                                             G_SEARCHPATH_SEPARATOR,
+                                             p_path);
+          else
+            envp[p_index] = g_strdup_printf ("PATH=%s", apppath);
+
+          g_free (&p_path[-5]);
+        }
+    }
+
+  do
+    {
+      GPid pid;
+
+      if (!expand_application_parameters (info,
+                                          command,
+                                          &objs,
+                                          &argc,
+                                          &argv,
+                                          error))
+        goto out;
+
+      if (!g_spawn_async (NULL,
+                          argv,
+                          envp,
+                          spawn_flags,
+                          NULL,
+                          NULL,
+                          &pid,
+                          error))
+          goto out;
+
+      if (launch_context != NULL)
+        {
+          GVariantBuilder builder;
+          GVariant *platform_data;
+
+          g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+          g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
+
+          platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
+          g_signal_emit_by_name (launch_context, "launched", info, platform_data);
+          g_variant_unref (platform_data);
+        }
+
+      g_strfreev (argv);
+      argv = NULL;
+    }
+  while (objs != NULL);
+
+  completed = TRUE;
+
+ out:
+  g_strfreev (argv);
+  g_strfreev (envp);
+  g_free (command);
+
+  return completed;
+}
+
+static void
+free_file_or_uri (gpointer ptr)
+{
+  file_or_uri *obj = ptr;
+  g_free (obj->file);
+  g_free (obj->uri);
+  g_free (obj);
 }
 
+
 static gboolean
-g_win32_app_info_launch (GAppInfo           *appinfo,
-                         GList              *files,
-                         GAppLaunchContext  *launch_context,
-                         GError            **error)
+g_win32_app_supports_uris (GWin32AppInfoApplication *app)
 {
-  gboolean success;
-  GList *paths = g_list_copy_deep (files, (GCopyFunc) g_file_get_path,
-                                   NULL);
+  gssize num_of_uris_supported;
 
-  success = g_win32_app_info_launch_locations (appinfo, paths,
-                                               launch_context, error);
+  if (app == NULL)
+    return FALSE;
+
+  num_of_uris_supported = (gssize) g_hash_table_size (app->supported_urls);
 
-  g_list_free_full (paths, g_free);
+  if (g_hash_table_lookup (app->supported_urls, "file"))
+    num_of_uris_supported -= 1;
 
-  return success;
+  return num_of_uris_supported > 0;
 }
 
+
 static gboolean
-g_win32_app_info_launch_uris (GAppInfo           *appinfo,
-                             GList              *uris,
-                             GAppLaunchContext  *launch_context,
-                             GError            **error)
+g_win32_app_info_supports_uris (GAppInfo *appinfo)
 {
-  return g_win32_app_info_launch_locations (appinfo, uris,
-                                            launch_context, error);
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  if (info->app == NULL)
+    return FALSE;
+
+  return g_win32_app_supports_uris (info->app);
 }
 
+
 static gboolean
-g_win32_app_info_should_show (GAppInfo *appinfo)
+g_win32_app_info_supports_files (GAppInfo *appinfo)
 {
   GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
 
-  if (info->no_open_with)
+  if (info->app == NULL)
     return FALSE;
-  
-  return TRUE;
+
+  return g_hash_table_size (info->app->supported_exts) > 0;
 }
 
+
 static gboolean
-g_win32_app_info_set_as_default_for_type (GAppInfo    *appinfo,
-                                          const char  *content_type,
-                                          GError     **error)
+g_win32_app_info_launch_uris (GAppInfo           *appinfo,
+                              GList              *uris,
+                              GAppLaunchContext  *launch_context,
+                              GError            **error)
 {
-  g_set_error_literal (error, G_IO_ERROR, 
-                       G_IO_ERROR_NOT_SUPPORTED, 
-                       _("association changes not supported on win32"));
-  return FALSE;
+  gboolean res = FALSE;
+  gboolean do_files;
+  GList *objs;
+
+  do_files = g_win32_app_info_supports_files (appinfo);
+
+  objs = NULL;
+  while (uris)
+    {
+      file_or_uri *obj;
+      obj = g_new0 (file_or_uri, 1);
+
+      if (do_files)
+        {
+          GFile *file;
+          gchar *path;
+
+          file = g_file_new_for_uri (uris->data);
+          path = g_file_get_path (file);
+          obj->file = path;
+          g_object_unref (file);
+        }
+
+      obj->uri = g_strdup (uris->data);
+
+      objs = g_list_prepend (objs, obj);
+      uris = uris->next;
+    }
+
+  objs = g_list_reverse (objs);
+
+  res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
+                                          objs,
+                                          launch_context,
+                                          G_SPAWN_SEARCH_PATH,
+                                          error);
+
+  g_list_free_full (objs, free_file_or_uri);
+
+  return res;
+}
+
+static gboolean
+g_win32_app_info_launch (GAppInfo           *appinfo,
+                         GList              *files,
+                         GAppLaunchContext  *launch_context,
+                         GError            **error)
+{
+  gboolean res = FALSE;
+  gboolean do_uris;
+  GList *objs;
+
+  do_uris = g_win32_app_info_supports_uris (appinfo);
+
+  objs = NULL;
+  while (files)
+    {
+      file_or_uri *obj;
+      obj = g_new0 (file_or_uri, 1);
+      obj->file = g_file_get_path (G_FILE (files->data));
+
+      if (do_uris)
+        obj->uri = g_file_get_uri (G_FILE (files->data));
+
+      objs = g_list_prepend (objs, obj);
+      files = files->next;
+    }
+
+  objs = g_list_reverse (objs);
+
+  res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
+                                          objs,
+                                          launch_context,
+                                          G_SPAWN_SEARCH_PATH,
+                                          error);
+
+  g_list_free_full (objs, free_file_or_uri);
+
+  return res;
+}
+
+static const char **
+g_win32_app_info_get_supported_types (GAppInfo *appinfo)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  return (const char**) info->supported_types;
 }
 
 GAppInfo *
 g_app_info_create_from_commandline (const char           *commandline,
-                                   const char           *application_name,
-                                   GAppInfoCreateFlags   flags,
-                                   GError              **error)
+                                    const char           *application_name,
+                                    GAppInfoCreateFlags   flags,
+                                    GError              **error)
 {
-  g_set_error_literal (error, G_IO_ERROR, 
-                       G_IO_ERROR_NOT_SUPPORTED, 
-                       _("Association creation not supported on win32"));
-  return NULL;
+  GWin32AppInfo *info;
+  GWin32AppInfoApplication *app;
+
+  g_return_val_if_fail (commandline, NULL);
+
+  info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
+
+  app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
+
+  if (application_name)
+    {
+      app->canonical_name = g_utf8_to_utf16 (application_name,
+                                             -1,
+                                             NULL,
+                                             NULL,
+                                             NULL);
+      app->canonical_name_u8 = g_strdup (application_name);
+      app->canonical_name_folded = g_utf8_casefold (application_name, -1);
+    }
+
+  app->command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
+  app->command_u8 = g_strdup (commandline);
+
+  extract_executable (app->command,
+                      &app->executable,
+                      &app->executable_basename,
+                      &app->executable_folded,
+                      NULL);
+
+  app->no_open_with = FALSE;
+  app->user_specific = FALSE;
+  app->default_app = FALSE;
+
+  info->app = app;
+  info->handler = NULL;
+
+  return G_APP_INFO (info);
 }
 
+/* GAppInfo interface init */
 
 static void
 g_win32_app_info_iface_init (GAppInfoIface *iface)
@@ -406,316 +4432,161 @@ g_win32_app_info_iface_init (GAppInfoIface *iface)
   iface->supports_uris = g_win32_app_info_supports_uris;
   iface->supports_files = g_win32_app_info_supports_files;
   iface->launch_uris = g_win32_app_info_launch_uris;
-  iface->should_show = g_win32_app_info_should_show;
-  iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;
-}
-
-static void
-enumerate_open_with_list (HKEY    dir_key,
-                         GList **prognames)
-{
-  DWORD index;
-  wchar_t name[256];
-  DWORD name_len, nbytes;
-  wchar_t data[256];
-  wchar_t *data_alloc;
-  DWORD type;
-
-  /* Must also look inside for a,b,c, + MRUList */
-  index = 0;
-  name_len = 256;
-  nbytes = sizeof (data) - 2;
-  while (RegEnumValueW (dir_key,
-                       index,
-                       name,
-                       &name_len,
-                       0,
-                       &type,
-                       (LPBYTE)data,
-                       &nbytes) == ERROR_SUCCESS)
-    {
-      data[nbytes/2] = '\0';
-      if (type == REG_SZ &&
-         /* Ignore things like MRUList, just look at 'a', 'b', 'c', etc */
-         name_len == 1)
-       {
-         data_alloc = (wchar_t *)g_memdup (data, nbytes + 2);
-         data_alloc[nbytes/2] = 0;
-         *prognames = g_list_prepend (*prognames, data_alloc);
-       }
-      index++;
-      name_len = 256;
-      nbytes = sizeof (data) - 2;
-    }
-  
-  index = 0;
-  name_len = 256;
-  while (RegEnumKeyExW (dir_key,
-                       index,
-                       name,
-                       &name_len,
-                       NULL,
-                       NULL,
-                       NULL,
-                       NULL) == ERROR_SUCCESS)
-    {
-      *prognames = g_list_prepend (*prognames, g_memdup (name, (name_len + 1) * 2));
-      index++;
-      name_len = 256;
-    }
+/*  iface->should_show = g_win32_app_info_should_show;*/
+/*  iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
+/*  iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
+/*  iface->add_supports_type = g_win32_app_info_add_supports_type;*/
+/*  iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
+/*  iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
+/*  iface->can_delete = g_win32_app_info_can_delete;*/
+/*  iface->do_delete = g_win32_app_info_delete;*/
+  iface->get_commandline = g_win32_app_info_get_commandline;
+  iface->get_display_name = g_win32_app_info_get_display_name;
+/*  iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
+  iface->get_supported_types = g_win32_app_info_get_supported_types;
 }
 
-static void
-enumerate_open_with_progids (HKEY dir_key,
-                            GList **progids)
+GAppInfo *
+g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
 {
-  DWORD index;
-  wchar_t name[256];
-  DWORD name_len, type;
+  GWin32AppInfoURLSchema *scheme;
+  char *scheme_down;
+  GAppInfo *result;
 
-  index = 0;
-  name_len = 256;
-  while (RegEnumValueW (dir_key,
-                       index,
-                       name,
-                       &name_len,
-                       0,
-                       &type,
-                       NULL,
-                       0) == ERROR_SUCCESS)
-    {
-      *progids = g_list_prepend (*progids, g_memdup (name, (name_len + 1) * 2));
-      index++;
-      name_len = 256;
-    }
-}
+  scheme_down = g_utf8_casefold (uri_scheme, -1);
 
-static void
-enumerate_open_with_root (HKEY    dir_key,
-                         GList **progids,
-                         GList **prognames)
-{
-  HKEY reg_key = NULL;
-  
-  if (RegOpenKeyExW (dir_key, L"OpenWithList", 0,
-                    KEY_READ, &reg_key) == ERROR_SUCCESS)
-    {
-      enumerate_open_with_list (reg_key, prognames);
-      RegCloseKey (reg_key);
-    }
-  
-  if (RegOpenKeyExW (dir_key, L"OpenWithProgids", 0,
-                    KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
-    {
-      enumerate_open_with_progids (reg_key, progids);
-      RegCloseKey (reg_key);
-    }
-}
+  if (!scheme_down)
+    return NULL;
 
-static gboolean
-app_info_in_list (GAppInfo *info, 
-                  GList    *list)
-{
-  while (list != NULL)
+  if (strcmp (scheme_down, "file") == 0)
     {
-      if (g_app_info_equal (info, list->data))
-       return TRUE;
-      list = list->next;
+      g_free (scheme_down);
+      return NULL;
     }
-  return FALSE;
-}
 
-GList *
-g_app_info_get_all_for_type (const char *content_type)
-{
-  GList *progids = NULL;
-  GList *prognames = NULL;
-  HKEY reg_key, sys_file_assoc_key, reg_key2;
-  wchar_t percieved_type[128];
-  DWORD nchars, key_type;
-  wchar_t *wc_key;
-  GList *l;
-  GList *infos;
+  g_win32_appinfo_init ();
+  G_LOCK (gio_win32_appinfo);
 
-  wc_key = g_utf8_to_utf16 (content_type, -1, NULL, NULL, NULL);
-  if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
-                    KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
-    {
-      enumerate_open_with_root (reg_key, &progids, &prognames);
-
-      nchars = sizeof (percieved_type) / sizeof(wchar_t);
-      if (RegQueryValueExW (reg_key, L"PerceivedType", 0,
-                           &key_type, (LPBYTE) percieved_type, &nchars) == ERROR_SUCCESS)
-       {
-         if (key_type == REG_SZ &&
-             RegOpenKeyExW (HKEY_CLASSES_ROOT, L"SystemFileAssociations", 0,
-                            KEY_QUERY_VALUE, &sys_file_assoc_key) == ERROR_SUCCESS)
-           {
-             if (RegOpenKeyExW (sys_file_assoc_key, percieved_type, 0,
-                                KEY_QUERY_VALUE, &reg_key2) == ERROR_SUCCESS)
-               {
-                 enumerate_open_with_root (reg_key2, &progids, &prognames);
-                 RegCloseKey (reg_key2);
-               }
-
-             RegCloseKey (sys_file_assoc_key);
-           }
-       }
-      RegCloseKey (reg_key);
-    }
-
-  if (RegOpenKeyExW (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts", 
0,
-                    KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
-    {
-      if (RegOpenKeyExW (reg_key, wc_key, 0,
-                        KEY_QUERY_VALUE, &reg_key2) == ERROR_SUCCESS)
-       {
-         enumerate_open_with_root (reg_key2, &progids, &prognames);
-         RegCloseKey (reg_key2);
-       }
-      
-      RegCloseKey (reg_key);
-    }
+  scheme = g_hash_table_lookup (urls, scheme_down);
+  g_free (scheme_down);
 
-  infos = NULL;
-  for (l = prognames; l != NULL; l = l->next)
-    {
-      GAppInfo *info;
+  if (scheme)
+    g_object_ref (scheme);
 
-      /* l->data ownership is taken */
-      info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, TRUE);
-      if (app_info_in_list (info, infos))
-       g_object_unref (info);
-      else
-       infos = g_list_prepend (infos, info);
-    }
-  g_list_free (prognames);
+  G_UNLOCK (gio_win32_appinfo);
 
-  for (l = progids; l != NULL; l = l->next)
-    {
-      GAppInfo *info;
+  result = NULL;
 
-      /* l->data ownership is taken */
-      info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, FALSE);
-      if (app_info_in_list (info, infos))
-       g_object_unref (info);
-      else
-       infos = g_list_prepend (infos, info);
-    }
-  g_list_free (progids);
-  
-  g_free (wc_key);
-  return g_list_reverse (infos);
-}
+  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);
 
-GList *
-g_app_info_get_recommended_for_type (const char *content_type)
-{
-  /* FIXME: this should generate a list of applications that are registered
-   * as direct handlers for the given content type, without using MIME subclassing.
-   * See g_app_info_get_recommended_for_type() in gdesktopappinfo.c for a reference
-   * UNIX implementation.
-   */
-  return g_app_info_get_all_for_type (content_type);
-}
+  if (scheme)
+    g_object_unref (scheme);
 
-GList *
-g_app_info_get_fallback_for_type (const char *content_type)
-{
-  /* FIXME: this should generate a list of applications that are registered
-   * as handlers for a superclass of the given content type, but are not
-   * direct handlers for the content type itself. See g_app_info_get_fallback_for_type()
-   * in gdesktopappinfo.c for a reference UNIX implementation.
-   */
-  return g_app_info_get_all_for_type (content_type);
-}
-
-/*
- * The windows api (AssocQueryString) doesn't distinguish between uri schemes
- * and file type extensions here, so we use the same implementation for both
- * g_app_info_get_default_for_type and g_app_info_get_default_for_uri_scheme
- */
-static GAppInfo *
-get_default_for_association (const char *association)
-{
-  wchar_t *wtype;
-  wchar_t buffer[1024];
-  DWORD buffer_size;
-
-  wtype = g_utf8_to_utf16 (association, -1, NULL, NULL, NULL);
-
-  /* Verify that we have some sort of app registered for this type */
-#ifdef AssocQueryString
-  buffer_size = 1024;
-  if (AssocQueryStringW (0,
-                        REAL_ASSOCSTR_COMMAND,
-                        wtype,
-                        NULL,
-                        buffer,
-                        &buffer_size) == S_OK)
-    /* Takes ownership of wtype */
-    return g_desktop_app_info_new_from_id (wtype, FALSE);
-#endif
-
-  g_free (wtype);
-  return NULL;
+  return result;
 }
 
 GAppInfo *
 g_app_info_get_default_for_type (const char *content_type,
                                  gboolean    must_support_uris)
 {
-  return get_default_for_association (content_type);
-}
+  GWin32AppInfoFileExtension *ext;
+  char *ext_down;
+  GWin32AppInfoHandler *handler;
+  GAppInfo *result;
+  GWin32AppInfoApplication *app;
+  GHashTableIter iter;
 
-GAppInfo *
-g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
-{
-  return get_default_for_association (uri_scheme);
+  ext_down = g_utf8_casefold (content_type, -1);
+
+  if (!ext_down)
+    return NULL;
+
+  g_win32_appinfo_init ();
+  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_free (ext_down);
+
+  result = NULL;
+
+  if (ext != NULL)
+    g_object_ref (ext);
+
+  G_UNLOCK (gio_win32_appinfo);
+
+  if (ext != NULL)
+    {
+      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
+        {
+          g_hash_table_iter_init (&iter, ext->handlers);
+
+          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;
+                }
+            }
+
+          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;
+                    }
+                }
+            }
+        }
+      g_object_unref (ext);
+    }
+
+  return result;
 }
 
 GList *
 g_app_info_get_all (void)
 {
-  DWORD index;
-  wchar_t name[256];
-  DWORD name_len;
-  HKEY reg_key;
+  GHashTableIter iter;
+  gpointer value;
   GList *infos;
-  GAppInfo *info;
+  GList *apps;
+  GList *apps_i;
 
-  if (RegOpenKeyExW (HKEY_CLASSES_ROOT, L"Applications", 0,
-                    KEY_READ, &reg_key) != ERROR_SUCCESS)
-    return NULL;
+  g_win32_appinfo_init ();
+  G_LOCK (gio_win32_appinfo);
+
+  apps = NULL;
+  g_hash_table_iter_init (&iter, apps_by_id);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
+
+  G_UNLOCK (gio_win32_appinfo);
 
   infos = NULL;
-  index = 0;
-  name_len = 256;
-  while (RegEnumKeyExW (reg_key,
-                       index,
-                       name,
-                       &name_len,
-                       NULL,
-                       NULL,
-                       NULL,
-                       NULL) == ERROR_SUCCESS)
-    {
-      wchar_t *name_dup = g_memdup (name, (name_len+1)*2);
-      /* name_dup ownership is taken */
-      info = g_desktop_app_info_new_from_id (name_dup, TRUE);
-      infos = g_list_prepend (infos, info);
-      
-      index++;
-      name_len = 256;
-    }
-  
-  RegCloseKey (reg_key);
-
-  return g_list_reverse (infos);
-}
-
-void
-g_app_info_reset_type_associations (const char *content_type)
-{
-  /* nothing to do */
+  for (apps_i = apps; apps_i; apps_i = apps_i->next)
+    infos = g_list_prepend (infos,
+                            g_win32_app_info_new_from_app (apps_i->data, NULL));
+
+  g_list_free_full (apps, g_object_unref);
+
+  return infos;
 }
diff --git a/gio/gwin32appinfo.h b/gio/gwin32appinfo.h
index 5414f68..16c1181 100644
--- a/gio/gwin32appinfo.h
+++ b/gio/gwin32appinfo.h
@@ -1,6 +1,7 @@
 /* GIO - GLib Input, Output and Streaming Library
  *
  * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2014 Руслан Ижбулатов
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,7 +16,8 @@
  * You should have received a copy of the GNU Lesser General
  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
- * Author: Alexander Larsson <alexl redhat com>
+ * Authors: Alexander Larsson <alexl redhat com>
+ *          Руслан Ижбулатов  <lrn1986 gmail com>
  */
 
 #ifndef __G_WIN32_APP_INFO_H__


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]