1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright (C) 2014 Руслан Ижбулатов
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 * Authors: Alexander Larsson <alexl@redhat.com>
20 * Руслан Ижбулатов <lrn1986@gmail.com>
27 #include "gcontenttype.h"
28 #include "gwin32appinfo.h"
32 #include <glib/gstdio.h>
34 #include <gio/gwin32registrykey.h>
38 #include <glib/gstdioprivate.h>
39 #include "glib-private.h"
41 /* We need to watch 8 places:
42 * 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
43 * (anything below that key)
44 * On change: re-enumerate subkeys, read their values.
45 * 1) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
46 * (anything below that key)
47 * On change: re-enumerate subkeys
48 * 2) HKEY_CURRENT_USER\\Software\\Clients (anything below that key)
49 * On change: re-read the whole hierarchy of handlers
50 * 3) HKEY_LOCAL_MACHINE\\Software\\Clients (anything below that key)
51 * On change: re-read the whole hierarchy of handlers
52 * 4) HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications (values of that key)
53 * On change: re-read the value list of registered applications
54 * 5) HKEY_CURRENT_USER\\Software\\RegisteredApplications (values of that key)
55 * On change: re-read the value list of registered applications
56 * 6) HKEY_CLASSES_ROOT\\Applications (anything below that key)
57 * On change: re-read the whole hierarchy of apps
58 * 7) HKEY_CLASSES_ROOT (only its subkeys)
59 * On change: re-enumerate subkeys, try to filter out wrong names.
62 * About verbs. A registry key (the name of that key is known as ProgID)
63 * can contain a "shell" subkey, which can then contain a number of verb
64 * subkeys (the most common being the "open" verb), and each of these
65 * contains a "command" subkey, which has a default string value that
66 * is the command to be run.
67 * Most ProgIDs are in HKEY_CLASSES_ROOT, but some are nested deeper in
68 * the registry (such as HKEY_CURRENT_USER\\Software\\<softwarename>).
70 * Verb selection works like this (according to https://docs.microsoft.com/en-us/windows/win32/shell/context ):
71 * 1) If "open" verb is available, that verb is used.
72 * 2) If the Shell subkey has a default string value, and if a verb subkey
73 * with that name exists, that verb is used.
74 * 3) The first subkey found in the list of verb subkeys is used.
75 * 4) The "openwith" verb is used
77 * Testing suggests that Windows never reaches the point 4 in any realistic
78 * circumstances. If a "command" subkey is missing for a verb, or if it has
79 * an empty string as its default value, the app launch fails
80 * (the "openwith" verb is not used, even if it's present).
81 * If the command is present, but is not valid (runs nonexisting executable,
82 * for example), then other verbs are not checked.
83 * It seems that when the documentation said "openwith verb", it meant
84 * that Windows invokes the default "Open with..." dialog (it does not
85 * look at the "openwith" verb subkey, even if it's there).
86 * If a verb subkey that is supposed to be used is present, but it lacks
87 * a command subkey, an error message is shown and nothing else happens.
90 #define _verb_idx(array,index) ((GWin32AppInfoShellVerb *) g_ptr_array_index (array, index))
92 #define _lookup_by_verb(array, verb, dst, itemtype) do { \
95 for (_index = 0; array && _index < array->len; _index++) \
97 _v = (itemtype *) g_ptr_array_index (array, _index); \
98 if (_wcsicmp (_v->verb_name, (verb)) == 0) \
104 if (array == NULL || _index >= array->len) \
108 #define _verb_lookup(array, verb, dst) _lookup_by_verb (array, verb, dst, GWin32AppInfoShellVerb)
110 /* Because with subcommands a verb would have
111 * a name like "foo\\bar", but the key its command
112 * should be looked for is "shell\\foo\\shell\\bar\\command"
114 typedef struct _reg_verb {
116 gunichar2 *shellpath;
119 typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
120 typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
121 typedef struct _GWin32AppInfoShellVerb GWin32AppInfoShellVerb;
122 typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
123 typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
125 typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
126 typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
127 typedef struct _GWin32AppInfoShellVerbClass GWin32AppInfoShellVerbClass;
128 typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
129 typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
131 struct _GWin32AppInfoURLSchemaClass
133 GObjectClass parent_class;
136 struct _GWin32AppInfoFileExtensionClass
138 GObjectClass parent_class;
141 struct _GWin32AppInfoHandlerClass
143 GObjectClass parent_class;
146 struct _GWin32AppInfoApplicationClass
148 GObjectClass parent_class;
151 struct _GWin32AppInfoShellVerbClass
153 GObjectClass parent_class;
156 struct _GWin32AppInfoURLSchema {
157 GObject parent_instance;
159 /* url schema (stuff before ':') */
162 /* url schema (stuff before ':'), in UTF-8 */
165 /* url schema (stuff before ':'), in UTF-8, folded */
166 gchar *schema_u8_folded;
168 /* Handler currently selected for this schema. Can be NULL. */
169 GWin32AppInfoHandler *chosen_handler;
171 /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this schema.
172 * Includes the chosen handler, if any.
174 GHashTable *handlers;
177 struct _GWin32AppInfoHandler {
178 GObject parent_instance;
180 /* Usually a class name in HKCR */
181 gunichar2 *handler_id;
183 /* Registry object obtained by opening @handler_id. Can be used to watch this handler. */
184 GWin32RegistryKey *key;
186 /* @handler_id, in UTF-8, folded */
187 gchar *handler_id_folded;
189 /* Icon of the application for this handler */
192 /* Verbs that this handler supports */
193 GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
196 struct _GWin32AppInfoShellVerb {
197 GObject parent_instance;
199 /* The verb that is used to invoke this handler. */
200 gunichar2 *verb_name;
202 /* User-friendly (localized) verb name. */
203 gchar *verb_displayname;
205 /* shell/verb/command */
208 /* Same as @command, but in UTF-8 */
211 /* Executable of the program (UTF-8) */
214 /* Executable of the program (for matching, in folded form; UTF-8) */
215 gchar *executable_folded;
217 /* Pointer to a location within @executable */
218 gchar *executable_basename;
220 /* If not NULL, then @executable and its derived fields contain the name
221 * of a DLL file (without the name of the function that rundll32.exe should
222 * invoke), and this field contains the name of the function to be invoked.
223 * The application is then invoked as 'rundll32.exe "dll_path",dll_function other_arguments...'.
227 /* The application that is linked to this verb. */
228 GWin32AppInfoApplication *app;
231 struct _GWin32AppInfoFileExtension {
232 GObject parent_instance;
234 /* File extension (with leading '.') */
235 gunichar2 *extension;
237 /* File extension (with leading '.'), in UTF-8 */
240 /* handler currently selected for this extension. Can be NULL. */
241 GWin32AppInfoHandler *chosen_handler;
243 /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this extension.
244 * Includes the chosen handler, if any.
246 GHashTable *handlers;
249 struct _GWin32AppInfoApplication {
250 GObject parent_instance;
252 /* Canonical name (used for key names).
253 * For applications tracked by id this is the root registry
254 * key path for the application.
255 * For applications tracked by executable name this is the
256 * basename of the executable.
257 * For fake applications this is the full filename of the
258 * executable (as far as it can be inferred from a command line,
259 * meaning that it can also be a basename, if that's
260 * all that a commandline happen to give us).
262 gunichar2 *canonical_name;
264 /* @canonical_name, in UTF-8 */
265 gchar *canonical_name_u8;
267 /* @canonical_name, in UTF-8, folded */
268 gchar *canonical_name_folded;
270 /* Human-readable name in English. Can be NULL */
271 gunichar2 *pretty_name;
273 /* Human-readable name in English, UTF-8. Can be NULL */
274 gchar *pretty_name_u8;
276 /* Human-readable name in user's language. Can be NULL */
277 gunichar2 *localized_pretty_name;
279 /* Human-readable name in user's language, UTF-8. Can be NULL */
280 gchar *localized_pretty_name_u8;
282 /* Description, could be in user's language. Can be NULL */
283 gunichar2 *description;
285 /* Description, could be in user's language, UTF-8. Can be NULL */
286 gchar *description_u8;
288 /* Verbs that this application supports */
289 GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
291 /* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
292 * UTF-8, folded) -> to a GWin32AppInfoHandler
293 * Schema can be used as a key in the urls hashmap.
295 GHashTable *supported_urls;
297 /* Explicitly supported extensions, hashmap from map-owned gchar ptr
298 * (.extension, UTF-8, folded) -> to a GWin32AppInfoHandler
299 * Extension can be used as a key in the extensions hashmap.
301 GHashTable *supported_exts;
303 /* Icon of the application (remember, handler can have its own icon too) */
306 /* Set to TRUE to prevent this app from appearing in lists of apps for
307 * opening files. This will not prevent it from appearing in lists of apps
308 * just for running, or lists of apps for opening exts/urls for which this
309 * app reports explicit support.
311 gboolean no_open_with;
313 /* Set to TRUE for applications from HKEY_CURRENT_USER.
314 * Give them priority over applications from HKEY_LOCAL_MACHINE, when all
315 * other things are equal.
317 gboolean user_specific;
319 /* Set to TRUE for applications that are machine-wide defaults (i.e. default
321 gboolean default_app;
324 #define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ())
325 #define G_WIN32_APPINFO_URL_SCHEMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema))
327 #define G_TYPE_WIN32_APPINFO_FILE_EXTENSION (g_win32_appinfo_file_extension_get_type ())
328 #define G_WIN32_APPINFO_FILE_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension))
330 #define G_TYPE_WIN32_APPINFO_HANDLER (g_win32_appinfo_handler_get_type ())
331 #define G_WIN32_APPINFO_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler))
333 #define G_TYPE_WIN32_APPINFO_APPLICATION (g_win32_appinfo_application_get_type ())
334 #define G_WIN32_APPINFO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication))
336 #define G_TYPE_WIN32_APPINFO_SHELL_VERB (g_win32_appinfo_shell_verb_get_type ())
337 #define G_WIN32_APPINFO_SHELL_VERB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_SHELL_VERB, GWin32AppInfoShellVerb))
339 GType g_win32_appinfo_url_schema_get_type (void) G_GNUC_CONST;
340 GType g_win32_appinfo_file_extension_get_type (void) G_GNUC_CONST;
341 GType g_win32_appinfo_shell_verb_get_type (void) G_GNUC_CONST;
342 GType g_win32_appinfo_handler_get_type (void) G_GNUC_CONST;
343 GType g_win32_appinfo_application_get_type (void) G_GNUC_CONST;
345 G_DEFINE_TYPE (GWin32AppInfoURLSchema, g_win32_appinfo_url_schema, G_TYPE_OBJECT)
346 G_DEFINE_TYPE (GWin32AppInfoFileExtension, g_win32_appinfo_file_extension, G_TYPE_OBJECT)
347 G_DEFINE_TYPE (GWin32AppInfoShellVerb, g_win32_appinfo_shell_verb, G_TYPE_OBJECT)
348 G_DEFINE_TYPE (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT)
349 G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT)
352 g_win32_appinfo_url_schema_dispose (GObject *object)
354 GWin32AppInfoURLSchema *url = G_WIN32_APPINFO_URL_SCHEMA (object);
356 g_clear_pointer (&url->schema, g_free);
357 g_clear_pointer (&url->schema_u8, g_free);
358 g_clear_pointer (&url->schema_u8_folded, g_free);
359 g_clear_object (&url->chosen_handler);
360 g_clear_pointer (&url->handlers, g_hash_table_destroy);
361 G_OBJECT_CLASS (g_win32_appinfo_url_schema_parent_class)->dispose (object);
366 g_win32_appinfo_handler_dispose (GObject *object)
368 GWin32AppInfoHandler *handler = G_WIN32_APPINFO_HANDLER (object);
370 g_clear_pointer (&handler->handler_id, g_free);
371 g_clear_pointer (&handler->handler_id_folded, g_free);
372 g_clear_object (&handler->key);
373 g_clear_object (&handler->icon);
374 g_clear_pointer (&handler->verbs, g_ptr_array_unref);
375 G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
379 g_win32_appinfo_file_extension_dispose (GObject *object)
381 GWin32AppInfoFileExtension *ext = G_WIN32_APPINFO_FILE_EXTENSION (object);
383 g_clear_pointer (&ext->extension, g_free);
384 g_clear_pointer (&ext->extension_u8, g_free);
385 g_clear_object (&ext->chosen_handler);
386 g_clear_pointer (&ext->handlers, g_hash_table_destroy);
387 G_OBJECT_CLASS (g_win32_appinfo_file_extension_parent_class)->dispose (object);
391 g_win32_appinfo_shell_verb_dispose (GObject *object)
393 GWin32AppInfoShellVerb *shverb = G_WIN32_APPINFO_SHELL_VERB (object);
395 g_clear_pointer (&shverb->verb_name, g_free);
396 g_clear_pointer (&shverb->verb_displayname, g_free);
397 g_clear_pointer (&shverb->command, g_free);
398 g_clear_pointer (&shverb->command_utf8, g_free);
399 g_clear_pointer (&shverb->executable_folded, g_free);
400 g_clear_pointer (&shverb->executable, g_free);
401 g_clear_pointer (&shverb->dll_function, g_free);
402 g_clear_object (&shverb->app);
403 G_OBJECT_CLASS (g_win32_appinfo_shell_verb_parent_class)->dispose (object);
407 g_win32_appinfo_application_dispose (GObject *object)
409 GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object);
411 g_clear_pointer (&app->canonical_name_u8, g_free);
412 g_clear_pointer (&app->canonical_name_folded, g_free);
413 g_clear_pointer (&app->canonical_name, g_free);
414 g_clear_pointer (&app->pretty_name, g_free);
415 g_clear_pointer (&app->localized_pretty_name, g_free);
416 g_clear_pointer (&app->description, g_free);
417 g_clear_pointer (&app->pretty_name_u8, g_free);
418 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
419 g_clear_pointer (&app->description_u8, g_free);
420 g_clear_pointer (&app->supported_urls, g_hash_table_destroy);
421 g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
422 g_clear_object (&app->icon);
423 g_clear_pointer (&app->verbs, g_ptr_array_unref);
424 G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
428 g_win32_appinfo_application_get_some_name (GWin32AppInfoApplication *app)
430 if (app->localized_pretty_name_u8)
431 return app->localized_pretty_name_u8;
433 if (app->pretty_name_u8)
434 return app->pretty_name_u8;
436 return app->canonical_name_u8;
440 g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
442 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
444 gobject_class->dispose = g_win32_appinfo_url_schema_dispose;
448 g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klass)
450 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
452 gobject_class->dispose = g_win32_appinfo_file_extension_dispose;
456 g_win32_appinfo_shell_verb_class_init (GWin32AppInfoShellVerbClass *klass)
458 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
460 gobject_class->dispose = g_win32_appinfo_shell_verb_dispose;
464 g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
466 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
468 gobject_class->dispose = g_win32_appinfo_handler_dispose;
472 g_win32_appinfo_application_class_init (GWin32AppInfoApplicationClass *klass)
474 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
476 gobject_class->dispose = g_win32_appinfo_application_dispose;
480 g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
482 self->handlers = g_hash_table_new_full (g_str_hash,
489 g_win32_appinfo_shell_verb_init (GWin32AppInfoShellVerb *self)
494 g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
496 self->handlers = g_hash_table_new_full (g_str_hash,
503 g_win32_appinfo_handler_init (GWin32AppInfoHandler *self)
505 self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
509 g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
511 self->supported_urls = g_hash_table_new_full (g_str_hash,
515 self->supported_exts = g_hash_table_new_full (g_str_hash,
519 self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
522 G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
524 /* Map of owned ".ext" (with '.', UTF-8, folded)
525 * to GWin32AppInfoFileExtension ptr
527 static GHashTable *extensions = NULL;
529 /* Map of owned "schema" (without ':', UTF-8, folded)
530 * to GWin32AppInfoURLSchema ptr
532 static GHashTable *urls = NULL;
534 /* Map of owned "appID" (UTF-8, folded) to
535 * a GWin32AppInfoApplication
537 static GHashTable *apps_by_id = NULL;
539 /* Map of owned "app.exe" (UTF-8, folded) to
540 * a GWin32AppInfoApplication.
541 * This map and its values are separate from apps_by_id. The fact that an app
542 * with known ID has the same executable [base]name as an app in this map does
543 * not mean that they are the same application.
545 static GHashTable *apps_by_exe = NULL;
547 /* Map of owned "path:\to\app.exe" (UTF-8, folded) to
548 * a GWin32AppInfoApplication.
549 * The app objects in this map are fake - they are linked to
550 * handlers that do not have any apps associated with them.
552 static GHashTable *fake_apps = NULL;
554 /* Map of owned "handler id" (UTF-8, folded)
555 * to a GWin32AppInfoHandler
557 static GHashTable *handlers = NULL;
559 /* Watch this whole subtree */
560 static GWin32RegistryKey *url_associations_key;
562 /* Watch this whole subtree */
563 static GWin32RegistryKey *file_exts_key;
565 /* Watch this whole subtree */
566 static GWin32RegistryKey *user_clients_key;
568 /* Watch this whole subtree */
569 static GWin32RegistryKey *system_clients_key;
572 static GWin32RegistryKey *user_registered_apps_key;
575 static GWin32RegistryKey *system_registered_apps_key;
577 /* Watch this whole subtree */
578 static GWin32RegistryKey *applications_key;
581 static GWin32RegistryKey *classes_root_key;
583 #define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
584 #define USER_CHOICE L"\\UserChoice"
585 #define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
586 #define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"
587 #define HKCR L"HKEY_CLASSES_ROOT\\"
588 #define HKCU L"HKEY_CURRENT_USER\\"
589 #define HKLM L"HKEY_LOCAL_MACHINE\\"
590 #define REG_PATH_MAX 256
591 #define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
594 * _g_win32_extract_executable(),
595 * _g_win32_fixup_broken_microsoft_rundll_commandline()
597 #include "giowin32-private.c"
600 read_handler_icon (GWin32RegistryKey *key,
603 GWin32RegistryKey *icon_key;
604 GWin32RegistryValueType default_type;
605 gchar *default_value;
611 icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
613 if (icon_key == NULL)
616 if (g_win32_registry_key_get_value (icon_key,
621 (gpointer *) &default_value,
625 if (default_type == G_WIN32_REGISTRY_VALUE_STR &&
626 default_value[0] != '\0')
627 *icon_out = g_themed_icon_new (default_value);
629 g_clear_pointer (&default_value, g_free);
632 g_object_unref (icon_key);
636 reg_verb_free (gpointer p)
641 g_free (((reg_verb *) p)->name);
642 g_free (((reg_verb *) p)->shellpath);
646 #define is_open(x) ( \
647 ((x)[0] == L'o' || (x)[0] == L'O') && \
648 ((x)[1] == L'p' || (x)[1] == L'P') && \
649 ((x)[2] == L'e' || (x)[2] == L'E') && \
650 ((x)[3] == L'n' || (x)[3] == L'N') && \
654 /* default verb (if any) comes first,
655 * then "open", then the rest of the verbs
656 * are sorted alphabetically
659 compare_verbs (gconstpointer a,
663 const reg_verb *ca = (const reg_verb *) a;
664 const reg_verb *cb = (const reg_verb *) b;
665 const gunichar2 *def = (const gunichar2 *) user_data;
671 if (_wcsicmp (ca->name, def) == 0)
673 else if (_wcsicmp (cb->name, def) == 0)
677 is_open_ca = is_open (ca->name);
678 is_open_cb = is_open (cb->name);
680 if (is_open_ca && !is_open_cb)
682 else if (is_open_ca && !is_open_cb)
685 return _wcsicmp (ca->name, cb->name);
688 static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
689 static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_list components);
691 static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
693 /* Called by process_verbs_commands.
694 * @verb is a verb name
695 * @command_line is the commandline of that verb
696 * @command_line_utf8 is the UTF-8 version of @command_line
697 * @verb_displayname is the prettier display name of the verb (might be NULL)
698 * @verb_is_preferred is TRUE if the verb is the preferred one
699 * @invent_new_verb_name is TRUE when the verb should be added
700 * even if a verb with such
701 * name already exists (in which case
702 * a new name is invented), unless
703 * the existing verb runs exactly the same
706 typedef void (*verb_command_func) (gpointer handler_data1,
707 gpointer handler_data2,
708 const gunichar2 *verb,
709 const gunichar2 *command_line,
710 const gchar *command_line_utf8,
711 const gchar *verb_displayname,
712 gboolean verb_is_preferred,
713 gboolean invent_new_verb_name);
715 static gunichar2 * decide_which_id_to_use (const gunichar2 *program_id,
716 GWin32RegistryKey **return_key,
717 gchar **return_handler_id_u8,
718 gchar **return_handler_id_u8_folded);
720 static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *schema,
721 const gchar *schema_u8,
722 const gchar *schema_u8_folded);
724 static GWin32AppInfoHandler * get_handler_object (const gchar *handler_id_u8_folded,
725 GWin32RegistryKey *handler_key,
726 const gunichar2 *handler_id);
728 static GWin32AppInfoFileExtension *get_ext_object (const gunichar2 *ext,
730 const gchar *ext_u8_folded);
733 static void process_verbs_commands (GList *verbs,
734 const reg_verb *preferred_verb,
735 const gunichar2 *path_to_progid,
736 const gunichar2 *progid,
737 gboolean autoprefer_first_verb,
738 verb_command_func handler,
739 gpointer handler_data1,
740 gpointer handler_data2);
742 static void handler_add_verb (gpointer handler_data1,
743 gpointer handler_data2,
744 const gunichar2 *verb,
745 const gunichar2 *command_line,
746 const gchar *command_line_utf8,
747 const gchar *verb_displayname,
748 gboolean verb_is_preferred,
749 gboolean invent_new_verb_name);
751 /* output_size is in *bytes*, not gunichar2s! */
753 build_registry_path (gunichar2 *output, gsize output_size, ...)
758 va_start (ap, output_size);
760 result = build_registry_pathv (output, output_size, ap);
767 /* output_size is in *bytes*, not gunichar2s! */
769 build_registry_pathv (gunichar2 *output, gsize output_size, va_list components)
773 gunichar2 *component;
779 G_VA_COPY (lentest, components);
781 for (length = 0, component = va_arg (lentest, gunichar2 *);
783 component = va_arg (lentest, gunichar2 *))
785 length += wcslen (component);
790 if ((length >= REG_PATH_MAX_SIZE) ||
791 (length * sizeof (gunichar2) >= output_size))
796 for (p = output, component = va_arg (components, gunichar2 *);
798 component = va_arg (components, gunichar2 *))
800 length = wcslen (component);
801 wcscat (p, component);
809 static GWin32RegistryKey *
810 _g_win32_registry_key_build_and_new_w (GError **error, ...)
813 gunichar2 key_path[REG_PATH_MAX_SIZE + 1];
814 GWin32RegistryKey *key;
816 va_start (ap, error);
820 if (build_registry_pathv (key_path, sizeof (key_path), ap))
821 key = g_win32_registry_key_new_w (key_path, error);
828 /* Gets the list of shell verbs (a GList of reg_verb, put into @verbs)
829 * from the @program_id_key.
830 * If one of the verbs should be preferred,
831 * a pointer to this verb (in the GList) will be
832 * put into @preferred_verb.
833 * Does not automatically assume that the first verb
834 * is preferred (when no other preferences exist).
835 * @verbname_prefix is prefixed to the name of the verb
836 * (this is used for subcommands) and is initially an
838 * @verbshell_prefix is the subkey of @program_id_key
839 * that contains the verbs. It is "Shell" initially,
840 * but grows with recursive invocations (for subcommands).
841 * Returns TRUE on success, FALSE on failure.
844 get_verbs (GWin32RegistryKey *program_id_key,
845 const reg_verb **preferred_verb,
847 const gunichar2 *verbname_prefix,
848 const gunichar2 *verbshell_prefix)
850 GWin32RegistrySubkeyIter iter;
851 GWin32RegistryKey *key;
852 GWin32RegistryValueType val_type;
853 gunichar2 *default_verb;
854 gsize verbshell_prefix_len;
855 gsize verbname_prefix_len;
858 g_assert (program_id_key && verbs && preferred_verb);
861 *preferred_verb = NULL;
863 key = g_win32_registry_key_get_child_w (program_id_key,
870 if (!g_win32_registry_subkey_iter_init (&iter, key, NULL))
872 g_object_unref (key);
877 verbshell_prefix_len = g_utf16_len (verbshell_prefix);
878 verbname_prefix_len = g_utf16_len (verbname_prefix);
880 while (g_win32_registry_subkey_iter_next (&iter, TRUE, NULL))
882 const gunichar2 *name;
884 GWin32RegistryKey *subkey;
885 gboolean has_subcommands;
887 GWin32RegistryValueType subc_type;
889 const gunichar2 *shell = L"Shell";
890 const gsize shell_len = g_utf16_len (shell);
892 if (!g_win32_registry_subkey_iter_get_name_w (&iter, &name, &name_len, NULL))
895 subkey = g_win32_registry_key_get_child_w (key,
899 g_assert (subkey != NULL);
900 /* The key we're looking at is "<some_root>/Shell/<this_key>",
901 * where "Shell" is verbshell_prefix.
902 * If it has a value named 'Subcommands' (doesn't matter what its data is),
903 * it means that this key has its own Shell subkey, the subkeys
904 * of which are shell commands (i.e. <some_root>/Shell/<this_key>/Shell/<some_other_keys>).
905 * To handle that, create new, extended nameprefix and shellprefix,
906 * and call the function recursively.
907 * name prefix "" -> "<this_key_name>\\"
908 * shell prefix "Shell" -> "Shell\\<this_key_name>\\Shell"
909 * The root, program_id_key, remains the same in all invocations.
910 * Essentially, we're flattening the command tree into a list.
912 has_subcommands = FALSE;
913 if (g_win32_registry_key_get_value_w (subkey,
921 subc_type == G_WIN32_REGISTRY_VALUE_STR)
923 gunichar2 *new_nameprefix = g_malloc ((verbname_prefix_len + name_len + 1 + 1) * sizeof (gunichar2));
924 gunichar2 *new_shellprefix = g_malloc ((verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1) * sizeof (gunichar2));
925 memcpy (&new_shellprefix[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
926 new_shellprefix[verbshell_prefix_len] = L'\\';
927 memcpy (&new_shellprefix[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
928 new_shellprefix[verbshell_prefix_len + 1 + name_len] = L'\\';
929 memcpy (&new_shellprefix[verbshell_prefix_len + 1 + name_len + 1], shell, shell_len * sizeof (gunichar2));
930 new_shellprefix[verbshell_prefix_len + 1 + name_len + 1 + shell_len] = 0;
932 memcpy (&new_nameprefix[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
933 memcpy (&new_nameprefix[verbname_prefix_len], name, (name_len) * sizeof (gunichar2));
934 new_nameprefix[verbname_prefix_len + name_len] = L'\\';
935 new_nameprefix[verbname_prefix_len + name_len + 1] = 0;
936 has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix);
937 g_free (new_shellprefix);
938 g_free (new_nameprefix);
941 g_clear_object (&subkey);
943 /* Presence of subcommands means that this key itself is not a command-key */
947 /* We don't look at the command sub-key and its value (the actual command line) here.
948 * We save the registry path instead, and use it later in process_verbs_commands().
949 * The name of the verb is also saved.
950 * verbname_prefix is prefixed to the verb name (it's either an empty string
951 * or already ends with a '\\', so no extra separators needed).
952 * verbshell_prefix is prefixed to the verb key path (this one needs a separator,
953 * because it never has one - all verbshell prefixes end with "Shell", not "Shell\\")
955 rverb = g_new0 (reg_verb, 1);
956 rverb->name = g_malloc ((verbname_prefix_len + name_len + 1) * sizeof (gunichar2));
957 memcpy (&rverb->name[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
958 memcpy (&rverb->name[verbname_prefix_len], name, name_len * sizeof (gunichar2));
959 rverb->name[verbname_prefix_len + name_len] = 0;
960 rverb->shellpath = g_malloc ((verbshell_prefix_len + 1 + name_len + 1) * sizeof (gunichar2));
961 memcpy (&rverb->shellpath[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
962 memcpy (&rverb->shellpath[verbshell_prefix_len], L"\\", sizeof (gunichar2));
963 memcpy (&rverb->shellpath[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
964 rverb->shellpath[verbshell_prefix_len + 1 + name_len] = 0;
965 *verbs = g_list_append (*verbs, rverb);
968 g_win32_registry_subkey_iter_clear (&iter);
972 g_object_unref (key);
979 if (g_win32_registry_key_get_value_w (key,
984 (void **) &default_verb,
987 (val_type != G_WIN32_REGISTRY_VALUE_STR ||
988 g_utf16_len (default_verb) <= 0))
989 g_clear_pointer (&default_verb, g_free);
991 g_object_unref (key);
993 /* Only sort at the top level */
994 if (verbname_prefix[0] == 0)
996 *verbs = g_list_sort_with_data (*verbs, compare_verbs, default_verb);
998 for (i = *verbs; default_verb && *preferred_verb == NULL && i; i = i->next)
999 if (_wcsicmp (default_verb, ((const reg_verb *) i->data)->name) == 0)
1000 *preferred_verb = (const reg_verb *) i->data;
1003 g_clear_pointer (&default_verb, g_free);
1008 /* Grabs a URL association (from HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
1009 * or from an application with Capabilities, or just a schema subkey in HKCR).
1010 * @program_id is a ProgID of the handler for the URL.
1011 * @schema is the schema for the URL.
1012 * @schema_u8 and @schema_u8_folded are UTF-8 and folded UTF-8
1014 * @app is the app to which the URL handler belongs (can be NULL).
1015 * @is_user_choice is TRUE if this association is clearly preferred
1018 get_url_association (const gunichar2 *program_id,
1019 const gunichar2 *schema,
1020 const gchar *schema_u8,
1021 const gchar *schema_u8_folded,
1022 GWin32AppInfoApplication *app,
1023 gboolean is_user_choice)
1025 GWin32AppInfoURLSchema *schema_rec;
1026 GWin32AppInfoHandler *handler_rec;
1027 gunichar2 *handler_id;
1029 const reg_verb *preferred_verb;
1030 gchar *handler_id_u8;
1031 gchar *handler_id_u8_folded;
1032 GWin32RegistryKey *handler_key;
1034 if ((handler_id = decide_which_id_to_use (program_id,
1037 &handler_id_u8_folded)) == NULL)
1040 if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
1042 g_clear_pointer (&handler_id, g_free);
1043 g_clear_pointer (&handler_id_u8, g_free);
1044 g_clear_pointer (&handler_id_u8_folded, g_free);
1045 g_clear_object (&handler_key);
1050 schema_rec = get_schema_object (schema,
1054 handler_rec = get_handler_object (handler_id_u8_folded,
1058 if (is_user_choice || schema_rec->chosen_handler == NULL)
1059 g_set_object (&schema_rec->chosen_handler, handler_rec);
1061 g_hash_table_insert (schema_rec->handlers,
1062 g_strdup (handler_id_u8_folded),
1063 g_object_ref (handler_rec));
1065 g_clear_object (&handler_key);
1068 g_hash_table_insert (app->supported_urls,
1069 g_strdup (schema_rec->schema_u8_folded),
1070 g_object_ref (handler_rec));
1072 process_verbs_commands (g_steal_pointer (&verbs),
1081 g_clear_pointer (&handler_id_u8, g_free);
1082 g_clear_pointer (&handler_id_u8_folded, g_free);
1083 g_clear_pointer (&handler_id, g_free);
1086 /* Grabs a file extension association (from HKCR\.ext or similar).
1087 * @program_id is a ProgID of the handler for the extension.
1088 * @file_extension is the extension (with the leading '.')
1089 * @app is the app to which the extension handler belongs (can be NULL).
1090 * @is_user_choice is TRUE if this is clearly the preferred association
1093 get_file_ext (const gunichar2 *program_id,
1094 const gunichar2 *file_extension,
1095 GWin32AppInfoApplication *app,
1096 gboolean is_user_choice)
1098 GWin32AppInfoHandler *handler_rec;
1099 gunichar2 *handler_id;
1100 const reg_verb *preferred_verb;
1102 gchar *handler_id_u8;
1103 gchar *handler_id_u8_folded;
1104 GWin32RegistryKey *handler_key;
1105 GWin32AppInfoFileExtension *file_extn;
1106 gchar *file_extension_u8;
1107 gchar *file_extension_u8_folded;
1109 if ((handler_id = decide_which_id_to_use (program_id,
1112 &handler_id_u8_folded)) == NULL)
1115 if (!g_utf16_to_utf8_and_fold (file_extension,
1118 &file_extension_u8_folded))
1120 g_clear_pointer (&handler_id, g_free);
1121 g_clear_pointer (&handler_id_u8, g_free);
1122 g_clear_pointer (&handler_id_u8_folded, g_free);
1123 g_clear_object (&handler_key);
1128 if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
1130 g_clear_pointer (&handler_id, g_free);
1131 g_clear_pointer (&handler_id_u8, g_free);
1132 g_clear_pointer (&handler_id_u8_folded, g_free);
1133 g_clear_object (&handler_key);
1134 g_clear_pointer (&file_extension_u8, g_free);
1135 g_clear_pointer (&file_extension_u8_folded, g_free);
1140 file_extn = get_ext_object (file_extension, file_extension_u8, file_extension_u8_folded);
1142 handler_rec = get_handler_object (handler_id_u8_folded,
1146 if (is_user_choice || file_extn->chosen_handler == NULL)
1147 g_set_object (&file_extn->chosen_handler, handler_rec);
1149 g_hash_table_insert (file_extn->handlers,
1150 g_strdup (handler_id_u8_folded),
1151 g_object_ref (handler_rec));
1154 g_hash_table_insert (app->supported_exts,
1155 g_strdup (file_extension_u8_folded),
1156 g_object_ref (handler_rec));
1158 g_clear_pointer (&file_extension_u8, g_free);
1159 g_clear_pointer (&file_extension_u8_folded, g_free);
1160 g_clear_object (&handler_key);
1162 process_verbs_commands (g_steal_pointer (&verbs),
1171 g_clear_pointer (&handler_id, g_free);
1172 g_clear_pointer (&handler_id_u8, g_free);
1173 g_clear_pointer (&handler_id_u8_folded, g_free);
1176 /* Returns either a @program_id or the string from
1177 * the default value of the program_id key (which is a name
1178 * of a proxy class), or NULL.
1179 * Does not check that proxy represents a valid
1180 * record, just checks that it exists.
1181 * Can return the class key (HKCR/program_id or HKCR/proxy_id).
1182 * Can convert returned value to UTF-8 and fold it.
1185 decide_which_id_to_use (const gunichar2 *program_id,
1186 GWin32RegistryKey **return_key,
1187 gchar **return_handler_id_u8,
1188 gchar **return_handler_id_u8_folded)
1190 GWin32RegistryKey *key;
1191 GWin32RegistryValueType val_type;
1192 gunichar2 *proxy_id;
1193 gunichar2 *return_id;
1195 gchar *handler_id_u8;
1196 gchar *handler_id_u8_folded;
1197 g_assert (program_id);
1202 /* Check the proxy first */
1203 key = g_win32_registry_key_get_child_w (classes_root_key, program_id, NULL);
1209 got_value = g_win32_registry_key_get_value_w (key,
1214 (void **) &proxy_id,
1217 if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
1218 g_clear_pointer (&proxy_id, g_free);
1224 GWin32RegistryKey *proxy_key;
1225 proxy_key = g_win32_registry_key_get_child_w (classes_root_key, proxy_id, NULL);
1230 *return_key = g_steal_pointer (&proxy_key);
1231 g_clear_object (&proxy_key);
1233 return_id = g_steal_pointer (&proxy_id);
1236 g_clear_pointer (&proxy_id, g_free);
1239 if ((return_handler_id_u8 ||
1240 return_handler_id_u8_folded) &&
1241 !g_utf16_to_utf8_and_fold (return_id == NULL ? program_id : return_id,
1244 &handler_id_u8_folded))
1246 g_clear_object (&key);
1248 g_clear_object (return_key);
1249 g_clear_pointer (&return_id, g_free);
1254 if (return_handler_id_u8)
1255 *return_handler_id_u8 = g_steal_pointer (&handler_id_u8);
1256 if (return_handler_id_u8_folded)
1257 *return_handler_id_u8_folded = g_steal_pointer (&handler_id_u8_folded);
1259 if (return_id == NULL && return_key)
1260 *return_key = g_steal_pointer (&key);
1261 g_clear_object (&key);
1263 if (return_id == NULL)
1264 return g_wcsdup (program_id, -1);
1269 /* Grabs the command for each verb from @verbs,
1270 * and invokes @handler for it. Consumes @verbs.
1271 * @path_to_progid and @progid are concatenated to
1272 * produce a path to the key where Shell/verb/command
1273 * subkeys are looked up.
1274 * @preferred_verb, if not NULL, will be used to inform
1275 * the @handler that a verb is preferred.
1276 * @autoprefer_first_verb will automatically make the first
1277 * verb to be preferred, if @preferred_verb is NULL.
1278 * @handler_data1 and @handler_data2 are passed to @handler as-is.
1281 process_verbs_commands (GList *verbs,
1282 const reg_verb *preferred_verb,
1283 const gunichar2 *path_to_progid,
1284 const gunichar2 *progid,
1285 gboolean autoprefer_first_verb,
1286 verb_command_func handler,
1287 gpointer handler_data1,
1288 gpointer handler_data2)
1293 g_assert (handler != NULL);
1294 g_assert (verbs != NULL);
1295 g_assert (progid != NULL);
1297 for (i = verbs; i; i = i->next)
1299 const reg_verb *verb = (const reg_verb *) i->data;
1300 GWin32RegistryKey *key;
1301 GWin32RegistryKey *verb_key;
1302 gunichar2 *command_value;
1303 gchar *command_value_utf8;
1304 GWin32RegistryValueType val_type;
1305 gunichar2 *verb_displayname;
1306 gchar *verb_displayname_u8;
1308 key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
1309 L"\\", verb->shellpath, L"\\command", NULL);
1313 g_debug ("%S%S\\shell\\%S does not have a \"command\" subkey",
1314 path_to_progid, progid, verb->shellpath);
1318 command_value = NULL;
1319 got_value = g_win32_registry_key_get_value_w (key,
1324 (void **) &command_value,
1327 g_clear_object (&key);
1330 val_type != G_WIN32_REGISTRY_VALUE_STR ||
1331 (command_value_utf8 = g_utf16_to_utf8 (command_value,
1337 g_clear_pointer (&command_value, g_free);
1341 verb_displayname = NULL;
1342 verb_displayname_u8 = NULL;
1343 verb_key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
1344 L"\\", verb->shellpath, NULL);
1348 gsize verb_displayname_len;
1350 got_value = g_win32_registry_key_get_value_w (verb_key,
1351 g_win32_registry_get_os_dirs_w (),
1355 (void **) &verb_displayname,
1356 &verb_displayname_len,
1360 val_type == G_WIN32_REGISTRY_VALUE_STR &&
1361 verb_displayname_len > sizeof (gunichar2))
1362 verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
1364 g_clear_pointer (&verb_displayname, g_free);
1366 if (verb_displayname_u8 == NULL)
1368 got_value = g_win32_registry_key_get_value_w (verb_key,
1373 (void **) &verb_displayname,
1374 &verb_displayname_len,
1378 val_type == G_WIN32_REGISTRY_VALUE_STR &&
1379 verb_displayname_len > sizeof (gunichar2))
1380 verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
1383 g_clear_pointer (&verb_displayname, g_free);
1384 g_clear_object (&verb_key);
1387 handler (handler_data1, handler_data2, verb->name, command_value, command_value_utf8,
1388 verb_displayname_u8,
1389 (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) ||
1390 (!preferred_verb && autoprefer_first_verb && i == verbs),
1393 g_clear_pointer (&command_value, g_free);
1394 g_clear_pointer (&command_value_utf8, g_free);
1395 g_clear_pointer (&verb_displayname_u8, g_free);
1398 g_list_free_full (verbs, reg_verb_free);
1401 /* Looks up a schema object identified by
1402 * @schema_u8_folded in the urls hash table.
1403 * If such object doesn't exist,
1404 * creates it and puts it into the urls hash table.
1405 * Returns the object.
1407 static GWin32AppInfoURLSchema *
1408 get_schema_object (const gunichar2 *schema,
1409 const gchar *schema_u8,
1410 const gchar *schema_u8_folded)
1412 GWin32AppInfoURLSchema *schema_rec;
1414 schema_rec = g_hash_table_lookup (urls, schema_u8_folded);
1416 if (schema_rec != NULL)
1419 schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
1420 schema_rec->schema = g_wcsdup (schema, -1);
1421 schema_rec->schema_u8 = g_strdup (schema_u8);
1422 schema_rec->schema_u8_folded = g_strdup (schema_u8_folded);
1423 g_hash_table_insert (urls, g_strdup (schema_rec->schema_u8_folded), schema_rec);
1428 /* Looks up a handler object identified by
1429 * @handler_id_u8_folded in the handlers hash table.
1430 * If such object doesn't exist,
1431 * creates it and puts it into the handlers hash table.
1432 * Returns the object.
1434 static GWin32AppInfoHandler *
1435 get_handler_object (const gchar *handler_id_u8_folded,
1436 GWin32RegistryKey *handler_key,
1437 const gunichar2 *handler_id)
1439 GWin32AppInfoHandler *handler_rec;
1441 handler_rec = g_hash_table_lookup (handlers, handler_id_u8_folded);
1443 if (handler_rec != NULL)
1446 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1447 handler_rec->key = g_object_ref (handler_key);
1448 handler_rec->handler_id = g_wcsdup (handler_id, -1);
1449 handler_rec->handler_id_folded = g_strdup (handler_id_u8_folded);
1450 read_handler_icon (handler_key, &handler_rec->icon);
1451 g_hash_table_insert (handlers, g_strdup (handler_id_u8_folded), handler_rec);
1457 handler_add_verb (gpointer handler_data1,
1458 gpointer handler_data2,
1459 const gunichar2 *verb,
1460 const gunichar2 *command_line,
1461 const gchar *command_line_utf8,
1462 const gchar *verb_displayname,
1463 gboolean verb_is_preferred,
1464 gboolean invent_new_verb_name)
1466 GWin32AppInfoHandler *handler_rec = (GWin32AppInfoHandler *) handler_data1;
1467 GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
1468 GWin32AppInfoShellVerb *shverb;
1470 _verb_lookup (handler_rec->verbs, verb, &shverb);
1475 shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
1476 shverb->verb_name = g_wcsdup (verb, -1);
1477 shverb->verb_displayname = g_strdup (verb_displayname);
1478 shverb->command = g_wcsdup (command_line, -1);
1479 shverb->command_utf8 = g_strdup (command_line_utf8);
1481 shverb->app = g_object_ref (app_rec);
1483 _g_win32_extract_executable (shverb->command,
1484 &shverb->executable,
1485 &shverb->executable_basename,
1486 &shverb->executable_folded,
1488 &shverb->dll_function);
1490 if (shverb->dll_function != NULL)
1491 _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
1493 if (!verb_is_preferred)
1494 g_ptr_array_add (handler_rec->verbs, shverb);
1496 g_ptr_array_insert (handler_rec->verbs, 0, shverb);
1499 /* Tries to generate a new name for a verb that looks
1500 * like "verb (%x)", where %x is an integer in range of [0;255).
1501 * On success puts new verb (and new verb displayname) into
1502 * @new_verb and @new_displayname and return TRUE.
1503 * On failure puts NULL into both and returns FALSE.
1506 generate_new_verb_name (GPtrArray *verbs,
1507 const gunichar2 *verb,
1508 const gchar *verb_displayname,
1509 gunichar2 **new_verb,
1510 gchar **new_displayname)
1513 GWin32AppInfoShellVerb *shverb;
1514 gsize orig_len = g_utf16_len (verb);
1515 gsize new_verb_name_len = orig_len + strlen (" ()") + 2 + 1;
1516 gunichar2 *new_verb_name = g_malloc (new_verb_name_len * sizeof (gunichar2));
1519 *new_displayname = NULL;
1521 memcpy (new_verb_name, verb, orig_len * sizeof (gunichar2));
1522 for (counter = 0; counter < 255; counter++)
1524 _snwprintf (&new_verb_name[orig_len], new_verb_name_len, L" (%x)", counter);
1525 _verb_lookup (verbs, new_verb_name, &shverb);
1529 *new_verb = new_verb_name;
1530 if (verb_displayname != NULL)
1531 *new_displayname = g_strdup_printf ("%s (%zx)", verb_displayname, counter);
1541 app_add_verb (gpointer handler_data1,
1542 gpointer handler_data2,
1543 const gunichar2 *verb,
1544 const gunichar2 *command_line,
1545 const gchar *command_line_utf8,
1546 const gchar *verb_displayname,
1547 gboolean verb_is_preferred,
1548 gboolean invent_new_verb_name)
1550 gunichar2 *new_verb = NULL;
1551 gchar *new_displayname = NULL;
1552 GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
1553 GWin32AppInfoShellVerb *shverb;
1555 _verb_lookup (app_rec->verbs, verb, &shverb);
1557 /* Special logic for fake apps - do our best to
1558 * collate all possible verbs in the app,
1559 * including the verbs that have the same name but
1560 * different commandlines, in which case a new
1561 * verb name has to be invented.
1567 if (!invent_new_verb_name)
1570 for (vi = 0; vi < app_rec->verbs->len; vi++)
1572 GWin32AppInfoShellVerb *app_verb;
1574 app_verb = _verb_idx (app_rec->verbs, vi);
1576 if (_wcsicmp (command_line, app_verb->command) == 0)
1580 if (vi < app_rec->verbs->len ||
1581 !generate_new_verb_name (app_rec->verbs,
1589 shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
1590 if (new_verb == NULL)
1591 shverb->verb_name = g_wcsdup (verb, -1);
1593 shverb->verb_name = g_steal_pointer (&new_verb);
1594 if (new_displayname == NULL)
1595 shverb->verb_displayname = g_strdup (verb_displayname);
1597 shverb->verb_displayname = g_steal_pointer (&new_displayname);
1599 shverb->command = g_wcsdup (command_line, -1);
1600 shverb->command_utf8 = g_strdup (command_line_utf8);
1601 shverb->app = g_object_ref (app_rec);
1603 _g_win32_extract_executable (shverb->command,
1604 &shverb->executable,
1605 &shverb->executable_basename,
1606 &shverb->executable_folded,
1608 &shverb->dll_function);
1610 if (shverb->dll_function != NULL)
1611 _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
1613 if (!verb_is_preferred)
1614 g_ptr_array_add (app_rec->verbs, shverb);
1616 g_ptr_array_insert (app_rec->verbs, 0, shverb);
1619 /* Looks up a file extension object identified by
1620 * @ext_u8_folded in the extensions hash table.
1621 * If such object doesn't exist,
1622 * creates it and puts it into the extensions hash table.
1623 * Returns the object.
1625 static GWin32AppInfoFileExtension *
1626 get_ext_object (const gunichar2 *ext,
1627 const gchar *ext_u8,
1628 const gchar *ext_u8_folded)
1630 GWin32AppInfoFileExtension *file_extn;
1632 if (g_hash_table_lookup_extended (extensions,
1635 (void **) &file_extn))
1638 file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1639 file_extn->extension = g_wcsdup (ext, -1);
1640 file_extn->extension_u8 = g_strdup (ext_u8);
1641 g_hash_table_insert (extensions, g_strdup (ext_u8_folded), file_extn);
1646 /* Iterates over HKCU\\Software\\Clients or HKLM\\Software\\Clients,
1647 * (depending on @user_registry being TRUE or FALSE),
1648 * collecting applications listed there.
1649 * Puts the path to the client key for each client into @priority_capable_apps
1650 * (only for clients with file or URL associations).
1653 collect_capable_apps_from_clients (GPtrArray *capable_apps,
1654 GPtrArray *priority_capable_apps,
1655 gboolean user_registry)
1657 GWin32RegistryKey *clients;
1658 GWin32RegistrySubkeyIter clients_iter;
1660 const gunichar2 *client_type_name;
1661 gsize client_type_name_len;
1666 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
1670 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
1673 if (clients == NULL)
1676 if (!g_win32_registry_subkey_iter_init (&clients_iter, clients, NULL))
1678 g_object_unref (clients);
1682 while (g_win32_registry_subkey_iter_next (&clients_iter, TRUE, NULL))
1684 GWin32RegistrySubkeyIter subkey_iter;
1685 GWin32RegistryKey *system_client_type;
1686 GWin32RegistryValueType default_type;
1687 gunichar2 *default_value = NULL;
1688 const gunichar2 *client_name;
1689 gsize client_name_len;
1691 if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
1693 &client_type_name_len,
1697 system_client_type = g_win32_registry_key_get_child_w (clients,
1701 if (system_client_type == NULL)
1704 if (g_win32_registry_key_get_value_w (system_client_type,
1709 (gpointer *) &default_value,
1713 if (default_type != G_WIN32_REGISTRY_VALUE_STR ||
1714 default_value[0] == L'\0')
1715 g_clear_pointer (&default_value, g_free);
1718 if (!g_win32_registry_subkey_iter_init (&subkey_iter,
1722 g_clear_pointer (&default_value, g_free);
1723 g_object_unref (system_client_type);
1727 while (g_win32_registry_subkey_iter_next (&subkey_iter, TRUE, NULL))
1729 GWin32RegistryKey *system_client;
1730 GWin32RegistryKey *system_client_assoc;
1734 if (!g_win32_registry_subkey_iter_get_name_w (&subkey_iter,
1740 system_client = g_win32_registry_key_get_child_w (system_client_type,
1744 if (system_client == NULL)
1749 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1750 L"Capabilities\\FileAssociations",
1753 if (system_client_assoc != NULL)
1756 g_object_unref (system_client_assoc);
1760 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
1761 L"Capabilities\\UrlAssociations",
1764 if (system_client_assoc != NULL)
1767 g_object_unref (system_client_assoc);
1773 keyname = g_wcsdup (g_win32_registry_key_get_path_w (system_client), -1);
1775 if (default_value && wcscmp (default_value, client_name) == 0)
1776 g_ptr_array_add (priority_capable_apps, keyname);
1778 g_ptr_array_add (capable_apps, keyname);
1781 g_object_unref (system_client);
1784 g_win32_registry_subkey_iter_clear (&subkey_iter);
1785 g_clear_pointer (&default_value, g_free);
1786 g_object_unref (system_client_type);
1789 g_win32_registry_subkey_iter_clear (&clients_iter);
1790 g_object_unref (clients);
1793 /* Iterates over HKCU\\Software\\RegisteredApplications or HKLM\\Software\\RegisteredApplications,
1794 * (depending on @user_registry being TRUE or FALSE),
1795 * collecting applications listed there.
1796 * Puts the path to the app key for each app into @capable_apps.
1799 collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
1800 gboolean user_registry)
1802 GWin32RegistryValueIter iter;
1803 const gunichar2 *reg_path;
1805 gunichar2 *value_data;
1806 gsize value_data_size;
1807 GWin32RegistryValueType value_type;
1808 GWin32RegistryKey *registered_apps;
1811 reg_path = L"HKEY_CURRENT_USER\\Software\\RegisteredApplications";
1813 reg_path = L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications";
1816 g_win32_registry_key_new_w (reg_path, NULL);
1818 if (!registered_apps)
1821 if (!g_win32_registry_value_iter_init (&iter, registered_apps, NULL))
1823 g_object_unref (registered_apps);
1828 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
1830 gunichar2 possible_location[REG_PATH_MAX_SIZE + 1];
1831 GWin32RegistryKey *location;
1834 if ((!g_win32_registry_value_iter_get_value_type (&iter,
1837 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
1838 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
1839 (void **) &value_data,
1842 (value_data_size < sizeof (gunichar2)) ||
1843 (value_data[0] == L'\0'))
1846 if (!build_registry_path (possible_location, sizeof (possible_location),
1847 user_registry ? HKCU : HKLM, value_data, NULL))
1850 location = g_win32_registry_key_new_w (possible_location, NULL);
1852 if (location == NULL)
1855 p = wcsrchr (possible_location, L'\\');
1860 g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
1863 g_object_unref (location);
1866 g_win32_registry_value_iter_clear (&iter);
1867 g_object_unref (registered_apps);
1870 /* Looks up an app object identified by
1871 * @canonical_name_folded in the @app_hashmap.
1872 * If such object doesn't exist,
1873 * creates it and puts it into the @app_hashmap.
1874 * Returns the object.
1876 static GWin32AppInfoApplication *
1877 get_app_object (GHashTable *app_hashmap,
1878 const gunichar2 *canonical_name,
1879 const gchar *canonical_name_u8,
1880 const gchar *canonical_name_folded,
1881 gboolean user_specific,
1882 gboolean default_app)
1884 GWin32AppInfoApplication *app;
1886 app = g_hash_table_lookup (app_hashmap, canonical_name_folded);
1891 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
1892 app->canonical_name = g_wcsdup (canonical_name, -1);
1893 app->canonical_name_u8 = g_strdup (canonical_name_u8);
1894 app->canonical_name_folded = g_strdup (canonical_name_folded);
1895 app->no_open_with = FALSE;
1896 app->user_specific = user_specific;
1897 app->default_app = default_app;
1898 g_hash_table_insert (app_hashmap,
1899 g_strdup (canonical_name_folded),
1905 /* Grabs an application that has Capabilities.
1906 * @app_key_path is the path to the application key
1907 * (which must have a "Capabilities" subkey).
1908 * @default_app is TRUE if the app has priority
1911 read_capable_app (const gunichar2 *app_key_path,
1912 gboolean user_specific,
1913 gboolean default_app)
1915 GWin32AppInfoApplication *app;
1916 gchar *canonical_name_u8 = NULL;
1917 gchar *canonical_name_folded = NULL;
1918 gchar *app_key_path_u8 = NULL;
1919 gchar *app_key_path_u8_folded = NULL;
1920 GWin32RegistryKey *appkey = NULL;
1921 gunichar2 *fallback_friendly_name;
1922 GWin32RegistryValueType vtype;
1924 gunichar2 *friendly_name;
1925 gunichar2 *description;
1926 gunichar2 *narrow_application_name;
1927 gunichar2 *icon_source;
1928 GWin32RegistryKey *capabilities;
1929 GWin32RegistryKey *default_icon_key;
1930 GWin32RegistryKey *associations;
1931 const reg_verb *preferred_verb;
1932 GList *verbs = NULL;
1935 capabilities = NULL;
1937 if (!g_utf16_to_utf8_and_fold (app_key_path,
1940 &canonical_name_folded) ||
1941 !g_utf16_to_utf8_and_fold (app_key_path,
1944 &app_key_path_u8_folded) ||
1945 (appkey = g_win32_registry_key_new_w (app_key_path, NULL)) == NULL ||
1946 (capabilities = g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL)) == NULL ||
1947 !get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell"))
1949 g_clear_pointer (&canonical_name_u8, g_free);
1950 g_clear_pointer (&canonical_name_folded, g_free);
1951 g_clear_object (&appkey);
1952 g_clear_pointer (&app_key_path_u8, g_free);
1953 g_clear_pointer (&app_key_path_u8_folded, g_free);
1958 app = get_app_object (apps_by_id,
1961 canonical_name_folded,
1965 process_verbs_commands (g_steal_pointer (&verbs),
1967 L"", /* [ab]use the fact that two strings are simply concatenated */
1968 g_win32_registry_key_get_path_w (capabilities),
1974 fallback_friendly_name = NULL;
1975 success = g_win32_registry_key_get_value_w (appkey,
1980 (void **) &fallback_friendly_name,
1984 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
1985 g_clear_pointer (&fallback_friendly_name, g_free);
1987 if (fallback_friendly_name &&
1988 app->pretty_name == NULL)
1990 app->pretty_name = g_wcsdup (fallback_friendly_name, -1);
1991 g_clear_pointer (&app->pretty_name_u8, g_free);
1992 app->pretty_name_u8 = g_utf16_to_utf8 (fallback_friendly_name,
1999 friendly_name = NULL;
2000 success = g_win32_registry_key_get_value_w (capabilities,
2001 g_win32_registry_get_os_dirs_w (),
2005 (void **) &friendly_name,
2010 vtype != G_WIN32_REGISTRY_VALUE_STR)
2011 g_clear_pointer (&friendly_name, g_free);
2013 if (friendly_name &&
2014 app->localized_pretty_name == NULL)
2016 app->localized_pretty_name = g_wcsdup (friendly_name, -1);
2017 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2018 app->localized_pretty_name_u8 = g_utf16_to_utf8 (friendly_name,
2026 success = g_win32_registry_key_get_value_w (capabilities,
2027 g_win32_registry_get_os_dirs_w (),
2029 L"ApplicationDescription",
2031 (void **) &description,
2035 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2036 g_clear_pointer (&description, g_free);
2038 if (description && app->description == NULL)
2040 app->description = g_wcsdup (description, -1);
2041 g_clear_pointer (&app->description_u8, g_free);
2042 app->description_u8 = g_utf16_to_utf8 (description, -1, NULL, NULL, NULL);
2045 default_icon_key = g_win32_registry_key_get_child_w (appkey,
2051 if (default_icon_key != NULL)
2053 success = g_win32_registry_key_get_value_w (default_icon_key,
2058 (void **) &icon_source,
2063 vtype != G_WIN32_REGISTRY_VALUE_STR)
2064 g_clear_pointer (&icon_source, g_free);
2066 g_object_unref (default_icon_key);
2069 if (icon_source == NULL)
2071 success = g_win32_registry_key_get_value_w (capabilities,
2076 (void **) &icon_source,
2081 vtype != G_WIN32_REGISTRY_VALUE_STR)
2082 g_clear_pointer (&icon_source, g_free);
2088 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
2089 app->icon = g_themed_icon_new (name);
2093 narrow_application_name = NULL;
2094 success = g_win32_registry_key_get_value_w (capabilities,
2095 g_win32_registry_get_os_dirs_w (),
2099 (void **) &narrow_application_name,
2103 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2104 g_clear_pointer (&narrow_application_name, g_free);
2106 if (narrow_application_name &&
2107 app->localized_pretty_name == NULL)
2109 app->localized_pretty_name = g_wcsdup (narrow_application_name, -1);
2110 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2111 app->localized_pretty_name_u8 = g_utf16_to_utf8 (narrow_application_name,
2118 associations = g_win32_registry_key_get_child_w (capabilities,
2119 L"FileAssociations",
2122 if (associations != NULL)
2124 GWin32RegistryValueIter iter;
2126 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
2128 gunichar2 *file_extension;
2129 gunichar2 *extension_handler;
2130 gsize file_extension_len;
2131 gsize extension_handler_size;
2132 GWin32RegistryValueType value_type;
2134 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2136 if ((!g_win32_registry_value_iter_get_value_type (&iter,
2139 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
2140 (!g_win32_registry_value_iter_get_name_w (&iter,
2142 &file_extension_len,
2144 (file_extension_len <= 0) ||
2145 (file_extension[0] != L'.') ||
2146 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2147 (void **) &extension_handler,
2148 &extension_handler_size,
2150 (extension_handler_size < sizeof (gunichar2)) ||
2151 (extension_handler[0] == L'\0'))
2154 get_file_ext (extension_handler, file_extension, app, FALSE);
2157 g_win32_registry_value_iter_clear (&iter);
2160 g_object_unref (associations);
2163 associations = g_win32_registry_key_get_child_w (capabilities, L"URLAssociations", NULL);
2165 if (associations != NULL)
2167 GWin32RegistryValueIter iter;
2169 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
2171 gunichar2 *url_schema;
2172 gunichar2 *schema_handler;
2173 gsize url_schema_len;
2174 gsize schema_handler_size;
2175 GWin32RegistryValueType value_type;
2177 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2180 gchar *schema_u8_folded;
2182 if ((!g_win32_registry_value_iter_get_value_type (&iter,
2185 ((value_type != G_WIN32_REGISTRY_VALUE_STR) &&
2186 (value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
2187 (!g_win32_registry_value_iter_get_name_w (&iter,
2191 (url_schema_len <= 0) ||
2192 (url_schema[0] == L'\0') ||
2193 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2194 (void **) &schema_handler,
2195 &schema_handler_size,
2197 (schema_handler_size < sizeof (gunichar2)) ||
2198 (schema_handler[0] == L'\0'))
2203 if (g_utf16_to_utf8_and_fold (url_schema,
2207 get_url_association (schema_handler, url_schema, schema_u8, schema_u8_folded, app, FALSE);
2209 g_clear_pointer (&schema_u8, g_free);
2210 g_clear_pointer (&schema_u8_folded, g_free);
2213 g_win32_registry_value_iter_clear (&iter);
2216 g_object_unref (associations);
2219 g_clear_pointer (&fallback_friendly_name, g_free);
2220 g_clear_pointer (&description, g_free);
2221 g_clear_pointer (&icon_source, g_free);
2222 g_clear_pointer (&narrow_application_name, g_free);
2224 g_object_unref (appkey);
2225 g_object_unref (capabilities);
2226 g_clear_pointer (&app_key_path_u8, g_free);
2227 g_clear_pointer (&app_key_path_u8_folded, g_free);
2228 g_clear_pointer (&canonical_name_u8, g_free);
2229 g_clear_pointer (&canonical_name_folded, g_free);
2232 /* Iterates over subkeys in HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
2233 * and calls get_url_association() for each one that has a user-chosen handler.
2236 read_urls (GWin32RegistryKey *url_associations)
2238 GWin32RegistrySubkeyIter url_iter;
2240 if (url_associations == NULL)
2243 if (!g_win32_registry_subkey_iter_init (&url_iter, url_associations, NULL))
2246 while (g_win32_registry_subkey_iter_next (&url_iter, TRUE, NULL))
2248 gchar *schema_u8 = NULL;
2249 gchar *schema_u8_folded = NULL;
2250 const gunichar2 *url_schema = NULL;
2251 gunichar2 *program_id = NULL;
2252 GWin32RegistryKey *user_choice = NULL;
2253 gsize url_schema_len;
2254 GWin32RegistryValueType val_type;
2256 if (g_win32_registry_subkey_iter_get_name_w (&url_iter,
2260 g_utf16_to_utf8_and_fold (url_schema,
2263 &schema_u8_folded) &&
2264 (user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
2265 url_schema, USER_CHOICE,
2267 g_win32_registry_key_get_value_w (user_choice,
2272 (void **) &program_id,
2275 val_type == G_WIN32_REGISTRY_VALUE_STR)
2276 get_url_association (program_id, url_schema, schema_u8, schema_u8_folded, NULL, TRUE);
2278 g_clear_pointer (&program_id, g_free);
2279 g_clear_pointer (&user_choice, g_object_unref);
2280 g_clear_pointer (&schema_u8, g_free);
2281 g_clear_pointer (&schema_u8_folded, g_free);
2284 g_win32_registry_subkey_iter_clear (&url_iter);
2287 /* Reads an application that is only registered by the basename of its
2288 * executable (and doesn't have Capabilities subkey).
2289 * @incapable_app is the registry key for the app.
2290 * @app_exe_basename is the basename of its executable.
2293 read_incapable_app (GWin32RegistryKey *incapable_app,
2294 const gunichar2 *app_exe_basename,
2295 const gchar *app_exe_basename_u8,
2296 const gchar *app_exe_basename_u8_folded)
2298 GWin32RegistryValueIter sup_iter;
2299 GWin32AppInfoApplication *app;
2301 const reg_verb *preferred_verb;
2302 gunichar2 *friendly_app_name;
2304 GWin32RegistryValueType vtype;
2305 gboolean no_open_with;
2306 GWin32RegistryKey *default_icon_key;
2307 gunichar2 *icon_source;
2309 GWin32RegistryKey *supported_key;
2311 if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell"))
2314 app = get_app_object (apps_by_exe,
2316 app_exe_basename_u8,
2317 app_exe_basename_u8_folded,
2321 process_verbs_commands (g_steal_pointer (&verbs),
2323 L"HKEY_CLASSES_ROOT\\Applications\\",
2330 friendly_app_name = NULL;
2331 success = g_win32_registry_key_get_value_w (incapable_app,
2332 g_win32_registry_get_os_dirs_w (),
2336 (void **) &friendly_app_name,
2340 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2341 g_clear_pointer (&friendly_app_name, g_free);
2343 no_open_with = g_win32_registry_key_get_value_w (incapable_app,
2353 g_win32_registry_key_get_child_w (incapable_app,
2359 if (default_icon_key != NULL)
2362 g_win32_registry_key_get_value_w (default_icon_key,
2367 (void **) &icon_source,
2371 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2372 g_clear_pointer (&icon_source, g_free);
2374 g_object_unref (default_icon_key);
2379 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
2381 icon = g_themed_icon_new (name);
2385 app->no_open_with = no_open_with;
2387 if (friendly_app_name &&
2388 app->localized_pretty_name == NULL)
2390 app->localized_pretty_name = g_wcsdup (friendly_app_name, -1);
2391 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2392 app->localized_pretty_name_u8 =
2393 g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL);
2396 if (icon && app->icon == NULL)
2397 app->icon = g_object_ref (icon);
2400 g_win32_registry_key_get_child_w (incapable_app,
2404 if (supported_key &&
2405 g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
2407 gunichar2 *ext_name;
2410 while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
2412 if ((!g_win32_registry_value_iter_get_name_w (&sup_iter,
2416 (ext_name_len <= 0) ||
2417 (ext_name[0] != L'.'))
2420 get_file_ext (ext_name, ext_name, app, FALSE);
2423 g_win32_registry_value_iter_clear (&sup_iter);
2426 g_clear_object (&supported_key);
2427 g_free (friendly_app_name);
2428 g_free (icon_source);
2430 g_clear_object (&icon);
2433 /* Iterates over subkeys of HKEY_CLASSES_ROOT\\Applications
2434 * and calls read_incapable_app() for each one.
2439 GWin32RegistryKey *applications_key;
2440 GWin32RegistrySubkeyIter app_iter;
2443 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
2445 if (applications_key == NULL)
2448 if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
2450 g_object_unref (applications_key);
2454 while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
2456 const gunichar2 *app_exe_basename;
2457 gsize app_exe_basename_len;
2458 GWin32RegistryKey *incapable_app;
2459 gchar *app_exe_basename_u8;
2460 gchar *app_exe_basename_u8_folded;
2462 if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
2464 &app_exe_basename_len,
2466 !g_utf16_to_utf8_and_fold (app_exe_basename,
2467 app_exe_basename_len,
2468 &app_exe_basename_u8,
2469 &app_exe_basename_u8_folded))
2473 g_win32_registry_key_get_child_w (applications_key,
2477 if (incapable_app != NULL)
2478 read_incapable_app (incapable_app,
2480 app_exe_basename_u8,
2481 app_exe_basename_u8_folded);
2483 g_clear_object (&incapable_app);
2484 g_clear_pointer (&app_exe_basename_u8, g_free);
2485 g_clear_pointer (&app_exe_basename_u8_folded, g_free);
2488 g_win32_registry_subkey_iter_clear (&app_iter);
2489 g_object_unref (applications_key);
2492 /* Iterates over subkeys of HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\
2493 * and calls get_file_ext() for each associated handler
2494 * (starting with user-chosen handler, if any)
2497 read_exts (GWin32RegistryKey *file_exts)
2499 GWin32RegistrySubkeyIter ext_iter;
2500 const gunichar2 *file_extension;
2501 gsize file_extension_len;
2503 if (file_exts == NULL)
2506 if (!g_win32_registry_subkey_iter_init (&ext_iter, file_exts, NULL))
2509 while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
2511 GWin32RegistryKey *open_with_progids;
2512 gunichar2 *program_id;
2513 GWin32RegistryValueIter iter;
2514 gunichar2 *value_name;
2515 gsize value_name_len;
2516 GWin32RegistryValueType value_type;
2517 GWin32RegistryKey *user_choice;
2519 if (!g_win32_registry_subkey_iter_get_name_w (&ext_iter,
2521 &file_extension_len,
2526 user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, file_extension,
2529 g_win32_registry_key_get_value_w (user_choice,
2534 (void **) &program_id,
2537 value_type == G_WIN32_REGISTRY_VALUE_STR)
2539 /* Note: program_id could be "ProgramID" or "Applications\\program.exe".
2540 * The code still works, but handler_id might have a backslash
2541 * in it - that might trip us up later on.
2542 * Even though in that case this is logically an "application"
2543 * registry entry, we don't treat it in any special way.
2544 * We do scan that registry branch anyway, just not here.
2546 get_file_ext (program_id, file_extension, NULL, TRUE);
2549 g_clear_object (&user_choice);
2550 g_clear_pointer (&program_id, g_free);
2552 open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
2557 if (open_with_progids == NULL)
2560 if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
2562 g_clear_object (&open_with_progids);
2566 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2568 if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
2571 (value_name_len == 0))
2574 get_file_ext (value_name, file_extension, NULL, FALSE);
2577 g_win32_registry_value_iter_clear (&iter);
2578 g_clear_object (&open_with_progids);
2581 g_win32_registry_subkey_iter_clear (&ext_iter);
2584 /* Iterates over subkeys in HKCR, calls
2585 * get_file_ext() for any subkey that starts with ".",
2586 * or get_url_association() for any subkey that could
2587 * be a URL schema and has a "URL Protocol" value.
2590 read_classes (GWin32RegistryKey *classes_root)
2592 GWin32RegistrySubkeyIter class_iter;
2593 const gunichar2 *class_name;
2594 gsize class_name_len;
2596 if (classes_root == NULL)
2599 if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL))
2602 while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL))
2604 if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
2608 (class_name_len <= 1))
2611 if (class_name[0] == L'.')
2613 GWin32RegistryKey *class_key;
2614 GWin32RegistryValueIter iter;
2615 GWin32RegistryKey *open_with_progids;
2616 gunichar2 *value_name;
2617 gsize value_name_len;
2619 /* Read the data from the HKCR\\.ext (usually proxied
2620 * to another HKCR subkey)
2622 get_file_ext (class_name, class_name, NULL, FALSE);
2624 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2626 if (class_key == NULL)
2629 open_with_progids = g_win32_registry_key_get_child_w (class_key, L"OpenWithProgids", NULL);
2630 g_clear_object (&class_key);
2632 if (open_with_progids == NULL)
2635 if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
2637 g_clear_object (&open_with_progids);
2641 /* Read the data for other handlers for this extension */
2642 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2644 if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
2647 (value_name_len == 0))
2650 get_file_ext (value_name, class_name, NULL, FALSE);
2653 g_win32_registry_value_iter_clear (&iter);
2654 g_clear_object (&open_with_progids);
2659 GWin32RegistryKey *class_key;
2661 GWin32RegistryValueType vtype;
2663 gchar *schema_u8_folded;
2665 for (i = 0; i < class_name_len; i++)
2666 if (!iswalpha (class_name[i]))
2669 if (i != class_name_len)
2672 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2674 if (class_key == NULL)
2677 success = g_win32_registry_key_get_value_w (class_key,
2685 g_clear_object (&class_key);
2688 vtype != G_WIN32_REGISTRY_VALUE_STR)
2691 if (!g_utf16_to_utf8_and_fold (class_name, -1, &schema_u8, &schema_u8_folded))
2694 get_url_association (class_name, class_name, schema_u8, schema_u8_folded, NULL, FALSE);
2696 g_clear_pointer (&schema_u8, g_free);
2697 g_clear_pointer (&schema_u8_folded, g_free);
2701 g_win32_registry_subkey_iter_clear (&class_iter);
2704 /* Iterates over all handlers and over all apps,
2705 * and links handler verbs to apps if a handler
2706 * runs the same executable as one of the app verbs.
2709 link_handlers_to_unregistered_apps (void)
2711 GHashTableIter iter;
2712 GHashTableIter app_iter;
2713 GWin32AppInfoHandler *handler;
2714 gchar *handler_id_fld;
2715 GWin32AppInfoApplication *app;
2716 gchar *canonical_name_fld;
2717 gchar *appexe_fld_basename;
2719 g_hash_table_iter_init (&iter, handlers);
2720 while (g_hash_table_iter_next (&iter,
2721 (gpointer *) &handler_id_fld,
2722 (gpointer *) &handler))
2726 for (vi = 0; vi < handler->verbs->len; vi++)
2728 GWin32AppInfoShellVerb *handler_verb;
2729 const gchar *handler_exe_basename;
2734 ERROR_GETTING_SH_INFO,
2735 } have_stat_handler = SH_UNKNOWN;
2736 GWin32PrivateStat handler_verb_exec_info;
2738 handler_verb = _verb_idx (handler->verbs, vi);
2740 if (handler_verb->app != NULL)
2743 handler_exe_basename = g_utf8_find_basename (handler_verb->executable_folded, -1);
2744 g_hash_table_iter_init (&app_iter, apps_by_id);
2746 while (g_hash_table_iter_next (&app_iter,
2747 (gpointer *) &canonical_name_fld,
2750 GWin32AppInfoShellVerb *app_verb;
2753 for (ai = 0; ai < app->verbs->len; ai++)
2755 GWin32PrivateStat app_verb_exec_info;
2756 const gchar *app_exe_basename;
2757 app_verb = _verb_idx (app->verbs, ai);
2759 app_exe_basename = g_utf8_find_basename (app_verb->executable_folded, -1);
2761 /* First check that the executable paths are identical */
2762 if (g_strcmp0 (app_verb->executable_folded, handler_verb->executable_folded) != 0)
2764 /* If not, check the basenames. If they are different, don't bother
2765 * with further checks.
2767 if (g_strcmp0 (app_exe_basename, handler_exe_basename) != 0)
2770 /* Get filesystem IDs for both files.
2771 * For the handler that is attempted only once.
2773 if (have_stat_handler == SH_UNKNOWN)
2775 if (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (handler_verb->executable_folded,
2776 &handler_verb_exec_info) == 0)
2777 have_stat_handler = GOT_SH_INFO;
2779 have_stat_handler = ERROR_GETTING_SH_INFO;
2782 if (have_stat_handler != GOT_SH_INFO ||
2783 (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (app_verb->executable_folded,
2784 &app_verb_exec_info) != 0) ||
2785 app_verb_exec_info.file_index != handler_verb_exec_info.file_index)
2789 handler_verb->app = g_object_ref (app);
2794 if (handler_verb->app != NULL)
2797 g_hash_table_iter_init (&app_iter, apps_by_exe);
2799 while (g_hash_table_iter_next (&app_iter,
2800 (gpointer *) &appexe_fld_basename,
2803 /* Use basename because apps_by_exe only has basenames */
2804 if (g_strcmp0 (handler_exe_basename, appexe_fld_basename) != 0)
2807 handler_verb->app = g_object_ref (app);
2814 /* Finds all .ext and schema: handler verbs that have no app linked to them,
2815 * creates a "fake app" object and links these verbs to these
2816 * objects. Objects are identified by the full path to
2817 * the executable being run, thus multiple different invocations
2818 * get grouped in a more-or-less natural way.
2819 * The iteration goes separately over .ext and schema: handlers
2820 * (instead of the global handlers hashmap) to allow us to
2821 * put the handlers into supported_urls or supported_exts as
2822 * needed (handler objects themselves have no knowledge of extensions
2823 * and/or URLs they are associated with).
2826 link_handlers_to_fake_apps (void)
2828 GHashTableIter iter;
2829 GHashTableIter handler_iter;
2830 gchar *extension_utf8_folded;
2831 GWin32AppInfoFileExtension *file_extn;
2832 gchar *handler_id_fld;
2833 GWin32AppInfoHandler *handler;
2834 gchar *url_utf8_folded;
2835 GWin32AppInfoURLSchema *schema;
2837 g_hash_table_iter_init (&iter, extensions);
2838 while (g_hash_table_iter_next (&iter,
2839 (gpointer *) &extension_utf8_folded,
2840 (gpointer *) &file_extn))
2842 g_hash_table_iter_init (&handler_iter, file_extn->handlers);
2843 while (g_hash_table_iter_next (&handler_iter,
2844 (gpointer *) &handler_id_fld,
2845 (gpointer *) &handler))
2849 for (vi = 0; vi < handler->verbs->len; vi++)
2851 GWin32AppInfoShellVerb *handler_verb;
2852 GWin32AppInfoApplication *app;
2853 gunichar2 *exename_utf16;
2854 handler_verb = _verb_idx (handler->verbs, vi);
2856 if (handler_verb->app != NULL)
2859 exename_utf16 = g_utf8_to_utf16 (handler_verb->executable, -1, NULL, NULL, NULL);
2860 if (exename_utf16 == NULL)
2863 app = get_app_object (fake_apps,
2865 handler_verb->executable,
2866 handler_verb->executable_folded,
2869 g_clear_pointer (&exename_utf16, g_free);
2870 handler_verb->app = g_object_ref (app);
2874 handler_verb->verb_name,
2875 handler_verb->command,
2876 handler_verb->command_utf8,
2877 handler_verb->verb_displayname,
2880 g_hash_table_insert (app->supported_exts,
2881 g_strdup (extension_utf8_folded),
2882 g_object_ref (handler));
2887 g_hash_table_iter_init (&iter, urls);
2888 while (g_hash_table_iter_next (&iter,
2889 (gpointer *) &url_utf8_folded,
2890 (gpointer *) &schema))
2892 g_hash_table_iter_init (&handler_iter, schema->handlers);
2893 while (g_hash_table_iter_next (&handler_iter,
2894 (gpointer *) &handler_id_fld,
2895 (gpointer *) &handler))
2899 for (vi = 0; vi < handler->verbs->len; vi++)
2901 GWin32AppInfoShellVerb *handler_verb;
2902 GWin32AppInfoApplication *app;
2903 gchar *command_utf8_folded;
2904 handler_verb = _verb_idx (handler->verbs, vi);
2906 if (handler_verb->app != NULL)
2909 command_utf8_folded = g_utf8_casefold (handler_verb->command_utf8, -1);
2910 app = get_app_object (fake_apps,
2911 handler_verb->command,
2912 handler_verb->command_utf8,
2913 command_utf8_folded,
2916 g_clear_pointer (&command_utf8_folded, g_free);
2917 handler_verb->app = g_object_ref (app);
2921 handler_verb->verb_name,
2922 handler_verb->command,
2923 handler_verb->command_utf8,
2924 handler_verb->verb_displayname,
2927 g_hash_table_insert (app->supported_urls,
2928 g_strdup (url_utf8_folded),
2929 g_object_ref (handler));
2937 update_registry_data (void)
2940 GPtrArray *capable_apps_keys;
2941 GPtrArray *user_capable_apps_keys;
2942 GPtrArray *priority_capable_apps_keys;
2943 GWin32RegistryKey *url_associations;
2944 GWin32RegistryKey *file_exts;
2945 GWin32RegistryKey *classes_root;
2946 DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, postproc_end;
2949 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
2952 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
2954 classes_root = g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT", NULL);
2956 capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
2957 user_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
2958 priority_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
2960 g_clear_pointer (&apps_by_id, g_hash_table_destroy);
2961 g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
2962 g_clear_pointer (&fake_apps, g_hash_table_destroy);
2963 g_clear_pointer (&urls, g_hash_table_destroy);
2964 g_clear_pointer (&extensions, g_hash_table_destroy);
2965 g_clear_pointer (&handlers, g_hash_table_destroy);
2967 collect_start = GetTickCount ();
2968 collect_capable_apps_from_clients (capable_apps_keys,
2969 priority_capable_apps_keys,
2971 collect_capable_apps_from_clients (user_capable_apps_keys,
2972 priority_capable_apps_keys,
2974 collect_capable_apps_from_registered_apps (user_capable_apps_keys,
2976 collect_capable_apps_from_registered_apps (capable_apps_keys,
2978 collect_end = GetTickCount ();
2981 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
2983 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
2985 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
2987 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
2989 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
2991 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
2992 alloc_end = GetTickCount ();
2994 for (i = 0; i < priority_capable_apps_keys->len; i++)
2995 read_capable_app (g_ptr_array_index (priority_capable_apps_keys, i),
2998 for (i = 0; i < user_capable_apps_keys->len; i++)
2999 read_capable_app (g_ptr_array_index (user_capable_apps_keys, i),
3002 for (i = 0; i < capable_apps_keys->len; i++)
3003 read_capable_app (g_ptr_array_index (capable_apps_keys, i),
3006 capable_end = GetTickCount ();
3008 read_urls (url_associations);
3009 url_end = GetTickCount ();
3010 read_exts (file_exts);
3011 ext_end = GetTickCount ();
3013 exeapp_end = GetTickCount ();
3014 read_classes (classes_root);
3015 classes_end = GetTickCount ();
3016 link_handlers_to_unregistered_apps ();
3017 link_handlers_to_fake_apps ();
3018 postproc_end = GetTickCount ();
3020 g_debug ("Collecting capable appnames: %lums\n"
3021 "Allocating hashtables:...... %lums\n"
3022 "Reading capable apps: %lums\n"
3023 "Reading URL associations:... %lums\n"
3024 "Reading extension assocs: %lums\n"
3025 "Reading exe-only apps:...... %lums\n"
3026 "Reading classes: %lums\n"
3027 "Postprocessing:..............%lums\n"
3029 collect_end - collect_start,
3030 alloc_end - collect_end,
3031 capable_end - alloc_end,
3032 url_end - capable_end,
3034 exeapp_end - ext_end,
3035 classes_end - exeapp_end,
3036 postproc_end - classes_end,
3037 postproc_end - collect_start);
3039 g_clear_object (&classes_root);
3040 g_clear_object (&url_associations);
3041 g_clear_object (&file_exts);
3042 g_ptr_array_free (capable_apps_keys, TRUE);
3043 g_ptr_array_free (user_capable_apps_keys, TRUE);
3044 g_ptr_array_free (priority_capable_apps_keys, TRUE);
3052 if (url_associations_key)
3053 g_win32_registry_key_watch (url_associations_key,
3055 G_WIN32_REGISTRY_WATCH_NAME |
3056 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3057 G_WIN32_REGISTRY_WATCH_VALUES,
3063 g_win32_registry_key_watch (file_exts_key,
3065 G_WIN32_REGISTRY_WATCH_NAME |
3066 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3067 G_WIN32_REGISTRY_WATCH_VALUES,
3072 if (user_clients_key)
3073 g_win32_registry_key_watch (user_clients_key,
3075 G_WIN32_REGISTRY_WATCH_NAME |
3076 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3077 G_WIN32_REGISTRY_WATCH_VALUES,
3082 if (system_clients_key)
3083 g_win32_registry_key_watch (system_clients_key,
3085 G_WIN32_REGISTRY_WATCH_NAME |
3086 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3087 G_WIN32_REGISTRY_WATCH_VALUES,
3092 if (applications_key)
3093 g_win32_registry_key_watch (applications_key,
3095 G_WIN32_REGISTRY_WATCH_NAME |
3096 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3097 G_WIN32_REGISTRY_WATCH_VALUES,
3102 if (user_registered_apps_key)
3103 g_win32_registry_key_watch (user_registered_apps_key,
3105 G_WIN32_REGISTRY_WATCH_NAME |
3106 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3107 G_WIN32_REGISTRY_WATCH_VALUES,
3112 if (system_registered_apps_key)
3113 g_win32_registry_key_watch (system_registered_apps_key,
3115 G_WIN32_REGISTRY_WATCH_NAME |
3116 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3117 G_WIN32_REGISTRY_WATCH_VALUES,
3122 if (classes_root_key)
3123 g_win32_registry_key_watch (classes_root_key,
3125 G_WIN32_REGISTRY_WATCH_NAME |
3126 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3127 G_WIN32_REGISTRY_WATCH_VALUES,
3135 g_win32_appinfo_init (void)
3137 static gsize initialized;
3139 if (g_once_init_enter (&initialized))
3141 url_associations_key =
3142 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3145 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3148 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
3150 system_clients_key =
3151 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
3154 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications",
3156 user_registered_apps_key =
3157 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
3159 system_registered_apps_key =
3160 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
3163 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT",
3168 update_registry_data ();
3170 g_once_init_leave (&initialized, TRUE);
3173 if ((url_associations_key && g_win32_registry_key_has_changed (url_associations_key)) ||
3174 (file_exts_key && g_win32_registry_key_has_changed (file_exts_key)) ||
3175 (user_clients_key && g_win32_registry_key_has_changed (user_clients_key)) ||
3176 (system_clients_key && g_win32_registry_key_has_changed (system_clients_key)) ||
3177 (applications_key && g_win32_registry_key_has_changed (applications_key)) ||
3178 (user_registered_apps_key && g_win32_registry_key_has_changed (user_registered_apps_key)) ||
3179 (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
3180 (classes_root_key && g_win32_registry_key_has_changed (classes_root_key)))
3182 G_LOCK (gio_win32_appinfo);
3183 update_registry_data ();
3185 G_UNLOCK (gio_win32_appinfo);
3190 static void g_win32_app_info_iface_init (GAppInfoIface *iface);
3192 struct _GWin32AppInfo
3194 GObject parent_instance;
3197 gchar **supported_types;
3199 GWin32AppInfoApplication *app;
3201 GWin32AppInfoHandler *handler;
3203 guint startup_notify : 1;
3206 G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
3207 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
3208 g_win32_app_info_iface_init))
3212 g_win32_app_info_finalize (GObject *object)
3214 GWin32AppInfo *info;
3216 info = G_WIN32_APP_INFO (object);
3218 g_clear_pointer (&info->supported_types, g_strfreev);
3219 g_clear_object (&info->app);
3220 g_clear_object (&info->handler);
3222 G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
3226 g_win32_app_info_class_init (GWin32AppInfoClass *klass)
3228 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3230 gobject_class->finalize = g_win32_app_info_finalize;
3234 g_win32_app_info_init (GWin32AppInfo *local)
3239 g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
3240 GWin32AppInfoHandler *handler)
3242 GWin32AppInfo *new_info;
3243 GHashTableIter iter;
3247 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3249 new_info->app = g_object_ref (app);
3251 g_win32_appinfo_init ();
3252 G_LOCK (gio_win32_appinfo);
3255 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3257 while (g_hash_table_iter_next (&iter, &ext, NULL))
3263 new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3266 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
3268 while (g_hash_table_iter_next (&iter, &ext, NULL))
3273 new_info->supported_types[i] = g_strdup ((gchar *) ext);
3277 G_UNLOCK (gio_win32_appinfo);
3279 new_info->supported_types[i] = NULL;
3281 new_info->handler = handler ? g_object_ref (handler) : NULL;
3283 return G_APP_INFO (new_info);
3288 g_win32_app_info_dup (GAppInfo *appinfo)
3290 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3291 GWin32AppInfo *new_info;
3293 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
3296 new_info->app = g_object_ref (info->app);
3299 new_info->handler = g_object_ref (info->handler);
3301 new_info->startup_notify = info->startup_notify;
3303 if (info->supported_types)
3307 for (i = 0; info->supported_types[i]; i++)
3310 new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
3312 for (i = 0; info->supported_types[i]; i++)
3313 new_info->supported_types[i] = g_strdup (info->supported_types[i]);
3315 new_info->supported_types[i] = NULL;
3318 return G_APP_INFO (new_info);
3322 g_win32_app_info_equal (GAppInfo *appinfo1,
3325 GWin32AppInfoShellVerb *shverb1 = NULL;
3326 GWin32AppInfoShellVerb *shverb2 = NULL;
3327 GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
3328 GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
3329 GWin32AppInfoApplication *app1 = info1->app;
3330 GWin32AppInfoApplication *app2 = info2->app;
3334 return info1 == info2;
3336 if (app1->canonical_name_folded != NULL &&
3337 app2->canonical_name_folded != NULL)
3338 return (g_strcmp0 (app1->canonical_name_folded,
3339 app2->canonical_name_folded)) == 0;
3341 if (app1->verbs->len > 0 &&
3342 app2->verbs->len > 0)
3344 shverb1 = _verb_idx (app1->verbs, 0);
3345 shverb2 = _verb_idx (app2->verbs, 0);
3346 if (shverb1->executable_folded != NULL &&
3347 shverb2->executable_folded != NULL)
3348 return (g_strcmp0 (shverb1->executable_folded,
3349 shverb2->executable_folded)) == 0;
3352 return app1 == app2;
3356 g_win32_app_info_get_id (GAppInfo *appinfo)
3358 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3359 GWin32AppInfoShellVerb *shverb;
3361 if (info->app == NULL)
3364 if (info->app->canonical_name_u8)
3365 return info->app->canonical_name_u8;
3367 if (info->app->verbs->len > 0 &&
3368 (shverb = _verb_idx (info->app->verbs, 0))->executable_basename != NULL)
3369 return shverb->executable_basename;
3375 g_win32_app_info_get_name (GAppInfo *appinfo)
3377 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3379 if (info->app && info->app->canonical_name_u8)
3380 return info->app->canonical_name_u8;
3382 return P_("Unnamed");
3386 g_win32_app_info_get_display_name (GAppInfo *appinfo)
3388 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3392 if (info->app->localized_pretty_name_u8)
3393 return info->app->localized_pretty_name_u8;
3394 else if (info->app->pretty_name_u8)
3395 return info->app->pretty_name_u8;
3398 return g_win32_app_info_get_name (appinfo);
3402 g_win32_app_info_get_description (GAppInfo *appinfo)
3404 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3406 if (info->app == NULL)
3409 return info->app->description_u8;
3413 g_win32_app_info_get_executable (GAppInfo *appinfo)
3415 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3417 if (info->app == NULL)
3420 if (info->app->verbs->len > 0)
3421 return _verb_idx (info->app->verbs, 0)->executable;
3427 g_win32_app_info_get_commandline (GAppInfo *appinfo)
3429 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3431 if (info->app == NULL)
3434 if (info->app->verbs->len > 0)
3435 return _verb_idx (info->app->verbs, 0)->command_utf8;
3441 g_win32_app_info_get_icon (GAppInfo *appinfo)
3443 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
3445 if (info->app == NULL)
3448 return info->app->icon;
3451 typedef struct _file_or_uri {
3457 expand_macro_single (char macro, file_or_uri *obj)
3459 char *result = NULL;
3476 /* TODO: handle 'l' and 'd' differently (longname and desktop name) */
3478 result = g_strdup (obj->uri);
3480 result = g_strdup (obj->file);
3485 result = g_shell_quote (obj->uri);
3490 result = g_shell_quote (obj->file);
3498 expand_macro (char macro,
3500 GWin32AppInfo *info,
3501 GList **stat_obj_list,
3504 GList *objs = *obj_list;
3506 gboolean result = FALSE;
3508 g_return_val_if_fail (exec != NULL, FALSE);
3511 Legend: (from http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
3512 %* - replace with all parameters
3513 %~ - replace with all parameters starting with and following the second parameter
3514 %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).
3515 %<n> (where N is 2 - 9), replace with the nth parameter
3518 %i - IDList stored in a shared memory handle is passed here.
3519 %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.
3520 %d - desktop absolute parsing name of the first parameter (for items that don't have file system paths)
3521 %v - for verbs that are none implies all, if there is no parameter passed this is the working directory
3522 %w - the working directory
3534 for (o = *stat_obj_list, i = 0;
3535 macro == '~' && o && i < 2;
3538 for (; o; o = o->next)
3540 expanded = expand_macro_single (macro, o->data);
3544 if (o != *stat_obj_list)
3545 g_string_append (exec, " ");
3547 g_string_append (exec, expanded);
3568 expanded = expand_macro_single (macro, o->data);
3572 if (o != *stat_obj_list)
3573 g_string_append (exec, " ");
3575 g_string_append (exec, expanded);
3628 for (o = *stat_obj_list, i = 0; o && i < n; o = o->next, i++);
3632 expanded = expand_macro_single (macro, o->data);
3636 if (o != *stat_obj_list)
3637 g_string_append (exec, " ");
3639 g_string_append (exec, expanded);
3658 expanded = g_get_current_dir ();
3659 g_string_append (exec, expanded);
3666 expanded = expand_macro_single (macro, objs->data);
3670 g_string_append (exec, expanded);
3683 expanded = expand_macro_single (macro, objs->data);
3687 g_string_append (exec, expanded);
3694 if (objs != NULL && expanded)
3695 g_string_append_c (exec, ' ');
3701 if (info->app && info->app->localized_pretty_name_u8)
3703 expanded = g_shell_quote (info->app->localized_pretty_name_u8);
3704 g_string_append (exec, expanded);
3709 case 'm': /* deprecated */
3710 case 'n': /* deprecated */
3711 case 'N': /* deprecated */
3712 /*case 'd': *//* deprecated */
3713 case 'D': /* deprecated */
3717 g_string_append_c (exec, '%');
3727 expand_application_parameters (GWin32AppInfo *info,
3728 const gchar *exec_line,
3734 GList *obj_list = *objs;
3735 GList **stat_obj_list = objs;
3736 const char *p = exec_line;
3737 GString *expanded_exec;
3741 expanded_exec = g_string_new (NULL);
3746 if (p[0] == '%' && p[1] != '\0')
3748 if (expand_macro (p[1],
3750 info, stat_obj_list,
3757 g_string_append_c (expanded_exec, *p);
3762 /* No file substitutions */
3763 if (obj_list == *objs && obj_list != NULL && !res)
3765 /* If there is no macro default to %f. This is also what KDE does */
3766 g_string_append_c (expanded_exec, ' ');
3767 expand_macro ('f', expanded_exec, info, stat_obj_list, objs);
3770 /* Replace '\\' with '/', because g_shell_parse_argv considers them
3771 * to be escape sequences.
3773 for (a_char = expanded_exec->str;
3774 a_char <= &expanded_exec->str[expanded_exec->len];
3777 if (*a_char == '\\')
3781 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
3782 g_string_free (expanded_exec, TRUE);
3788 get_appath_for_exe (const gchar *exe_basename)
3790 GWin32RegistryKey *apppath_key = NULL;
3791 GWin32RegistryValueType val_type;
3792 gchar *appath = NULL;
3794 gchar *key_path = g_strdup_printf ("HKEY_LOCAL_MACHINE\\"
3800 "%s", exe_basename);
3802 apppath_key = g_win32_registry_key_new (key_path, NULL);
3803 g_clear_pointer (&key_path, g_free);
3805 if (apppath_key == NULL)
3808 got_value = g_win32_registry_key_get_value (apppath_key,
3817 g_object_unref (apppath_key);
3820 val_type == G_WIN32_REGISTRY_VALUE_STR)
3823 g_clear_pointer (&appath, g_free);
3830 g_win32_app_info_launch_internal (GWin32AppInfo *info,
3832 GAppLaunchContext *launch_context,
3833 GSpawnFlags spawn_flags,
3836 gboolean completed = FALSE;
3837 char **argv, **envp;
3839 const gchar *command;
3841 GWin32AppInfoShellVerb *shverb;
3843 g_return_val_if_fail (info != NULL, FALSE);
3844 g_return_val_if_fail (info->app != NULL, FALSE);
3849 envp = g_app_launch_context_get_environment (launch_context);
3851 envp = g_get_environ ();
3855 if (info->handler != NULL &&
3856 info->handler->verbs->len > 0)
3857 shverb = _verb_idx (info->handler->verbs, 0);
3858 else if (info->app->verbs->len > 0)
3859 shverb = _verb_idx (info->app->verbs, 0);
3863 if (info->handler == NULL)
3864 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3865 P_("The app ‘%s’ in the application object has no verbs"),
3866 g_win32_appinfo_application_get_some_name (info->app));
3867 else if (info->handler->verbs->len == 0)
3868 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3869 P_("The app ‘%s’ and the handler ‘%s’ in the application object have no verbs"),
3870 g_win32_appinfo_application_get_some_name (info->app),
3871 info->handler->handler_id_folded);
3876 g_assert (shverb->command_utf8 != NULL);
3877 command = shverb->command_utf8;
3878 apppath = get_appath_for_exe (shverb->executable_basename);
3885 for (p = envp, p_index = 0; p[0]; p++, p_index++)
3886 if ((p[0][0] == 'p' || p[0][0] == 'P') &&
3887 (p[0][1] == 'a' || p[0][1] == 'A') &&
3888 (p[0][2] == 't' || p[0][2] == 'T') &&
3889 (p[0][3] == 'h' || p[0][3] == 'H') &&
3896 new_envp = g_new (char *, g_strv_length (envp) + 2);
3897 new_envp[0] = g_strdup_printf ("PATH=%s", apppath);
3899 for (p_index = 0; p_index <= g_strv_length (envp); p_index++)
3900 new_envp[1 + p_index] = envp[p_index];
3911 if (p_path[0] != '\0')
3912 envp[p_index] = g_strdup_printf ("PATH=%s%c%s",
3914 G_SEARCHPATH_SEPARATOR,
3917 envp[p_index] = g_strdup_printf ("PATH=%s", apppath);
3919 g_free (&p_path[-5]);
3927 if (!expand_application_parameters (info,
3935 if (!g_spawn_async (NULL,
3945 if (launch_context != NULL)
3947 GVariantBuilder builder;
3948 GVariant *platform_data;
3950 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
3951 g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
3953 platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
3954 g_signal_emit_by_name (launch_context, "launched", info, platform_data);
3955 g_variant_unref (platform_data);
3961 while (objs != NULL);
3973 free_file_or_uri (gpointer ptr)
3975 file_or_uri *obj = ptr;
3983 g_win32_app_supports_uris (GWin32AppInfoApplication *app)
3985 gssize num_of_uris_supported;
3990 num_of_uris_supported = (gssize) g_hash_table_size (app->supported_urls);
3992 if (g_hash_table_lookup (app->supported_urls, "file"))
3993 num_of_uris_supported -= 1;
3995 return num_of_uris_supported > 0;
4000 g_win32_app_info_supports_uris (GAppInfo *appinfo)
4002 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4004 if (info->app == NULL)
4007 return g_win32_app_supports_uris (info->app);
4012 g_win32_app_info_supports_files (GAppInfo *appinfo)
4014 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4016 if (info->app == NULL)
4019 return g_hash_table_size (info->app->supported_exts) > 0;
4024 g_win32_app_info_launch_uris (GAppInfo *appinfo,
4026 GAppLaunchContext *launch_context,
4029 gboolean res = FALSE;
4033 do_files = g_win32_app_info_supports_files (appinfo);
4039 obj = g_new0 (file_or_uri, 1);
4046 file = g_file_new_for_uri (uris->data);
4047 path = g_file_get_path (file);
4049 g_object_unref (file);
4052 obj->uri = g_strdup (uris->data);
4054 objs = g_list_prepend (objs, obj);
4058 objs = g_list_reverse (objs);
4060 res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4063 G_SPAWN_SEARCH_PATH,
4066 g_list_free_full (objs, free_file_or_uri);
4072 g_win32_app_info_launch (GAppInfo *appinfo,
4074 GAppLaunchContext *launch_context,
4077 gboolean res = FALSE;
4081 do_uris = g_win32_app_info_supports_uris (appinfo);
4087 obj = g_new0 (file_or_uri, 1);
4088 obj->file = g_file_get_path (G_FILE (files->data));
4091 obj->uri = g_file_get_uri (G_FILE (files->data));
4093 objs = g_list_prepend (objs, obj);
4094 files = files->next;
4097 objs = g_list_reverse (objs);
4099 res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
4102 G_SPAWN_SEARCH_PATH,
4105 g_list_free_full (objs, free_file_or_uri);
4110 static const char **
4111 g_win32_app_info_get_supported_types (GAppInfo *appinfo)
4113 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4115 return (const char**) info->supported_types;
4119 g_app_info_create_from_commandline (const char *commandline,
4120 const char *application_name,
4121 GAppInfoCreateFlags flags,
4124 GWin32AppInfo *info;
4125 GWin32AppInfoApplication *app;
4126 gunichar2 *app_command;
4128 g_return_val_if_fail (commandline, NULL);
4130 app_command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
4132 if (app_command == NULL)
4135 info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
4136 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
4138 app->no_open_with = FALSE;
4139 app->user_specific = FALSE;
4140 app->default_app = FALSE;
4142 if (application_name)
4144 app->canonical_name = g_utf8_to_utf16 (application_name,
4149 app->canonical_name_u8 = g_strdup (application_name);
4150 app->canonical_name_folded = g_utf8_casefold (application_name, -1);
4162 g_clear_pointer (&app_command, g_free);
4164 info->handler = NULL;
4166 return G_APP_INFO (info);
4169 /* GAppInfo interface init */
4172 g_win32_app_info_iface_init (GAppInfoIface *iface)
4174 iface->dup = g_win32_app_info_dup;
4175 iface->equal = g_win32_app_info_equal;
4176 iface->get_id = g_win32_app_info_get_id;
4177 iface->get_name = g_win32_app_info_get_name;
4178 iface->get_description = g_win32_app_info_get_description;
4179 iface->get_executable = g_win32_app_info_get_executable;
4180 iface->get_icon = g_win32_app_info_get_icon;
4181 iface->launch = g_win32_app_info_launch;
4182 iface->supports_uris = g_win32_app_info_supports_uris;
4183 iface->supports_files = g_win32_app_info_supports_files;
4184 iface->launch_uris = g_win32_app_info_launch_uris;
4185 /* iface->should_show = g_win32_app_info_should_show;*/
4186 /* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
4187 /* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
4188 /* iface->add_supports_type = g_win32_app_info_add_supports_type;*/
4189 /* iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
4190 /* iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
4191 /* iface->can_delete = g_win32_app_info_can_delete;*/
4192 /* iface->do_delete = g_win32_app_info_delete;*/
4193 iface->get_commandline = g_win32_app_info_get_commandline;
4194 iface->get_display_name = g_win32_app_info_get_display_name;
4195 /* iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
4196 iface->get_supported_types = g_win32_app_info_get_supported_types;
4200 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
4202 GWin32AppInfoURLSchema *scheme = NULL;
4205 GWin32AppInfoShellVerb *shverb;
4207 scheme_down = g_utf8_casefold (uri_scheme, -1);
4212 if (strcmp (scheme_down, "file") == 0)
4214 g_free (scheme_down);
4219 g_win32_appinfo_init ();
4220 G_LOCK (gio_win32_appinfo);
4222 g_set_object (&scheme, g_hash_table_lookup (urls, scheme_down));
4223 g_free (scheme_down);
4225 G_UNLOCK (gio_win32_appinfo);
4229 if (scheme != NULL &&
4230 scheme->chosen_handler != NULL &&
4231 scheme->chosen_handler->verbs->len > 0 &&
4232 (shverb = _verb_idx (scheme->chosen_handler->verbs, 0))->app != NULL)
4233 result = g_win32_app_info_new_from_app (shverb->app,
4234 scheme->chosen_handler);
4236 g_clear_object (&scheme);
4242 g_app_info_get_default_for_type (const char *content_type,
4243 gboolean must_support_uris)
4245 GWin32AppInfoFileExtension *ext = NULL;
4248 GWin32AppInfoShellVerb *shverb;
4250 ext_down = g_utf8_casefold (content_type, -1);
4255 g_win32_appinfo_init ();
4256 G_LOCK (gio_win32_appinfo);
4258 /* Assuming that "content_type" is a file extension, not a MIME type */
4259 g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
4262 G_UNLOCK (gio_win32_appinfo);
4269 if (ext->chosen_handler != NULL &&
4270 ext->chosen_handler->verbs->len > 0 &&
4271 (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL &&
4272 (!must_support_uris ||
4273 g_win32_app_supports_uris (shverb->app)))
4274 result = g_win32_app_info_new_from_app (shverb->app,
4275 ext->chosen_handler);
4278 GHashTableIter iter;
4279 GWin32AppInfoHandler *handler;
4281 g_hash_table_iter_init (&iter, ext->handlers);
4283 while (result == NULL &&
4284 g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4286 if (handler->verbs->len == 0)
4289 shverb = _verb_idx (handler->verbs, 0);
4292 (!must_support_uris ||
4293 g_win32_app_supports_uris (shverb->app)))
4294 result = g_win32_app_info_new_from_app (shverb->app, handler);
4298 g_clear_object (&ext);
4304 g_app_info_get_all (void)
4306 GHashTableIter iter;
4312 g_win32_appinfo_init ();
4313 G_LOCK (gio_win32_appinfo);
4316 g_hash_table_iter_init (&iter, apps_by_id);
4317 while (g_hash_table_iter_next (&iter, NULL, &value))
4318 apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
4320 G_UNLOCK (gio_win32_appinfo);
4323 for (apps_i = apps; apps_i; apps_i = apps_i->next)
4324 infos = g_list_prepend (infos,
4325 g_win32_app_info_new_from_app (apps_i->data, NULL));
4327 g_list_free_full (apps, g_object_unref);
4333 g_app_info_get_all_for_type (const char *content_type)
4335 GWin32AppInfoFileExtension *ext = NULL;
4337 GWin32AppInfoHandler *handler;
4338 GHashTableIter iter;
4339 GHashTable *apps = NULL;
4341 GWin32AppInfoShellVerb *shverb;
4343 ext_down = g_utf8_casefold (content_type, -1);
4348 g_win32_appinfo_init ();
4349 G_LOCK (gio_win32_appinfo);
4351 /* Assuming that "content_type" is a file extension, not a MIME type */
4352 g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
4355 G_UNLOCK (gio_win32_appinfo);
4361 /* Used as a set to ensure uniqueness */
4362 apps = g_hash_table_new (g_direct_hash, g_direct_equal);
4364 if (ext->chosen_handler != NULL &&
4365 ext->chosen_handler->verbs->len > 0 &&
4366 (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL)
4368 g_hash_table_add (apps, shverb->app);
4369 result = g_list_prepend (result,
4370 g_win32_app_info_new_from_app (shverb->app,
4371 ext->chosen_handler));
4374 g_hash_table_iter_init (&iter, ext->handlers);
4376 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
4380 for (vi = 0; vi < handler->verbs->len; vi++)
4382 shverb = _verb_idx (handler->verbs, vi);
4384 if (shverb->app == NULL ||
4385 g_hash_table_contains (apps, shverb->app))
4388 g_hash_table_add (apps, shverb->app);
4389 result = g_list_prepend (result,
4390 g_win32_app_info_new_from_app (shverb->app,
4395 g_clear_object (&ext);
4396 result = g_list_reverse (result);
4397 g_hash_table_unref (apps);
4403 g_app_info_get_fallback_for_type (const gchar *content_type)
4405 /* TODO: fix this once gcontenttype support is improved */
4406 return g_app_info_get_all_for_type (content_type);
4410 g_app_info_get_recommended_for_type (const gchar *content_type)
4412 /* TODO: fix this once gcontenttype support is improved */
4413 return g_app_info_get_all_for_type (content_type);
4417 g_app_info_reset_type_associations (const char *content_type)