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>
29 #include "gcontenttype.h"
30 #include "gwin32appinfo.h"
34 #include <glib/gstdio.h>
36 #include <gio/gwin32registrykey.h>
38 /* Contains the definitions from shlobj.h that are
39 * guarded as Windows8-or-newer and are unavailable
40 * to GLib, being only Windows7-or-newer.
42 #include "gwin32api-application-activation-manager.h"
45 /* For SHLoadIndirectString() */
48 #include <glib/gstdioprivate.h>
49 #include "giowin32-priv.h"
50 #include "glib-private.h"
52 /* We need to watch 8 places:
53 * 0) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations
54 * (anything below that key)
55 * On change: re-enumerate subkeys, read their values.
56 * 1) HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts
57 * (anything below that key)
58 * On change: re-enumerate subkeys
59 * 2) HKEY_CURRENT_USER\\Software\\Clients (anything below that key)
60 * On change: re-read the whole hierarchy of handlers
61 * 3) HKEY_LOCAL_MACHINE\\Software\\Clients (anything below that key)
62 * On change: re-read the whole hierarchy of handlers
63 * 4) HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications (values of that key)
64 * On change: re-read the value list of registered applications
65 * 5) HKEY_CURRENT_USER\\Software\\RegisteredApplications (values of that key)
66 * On change: re-read the value list of registered applications
67 * 6) HKEY_CLASSES_ROOT\\Applications (anything below that key)
68 * On change: re-read the whole hierarchy of apps
69 * 7) HKEY_CLASSES_ROOT (only its subkeys)
70 * On change: re-enumerate subkeys, try to filter out wrong names.
73 * About verbs. A registry key (the name of that key is known as ProgID)
74 * can contain a "shell" subkey, which can then contain a number of verb
75 * subkeys (the most common being the "open" verb), and each of these
76 * contains a "command" subkey, which has a default string value that
77 * is the command to be run.
78 * Most ProgIDs are in HKEY_CLASSES_ROOT, but some are nested deeper in
79 * the registry (such as HKEY_CURRENT_USER\\Software\\<softwarename>).
81 * Verb selection works like this (according to https://docs.microsoft.com/en-us/windows/win32/shell/context ):
82 * 1) If "open" verb is available, that verb is used.
83 * 2) If the Shell subkey has a default string value, and if a verb subkey
84 * with that name exists, that verb is used.
85 * 3) The first subkey found in the list of verb subkeys is used.
86 * 4) The "openwith" verb is used
88 * Testing suggests that Windows never reaches the point 4 in any realistic
89 * circumstances. If a "command" subkey is missing for a verb, or if it has
90 * an empty string as its default value, the app launch fails
91 * (the "openwith" verb is not used, even if it's present).
92 * If the command is present, but is not valid (runs nonexisting executable,
93 * for example), then other verbs are not checked.
94 * It seems that when the documentation said "openwith verb", it meant
95 * that Windows invokes the default "Open with..." dialog (it does not
96 * look at the "openwith" verb subkey, even if it's there).
97 * If a verb subkey that is supposed to be used is present, but it lacks
98 * a command subkey, an error message is shown and nothing else happens.
101 #define _verb_idx(array,index) ((GWin32AppInfoShellVerb *) g_ptr_array_index (array, index))
103 #define _lookup_by_verb(array, verb, dst, itemtype) do { \
106 for (_index = 0; array && _index < array->len; _index++) \
108 _v = (itemtype *) g_ptr_array_index (array, _index); \
109 if (_wcsicmp (_v->verb_name, (verb)) == 0) \
115 if (array == NULL || _index >= array->len) \
119 #define _verb_lookup(array, verb, dst) _lookup_by_verb (array, verb, dst, GWin32AppInfoShellVerb)
121 /* Because with subcommands a verb would have
122 * a name like "foo\\bar", but the key its command
123 * should be looked for is "shell\\foo\\shell\\bar\\command"
125 typedef struct _reg_verb {
127 gunichar2 *shellpath;
130 typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema;
131 typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension;
132 typedef struct _GWin32AppInfoShellVerb GWin32AppInfoShellVerb;
133 typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler;
134 typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication;
136 typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass;
137 typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass;
138 typedef struct _GWin32AppInfoShellVerbClass GWin32AppInfoShellVerbClass;
139 typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass;
140 typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass;
142 struct _GWin32AppInfoURLSchemaClass
144 GObjectClass parent_class;
147 struct _GWin32AppInfoFileExtensionClass
149 GObjectClass parent_class;
152 struct _GWin32AppInfoHandlerClass
154 GObjectClass parent_class;
157 struct _GWin32AppInfoApplicationClass
159 GObjectClass parent_class;
162 struct _GWin32AppInfoShellVerbClass
164 GObjectClass parent_class;
167 struct _GWin32AppInfoURLSchema {
168 GObject parent_instance;
170 /* url schema (stuff before ':') */
173 /* url schema (stuff before ':'), in UTF-8 */
176 /* url schema (stuff before ':'), in UTF-8, folded */
177 gchar *schema_u8_folded;
179 /* Handler currently selected for this schema. Can be NULL. */
180 GWin32AppInfoHandler *chosen_handler;
182 /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this schema.
183 * Includes the chosen handler, if any.
185 GHashTable *handlers;
188 struct _GWin32AppInfoHandler {
189 GObject parent_instance;
191 /* Usually a class name in HKCR */
192 gunichar2 *handler_id;
194 /* Registry object obtained by opening @handler_id.
195 * Can be used to watch this handler.
196 * May be %NULL (for fake handlers that we made up).
198 GWin32RegistryKey *key;
200 /* @handler_id, in UTF-8, folded */
201 gchar *handler_id_folded;
203 /* Icon of the application for this handler */
206 /* Verbs that this handler supports */
207 GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
209 /* AppUserModelID for a UWP application. When this is not NULL,
210 * this handler launches a UWP application.
211 * UWP applications are launched using a COM interface and have no commandlines,
212 * and the verbs will reflect that too.
214 gunichar2 *uwp_aumid;
217 struct _GWin32AppInfoShellVerb {
218 GObject parent_instance;
220 /* The verb that is used to invoke this handler. */
221 gunichar2 *verb_name;
223 /* User-friendly (localized) verb name. */
224 gchar *verb_displayname;
226 /* %TRUE if this verb is for a UWP app.
227 * It means that @command, @executable and @dll_function are %NULL.
231 /* shell/verb/command */
234 /* Same as @command, but in UTF-8 */
237 /* Executable of the program (UTF-8) */
240 /* Executable of the program (for matching, in folded form; UTF-8) */
241 gchar *executable_folded;
243 /* Pointer to a location within @executable */
244 gchar *executable_basename;
246 /* If not NULL, then @executable and its derived fields contain the name
247 * of a DLL file (without the name of the function that rundll32.exe should
248 * invoke), and this field contains the name of the function to be invoked.
249 * The application is then invoked as 'rundll32.exe "dll_path",dll_function other_arguments...'.
253 /* The application that is linked to this verb. */
254 GWin32AppInfoApplication *app;
257 struct _GWin32AppInfoFileExtension {
258 GObject parent_instance;
260 /* File extension (with leading '.') */
261 gunichar2 *extension;
263 /* File extension (with leading '.'), in UTF-8 */
266 /* handler currently selected for this extension. Can be NULL. */
267 GWin32AppInfoHandler *chosen_handler;
269 /* Maps folded handler IDs -> to GWin32AppInfoHandlers for this extension.
270 * Includes the chosen handler, if any.
272 GHashTable *handlers;
275 struct _GWin32AppInfoApplication {
276 GObject parent_instance;
278 /* Canonical name (used for key names).
279 * For applications tracked by id this is the root registry
280 * key path for the application.
281 * For applications tracked by executable name this is the
282 * basename of the executable.
283 * For UWP apps this is the AppUserModelID.
284 * For fake applications this is the full filename of the
285 * executable (as far as it can be inferred from a command line,
286 * meaning that it can also be a basename, if that's
287 * all that a commandline happen to give us).
289 gunichar2 *canonical_name;
291 /* @canonical_name, in UTF-8 */
292 gchar *canonical_name_u8;
294 /* @canonical_name, in UTF-8, folded */
295 gchar *canonical_name_folded;
297 /* Human-readable name in English. Can be NULL */
298 gunichar2 *pretty_name;
300 /* Human-readable name in English, UTF-8. Can be NULL */
301 gchar *pretty_name_u8;
303 /* Human-readable name in user's language. Can be NULL */
304 gunichar2 *localized_pretty_name;
306 /* Human-readable name in user's language, UTF-8. Can be NULL */
307 gchar *localized_pretty_name_u8;
309 /* Description, could be in user's language. Can be NULL */
310 gunichar2 *description;
312 /* Description, could be in user's language, UTF-8. Can be NULL */
313 gchar *description_u8;
315 /* Verbs that this application supports */
316 GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
318 /* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
319 * UTF-8, folded) -> to a GWin32AppInfoHandler
320 * Schema can be used as a key in the urls hashmap.
322 GHashTable *supported_urls;
324 /* Explicitly supported extensions, hashmap from map-owned gchar ptr
325 * (.extension, UTF-8, folded) -> to a GWin32AppInfoHandler
326 * Extension can be used as a key in the extensions hashmap.
328 GHashTable *supported_exts;
330 /* Icon of the application (remember, handler can have its own icon too) */
333 /* Set to TRUE to prevent this app from appearing in lists of apps for
334 * opening files. This will not prevent it from appearing in lists of apps
335 * just for running, or lists of apps for opening exts/urls for which this
336 * app reports explicit support.
338 gboolean no_open_with;
340 /* Set to TRUE for applications from HKEY_CURRENT_USER.
341 * Give them priority over applications from HKEY_LOCAL_MACHINE, when all
342 * other things are equal.
344 gboolean user_specific;
346 /* Set to TRUE for applications that are machine-wide defaults (i.e. default
348 gboolean default_app;
350 /* Set to TRUE for UWP applications */
354 #define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ())
355 #define G_WIN32_APPINFO_URL_SCHEMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema))
357 #define G_TYPE_WIN32_APPINFO_FILE_EXTENSION (g_win32_appinfo_file_extension_get_type ())
358 #define G_WIN32_APPINFO_FILE_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension))
360 #define G_TYPE_WIN32_APPINFO_HANDLER (g_win32_appinfo_handler_get_type ())
361 #define G_WIN32_APPINFO_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler))
363 #define G_TYPE_WIN32_APPINFO_APPLICATION (g_win32_appinfo_application_get_type ())
364 #define G_WIN32_APPINFO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication))
366 #define G_TYPE_WIN32_APPINFO_SHELL_VERB (g_win32_appinfo_shell_verb_get_type ())
367 #define G_WIN32_APPINFO_SHELL_VERB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_SHELL_VERB, GWin32AppInfoShellVerb))
369 GType g_win32_appinfo_url_schema_get_type (void) G_GNUC_CONST;
370 GType g_win32_appinfo_file_extension_get_type (void) G_GNUC_CONST;
371 GType g_win32_appinfo_shell_verb_get_type (void) G_GNUC_CONST;
372 GType g_win32_appinfo_handler_get_type (void) G_GNUC_CONST;
373 GType g_win32_appinfo_application_get_type (void) G_GNUC_CONST;
375 G_DEFINE_TYPE (GWin32AppInfoURLSchema, g_win32_appinfo_url_schema, G_TYPE_OBJECT)
376 G_DEFINE_TYPE (GWin32AppInfoFileExtension, g_win32_appinfo_file_extension, G_TYPE_OBJECT)
377 G_DEFINE_TYPE (GWin32AppInfoShellVerb, g_win32_appinfo_shell_verb, G_TYPE_OBJECT)
378 G_DEFINE_TYPE (GWin32AppInfoHandler, g_win32_appinfo_handler, G_TYPE_OBJECT)
379 G_DEFINE_TYPE (GWin32AppInfoApplication, g_win32_appinfo_application, G_TYPE_OBJECT)
382 g_win32_appinfo_url_schema_dispose (GObject *object)
384 GWin32AppInfoURLSchema *url = G_WIN32_APPINFO_URL_SCHEMA (object);
386 g_clear_pointer (&url->schema, g_free);
387 g_clear_pointer (&url->schema_u8, g_free);
388 g_clear_pointer (&url->schema_u8_folded, g_free);
389 g_clear_object (&url->chosen_handler);
390 g_clear_pointer (&url->handlers, g_hash_table_destroy);
391 G_OBJECT_CLASS (g_win32_appinfo_url_schema_parent_class)->dispose (object);
396 g_win32_appinfo_handler_dispose (GObject *object)
398 GWin32AppInfoHandler *handler = G_WIN32_APPINFO_HANDLER (object);
400 g_clear_pointer (&handler->handler_id, g_free);
401 g_clear_pointer (&handler->handler_id_folded, g_free);
402 g_clear_object (&handler->key);
403 g_clear_object (&handler->icon);
404 g_clear_pointer (&handler->verbs, g_ptr_array_unref);
405 g_clear_pointer (&handler->uwp_aumid, g_free);
406 G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
410 g_win32_appinfo_file_extension_dispose (GObject *object)
412 GWin32AppInfoFileExtension *ext = G_WIN32_APPINFO_FILE_EXTENSION (object);
414 g_clear_pointer (&ext->extension, g_free);
415 g_clear_pointer (&ext->extension_u8, g_free);
416 g_clear_object (&ext->chosen_handler);
417 g_clear_pointer (&ext->handlers, g_hash_table_destroy);
418 G_OBJECT_CLASS (g_win32_appinfo_file_extension_parent_class)->dispose (object);
422 g_win32_appinfo_shell_verb_dispose (GObject *object)
424 GWin32AppInfoShellVerb *shverb = G_WIN32_APPINFO_SHELL_VERB (object);
426 g_clear_pointer (&shverb->verb_name, g_free);
427 g_clear_pointer (&shverb->verb_displayname, g_free);
428 g_clear_pointer (&shverb->command, g_free);
429 g_clear_pointer (&shverb->command_utf8, g_free);
430 g_clear_pointer (&shverb->executable_folded, g_free);
431 g_clear_pointer (&shverb->executable, g_free);
432 g_clear_pointer (&shverb->dll_function, g_free);
433 g_clear_object (&shverb->app);
434 G_OBJECT_CLASS (g_win32_appinfo_shell_verb_parent_class)->dispose (object);
438 g_win32_appinfo_application_dispose (GObject *object)
440 GWin32AppInfoApplication *app = G_WIN32_APPINFO_APPLICATION (object);
442 g_clear_pointer (&app->canonical_name_u8, g_free);
443 g_clear_pointer (&app->canonical_name_folded, g_free);
444 g_clear_pointer (&app->canonical_name, g_free);
445 g_clear_pointer (&app->pretty_name, g_free);
446 g_clear_pointer (&app->localized_pretty_name, g_free);
447 g_clear_pointer (&app->description, g_free);
448 g_clear_pointer (&app->pretty_name_u8, g_free);
449 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
450 g_clear_pointer (&app->description_u8, g_free);
451 g_clear_pointer (&app->supported_urls, g_hash_table_destroy);
452 g_clear_pointer (&app->supported_exts, g_hash_table_destroy);
453 g_clear_object (&app->icon);
454 g_clear_pointer (&app->verbs, g_ptr_array_unref);
455 G_OBJECT_CLASS (g_win32_appinfo_application_parent_class)->dispose (object);
459 g_win32_appinfo_application_get_some_name (GWin32AppInfoApplication *app)
461 if (app->localized_pretty_name_u8)
462 return app->localized_pretty_name_u8;
464 if (app->pretty_name_u8)
465 return app->pretty_name_u8;
467 return app->canonical_name_u8;
471 g_win32_appinfo_url_schema_class_init (GWin32AppInfoURLSchemaClass *klass)
473 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
475 gobject_class->dispose = g_win32_appinfo_url_schema_dispose;
479 g_win32_appinfo_file_extension_class_init (GWin32AppInfoFileExtensionClass *klass)
481 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
483 gobject_class->dispose = g_win32_appinfo_file_extension_dispose;
487 g_win32_appinfo_shell_verb_class_init (GWin32AppInfoShellVerbClass *klass)
489 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
491 gobject_class->dispose = g_win32_appinfo_shell_verb_dispose;
495 g_win32_appinfo_handler_class_init (GWin32AppInfoHandlerClass *klass)
497 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
499 gobject_class->dispose = g_win32_appinfo_handler_dispose;
503 g_win32_appinfo_application_class_init (GWin32AppInfoApplicationClass *klass)
505 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
507 gobject_class->dispose = g_win32_appinfo_application_dispose;
511 g_win32_appinfo_url_schema_init (GWin32AppInfoURLSchema *self)
513 self->handlers = g_hash_table_new_full (g_str_hash,
520 g_win32_appinfo_shell_verb_init (GWin32AppInfoShellVerb *self)
525 g_win32_appinfo_file_extension_init (GWin32AppInfoFileExtension *self)
527 self->handlers = g_hash_table_new_full (g_str_hash,
534 g_win32_appinfo_handler_init (GWin32AppInfoHandler *self)
536 self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
540 g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
542 self->supported_urls = g_hash_table_new_full (g_str_hash,
546 self->supported_exts = g_hash_table_new_full (g_str_hash,
550 self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
553 /* The AppInfo threadpool that does asynchronous AppInfo tree rebuilds */
554 static GThreadPool *gio_win32_appinfo_threadpool;
556 /* This mutex is held by a thread that reads or writes the AppInfo tree.
557 * (tree object references can be obtained and later read without
558 * holding this mutex, since objects are practically immutable).
560 static GMutex gio_win32_appinfo_mutex;
562 /* Any thread wanting to access AppInfo can wait on this condition */
563 static GCond gio_win32_appinfo_cond;
565 /* Increased to indicate that AppInfo tree does needs to be rebuilt.
566 * AppInfo thread checks this to see if it needs to
567 * do a tree re-build. If the value changes during a rebuild,
568 * another rebuild is triggered after that.
569 * Other threads check this to see if they need
570 * to wait for a tree re-build to finish.
572 static gint gio_win32_appinfo_update_counter = 0;
574 /* Map of owned ".ext" (with '.', UTF-8, folded)
575 * to GWin32AppInfoFileExtension ptr
577 static GHashTable *extensions = NULL;
579 /* Map of owned "schema" (without ':', UTF-8, folded)
580 * to GWin32AppInfoURLSchema ptr
582 static GHashTable *urls = NULL;
584 /* Map of owned "appID" (UTF-8, folded) to
585 * a GWin32AppInfoApplication
587 static GHashTable *apps_by_id = NULL;
589 /* Map of owned "app.exe" (UTF-8, folded) to
590 * a GWin32AppInfoApplication.
591 * This map and its values are separate from apps_by_id. The fact that an app
592 * with known ID has the same executable [base]name as an app in this map does
593 * not mean that they are the same application.
595 static GHashTable *apps_by_exe = NULL;
597 /* Map of owned "path:\to\app.exe" (UTF-8, folded) to
598 * a GWin32AppInfoApplication.
599 * The app objects in this map are fake - they are linked to
600 * handlers that do not have any apps associated with them.
602 static GHashTable *fake_apps = NULL;
604 /* Map of owned "handler id" (UTF-8, folded)
605 * to a GWin32AppInfoHandler
607 static GHashTable *handlers = NULL;
609 /* Temporary (only exists while the registry is being scanned) table
610 * that maps GWin32RegistryKey objects (keeps a ref) to owned AUMId wchar strings.
612 static GHashTable *uwp_handler_table = NULL;
614 /* Watch this whole subtree */
615 static GWin32RegistryKey *url_associations_key;
617 /* Watch this whole subtree */
618 static GWin32RegistryKey *file_exts_key;
620 /* Watch this whole subtree */
621 static GWin32RegistryKey *user_clients_key;
623 /* Watch this whole subtree */
624 static GWin32RegistryKey *system_clients_key;
627 static GWin32RegistryKey *user_registered_apps_key;
630 static GWin32RegistryKey *system_registered_apps_key;
632 /* Watch this whole subtree */
633 static GWin32RegistryKey *applications_key;
636 static GWin32RegistryKey *classes_root_key;
638 #define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
639 #define USER_CHOICE L"\\UserChoice"
640 #define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
641 #define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"
642 #define HKCR L"HKEY_CLASSES_ROOT\\"
643 #define HKCU L"HKEY_CURRENT_USER\\"
644 #define HKLM L"HKEY_LOCAL_MACHINE\\"
645 #define REG_PATH_MAX 256
646 #define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
649 * _g_win32_extract_executable(),
650 * _g_win32_fixup_broken_microsoft_rundll_commandline()
652 #include "giowin32-private.c"
654 /* for g_win32_package_parser_enum_packages() */
655 #include "gwin32packageparser.h"
658 read_handler_icon (GWin32RegistryKey *key,
661 GWin32RegistryKey *icon_key;
662 GWin32RegistryValueType default_type;
663 gchar *default_value;
669 icon_key = g_win32_registry_key_get_child_w (key, L"DefaultIcon", NULL);
671 if (icon_key == NULL)
674 if (g_win32_registry_key_get_value (icon_key,
679 (gpointer *) &default_value,
683 /* TODO: For UWP handlers this string is usually in @{...} form,
684 * see grab_registry_string() below. Right now this
685 * string is read as-is and the icon would silently fail to load.
686 * Also, right now handler icon is not used anywhere
687 * (only app icon is used).
689 if (default_type == G_WIN32_REGISTRY_VALUE_STR &&
690 default_value[0] != '\0')
691 *icon_out = g_themed_icon_new (default_value);
693 g_clear_pointer (&default_value, g_free);
696 g_object_unref (icon_key);
700 reg_verb_free (gpointer p)
705 g_free (((reg_verb *) p)->name);
706 g_free (((reg_verb *) p)->shellpath);
710 #define is_open(x) ( \
711 ((x)[0] == L'o' || (x)[0] == L'O') && \
712 ((x)[1] == L'p' || (x)[1] == L'P') && \
713 ((x)[2] == L'e' || (x)[2] == L'E') && \
714 ((x)[3] == L'n' || (x)[3] == L'N') && \
718 /* default verb (if any) comes first,
719 * then "open", then the rest of the verbs
720 * are sorted alphabetically
723 compare_verbs (gconstpointer a,
727 const reg_verb *ca = (const reg_verb *) a;
728 const reg_verb *cb = (const reg_verb *) b;
729 const gunichar2 *def = (const gunichar2 *) user_data;
735 if (_wcsicmp (ca->name, def) == 0)
737 else if (_wcsicmp (cb->name, def) == 0)
741 is_open_ca = is_open (ca->name);
742 is_open_cb = is_open (cb->name);
744 if (is_open_ca && !is_open_cb)
746 else if (is_open_ca && !is_open_cb)
749 return _wcsicmp (ca->name, cb->name);
752 static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) G_GNUC_NULL_TERMINATED;
753 static gboolean build_registry_pathv (gunichar2 *output, gsize output_size, va_list components);
755 static GWin32RegistryKey *_g_win32_registry_key_build_and_new_w (GError **error, ...) G_GNUC_NULL_TERMINATED;
757 /* Called by process_verbs_commands.
758 * @verb is a verb name
759 * @command_line is the commandline of that verb
760 * @command_line_utf8 is the UTF-8 version of @command_line
761 * @verb_displayname is the prettier display name of the verb (might be NULL)
762 * @verb_is_preferred is TRUE if the verb is the preferred one
763 * @invent_new_verb_name is TRUE when the verb should be added
764 * even if a verb with such
765 * name already exists (in which case
766 * a new name is invented), unless
767 * the existing verb runs exactly the same
770 typedef void (*verb_command_func) (gpointer handler_data1,
771 gpointer handler_data2,
772 const gunichar2 *verb,
773 const gunichar2 *command_line,
774 const gchar *command_line_utf8,
775 const gchar *verb_displayname,
776 gboolean verb_is_preferred,
777 gboolean invent_new_verb_name);
779 static gunichar2 * decide_which_id_to_use (const gunichar2 *program_id,
780 GWin32RegistryKey **return_key,
781 gchar **return_handler_id_u8,
782 gchar **return_handler_id_u8_folded,
783 gunichar2 **return_uwp_aumid);
785 static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *schema,
786 const gchar *schema_u8,
787 const gchar *schema_u8_folded);
789 static GWin32AppInfoHandler * get_handler_object (const gchar *handler_id_u8_folded,
790 GWin32RegistryKey *handler_key,
791 const gunichar2 *handler_id,
792 const gunichar2 *uwp_aumid);
794 static GWin32AppInfoFileExtension *get_ext_object (const gunichar2 *ext,
796 const gchar *ext_u8_folded);
799 static void process_verbs_commands (GList *verbs,
800 const reg_verb *preferred_verb,
801 const gunichar2 *path_to_progid,
802 const gunichar2 *progid,
803 gboolean autoprefer_first_verb,
804 verb_command_func handler,
805 gpointer handler_data1,
806 gpointer handler_data2);
808 static void handler_add_verb (gpointer handler_data1,
809 gpointer handler_data2,
810 const gunichar2 *verb,
811 const gunichar2 *command_line,
812 const gchar *command_line_utf8,
813 const gchar *verb_displayname,
814 gboolean verb_is_preferred,
815 gboolean invent_new_verb_name);
817 static void process_uwp_verbs (GList *verbs,
818 const reg_verb *preferred_verb,
819 const gunichar2 *path_to_progid,
820 const gunichar2 *progid,
821 gboolean autoprefer_first_verb,
822 GWin32AppInfoHandler *handler_rec,
823 GWin32AppInfoApplication *app);
825 static void uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec,
826 GWin32AppInfoApplication *app,
827 const gunichar2 *verb,
828 const gchar *verb_displayname,
829 gboolean verb_is_preferred);
831 /* output_size is in *bytes*, not gunichar2s! */
833 build_registry_path (gunichar2 *output, gsize output_size, ...)
838 va_start (ap, output_size);
840 result = build_registry_pathv (output, output_size, ap);
847 /* output_size is in *bytes*, not gunichar2s! */
849 build_registry_pathv (gunichar2 *output, gsize output_size, va_list components)
853 gunichar2 *component;
859 G_VA_COPY (lentest, components);
861 for (length = 0, component = va_arg (lentest, gunichar2 *);
863 component = va_arg (lentest, gunichar2 *))
865 length += wcslen (component);
870 if ((length >= REG_PATH_MAX_SIZE) ||
871 (length * sizeof (gunichar2) >= output_size))
876 for (p = output, component = va_arg (components, gunichar2 *);
878 component = va_arg (components, gunichar2 *))
880 length = wcslen (component);
881 wcscat (p, component);
889 static GWin32RegistryKey *
890 _g_win32_registry_key_build_and_new_w (GError **error, ...)
893 gunichar2 key_path[REG_PATH_MAX_SIZE + 1];
894 GWin32RegistryKey *key;
896 va_start (ap, error);
900 if (build_registry_pathv (key_path, sizeof (key_path), ap))
901 key = g_win32_registry_key_new_w (key_path, error);
908 /* Gets the list of shell verbs (a GList of reg_verb, put into @verbs)
909 * from the @program_id_key.
910 * If one of the verbs should be preferred,
911 * a pointer to this verb (in the GList) will be
912 * put into @preferred_verb.
913 * Does not automatically assume that the first verb
914 * is preferred (when no other preferences exist).
915 * @verbname_prefix is prefixed to the name of the verb
916 * (this is used for subcommands) and is initially an
918 * @verbshell_prefix is the subkey of @program_id_key
919 * that contains the verbs. It is "Shell" initially,
920 * but grows with recursive invocations (for subcommands).
921 * @is_uwp points to a boolean, which
922 * indicates whether the function is being called for a UWP app.
923 * It might be switched from %TRUE to %FALSE on return,
924 * if the application turns out to not to be UWP on closer inspection.
925 * If the application is already known not to be UWP before the
926 * call, this pointer can be %NULL instead.
927 * Returns TRUE on success, FALSE on failure.
930 get_verbs (GWin32RegistryKey *program_id_key,
931 const reg_verb **preferred_verb,
933 const gunichar2 *verbname_prefix,
934 const gunichar2 *verbshell_prefix,
937 GWin32RegistrySubkeyIter iter;
938 GWin32RegistryKey *key;
939 GWin32RegistryValueType val_type;
940 gunichar2 *default_verb;
941 gsize verbshell_prefix_len;
942 gsize verbname_prefix_len;
945 g_assert (program_id_key && verbs && preferred_verb);
948 *preferred_verb = NULL;
950 key = g_win32_registry_key_get_child_w (program_id_key,
957 if (!g_win32_registry_subkey_iter_init (&iter, key, NULL))
959 g_object_unref (key);
964 verbshell_prefix_len = g_utf16_len (verbshell_prefix);
965 verbname_prefix_len = g_utf16_len (verbname_prefix);
967 while (g_win32_registry_subkey_iter_next (&iter, TRUE, NULL))
969 const gunichar2 *name;
971 GWin32RegistryKey *subkey;
972 gboolean has_subcommands;
974 GWin32RegistryValueType subc_type;
976 const gunichar2 *shell = L"Shell";
977 const gsize shell_len = g_utf16_len (shell);
979 if (!g_win32_registry_subkey_iter_get_name_w (&iter, &name, &name_len, NULL))
982 subkey = g_win32_registry_key_get_child_w (key,
986 g_assert (subkey != NULL);
987 /* The key we're looking at is "<some_root>/Shell/<this_key>",
988 * where "Shell" is verbshell_prefix.
989 * If it has a value named 'Subcommands' (doesn't matter what its data is),
990 * it means that this key has its own Shell subkey, the subkeys
991 * of which are shell commands (i.e. <some_root>/Shell/<this_key>/Shell/<some_other_keys>).
992 * To handle that, create new, extended nameprefix and shellprefix,
993 * and call the function recursively.
994 * name prefix "" -> "<this_key_name>\\"
995 * shell prefix "Shell" -> "Shell\\<this_key_name>\\Shell"
996 * The root, program_id_key, remains the same in all invocations.
997 * Essentially, we're flattening the command tree into a list.
999 has_subcommands = FALSE;
1000 if ((is_uwp == NULL || !(*is_uwp)) && /* Assume UWP apps don't have subcommands */
1001 g_win32_registry_key_get_value_w (subkey,
1009 subc_type == G_WIN32_REGISTRY_VALUE_STR)
1011 gboolean dummy = FALSE;
1012 gunichar2 *new_nameprefix = g_new (gunichar2, verbname_prefix_len + name_len + 1 + 1);
1013 gunichar2 *new_shellprefix = g_new (gunichar2, verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1);
1014 memcpy (&new_shellprefix[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
1015 new_shellprefix[verbshell_prefix_len] = L'\\';
1016 memcpy (&new_shellprefix[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
1017 new_shellprefix[verbshell_prefix_len + 1 + name_len] = L'\\';
1018 memcpy (&new_shellprefix[verbshell_prefix_len + 1 + name_len + 1], shell, shell_len * sizeof (gunichar2));
1019 new_shellprefix[verbshell_prefix_len + 1 + name_len + 1 + shell_len] = 0;
1021 memcpy (&new_nameprefix[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
1022 memcpy (&new_nameprefix[verbname_prefix_len], name, (name_len) * sizeof (gunichar2));
1023 new_nameprefix[verbname_prefix_len + name_len] = L'\\';
1024 new_nameprefix[verbname_prefix_len + name_len + 1] = 0;
1025 has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix, &dummy);
1026 g_free (new_shellprefix);
1027 g_free (new_nameprefix);
1030 /* Presence of subcommands means that this key itself is not a command-key */
1031 if (has_subcommands)
1033 g_clear_object (&subkey);
1037 if (is_uwp != NULL && *is_uwp &&
1038 !g_win32_registry_key_get_value_w (subkey,
1041 L"ActivatableClassId",
1047 /* We expected a UWP app, but it lacks ActivatableClassId
1048 * on a verb, which means that it does not behave like
1049 * a UWP app should (msedge being an example - it's UWP,
1050 * but has its own launchable exe file and a simple ID),
1051 * so we have to treat it like a normal app.
1056 g_clear_object (&subkey);
1058 /* We don't look at the command sub-key and its value (the actual command line) here.
1059 * We save the registry path instead, and use it later in process_verbs_commands().
1060 * The name of the verb is also saved.
1061 * verbname_prefix is prefixed to the verb name (it's either an empty string
1062 * or already ends with a '\\', so no extra separators needed).
1063 * verbshell_prefix is prefixed to the verb key path (this one needs a separator,
1064 * because it never has one - all verbshell prefixes end with "Shell", not "Shell\\")
1066 rverb = g_new0 (reg_verb, 1);
1067 rverb->name = g_new (gunichar2, verbname_prefix_len + name_len + 1);
1068 memcpy (&rverb->name[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
1069 memcpy (&rverb->name[verbname_prefix_len], name, name_len * sizeof (gunichar2));
1070 rverb->name[verbname_prefix_len + name_len] = 0;
1071 rverb->shellpath = g_new (gunichar2, verbshell_prefix_len + 1 + name_len + 1);
1072 memcpy (&rverb->shellpath[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
1073 memcpy (&rverb->shellpath[verbshell_prefix_len], L"\\", sizeof (gunichar2));
1074 memcpy (&rverb->shellpath[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
1075 rverb->shellpath[verbshell_prefix_len + 1 + name_len] = 0;
1076 *verbs = g_list_append (*verbs, rverb);
1079 g_win32_registry_subkey_iter_clear (&iter);
1083 g_object_unref (key);
1088 default_verb = NULL;
1090 if (g_win32_registry_key_get_value_w (key,
1095 (void **) &default_verb,
1098 (val_type != G_WIN32_REGISTRY_VALUE_STR ||
1099 g_utf16_len (default_verb) <= 0))
1100 g_clear_pointer (&default_verb, g_free);
1102 g_object_unref (key);
1104 /* Only sort at the top level */
1105 if (verbname_prefix[0] == 0)
1107 *verbs = g_list_sort_with_data (*verbs, compare_verbs, default_verb);
1109 for (i = *verbs; default_verb && *preferred_verb == NULL && i; i = i->next)
1110 if (_wcsicmp (default_verb, ((const reg_verb *) i->data)->name) == 0)
1111 *preferred_verb = (const reg_verb *) i->data;
1114 g_clear_pointer (&default_verb, g_free);
1119 /* Grabs a URL association (from HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
1120 * or from an application with Capabilities, or just a schema subkey in HKCR).
1121 * @program_id is a ProgID of the handler for the URL.
1122 * @schema is the schema for the URL.
1123 * @schema_u8 and @schema_u8_folded are UTF-8 and folded UTF-8
1125 * @app is the app to which the URL handler belongs (can be NULL).
1126 * @is_user_choice is TRUE if this association is clearly preferred
1129 get_url_association (const gunichar2 *program_id,
1130 const gunichar2 *schema,
1131 const gchar *schema_u8,
1132 const gchar *schema_u8_folded,
1133 GWin32AppInfoApplication *app,
1134 gboolean is_user_choice)
1136 GWin32AppInfoURLSchema *schema_rec;
1137 GWin32AppInfoHandler *handler_rec;
1138 gunichar2 *handler_id;
1140 const reg_verb *preferred_verb;
1141 gchar *handler_id_u8;
1142 gchar *handler_id_u8_folded;
1143 gunichar2 *uwp_aumid;
1145 GWin32RegistryKey *handler_key;
1147 if ((handler_id = decide_which_id_to_use (program_id,
1150 &handler_id_u8_folded,
1151 &uwp_aumid)) == NULL)
1154 is_uwp = uwp_aumid != NULL;
1156 if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp))
1158 g_clear_pointer (&handler_id, g_free);
1159 g_clear_pointer (&handler_id_u8, g_free);
1160 g_clear_pointer (&handler_id_u8_folded, g_free);
1161 g_clear_object (&handler_key);
1162 g_clear_pointer (&uwp_aumid, g_free);
1167 if (!is_uwp && uwp_aumid != NULL)
1168 g_clear_pointer (&uwp_aumid, g_free);
1170 schema_rec = get_schema_object (schema,
1174 handler_rec = get_handler_object (handler_id_u8_folded,
1179 if (is_user_choice || schema_rec->chosen_handler == NULL)
1180 g_set_object (&schema_rec->chosen_handler, handler_rec);
1182 g_hash_table_insert (schema_rec->handlers,
1183 g_strdup (handler_id_u8_folded),
1184 g_object_ref (handler_rec));
1186 g_clear_object (&handler_key);
1189 g_hash_table_insert (app->supported_urls,
1190 g_strdup (schema_rec->schema_u8_folded),
1191 g_object_ref (handler_rec));
1193 if (uwp_aumid == NULL)
1194 process_verbs_commands (g_steal_pointer (&verbs),
1203 process_uwp_verbs (g_steal_pointer (&verbs),
1212 g_clear_pointer (&handler_id_u8, g_free);
1213 g_clear_pointer (&handler_id_u8_folded, g_free);
1214 g_clear_pointer (&handler_id, g_free);
1215 g_clear_pointer (&uwp_aumid, g_free);
1218 /* Grabs a file extension association (from HKCR\.ext or similar).
1219 * @program_id is a ProgID of the handler for the extension.
1220 * @file_extension is the extension (with the leading '.')
1221 * @app is the app to which the extension handler belongs (can be NULL).
1222 * @is_user_choice is TRUE if this is clearly the preferred association
1225 get_file_ext (const gunichar2 *program_id,
1226 const gunichar2 *file_extension,
1227 GWin32AppInfoApplication *app,
1228 gboolean is_user_choice)
1230 GWin32AppInfoHandler *handler_rec;
1231 gunichar2 *handler_id;
1232 const reg_verb *preferred_verb;
1234 gchar *handler_id_u8;
1235 gchar *handler_id_u8_folded;
1236 gunichar2 *uwp_aumid;
1238 GWin32RegistryKey *handler_key;
1239 GWin32AppInfoFileExtension *file_extn;
1240 gchar *file_extension_u8;
1241 gchar *file_extension_u8_folded;
1243 if ((handler_id = decide_which_id_to_use (program_id,
1246 &handler_id_u8_folded,
1247 &uwp_aumid)) == NULL)
1250 if (!g_utf16_to_utf8_and_fold (file_extension,
1253 &file_extension_u8_folded))
1255 g_clear_pointer (&handler_id, g_free);
1256 g_clear_pointer (&handler_id_u8, g_free);
1257 g_clear_pointer (&handler_id_u8_folded, g_free);
1258 g_clear_pointer (&uwp_aumid, g_free);
1259 g_clear_object (&handler_key);
1264 is_uwp = uwp_aumid != NULL;
1266 if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp))
1268 g_clear_pointer (&handler_id, g_free);
1269 g_clear_pointer (&handler_id_u8, g_free);
1270 g_clear_pointer (&handler_id_u8_folded, g_free);
1271 g_clear_object (&handler_key);
1272 g_clear_pointer (&file_extension_u8, g_free);
1273 g_clear_pointer (&file_extension_u8_folded, g_free);
1274 g_clear_pointer (&uwp_aumid, g_free);
1279 if (!is_uwp && uwp_aumid != NULL)
1280 g_clear_pointer (&uwp_aumid, g_free);
1282 file_extn = get_ext_object (file_extension, file_extension_u8, file_extension_u8_folded);
1284 handler_rec = get_handler_object (handler_id_u8_folded,
1289 if (is_user_choice || file_extn->chosen_handler == NULL)
1290 g_set_object (&file_extn->chosen_handler, handler_rec);
1292 g_hash_table_insert (file_extn->handlers,
1293 g_strdup (handler_id_u8_folded),
1294 g_object_ref (handler_rec));
1297 g_hash_table_insert (app->supported_exts,
1298 g_strdup (file_extension_u8_folded),
1299 g_object_ref (handler_rec));
1301 g_clear_pointer (&file_extension_u8, g_free);
1302 g_clear_pointer (&file_extension_u8_folded, g_free);
1303 g_clear_object (&handler_key);
1305 if (uwp_aumid == NULL)
1306 process_verbs_commands (g_steal_pointer (&verbs),
1315 process_uwp_verbs (g_steal_pointer (&verbs),
1323 g_clear_pointer (&handler_id, g_free);
1324 g_clear_pointer (&handler_id_u8, g_free);
1325 g_clear_pointer (&handler_id_u8_folded, g_free);
1326 g_clear_pointer (&uwp_aumid, g_free);
1329 /* Returns either a @program_id or the string from
1330 * the default value of the program_id key (which is a name
1331 * of a proxy class), or NULL.
1332 * Does not check that proxy represents a valid
1333 * record, just checks that it exists.
1334 * Can return the class key (HKCR/program_id or HKCR/proxy_id).
1335 * Can convert returned value to UTF-8 and fold it.
1338 decide_which_id_to_use (const gunichar2 *program_id,
1339 GWin32RegistryKey **return_key,
1340 gchar **return_handler_id_u8,
1341 gchar **return_handler_id_u8_folded,
1342 gunichar2 **return_uwp_aumid)
1344 GWin32RegistryKey *key;
1345 GWin32RegistryKey *uwp_key;
1346 GWin32RegistryValueType val_type;
1347 gunichar2 *proxy_id;
1348 gunichar2 *return_id;
1349 gunichar2 *uwp_aumid;
1351 gchar *handler_id_u8;
1352 gchar *handler_id_u8_folded;
1353 g_assert (program_id);
1358 if (return_uwp_aumid)
1359 *return_uwp_aumid = NULL;
1361 key = g_win32_registry_key_get_child_w (classes_root_key, program_id, NULL);
1366 /* Check for UWP first */
1368 uwp_key = g_win32_registry_key_get_child_w (key, L"Application", NULL);
1370 if (uwp_key != NULL)
1372 got_value = g_win32_registry_key_get_value_w (uwp_key,
1377 (void **) &uwp_aumid,
1380 if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
1381 g_clear_pointer (&uwp_aumid, g_free);
1383 /* Other values in the Application key contain useful information
1384 * (description, name, icon), but it's inconvenient to read
1385 * it here (we don't have an app object *yet*). Store the key
1386 * in a table instead, and look at it later.
1388 if (uwp_aumid == NULL)
1389 g_debug ("ProgramID %S looks like a UWP application, but isn't",
1392 g_hash_table_insert (uwp_handler_table, g_object_ref (uwp_key), g_wcsdup (uwp_aumid, -1));
1394 g_object_unref (uwp_key);
1397 /* Then check for proxy */
1400 if (uwp_aumid == NULL)
1402 got_value = g_win32_registry_key_get_value_w (key,
1407 (void **) &proxy_id,
1410 if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
1411 g_clear_pointer (&proxy_id, g_free);
1418 GWin32RegistryKey *proxy_key;
1419 proxy_key = g_win32_registry_key_get_child_w (classes_root_key, proxy_id, NULL);
1424 *return_key = g_steal_pointer (&proxy_key);
1425 g_clear_object (&proxy_key);
1427 return_id = g_steal_pointer (&proxy_id);
1430 g_clear_pointer (&proxy_id, g_free);
1433 if ((return_handler_id_u8 ||
1434 return_handler_id_u8_folded) &&
1435 !g_utf16_to_utf8_and_fold (return_id == NULL ? program_id : return_id,
1438 &handler_id_u8_folded))
1440 g_clear_object (&key);
1442 g_clear_object (return_key);
1443 g_clear_pointer (&return_id, g_free);
1448 if (return_handler_id_u8)
1449 *return_handler_id_u8 = g_steal_pointer (&handler_id_u8);
1450 g_clear_pointer (&handler_id_u8, g_free);
1451 if (return_handler_id_u8_folded)
1452 *return_handler_id_u8_folded = g_steal_pointer (&handler_id_u8_folded);
1453 g_clear_pointer (&handler_id_u8_folded, g_free);
1454 if (return_uwp_aumid)
1455 *return_uwp_aumid = g_steal_pointer (&uwp_aumid);
1456 g_clear_pointer (&uwp_aumid, g_free);
1458 if (return_id == NULL && return_key)
1459 *return_key = g_steal_pointer (&key);
1460 g_clear_object (&key);
1462 if (return_id == NULL)
1463 return g_wcsdup (program_id, -1);
1468 /* Grabs the command for each verb from @verbs,
1469 * and invokes @handler for it. Consumes @verbs.
1470 * @path_to_progid and @progid are concatenated to
1471 * produce a path to the key where Shell/verb/command
1472 * subkeys are looked up.
1473 * @preferred_verb, if not NULL, will be used to inform
1474 * the @handler that a verb is preferred.
1475 * @autoprefer_first_verb will automatically make the first
1476 * verb to be preferred, if @preferred_verb is NULL.
1477 * @handler_data1 and @handler_data2 are passed to @handler as-is.
1480 process_verbs_commands (GList *verbs,
1481 const reg_verb *preferred_verb,
1482 const gunichar2 *path_to_progid,
1483 const gunichar2 *progid,
1484 gboolean autoprefer_first_verb,
1485 verb_command_func handler,
1486 gpointer handler_data1,
1487 gpointer handler_data2)
1492 g_assert (handler != NULL);
1493 g_assert (verbs != NULL);
1494 g_assert (progid != NULL);
1496 for (i = verbs; i; i = i->next)
1498 const reg_verb *verb = (const reg_verb *) i->data;
1499 GWin32RegistryKey *key;
1500 GWin32RegistryKey *verb_key;
1501 gunichar2 *command_value;
1502 gchar *command_value_utf8;
1503 GWin32RegistryValueType val_type;
1504 gunichar2 *verb_displayname;
1505 gchar *verb_displayname_u8;
1507 key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
1508 L"\\", verb->shellpath, L"\\command", NULL);
1512 g_debug ("%S%S\\shell\\%S does not have a \"command\" subkey",
1513 path_to_progid, progid, verb->shellpath);
1517 command_value = NULL;
1518 got_value = g_win32_registry_key_get_value_w (key,
1523 (void **) &command_value,
1526 g_clear_object (&key);
1529 val_type != G_WIN32_REGISTRY_VALUE_STR ||
1530 (command_value_utf8 = g_utf16_to_utf8 (command_value,
1536 g_clear_pointer (&command_value, g_free);
1540 verb_displayname = NULL;
1541 verb_displayname_u8 = NULL;
1542 verb_key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
1543 L"\\", verb->shellpath, NULL);
1547 gsize verb_displayname_len;
1549 got_value = g_win32_registry_key_get_value_w (verb_key,
1550 g_win32_registry_get_os_dirs_w (),
1554 (void **) &verb_displayname,
1555 &verb_displayname_len,
1559 val_type == G_WIN32_REGISTRY_VALUE_STR &&
1560 verb_displayname_len > sizeof (gunichar2))
1561 verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
1563 g_clear_pointer (&verb_displayname, g_free);
1565 if (verb_displayname_u8 == NULL)
1567 got_value = g_win32_registry_key_get_value_w (verb_key,
1572 (void **) &verb_displayname,
1573 &verb_displayname_len,
1577 val_type == G_WIN32_REGISTRY_VALUE_STR &&
1578 verb_displayname_len > sizeof (gunichar2))
1579 verb_displayname_u8 = g_utf16_to_utf8 (verb_displayname, -1, NULL, NULL, NULL);
1582 g_clear_pointer (&verb_displayname, g_free);
1583 g_clear_object (&verb_key);
1586 handler (handler_data1, handler_data2, verb->name, command_value, command_value_utf8,
1587 verb_displayname_u8,
1588 (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) ||
1589 (!preferred_verb && autoprefer_first_verb && i == verbs),
1592 g_clear_pointer (&command_value, g_free);
1593 g_clear_pointer (&command_value_utf8, g_free);
1594 g_clear_pointer (&verb_displayname_u8, g_free);
1597 g_list_free_full (verbs, reg_verb_free);
1601 process_uwp_verbs (GList *verbs,
1602 const reg_verb *preferred_verb,
1603 const gunichar2 *path_to_progid,
1604 const gunichar2 *progid,
1605 gboolean autoprefer_first_verb,
1606 GWin32AppInfoHandler *handler_rec,
1607 GWin32AppInfoApplication *app)
1611 g_assert (verbs != NULL);
1613 for (i = verbs; i; i = i->next)
1615 const reg_verb *verb = (const reg_verb *) i->data;
1616 GWin32RegistryKey *key;
1618 GWin32RegistryValueType val_type;
1622 key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
1623 L"\\", verb->shellpath, NULL);
1627 g_debug ("%S%S\\%S does not exist",
1628 path_to_progid, progid, verb->shellpath);
1632 got_value = g_win32_registry_key_get_value_w (key,
1633 g_win32_registry_get_os_dirs_w (),
1635 L"ActivatableClassId",
1642 val_type == G_WIN32_REGISTRY_VALUE_STR &&
1643 acid_len > sizeof (gunichar2))
1645 /* TODO: default value of a shell subkey, if not empty,
1646 * migh contain something like @{Some.Identifier_1234.456.678.789_some_words?ms-resource://Arbitrary.Path/Pointing/Somewhere}
1647 * and it might be possible to turn it into a nice displayname.
1649 uwp_handler_add_verb (handler_rec,
1653 (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) ||
1654 (!preferred_verb && autoprefer_first_verb && i == verbs));
1658 g_debug ("%S%S\\%S does not have an ActivatableClassId string value",
1659 path_to_progid, progid, verb->shellpath);
1662 g_clear_pointer (&acid, g_free);
1663 g_clear_object (&key);
1666 g_list_free_full (verbs, reg_verb_free);
1669 /* Looks up a schema object identified by
1670 * @schema_u8_folded in the urls hash table.
1671 * If such object doesn't exist,
1672 * creates it and puts it into the urls hash table.
1673 * Returns the object.
1675 static GWin32AppInfoURLSchema *
1676 get_schema_object (const gunichar2 *schema,
1677 const gchar *schema_u8,
1678 const gchar *schema_u8_folded)
1680 GWin32AppInfoURLSchema *schema_rec;
1682 schema_rec = g_hash_table_lookup (urls, schema_u8_folded);
1684 if (schema_rec != NULL)
1687 schema_rec = g_object_new (G_TYPE_WIN32_APPINFO_URL_SCHEMA, NULL);
1688 schema_rec->schema = g_wcsdup (schema, -1);
1689 schema_rec->schema_u8 = g_strdup (schema_u8);
1690 schema_rec->schema_u8_folded = g_strdup (schema_u8_folded);
1691 g_hash_table_insert (urls, g_strdup (schema_rec->schema_u8_folded), schema_rec);
1696 /* Looks up a handler object identified by
1697 * @handler_id_u8_folded in the handlers hash table.
1698 * If such object doesn't exist,
1699 * creates it and puts it into the handlers hash table.
1700 * Returns the object.
1702 static GWin32AppInfoHandler *
1703 get_handler_object (const gchar *handler_id_u8_folded,
1704 GWin32RegistryKey *handler_key,
1705 const gunichar2 *handler_id,
1706 const gunichar2 *uwp_aumid)
1708 GWin32AppInfoHandler *handler_rec;
1710 handler_rec = g_hash_table_lookup (handlers, handler_id_u8_folded);
1712 if (handler_rec != NULL)
1715 handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
1717 handler_rec->key = g_object_ref (handler_key);
1718 handler_rec->handler_id = g_wcsdup (handler_id, -1);
1719 handler_rec->handler_id_folded = g_strdup (handler_id_u8_folded);
1721 handler_rec->uwp_aumid = g_wcsdup (uwp_aumid, -1);
1723 read_handler_icon (handler_key, &handler_rec->icon);
1724 g_hash_table_insert (handlers, g_strdup (handler_id_u8_folded), handler_rec);
1730 handler_add_verb (gpointer handler_data1,
1731 gpointer handler_data2,
1732 const gunichar2 *verb,
1733 const gunichar2 *command_line,
1734 const gchar *command_line_utf8,
1735 const gchar *verb_displayname,
1736 gboolean verb_is_preferred,
1737 gboolean invent_new_verb_name)
1739 GWin32AppInfoHandler *handler_rec = (GWin32AppInfoHandler *) handler_data1;
1740 GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
1741 GWin32AppInfoShellVerb *shverb;
1743 _verb_lookup (handler_rec->verbs, verb, &shverb);
1748 shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
1749 shverb->verb_name = g_wcsdup (verb, -1);
1750 shverb->verb_displayname = g_strdup (verb_displayname);
1751 shverb->command = g_wcsdup (command_line, -1);
1752 shverb->command_utf8 = g_strdup (command_line_utf8);
1753 shverb->is_uwp = FALSE; /* This function is for non-UWP verbs only */
1755 shverb->app = g_object_ref (app_rec);
1757 _g_win32_extract_executable (shverb->command,
1758 &shverb->executable,
1759 &shverb->executable_basename,
1760 &shverb->executable_folded,
1762 &shverb->dll_function);
1764 if (shverb->dll_function != NULL)
1765 _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
1767 if (!verb_is_preferred)
1768 g_ptr_array_add (handler_rec->verbs, shverb);
1770 g_ptr_array_insert (handler_rec->verbs, 0, shverb);
1773 /* Tries to generate a new name for a verb that looks
1774 * like "verb (%x)", where %x is an integer in range of [0;255).
1775 * On success puts new verb (and new verb displayname) into
1776 * @new_verb and @new_displayname and return TRUE.
1777 * On failure puts NULL into both and returns FALSE.
1780 generate_new_verb_name (GPtrArray *verbs,
1781 const gunichar2 *verb,
1782 const gchar *verb_displayname,
1783 gunichar2 **new_verb,
1784 gchar **new_displayname)
1787 GWin32AppInfoShellVerb *shverb;
1788 gsize orig_len = g_utf16_len (verb);
1789 gsize new_verb_name_len = orig_len + strlen (" ()") + 2 + 1;
1790 gunichar2 *new_verb_name = g_new (gunichar2, new_verb_name_len);
1793 *new_displayname = NULL;
1795 memcpy (new_verb_name, verb, orig_len * sizeof (gunichar2));
1796 for (counter = 0; counter < 255; counter++)
1798 _snwprintf (&new_verb_name[orig_len], new_verb_name_len, L" (%x)", counter);
1799 _verb_lookup (verbs, new_verb_name, &shverb);
1803 *new_verb = new_verb_name;
1804 if (verb_displayname != NULL)
1805 *new_displayname = g_strdup_printf ("%s (%zx)", verb_displayname, counter);
1815 app_add_verb (gpointer handler_data1,
1816 gpointer handler_data2,
1817 const gunichar2 *verb,
1818 const gunichar2 *command_line,
1819 const gchar *command_line_utf8,
1820 const gchar *verb_displayname,
1821 gboolean verb_is_preferred,
1822 gboolean invent_new_verb_name)
1824 gunichar2 *new_verb = NULL;
1825 gchar *new_displayname = NULL;
1826 GWin32AppInfoApplication *app_rec = (GWin32AppInfoApplication *) handler_data2;
1827 GWin32AppInfoShellVerb *shverb;
1829 _verb_lookup (app_rec->verbs, verb, &shverb);
1831 /* Special logic for fake apps - do our best to
1832 * collate all possible verbs in the app,
1833 * including the verbs that have the same name but
1834 * different commandlines, in which case a new
1835 * verb name has to be invented.
1841 if (!invent_new_verb_name)
1844 for (vi = 0; vi < app_rec->verbs->len; vi++)
1846 GWin32AppInfoShellVerb *app_verb;
1848 app_verb = _verb_idx (app_rec->verbs, vi);
1850 if (_wcsicmp (command_line, app_verb->command) == 0)
1854 if (vi < app_rec->verbs->len ||
1855 !generate_new_verb_name (app_rec->verbs,
1863 shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
1864 if (new_verb == NULL)
1865 shverb->verb_name = g_wcsdup (verb, -1);
1867 shverb->verb_name = g_steal_pointer (&new_verb);
1868 if (new_displayname == NULL)
1869 shverb->verb_displayname = g_strdup (verb_displayname);
1871 shverb->verb_displayname = g_steal_pointer (&new_displayname);
1873 shverb->command = g_wcsdup (command_line, -1);
1874 shverb->command_utf8 = g_strdup (command_line_utf8);
1875 shverb->app = g_object_ref (app_rec);
1877 _g_win32_extract_executable (shverb->command,
1878 &shverb->executable,
1879 &shverb->executable_basename,
1880 &shverb->executable_folded,
1882 &shverb->dll_function);
1884 if (shverb->dll_function != NULL)
1885 _g_win32_fixup_broken_microsoft_rundll_commandline (shverb->command);
1887 if (!verb_is_preferred)
1888 g_ptr_array_add (app_rec->verbs, shverb);
1890 g_ptr_array_insert (app_rec->verbs, 0, shverb);
1894 uwp_app_add_verb (GWin32AppInfoApplication *app_rec,
1895 const gunichar2 *verb,
1896 const gchar *verb_displayname)
1898 GWin32AppInfoShellVerb *shverb;
1900 _verb_lookup (app_rec->verbs, verb, &shverb);
1905 shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
1906 shverb->verb_name = g_wcsdup (verb, -1);
1907 shverb->app = g_object_ref (app_rec);
1908 shverb->verb_displayname = g_strdup (verb_displayname);
1910 shverb->is_uwp = TRUE;
1912 /* Strictly speaking, this is unnecessary, but
1913 * let's make it clear that UWP verbs have no
1914 * commands and executables.
1916 shverb->command = NULL;
1917 shverb->command_utf8 = NULL;
1918 shverb->executable = NULL;
1919 shverb->executable_basename = NULL;
1920 shverb->executable_folded = NULL;
1921 shverb->dll_function = NULL;
1923 g_ptr_array_add (app_rec->verbs, shverb);
1927 uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec,
1928 GWin32AppInfoApplication *app,
1929 const gunichar2 *verb,
1930 const gchar *verb_displayname,
1931 gboolean verb_is_preferred)
1933 GWin32AppInfoShellVerb *shverb;
1935 _verb_lookup (handler_rec->verbs, verb, &shverb);
1940 shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
1941 shverb->verb_name = g_wcsdup (verb, -1);
1942 shverb->verb_displayname = g_strdup (verb_displayname);
1944 shverb->is_uwp = TRUE;
1947 shverb->app = g_object_ref (app);
1949 shverb->command = NULL;
1950 shverb->command_utf8 = NULL;
1951 shverb->executable = NULL;
1952 shverb->executable_basename = NULL;
1953 shverb->executable_folded = NULL;
1954 shverb->dll_function = NULL;
1956 if (!verb_is_preferred)
1957 g_ptr_array_add (handler_rec->verbs, shverb);
1959 g_ptr_array_insert (handler_rec->verbs, 0, shverb);
1962 /* Looks up a file extension object identified by
1963 * @ext_u8_folded in the extensions hash table.
1964 * If such object doesn't exist,
1965 * creates it and puts it into the extensions hash table.
1966 * Returns the object.
1968 static GWin32AppInfoFileExtension *
1969 get_ext_object (const gunichar2 *ext,
1970 const gchar *ext_u8,
1971 const gchar *ext_u8_folded)
1973 GWin32AppInfoFileExtension *file_extn;
1975 if (g_hash_table_lookup_extended (extensions,
1978 (void **) &file_extn))
1981 file_extn = g_object_new (G_TYPE_WIN32_APPINFO_FILE_EXTENSION, NULL);
1982 file_extn->extension = g_wcsdup (ext, -1);
1983 file_extn->extension_u8 = g_strdup (ext_u8);
1984 g_hash_table_insert (extensions, g_strdup (ext_u8_folded), file_extn);
1989 /* Iterates over HKCU\\Software\\Clients or HKLM\\Software\\Clients,
1990 * (depending on @user_registry being TRUE or FALSE),
1991 * collecting applications listed there.
1992 * Puts the path to the client key for each client into @priority_capable_apps
1993 * (only for clients with file or URL associations).
1996 collect_capable_apps_from_clients (GPtrArray *capable_apps,
1997 GPtrArray *priority_capable_apps,
1998 gboolean user_registry)
2000 GWin32RegistryKey *clients;
2001 GWin32RegistrySubkeyIter clients_iter;
2003 const gunichar2 *client_type_name;
2004 gsize client_type_name_len;
2009 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
2013 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
2016 if (clients == NULL)
2019 if (!g_win32_registry_subkey_iter_init (&clients_iter, clients, NULL))
2021 g_object_unref (clients);
2025 while (g_win32_registry_subkey_iter_next (&clients_iter, TRUE, NULL))
2027 GWin32RegistrySubkeyIter subkey_iter;
2028 GWin32RegistryKey *system_client_type;
2029 GWin32RegistryValueType default_type;
2030 gunichar2 *default_value = NULL;
2031 const gunichar2 *client_name;
2032 gsize client_name_len;
2034 if (!g_win32_registry_subkey_iter_get_name_w (&clients_iter,
2036 &client_type_name_len,
2040 system_client_type = g_win32_registry_key_get_child_w (clients,
2044 if (system_client_type == NULL)
2047 if (g_win32_registry_key_get_value_w (system_client_type,
2052 (gpointer *) &default_value,
2056 if (default_type != G_WIN32_REGISTRY_VALUE_STR ||
2057 default_value[0] == L'\0')
2058 g_clear_pointer (&default_value, g_free);
2061 if (!g_win32_registry_subkey_iter_init (&subkey_iter,
2065 g_clear_pointer (&default_value, g_free);
2066 g_object_unref (system_client_type);
2070 while (g_win32_registry_subkey_iter_next (&subkey_iter, TRUE, NULL))
2072 GWin32RegistryKey *system_client;
2073 GWin32RegistryKey *system_client_assoc;
2077 if (!g_win32_registry_subkey_iter_get_name_w (&subkey_iter,
2083 system_client = g_win32_registry_key_get_child_w (system_client_type,
2087 if (system_client == NULL)
2092 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
2093 L"Capabilities\\FileAssociations",
2096 if (system_client_assoc != NULL)
2099 g_object_unref (system_client_assoc);
2103 system_client_assoc = g_win32_registry_key_get_child_w (system_client,
2104 L"Capabilities\\UrlAssociations",
2107 if (system_client_assoc != NULL)
2110 g_object_unref (system_client_assoc);
2116 keyname = g_wcsdup (g_win32_registry_key_get_path_w (system_client), -1);
2118 if (default_value && wcscmp (default_value, client_name) == 0)
2119 g_ptr_array_add (priority_capable_apps, keyname);
2121 g_ptr_array_add (capable_apps, keyname);
2124 g_object_unref (system_client);
2127 g_win32_registry_subkey_iter_clear (&subkey_iter);
2128 g_clear_pointer (&default_value, g_free);
2129 g_object_unref (system_client_type);
2132 g_win32_registry_subkey_iter_clear (&clients_iter);
2133 g_object_unref (clients);
2136 /* Iterates over HKCU\\Software\\RegisteredApplications or HKLM\\Software\\RegisteredApplications,
2137 * (depending on @user_registry being TRUE or FALSE),
2138 * collecting applications listed there.
2139 * Puts the path to the app key for each app into @capable_apps.
2142 collect_capable_apps_from_registered_apps (GPtrArray *capable_apps,
2143 gboolean user_registry)
2145 GWin32RegistryValueIter iter;
2146 const gunichar2 *reg_path;
2148 gunichar2 *value_data;
2149 gsize value_data_size;
2150 GWin32RegistryValueType value_type;
2151 GWin32RegistryKey *registered_apps;
2154 reg_path = L"HKEY_CURRENT_USER\\Software\\RegisteredApplications";
2156 reg_path = L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications";
2159 g_win32_registry_key_new_w (reg_path, NULL);
2161 if (!registered_apps)
2164 if (!g_win32_registry_value_iter_init (&iter, registered_apps, NULL))
2166 g_object_unref (registered_apps);
2171 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2173 gunichar2 possible_location[REG_PATH_MAX_SIZE + 1];
2174 GWin32RegistryKey *location;
2177 if ((!g_win32_registry_value_iter_get_value_type (&iter,
2180 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
2181 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2182 (void **) &value_data,
2185 (value_data_size < sizeof (gunichar2)) ||
2186 (value_data[0] == L'\0'))
2189 if (!build_registry_path (possible_location, sizeof (possible_location),
2190 user_registry ? HKCU : HKLM, value_data, NULL))
2193 location = g_win32_registry_key_new_w (possible_location, NULL);
2195 if (location == NULL)
2198 p = wcsrchr (possible_location, L'\\');
2203 g_ptr_array_add (capable_apps, g_wcsdup (possible_location, -1));
2206 g_object_unref (location);
2209 g_win32_registry_value_iter_clear (&iter);
2210 g_object_unref (registered_apps);
2213 /* Looks up an app object identified by
2214 * @canonical_name_folded in the @app_hashmap.
2215 * If such object doesn't exist,
2216 * creates it and puts it into the @app_hashmap.
2217 * Returns the object.
2219 static GWin32AppInfoApplication *
2220 get_app_object (GHashTable *app_hashmap,
2221 const gunichar2 *canonical_name,
2222 const gchar *canonical_name_u8,
2223 const gchar *canonical_name_folded,
2224 gboolean user_specific,
2225 gboolean default_app,
2228 GWin32AppInfoApplication *app;
2230 app = g_hash_table_lookup (app_hashmap, canonical_name_folded);
2235 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
2236 app->canonical_name = g_wcsdup (canonical_name, -1);
2237 app->canonical_name_u8 = g_strdup (canonical_name_u8);
2238 app->canonical_name_folded = g_strdup (canonical_name_folded);
2239 app->no_open_with = FALSE;
2240 app->user_specific = user_specific;
2241 app->default_app = default_app;
2242 app->is_uwp = is_uwp;
2243 g_hash_table_insert (app_hashmap,
2244 g_strdup (canonical_name_folded),
2250 /* Grabs an application that has Capabilities.
2251 * @app_key_path is the path to the application key
2252 * (which must have a "Capabilities" subkey).
2253 * @default_app is TRUE if the app has priority
2256 read_capable_app (const gunichar2 *app_key_path,
2257 gboolean user_specific,
2258 gboolean default_app)
2260 GWin32AppInfoApplication *app;
2261 gchar *canonical_name_u8 = NULL;
2262 gchar *canonical_name_folded = NULL;
2263 gchar *app_key_path_u8 = NULL;
2264 gchar *app_key_path_u8_folded = NULL;
2265 GWin32RegistryKey *appkey = NULL;
2266 gunichar2 *fallback_friendly_name;
2267 GWin32RegistryValueType vtype;
2269 gunichar2 *friendly_name;
2270 gunichar2 *description;
2271 gunichar2 *narrow_application_name;
2272 gunichar2 *icon_source;
2273 GWin32RegistryKey *capabilities;
2274 GWin32RegistryKey *default_icon_key;
2275 GWin32RegistryKey *associations;
2276 const reg_verb *preferred_verb;
2277 GList *verbs = NULL;
2278 gboolean verbs_in_root_key = TRUE;
2281 capabilities = NULL;
2283 if (!g_utf16_to_utf8_and_fold (app_key_path,
2286 &canonical_name_folded) ||
2287 !g_utf16_to_utf8_and_fold (app_key_path,
2290 &app_key_path_u8_folded) ||
2291 (appkey = g_win32_registry_key_new_w (app_key_path, NULL)) == NULL ||
2292 (capabilities = g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL)) == NULL ||
2293 !(get_verbs (appkey, &preferred_verb, &verbs, L"", L"Shell", NULL) ||
2294 (verbs_in_root_key = FALSE) ||
2295 get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell", NULL)))
2297 g_clear_pointer (&canonical_name_u8, g_free);
2298 g_clear_pointer (&canonical_name_folded, g_free);
2299 g_clear_object (&appkey);
2300 g_clear_object (&capabilities);
2301 g_clear_pointer (&app_key_path_u8, g_free);
2302 g_clear_pointer (&app_key_path_u8_folded, g_free);
2307 app = get_app_object (apps_by_id,
2310 canonical_name_folded,
2315 process_verbs_commands (g_steal_pointer (&verbs),
2317 L"", /* [ab]use the fact that two strings are simply concatenated */
2318 verbs_in_root_key ? app_key_path : g_win32_registry_key_get_path_w (capabilities),
2324 fallback_friendly_name = NULL;
2325 success = g_win32_registry_key_get_value_w (appkey,
2330 (void **) &fallback_friendly_name,
2334 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2335 g_clear_pointer (&fallback_friendly_name, g_free);
2337 if (fallback_friendly_name &&
2338 app->pretty_name == NULL)
2340 app->pretty_name = g_wcsdup (fallback_friendly_name, -1);
2341 g_clear_pointer (&app->pretty_name_u8, g_free);
2342 app->pretty_name_u8 = g_utf16_to_utf8 (fallback_friendly_name,
2349 friendly_name = NULL;
2350 success = g_win32_registry_key_get_value_w (capabilities,
2351 g_win32_registry_get_os_dirs_w (),
2355 (void **) &friendly_name,
2360 vtype != G_WIN32_REGISTRY_VALUE_STR)
2361 g_clear_pointer (&friendly_name, g_free);
2363 if (friendly_name &&
2364 app->localized_pretty_name == NULL)
2366 app->localized_pretty_name = g_wcsdup (friendly_name, -1);
2367 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2368 app->localized_pretty_name_u8 = g_utf16_to_utf8 (friendly_name,
2376 success = g_win32_registry_key_get_value_w (capabilities,
2377 g_win32_registry_get_os_dirs_w (),
2379 L"ApplicationDescription",
2381 (void **) &description,
2385 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2386 g_clear_pointer (&description, g_free);
2388 if (description && app->description == NULL)
2390 app->description = g_wcsdup (description, -1);
2391 g_clear_pointer (&app->description_u8, g_free);
2392 app->description_u8 = g_utf16_to_utf8 (description, -1, NULL, NULL, NULL);
2395 default_icon_key = g_win32_registry_key_get_child_w (appkey,
2401 if (default_icon_key != NULL)
2403 success = g_win32_registry_key_get_value_w (default_icon_key,
2408 (void **) &icon_source,
2413 vtype != G_WIN32_REGISTRY_VALUE_STR)
2414 g_clear_pointer (&icon_source, g_free);
2416 g_object_unref (default_icon_key);
2419 if (icon_source == NULL)
2421 success = g_win32_registry_key_get_value_w (capabilities,
2426 (void **) &icon_source,
2431 vtype != G_WIN32_REGISTRY_VALUE_STR)
2432 g_clear_pointer (&icon_source, g_free);
2438 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
2439 app->icon = g_themed_icon_new (name);
2443 narrow_application_name = NULL;
2444 success = g_win32_registry_key_get_value_w (capabilities,
2445 g_win32_registry_get_os_dirs_w (),
2449 (void **) &narrow_application_name,
2453 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2454 g_clear_pointer (&narrow_application_name, g_free);
2456 if (narrow_application_name &&
2457 app->localized_pretty_name == NULL)
2459 app->localized_pretty_name = g_wcsdup (narrow_application_name, -1);
2460 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2461 app->localized_pretty_name_u8 = g_utf16_to_utf8 (narrow_application_name,
2468 associations = g_win32_registry_key_get_child_w (capabilities,
2469 L"FileAssociations",
2472 if (associations != NULL)
2474 GWin32RegistryValueIter iter;
2476 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
2478 gunichar2 *file_extension;
2479 gunichar2 *extension_handler;
2480 gsize file_extension_len;
2481 gsize extension_handler_size;
2482 GWin32RegistryValueType value_type;
2484 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2486 if ((!g_win32_registry_value_iter_get_value_type (&iter,
2489 (value_type != G_WIN32_REGISTRY_VALUE_STR) ||
2490 (!g_win32_registry_value_iter_get_name_w (&iter,
2492 &file_extension_len,
2494 (file_extension_len <= 0) ||
2495 (file_extension[0] != L'.') ||
2496 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2497 (void **) &extension_handler,
2498 &extension_handler_size,
2500 (extension_handler_size < sizeof (gunichar2)) ||
2501 (extension_handler[0] == L'\0'))
2504 get_file_ext (extension_handler, file_extension, app, FALSE);
2507 g_win32_registry_value_iter_clear (&iter);
2510 g_object_unref (associations);
2513 associations = g_win32_registry_key_get_child_w (capabilities, L"URLAssociations", NULL);
2515 if (associations != NULL)
2517 GWin32RegistryValueIter iter;
2519 if (g_win32_registry_value_iter_init (&iter, associations, NULL))
2521 gunichar2 *url_schema;
2522 gunichar2 *schema_handler;
2523 gsize url_schema_len;
2524 gsize schema_handler_size;
2525 GWin32RegistryValueType value_type;
2527 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2530 gchar *schema_u8_folded;
2532 if ((!g_win32_registry_value_iter_get_value_type (&iter,
2535 ((value_type != G_WIN32_REGISTRY_VALUE_STR) &&
2536 (value_type != G_WIN32_REGISTRY_VALUE_EXPAND_STR)) ||
2537 (!g_win32_registry_value_iter_get_name_w (&iter,
2541 (url_schema_len <= 0) ||
2542 (url_schema[0] == L'\0') ||
2543 (!g_win32_registry_value_iter_get_data_w (&iter, TRUE,
2544 (void **) &schema_handler,
2545 &schema_handler_size,
2547 (schema_handler_size < sizeof (gunichar2)) ||
2548 (schema_handler[0] == L'\0'))
2553 if (g_utf16_to_utf8_and_fold (url_schema,
2557 get_url_association (schema_handler, url_schema, schema_u8, schema_u8_folded, app, FALSE);
2559 g_clear_pointer (&schema_u8, g_free);
2560 g_clear_pointer (&schema_u8_folded, g_free);
2563 g_win32_registry_value_iter_clear (&iter);
2566 g_object_unref (associations);
2569 g_clear_pointer (&fallback_friendly_name, g_free);
2570 g_clear_pointer (&description, g_free);
2571 g_clear_pointer (&icon_source, g_free);
2572 g_clear_pointer (&narrow_application_name, g_free);
2574 g_object_unref (appkey);
2575 g_object_unref (capabilities);
2576 g_clear_pointer (&app_key_path_u8, g_free);
2577 g_clear_pointer (&app_key_path_u8_folded, g_free);
2578 g_clear_pointer (&canonical_name_u8, g_free);
2579 g_clear_pointer (&canonical_name_folded, g_free);
2582 /* Iterates over subkeys in HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
2583 * and calls get_url_association() for each one that has a user-chosen handler.
2586 read_urls (GWin32RegistryKey *url_associations)
2588 GWin32RegistrySubkeyIter url_iter;
2590 if (url_associations == NULL)
2593 if (!g_win32_registry_subkey_iter_init (&url_iter, url_associations, NULL))
2596 while (g_win32_registry_subkey_iter_next (&url_iter, TRUE, NULL))
2598 gchar *schema_u8 = NULL;
2599 gchar *schema_u8_folded = NULL;
2600 const gunichar2 *url_schema = NULL;
2601 gunichar2 *program_id = NULL;
2602 GWin32RegistryKey *user_choice = NULL;
2603 gsize url_schema_len;
2604 GWin32RegistryValueType val_type;
2606 if (g_win32_registry_subkey_iter_get_name_w (&url_iter,
2610 g_utf16_to_utf8_and_fold (url_schema,
2613 &schema_u8_folded) &&
2614 (user_choice = _g_win32_registry_key_build_and_new_w (NULL, URL_ASSOCIATIONS,
2615 url_schema, USER_CHOICE,
2617 g_win32_registry_key_get_value_w (user_choice,
2622 (void **) &program_id,
2625 val_type == G_WIN32_REGISTRY_VALUE_STR)
2626 get_url_association (program_id, url_schema, schema_u8, schema_u8_folded, NULL, TRUE);
2628 g_clear_pointer (&program_id, g_free);
2629 g_clear_pointer (&user_choice, g_object_unref);
2630 g_clear_pointer (&schema_u8, g_free);
2631 g_clear_pointer (&schema_u8_folded, g_free);
2634 g_win32_registry_subkey_iter_clear (&url_iter);
2637 /* Reads an application that is only registered by the basename of its
2638 * executable (and doesn't have Capabilities subkey).
2639 * @incapable_app is the registry key for the app.
2640 * @app_exe_basename is the basename of its executable.
2643 read_incapable_app (GWin32RegistryKey *incapable_app,
2644 const gunichar2 *app_exe_basename,
2645 const gchar *app_exe_basename_u8,
2646 const gchar *app_exe_basename_u8_folded)
2648 GWin32RegistryValueIter sup_iter;
2649 GWin32AppInfoApplication *app;
2651 const reg_verb *preferred_verb;
2652 gunichar2 *friendly_app_name;
2654 GWin32RegistryValueType vtype;
2655 gboolean no_open_with;
2656 GWin32RegistryKey *default_icon_key;
2657 gunichar2 *icon_source;
2659 GWin32RegistryKey *supported_key;
2661 if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell", NULL))
2664 app = get_app_object (apps_by_exe,
2666 app_exe_basename_u8,
2667 app_exe_basename_u8_folded,
2672 process_verbs_commands (g_steal_pointer (&verbs),
2674 L"HKEY_CLASSES_ROOT\\Applications\\",
2681 friendly_app_name = NULL;
2682 success = g_win32_registry_key_get_value_w (incapable_app,
2683 g_win32_registry_get_os_dirs_w (),
2687 (void **) &friendly_app_name,
2691 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2692 g_clear_pointer (&friendly_app_name, g_free);
2694 no_open_with = g_win32_registry_key_get_value_w (incapable_app,
2704 g_win32_registry_key_get_child_w (incapable_app,
2710 if (default_icon_key != NULL)
2713 g_win32_registry_key_get_value_w (default_icon_key,
2718 (void **) &icon_source,
2722 if (success && vtype != G_WIN32_REGISTRY_VALUE_STR)
2723 g_clear_pointer (&icon_source, g_free);
2725 g_object_unref (default_icon_key);
2730 gchar *name = g_utf16_to_utf8 (icon_source, -1, NULL, NULL, NULL);
2732 icon = g_themed_icon_new (name);
2736 app->no_open_with = no_open_with;
2738 if (friendly_app_name &&
2739 app->localized_pretty_name == NULL)
2741 app->localized_pretty_name = g_wcsdup (friendly_app_name, -1);
2742 g_clear_pointer (&app->localized_pretty_name_u8, g_free);
2743 app->localized_pretty_name_u8 =
2744 g_utf16_to_utf8 (friendly_app_name, -1, NULL, NULL, NULL);
2747 if (icon && app->icon == NULL)
2748 app->icon = g_object_ref (icon);
2751 g_win32_registry_key_get_child_w (incapable_app,
2755 if (supported_key &&
2756 g_win32_registry_value_iter_init (&sup_iter, supported_key, NULL))
2758 gunichar2 *ext_name;
2761 while (g_win32_registry_value_iter_next (&sup_iter, TRUE, NULL))
2763 if ((!g_win32_registry_value_iter_get_name_w (&sup_iter,
2767 (ext_name_len <= 0) ||
2768 (ext_name[0] != L'.'))
2771 get_file_ext (ext_name, ext_name, app, FALSE);
2774 g_win32_registry_value_iter_clear (&sup_iter);
2777 g_clear_object (&supported_key);
2778 g_free (friendly_app_name);
2779 g_free (icon_source);
2781 g_clear_object (&icon);
2784 /* Iterates over subkeys of HKEY_CLASSES_ROOT\\Applications
2785 * and calls read_incapable_app() for each one.
2790 GWin32RegistryKey *applications_key;
2791 GWin32RegistrySubkeyIter app_iter;
2794 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications", NULL);
2796 if (applications_key == NULL)
2799 if (!g_win32_registry_subkey_iter_init (&app_iter, applications_key, NULL))
2801 g_object_unref (applications_key);
2805 while (g_win32_registry_subkey_iter_next (&app_iter, TRUE, NULL))
2807 const gunichar2 *app_exe_basename;
2808 gsize app_exe_basename_len;
2809 GWin32RegistryKey *incapable_app;
2810 gchar *app_exe_basename_u8;
2811 gchar *app_exe_basename_u8_folded;
2813 if (!g_win32_registry_subkey_iter_get_name_w (&app_iter,
2815 &app_exe_basename_len,
2817 !g_utf16_to_utf8_and_fold (app_exe_basename,
2818 app_exe_basename_len,
2819 &app_exe_basename_u8,
2820 &app_exe_basename_u8_folded))
2824 g_win32_registry_key_get_child_w (applications_key,
2828 if (incapable_app != NULL)
2829 read_incapable_app (incapable_app,
2831 app_exe_basename_u8,
2832 app_exe_basename_u8_folded);
2834 g_clear_object (&incapable_app);
2835 g_clear_pointer (&app_exe_basename_u8, g_free);
2836 g_clear_pointer (&app_exe_basename_u8_folded, g_free);
2839 g_win32_registry_subkey_iter_clear (&app_iter);
2840 g_object_unref (applications_key);
2843 /* Iterates over subkeys of HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\
2844 * and calls get_file_ext() for each associated handler
2845 * (starting with user-chosen handler, if any)
2848 read_exts (GWin32RegistryKey *file_exts)
2850 GWin32RegistrySubkeyIter ext_iter;
2851 const gunichar2 *file_extension;
2852 gsize file_extension_len;
2854 if (file_exts == NULL)
2857 if (!g_win32_registry_subkey_iter_init (&ext_iter, file_exts, NULL))
2860 while (g_win32_registry_subkey_iter_next (&ext_iter, TRUE, NULL))
2862 GWin32RegistryKey *open_with_progids;
2863 gunichar2 *program_id;
2864 GWin32RegistryValueIter iter;
2865 gunichar2 *value_name;
2866 gsize value_name_len;
2867 GWin32RegistryValueType value_type;
2868 GWin32RegistryKey *user_choice;
2870 if (!g_win32_registry_subkey_iter_get_name_w (&ext_iter,
2872 &file_extension_len,
2877 user_choice = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS, file_extension,
2880 g_win32_registry_key_get_value_w (user_choice,
2885 (void **) &program_id,
2888 value_type == G_WIN32_REGISTRY_VALUE_STR)
2890 /* Note: program_id could be "ProgramID" or "Applications\\program.exe".
2891 * The code still works, but handler_id might have a backslash
2892 * in it - that might trip us up later on.
2893 * Even though in that case this is logically an "application"
2894 * registry entry, we don't treat it in any special way.
2895 * We do scan that registry branch anyway, just not here.
2897 get_file_ext (program_id, file_extension, NULL, TRUE);
2900 g_clear_object (&user_choice);
2901 g_clear_pointer (&program_id, g_free);
2903 open_with_progids = _g_win32_registry_key_build_and_new_w (NULL, FILE_EXTS,
2908 if (open_with_progids == NULL)
2911 if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
2913 g_clear_object (&open_with_progids);
2917 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2919 if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
2922 (value_name_len == 0))
2925 get_file_ext (value_name, file_extension, NULL, FALSE);
2928 g_win32_registry_value_iter_clear (&iter);
2929 g_clear_object (&open_with_progids);
2932 g_win32_registry_subkey_iter_clear (&ext_iter);
2935 /* Iterates over subkeys in HKCR, calls
2936 * get_file_ext() for any subkey that starts with ".",
2937 * or get_url_association() for any subkey that could
2938 * be a URL schema and has a "URL Protocol" value.
2941 read_classes (GWin32RegistryKey *classes_root)
2943 GWin32RegistrySubkeyIter class_iter;
2944 const gunichar2 *class_name;
2945 gsize class_name_len;
2947 if (classes_root == NULL)
2950 if (!g_win32_registry_subkey_iter_init (&class_iter, classes_root, NULL))
2953 while (g_win32_registry_subkey_iter_next (&class_iter, TRUE, NULL))
2955 if ((!g_win32_registry_subkey_iter_get_name_w (&class_iter,
2959 (class_name_len <= 1))
2962 if (class_name[0] == L'.')
2964 GWin32RegistryKey *class_key;
2965 GWin32RegistryValueIter iter;
2966 GWin32RegistryKey *open_with_progids;
2967 gunichar2 *value_name;
2968 gsize value_name_len;
2970 /* Read the data from the HKCR\\.ext (usually proxied
2971 * to another HKCR subkey)
2973 get_file_ext (class_name, class_name, NULL, FALSE);
2975 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
2977 if (class_key == NULL)
2980 open_with_progids = g_win32_registry_key_get_child_w (class_key, L"OpenWithProgids", NULL);
2981 g_clear_object (&class_key);
2983 if (open_with_progids == NULL)
2986 if (!g_win32_registry_value_iter_init (&iter, open_with_progids, NULL))
2988 g_clear_object (&open_with_progids);
2992 /* Read the data for other handlers for this extension */
2993 while (g_win32_registry_value_iter_next (&iter, TRUE, NULL))
2995 if (!g_win32_registry_value_iter_get_name_w (&iter, &value_name,
2998 (value_name_len == 0))
3001 get_file_ext (value_name, class_name, NULL, FALSE);
3004 g_win32_registry_value_iter_clear (&iter);
3005 g_clear_object (&open_with_progids);
3010 GWin32RegistryKey *class_key;
3012 GWin32RegistryValueType vtype;
3014 gchar *schema_u8_folded;
3016 for (i = 0; i < class_name_len; i++)
3017 if (!iswalpha (class_name[i]))
3020 if (i != class_name_len)
3023 class_key = g_win32_registry_key_get_child_w (classes_root, class_name, NULL);
3025 if (class_key == NULL)
3028 success = g_win32_registry_key_get_value_w (class_key,
3036 g_clear_object (&class_key);
3039 vtype != G_WIN32_REGISTRY_VALUE_STR)
3042 if (!g_utf16_to_utf8_and_fold (class_name, -1, &schema_u8, &schema_u8_folded))
3045 get_url_association (class_name, class_name, schema_u8, schema_u8_folded, NULL, FALSE);
3047 g_clear_pointer (&schema_u8, g_free);
3048 g_clear_pointer (&schema_u8_folded, g_free);
3052 g_win32_registry_subkey_iter_clear (&class_iter);
3055 /* Iterates over all handlers and over all apps,
3056 * and links handler verbs to apps if a handler
3057 * runs the same executable as one of the app verbs.
3060 link_handlers_to_unregistered_apps (void)
3062 GHashTableIter iter;
3063 GHashTableIter app_iter;
3064 GWin32AppInfoHandler *handler;
3065 gchar *handler_id_fld;
3066 GWin32AppInfoApplication *app;
3067 gchar *canonical_name_fld;
3068 gchar *appexe_fld_basename;
3070 g_hash_table_iter_init (&iter, handlers);
3071 while (g_hash_table_iter_next (&iter,
3072 (gpointer *) &handler_id_fld,
3073 (gpointer *) &handler))
3077 if (handler->uwp_aumid != NULL)
3080 for (vi = 0; vi < handler->verbs->len; vi++)
3082 GWin32AppInfoShellVerb *handler_verb;
3083 const gchar *handler_exe_basename;
3088 ERROR_GETTING_SH_INFO,
3089 } have_stat_handler = SH_UNKNOWN;
3090 GWin32PrivateStat handler_verb_exec_info;
3092 handler_verb = _verb_idx (handler->verbs, vi);
3094 if (handler_verb->app != NULL)
3097 handler_exe_basename = g_utf8_find_basename (handler_verb->executable_folded, -1);
3098 g_hash_table_iter_init (&app_iter, apps_by_id);
3100 while (g_hash_table_iter_next (&app_iter,
3101 (gpointer *) &canonical_name_fld,
3104 GWin32AppInfoShellVerb *app_verb;
3110 for (ai = 0; ai < app->verbs->len; ai++)
3112 GWin32PrivateStat app_verb_exec_info;
3113 const gchar *app_exe_basename;
3114 app_verb = _verb_idx (app->verbs, ai);
3116 app_exe_basename = g_utf8_find_basename (app_verb->executable_folded, -1);
3118 /* First check that the executable paths are identical */
3119 if (g_strcmp0 (app_verb->executable_folded, handler_verb->executable_folded) != 0)
3121 /* If not, check the basenames. If they are different, don't bother
3122 * with further checks.
3124 if (g_strcmp0 (app_exe_basename, handler_exe_basename) != 0)
3127 /* Get filesystem IDs for both files.
3128 * For the handler that is attempted only once.
3130 if (have_stat_handler == SH_UNKNOWN)
3132 if (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (handler_verb->executable_folded,
3133 &handler_verb_exec_info) == 0)
3134 have_stat_handler = GOT_SH_INFO;
3136 have_stat_handler = ERROR_GETTING_SH_INFO;
3139 if (have_stat_handler != GOT_SH_INFO ||
3140 (GLIB_PRIVATE_CALL (g_win32_stat_utf8) (app_verb->executable_folded,
3141 &app_verb_exec_info) != 0) ||
3142 app_verb_exec_info.file_index != handler_verb_exec_info.file_index)
3146 handler_verb->app = g_object_ref (app);
3151 if (handler_verb->app != NULL)
3154 g_hash_table_iter_init (&app_iter, apps_by_exe);
3156 while (g_hash_table_iter_next (&app_iter,
3157 (gpointer *) &appexe_fld_basename,
3163 /* Use basename because apps_by_exe only has basenames */
3164 if (g_strcmp0 (handler_exe_basename, appexe_fld_basename) != 0)
3167 handler_verb->app = g_object_ref (app);
3174 /* Finds all .ext and schema: handler verbs that have no app linked to them,
3175 * creates a "fake app" object and links these verbs to these
3176 * objects. Objects are identified by the full path to
3177 * the executable being run, thus multiple different invocations
3178 * get grouped in a more-or-less natural way.
3179 * The iteration goes separately over .ext and schema: handlers
3180 * (instead of the global handlers hashmap) to allow us to
3181 * put the handlers into supported_urls or supported_exts as
3182 * needed (handler objects themselves have no knowledge of extensions
3183 * and/or URLs they are associated with).
3186 link_handlers_to_fake_apps (void)
3188 GHashTableIter iter;
3189 GHashTableIter handler_iter;
3190 gchar *extension_utf8_folded;
3191 GWin32AppInfoFileExtension *file_extn;
3192 gchar *handler_id_fld;
3193 GWin32AppInfoHandler *handler;
3194 gchar *url_utf8_folded;
3195 GWin32AppInfoURLSchema *schema;
3197 g_hash_table_iter_init (&iter, extensions);
3198 while (g_hash_table_iter_next (&iter,
3199 (gpointer *) &extension_utf8_folded,
3200 (gpointer *) &file_extn))
3202 g_hash_table_iter_init (&handler_iter, file_extn->handlers);
3203 while (g_hash_table_iter_next (&handler_iter,
3204 (gpointer *) &handler_id_fld,
3205 (gpointer *) &handler))
3209 if (handler->uwp_aumid != NULL)
3212 for (vi = 0; vi < handler->verbs->len; vi++)
3214 GWin32AppInfoShellVerb *handler_verb;
3215 GWin32AppInfoApplication *app;
3216 gunichar2 *exename_utf16;
3217 handler_verb = _verb_idx (handler->verbs, vi);
3219 if (handler_verb->app != NULL)
3222 exename_utf16 = g_utf8_to_utf16 (handler_verb->executable, -1, NULL, NULL, NULL);
3223 if (exename_utf16 == NULL)
3226 app = get_app_object (fake_apps,
3228 handler_verb->executable,
3229 handler_verb->executable_folded,
3233 g_clear_pointer (&exename_utf16, g_free);
3234 handler_verb->app = g_object_ref (app);
3238 handler_verb->verb_name,
3239 handler_verb->command,
3240 handler_verb->command_utf8,
3241 handler_verb->verb_displayname,
3244 g_hash_table_insert (app->supported_exts,
3245 g_strdup (extension_utf8_folded),
3246 g_object_ref (handler));
3251 g_hash_table_iter_init (&iter, urls);
3252 while (g_hash_table_iter_next (&iter,
3253 (gpointer *) &url_utf8_folded,
3254 (gpointer *) &schema))
3256 g_hash_table_iter_init (&handler_iter, schema->handlers);
3257 while (g_hash_table_iter_next (&handler_iter,
3258 (gpointer *) &handler_id_fld,
3259 (gpointer *) &handler))
3263 if (handler->uwp_aumid != NULL)
3266 for (vi = 0; vi < handler->verbs->len; vi++)
3268 GWin32AppInfoShellVerb *handler_verb;
3269 GWin32AppInfoApplication *app;
3270 gchar *command_utf8_folded;
3271 handler_verb = _verb_idx (handler->verbs, vi);
3273 if (handler_verb->app != NULL)
3276 command_utf8_folded = g_utf8_casefold (handler_verb->command_utf8, -1);
3277 app = get_app_object (fake_apps,
3278 handler_verb->command,
3279 handler_verb->command_utf8,
3280 command_utf8_folded,
3284 g_clear_pointer (&command_utf8_folded, g_free);
3285 handler_verb->app = g_object_ref (app);
3289 handler_verb->verb_name,
3290 handler_verb->command,
3291 handler_verb->command_utf8,
3292 handler_verb->verb_displayname,
3295 g_hash_table_insert (app->supported_urls,
3296 g_strdup (url_utf8_folded),
3297 g_object_ref (handler));
3303 static GWin32AppInfoHandler *
3304 find_uwp_handler_for_ext (GWin32AppInfoFileExtension *file_extn,
3305 const gunichar2 *app_user_model_id)
3307 GHashTableIter handler_iter;
3308 gchar *handler_id_fld;
3309 GWin32AppInfoHandler *handler;
3311 g_hash_table_iter_init (&handler_iter, file_extn->handlers);
3312 while (g_hash_table_iter_next (&handler_iter,
3313 (gpointer *) &handler_id_fld,
3314 (gpointer *) &handler))
3316 if (handler->uwp_aumid == NULL)
3319 if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0)
3326 static GWin32AppInfoHandler *
3327 find_uwp_handler_for_schema (GWin32AppInfoURLSchema *schema,
3328 const gunichar2 *app_user_model_id)
3330 GHashTableIter handler_iter;
3331 gchar *handler_id_fld;
3332 GWin32AppInfoHandler *handler;
3334 g_hash_table_iter_init (&handler_iter, schema->handlers);
3335 while (g_hash_table_iter_next (&handler_iter,
3336 (gpointer *) &handler_id_fld,
3337 (gpointer *) &handler))
3339 if (handler->uwp_aumid == NULL)
3342 if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0)
3350 uwp_package_cb (gpointer user_data,
3351 const gunichar2 *full_package_name,
3352 const gunichar2 *package_name,
3353 const gunichar2 *app_user_model_id,
3354 gboolean show_in_applist,
3355 GPtrArray *supported_extgroups,
3356 GPtrArray *supported_protocols)
3358 gint i, i_verb, i_ext;
3359 gint extensions_considered;
3360 GWin32AppInfoApplication *app;
3361 gchar *app_user_model_id_u8;
3362 gchar *app_user_model_id_u8_folded;
3363 GHashTableIter iter;
3364 GWin32AppInfoHandler *ext;
3365 GWin32AppInfoHandler *url;
3367 if (!g_utf16_to_utf8_and_fold (app_user_model_id,
3369 &app_user_model_id_u8,
3370 &app_user_model_id_u8_folded))
3373 app = get_app_object (apps_by_id,
3375 app_user_model_id_u8,
3376 app_user_model_id_u8_folded,
3381 extensions_considered = 0;
3383 for (i = 0; i < supported_extgroups->len; i++)
3385 GWin32PackageExtGroup *grp = (GWin32PackageExtGroup *) g_ptr_array_index (supported_extgroups, i);
3387 extensions_considered += grp->extensions->len;
3389 for (i_ext = 0; i_ext < grp->extensions->len; i_ext++)
3391 wchar_t *ext = (wchar_t *) g_ptr_array_index (grp->extensions, i_ext);
3393 gchar *ext_u8_folded;
3394 GWin32AppInfoFileExtension *file_extn;
3395 GWin32AppInfoHandler *handler_rec;
3397 if (!g_utf16_to_utf8_and_fold (ext,
3403 file_extn = get_ext_object (ext, ext_u8, ext_u8_folded);
3405 handler_rec = find_uwp_handler_for_ext (file_extn, app_user_model_id);
3407 if (handler_rec == NULL)
3409 /* Use AppUserModelId as the ID of the new fake handler */
3410 handler_rec = get_handler_object (app_user_model_id_u8_folded,
3414 g_hash_table_insert (file_extn->handlers,
3415 g_strdup (app_user_model_id_u8_folded),
3416 g_object_ref (handler_rec));
3419 if (file_extn->chosen_handler == NULL)
3420 g_set_object (&file_extn->chosen_handler, handler_rec);
3422 /* This is somewhat wasteful, but for 100% correct handling
3423 * we need to remember which extensions (handlers) support
3424 * which verbs, and each handler gets its own copy of the
3425 * verb object, since our design is handler-centric,
3426 * not verb-centric. The app also gets a list of verbs,
3427 * but without handlers it would have no idea which
3428 * verbs can be used with which extensions.
3430 for (i_verb = 0; i_verb < grp->verbs->len; i_verb++)
3432 wchar_t *verb = NULL;
3434 verb = (wchar_t *) g_ptr_array_index (grp->verbs, i_verb);
3435 /* *_add_verb() functions are no-ops when a verb already exists,
3436 * so we're free to call them as many times as we want.
3438 uwp_handler_add_verb (handler_rec,
3445 g_hash_table_insert (app->supported_exts,
3446 g_steal_pointer (&ext_u8_folded),
3447 g_object_ref (handler_rec));
3451 g_hash_table_iter_init (&iter, app->supported_exts);
3453 /* Pile up all handler verbs into the app too,
3454 * for cases when we don't have a ref to a handler.
3456 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &ext))
3463 for (i_hverb = 0; i_hverb < ext->verbs->len; i_hverb++)
3465 GWin32AppInfoShellVerb *handler_verb;
3467 handler_verb = _verb_idx (ext->verbs, i_hverb);
3468 uwp_app_add_verb (app, handler_verb->verb_name, handler_verb->verb_displayname);
3469 if (handler_verb->app == NULL && handler_verb->is_uwp)
3470 handler_verb->app = g_object_ref (app);
3474 if (app->verbs->len == 0 && extensions_considered > 0)
3475 g_warning ("Unexpectedly, UWP app `%S' (AUMId `%s') supports %d extensions but has no verbs",
3476 full_package_name, app_user_model_id_u8, extensions_considered);
3478 for (i = 0; i < supported_protocols->len; i++)
3480 wchar_t *proto = (wchar_t *) g_ptr_array_index (supported_protocols, i);
3482 gchar *proto_u8_folded;
3483 GWin32AppInfoURLSchema *schema_rec;
3484 GWin32AppInfoHandler *handler_rec;
3486 if (!g_utf16_to_utf8_and_fold (proto,
3492 schema_rec = get_schema_object (proto,
3498 handler_rec = find_uwp_handler_for_schema (schema_rec, app_user_model_id);
3500 if (handler_rec == NULL)
3502 /* Use AppUserModelId as the ID of the new fake handler */
3503 handler_rec = get_handler_object (app_user_model_id_u8_folded,
3508 g_hash_table_insert (schema_rec->handlers,
3509 g_strdup (app_user_model_id_u8_folded),
3510 g_object_ref (handler_rec));
3513 if (schema_rec->chosen_handler == NULL)
3514 g_set_object (&schema_rec->chosen_handler, handler_rec);
3516 /* Technically, UWP apps don't use verbs for URIs,
3517 * but we only store an app field in verbs,
3518 * so each UWP URI handler has to have one.
3519 * Let's call it "open".
3521 uwp_handler_add_verb (handler_rec,
3527 g_hash_table_insert (app->supported_urls,
3528 g_steal_pointer (&proto_u8_folded),
3529 g_object_ref (handler_rec));
3532 g_hash_table_iter_init (&iter, app->supported_urls);
3534 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &url))
3541 for (i_hverb = 0; i_hverb < url->verbs->len; i_hverb++)
3543 GWin32AppInfoShellVerb *handler_verb;
3545 handler_verb = _verb_idx (url->verbs, i_hverb);
3546 uwp_app_add_verb (app, handler_verb->verb_name, handler_verb->verb_displayname);
3547 if (handler_verb->app == NULL && handler_verb->is_uwp)
3548 handler_verb->app = g_object_ref (app);
3552 g_free (app_user_model_id_u8);
3553 g_free (app_user_model_id_u8_folded);
3558 /* Calls SHLoadIndirectString() in a loop to resolve
3559 * a string in @{...} format (also supports other indirect
3560 * strings, but we aren't using it for those).
3561 * Consumes the input, but may return it unmodified
3562 * (not an indirect string). May return %NULL (the string
3563 * is indirect, but the OS failed to load it).
3566 resolve_string (gunichar2 *at_string)
3569 gunichar2 *result = NULL;
3571 /* This value is arbitrary */
3572 const gsize reasonable_size_limit = 8192;
3574 if (at_string == NULL || at_string[0] != L'@')
3577 /* In case of a no-op @at_string will be copied into the output,
3578 * buffer so allocate at least that much.
3580 result_size = wcslen (at_string) + 1;
3584 result = g_renew (gunichar2, result, result_size);
3585 /* Since there's no built-in way to detect too small buffer size,
3586 * we do so by putting a sentinel at the end of the buffer.
3587 * If it's 0 (result is always 0-terminated, even if the buffer
3588 * is too small), then try larger buffer.
3590 result[result_size - 1] = 0xff;
3591 /* This string accepts size in characters, not bytes. */
3592 hr = SHLoadIndirectString (at_string, result, result_size, NULL);
3593 if (!SUCCEEDED (hr))
3599 else if (result[result_size - 1] != 0 ||
3600 result_size >= reasonable_size_limit)
3602 /* Now that the length is known, allocate the exact amount */
3603 gunichar2 *copy = g_wcsdup (result, -1);
3612 g_assert_not_reached ();
3618 grab_registry_string (GWin32RegistryKey *handler_appkey,
3619 const gunichar2 *value_name,
3620 gunichar2 **destination,
3621 gchar **destination_u8)
3625 GWin32RegistryValueType vtype;
3626 const gunichar2 *ms_resource_prefix = L"ms-resource:";
3627 gsize ms_resource_prefix_len = wcslen (ms_resource_prefix);
3629 /* Right now this function is not used without destination,
3630 * enforce this. destination_u8 is optional.
3632 g_assert (destination != NULL);
3634 if (*destination != NULL)
3637 if (g_win32_registry_key_get_value_w (handler_appkey,
3645 vtype != G_WIN32_REGISTRY_VALUE_STR)
3646 g_clear_pointer (&value, g_free);
3648 /* There's no way for us to resolve "ms-resource:..." strings */
3649 if (value != NULL &&
3650 value_size >= ms_resource_prefix_len &&
3653 ms_resource_prefix_len * sizeof (gunichar2)) == 0)
3654 g_clear_pointer (&value, g_free);
3659 *destination = resolve_string (g_steal_pointer (&value));
3661 if (*destination == NULL)
3665 *destination_u8 = g_utf16_to_utf8 (*destination, -1, NULL, NULL, NULL);
3669 read_uwp_handler_info (void)
3671 GHashTableIter iter;
3672 GWin32RegistryKey *handler_appkey;
3675 g_hash_table_iter_init (&iter, uwp_handler_table);
3677 while (g_hash_table_iter_next (&iter, (gpointer *) &handler_appkey, (gpointer *) &aumid))
3679 gchar *aumid_u8_folded;
3680 GWin32AppInfoApplication *app;
3682 if (!g_utf16_to_utf8_and_fold (aumid,
3688 app = g_hash_table_lookup (apps_by_id, aumid_u8_folded);
3689 g_clear_pointer (&aumid_u8_folded, g_free);
3694 grab_registry_string (handler_appkey, L"ApplicationDescription", &app->description, &app->description_u8);
3695 grab_registry_string (handler_appkey, L"ApplicationName", &app->localized_pretty_name, &app->localized_pretty_name_u8);
3696 /* TODO: ApplicationIcon value (usually also @{...}) resolves into
3697 * an image (PNG only?) with implicit multiple variants (scale, size, etc).
3703 update_registry_data (void)
3706 GPtrArray *capable_apps_keys;
3707 GPtrArray *user_capable_apps_keys;
3708 GPtrArray *priority_capable_apps_keys;
3709 GWin32RegistryKey *url_associations;
3710 GWin32RegistryKey *file_exts;
3711 GWin32RegistryKey *classes_root;
3712 DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, uwp_end, postproc_end;
3713 GError *error = NULL;
3716 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3719 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3721 classes_root = g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT", NULL);
3723 capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3724 user_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3725 priority_capable_apps_keys = g_ptr_array_new_with_free_func (g_free);
3727 g_clear_pointer (&apps_by_id, g_hash_table_destroy);
3728 g_clear_pointer (&apps_by_exe, g_hash_table_destroy);
3729 g_clear_pointer (&fake_apps, g_hash_table_destroy);
3730 g_clear_pointer (&urls, g_hash_table_destroy);
3731 g_clear_pointer (&extensions, g_hash_table_destroy);
3732 g_clear_pointer (&handlers, g_hash_table_destroy);
3734 collect_start = GetTickCount ();
3735 collect_capable_apps_from_clients (capable_apps_keys,
3736 priority_capable_apps_keys,
3738 collect_capable_apps_from_clients (user_capable_apps_keys,
3739 priority_capable_apps_keys,
3741 collect_capable_apps_from_registered_apps (user_capable_apps_keys,
3743 collect_capable_apps_from_registered_apps (capable_apps_keys,
3745 collect_end = GetTickCount ();
3748 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3750 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3752 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3754 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3756 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3758 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
3760 g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_free);
3761 alloc_end = GetTickCount ();
3763 for (i = 0; i < priority_capable_apps_keys->len; i++)
3764 read_capable_app (g_ptr_array_index (priority_capable_apps_keys, i),
3767 for (i = 0; i < user_capable_apps_keys->len; i++)
3768 read_capable_app (g_ptr_array_index (user_capable_apps_keys, i),
3771 for (i = 0; i < capable_apps_keys->len; i++)
3772 read_capable_app (g_ptr_array_index (capable_apps_keys, i),
3775 capable_end = GetTickCount ();
3777 read_urls (url_associations);
3778 url_end = GetTickCount ();
3779 read_exts (file_exts);
3780 ext_end = GetTickCount ();
3782 exeapp_end = GetTickCount ();
3783 read_classes (classes_root);
3784 classes_end = GetTickCount ();
3786 if (!g_win32_package_parser_enum_packages (uwp_package_cb, NULL, &error))
3788 g_debug ("Unable to get UWP apps: %s", error->message);
3789 g_clear_error (&error);
3792 read_uwp_handler_info ();
3794 uwp_end = GetTickCount ();
3795 link_handlers_to_unregistered_apps ();
3796 link_handlers_to_fake_apps ();
3797 postproc_end = GetTickCount ();
3799 g_debug ("Collecting capable appnames: %lums\n"
3800 "Allocating hashtables:...... %lums\n"
3801 "Reading capable apps: %lums\n"
3802 "Reading URL associations:... %lums\n"
3803 "Reading extension assocs: %lums\n"
3804 "Reading exe-only apps:...... %lums\n"
3805 "Reading classes: %lums\n"
3806 "Reading UWP apps: %lums\n"
3807 "Postprocessing:..............%lums\n"
3809 collect_end - collect_start,
3810 alloc_end - collect_end,
3811 capable_end - alloc_end,
3812 url_end - capable_end,
3814 exeapp_end - ext_end,
3815 classes_end - exeapp_end,
3816 uwp_end - classes_end,
3817 postproc_end - uwp_end,
3818 postproc_end - collect_start);
3820 g_clear_object (&classes_root);
3821 g_clear_object (&url_associations);
3822 g_clear_object (&file_exts);
3823 g_ptr_array_free (capable_apps_keys, TRUE);
3824 g_ptr_array_free (user_capable_apps_keys, TRUE);
3825 g_ptr_array_free (priority_capable_apps_keys, TRUE);
3826 g_hash_table_unref (uwp_handler_table);
3831 /* This function is called when any of our registry watchers detect
3832 * changes in the registry.
3835 keys_updated (GWin32RegistryKey *key,
3838 /* Indicate the tree as not up-to-date, push a new job for the AppInfo thread */
3839 g_atomic_int_inc (&gio_win32_appinfo_update_counter);
3840 /* We don't use the data pointer, but it must be non-NULL */
3841 g_thread_pool_push (gio_win32_appinfo_threadpool, (gpointer) keys_updated, NULL);
3847 if (url_associations_key)
3848 g_win32_registry_key_watch (url_associations_key,
3850 G_WIN32_REGISTRY_WATCH_NAME |
3851 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3852 G_WIN32_REGISTRY_WATCH_VALUES,
3858 g_win32_registry_key_watch (file_exts_key,
3860 G_WIN32_REGISTRY_WATCH_NAME |
3861 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3862 G_WIN32_REGISTRY_WATCH_VALUES,
3867 if (user_clients_key)
3868 g_win32_registry_key_watch (user_clients_key,
3870 G_WIN32_REGISTRY_WATCH_NAME |
3871 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3872 G_WIN32_REGISTRY_WATCH_VALUES,
3877 if (system_clients_key)
3878 g_win32_registry_key_watch (system_clients_key,
3880 G_WIN32_REGISTRY_WATCH_NAME |
3881 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3882 G_WIN32_REGISTRY_WATCH_VALUES,
3887 if (applications_key)
3888 g_win32_registry_key_watch (applications_key,
3890 G_WIN32_REGISTRY_WATCH_NAME |
3891 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3892 G_WIN32_REGISTRY_WATCH_VALUES,
3897 if (user_registered_apps_key)
3898 g_win32_registry_key_watch (user_registered_apps_key,
3900 G_WIN32_REGISTRY_WATCH_NAME |
3901 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3902 G_WIN32_REGISTRY_WATCH_VALUES,
3907 if (system_registered_apps_key)
3908 g_win32_registry_key_watch (system_registered_apps_key,
3910 G_WIN32_REGISTRY_WATCH_NAME |
3911 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3912 G_WIN32_REGISTRY_WATCH_VALUES,
3917 if (classes_root_key)
3918 g_win32_registry_key_watch (classes_root_key,
3920 G_WIN32_REGISTRY_WATCH_NAME |
3921 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
3922 G_WIN32_REGISTRY_WATCH_VALUES,
3928 /* This is the main function of the AppInfo thread */
3930 gio_win32_appinfo_thread_func (gpointer data,
3934 g_mutex_lock (&gio_win32_appinfo_mutex);
3935 saved_counter = g_atomic_int_get (&gio_win32_appinfo_update_counter);
3937 if (saved_counter > 0)
3938 update_registry_data ();
3939 /* If the counter didn't change while we were working, then set it to zero.
3940 * Otherwise we need to rebuild the tree again, so keep it greater than zero.
3941 * Numeric value doesn't matter - even if we're asked to rebuild N times,
3942 * we just need to rebuild once, and as long as there were no new rebuild
3943 * requests while we were working, we're done.
3945 if (g_atomic_int_compare_and_exchange (&gio_win32_appinfo_update_counter,
3948 g_cond_broadcast (&gio_win32_appinfo_cond);
3950 g_mutex_unlock (&gio_win32_appinfo_mutex);
3953 /* Initializes Windows AppInfo. Creates the registry watchers,
3954 * the AppInfo thread, and initiates an update of the AppInfo tree.
3955 * Called with do_wait = `FALSE` at startup to prevent it from
3956 * blocking until the tree is updated. All subsequent calls
3957 * from everywhere else are made with do_wait = `TRUE`, blocking
3958 * until the tree is re-built (if needed).
3961 gio_win32_appinfo_init (gboolean do_wait)
3963 static gsize initialized;
3965 if (g_once_init_enter (&initialized))
3967 url_associations_key =
3968 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
3971 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts",
3974 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Clients",
3976 system_clients_key =
3977 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\Clients",
3980 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT\\Applications",
3982 user_registered_apps_key =
3983 g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\RegisteredApplications",
3985 system_registered_apps_key =
3986 g_win32_registry_key_new_w (L"HKEY_LOCAL_MACHINE\\Software\\RegisteredApplications",
3989 g_win32_registry_key_new_w (L"HKEY_CLASSES_ROOT",
3994 /* We don't really require an exclusive pool, but the implementation
3995 * details might cause the g_thread_pool_push() call below to block
3996 * if the pool is not exclusive (specifically - for POSIX threads backend
3997 * lacking thread scheduler settings).
3999 gio_win32_appinfo_threadpool = g_thread_pool_new (gio_win32_appinfo_thread_func,
4004 g_mutex_init (&gio_win32_appinfo_mutex);
4005 g_cond_init (&gio_win32_appinfo_cond);
4006 g_atomic_int_set (&gio_win32_appinfo_update_counter, 1);
4007 /* Trigger initial tree build. Fake data pointer. */
4008 g_thread_pool_push (gio_win32_appinfo_threadpool, (gpointer) keys_updated, NULL);
4010 g_once_init_leave (&initialized, TRUE);
4016 /* Previously, we checked each of the watched keys here.
4017 * Now we just look at the update counter, because each key
4018 * has a change callback keys_updated, which increments this counter.
4020 if (g_atomic_int_get (&gio_win32_appinfo_update_counter) > 0)
4022 g_mutex_lock (&gio_win32_appinfo_mutex);
4023 while (g_atomic_int_get (&gio_win32_appinfo_update_counter) > 0)
4024 g_cond_wait (&gio_win32_appinfo_cond, &gio_win32_appinfo_mutex);
4026 g_mutex_unlock (&gio_win32_appinfo_mutex);
4031 static void g_win32_app_info_iface_init (GAppInfoIface *iface);
4033 struct _GWin32AppInfo
4035 GObject parent_instance;
4038 gchar **supported_types;
4040 GWin32AppInfoApplication *app;
4042 GWin32AppInfoHandler *handler;
4044 guint startup_notify : 1;
4047 G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
4048 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
4049 g_win32_app_info_iface_init))
4053 g_win32_app_info_finalize (GObject *object)
4055 GWin32AppInfo *info;
4057 info = G_WIN32_APP_INFO (object);
4059 g_clear_pointer (&info->supported_types, g_strfreev);
4060 g_clear_object (&info->app);
4061 g_clear_object (&info->handler);
4063 G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize (object);
4067 g_win32_app_info_class_init (GWin32AppInfoClass *klass)
4069 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
4071 gobject_class->finalize = g_win32_app_info_finalize;
4075 g_win32_app_info_init (GWin32AppInfo *local)
4080 g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
4081 GWin32AppInfoHandler *handler)
4083 GWin32AppInfo *new_info;
4084 GHashTableIter iter;
4088 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
4090 new_info->app = g_object_ref (app);
4092 gio_win32_appinfo_init (TRUE);
4093 g_mutex_lock (&gio_win32_appinfo_mutex);
4096 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
4098 while (g_hash_table_iter_next (&iter, &ext, NULL))
4104 new_info->supported_types = g_new (gchar *, i + 1);
4107 g_hash_table_iter_init (&iter, new_info->app->supported_exts);
4109 while (g_hash_table_iter_next (&iter, &ext, NULL))
4114 new_info->supported_types[i] = g_strdup ((gchar *) ext);
4118 g_mutex_unlock (&gio_win32_appinfo_mutex);
4120 new_info->supported_types[i] = NULL;
4122 new_info->handler = handler ? g_object_ref (handler) : NULL;
4124 return G_APP_INFO (new_info);
4129 g_win32_app_info_dup (GAppInfo *appinfo)
4131 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4132 GWin32AppInfo *new_info;
4134 new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
4137 new_info->app = g_object_ref (info->app);
4140 new_info->handler = g_object_ref (info->handler);
4142 new_info->startup_notify = info->startup_notify;
4144 if (info->supported_types)
4148 for (i = 0; info->supported_types[i]; i++)
4151 new_info->supported_types = g_new (gchar *, i + 1);
4153 for (i = 0; info->supported_types[i]; i++)
4154 new_info->supported_types[i] = g_strdup (info->supported_types[i]);
4156 new_info->supported_types[i] = NULL;
4159 return G_APP_INFO (new_info);
4163 g_win32_app_info_equal (GAppInfo *appinfo1,
4166 GWin32AppInfoShellVerb *shverb1 = NULL;
4167 GWin32AppInfoShellVerb *shverb2 = NULL;
4168 GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
4169 GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
4170 GWin32AppInfoApplication *app1 = info1->app;
4171 GWin32AppInfoApplication *app2 = info2->app;
4175 return info1 == info2;
4177 if (app1->canonical_name_folded != NULL &&
4178 app2->canonical_name_folded != NULL)
4179 return (g_strcmp0 (app1->canonical_name_folded,
4180 app2->canonical_name_folded)) == 0;
4182 if (app1->verbs->len > 0 &&
4183 app2->verbs->len > 0)
4185 shverb1 = _verb_idx (app1->verbs, 0);
4186 shverb2 = _verb_idx (app2->verbs, 0);
4187 if (shverb1->executable_folded != NULL &&
4188 shverb2->executable_folded != NULL)
4189 return (g_strcmp0 (shverb1->executable_folded,
4190 shverb2->executable_folded)) == 0;
4193 return app1 == app2;
4197 g_win32_app_info_get_id (GAppInfo *appinfo)
4199 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4200 GWin32AppInfoShellVerb *shverb;
4202 if (info->app == NULL)
4205 if (info->app->canonical_name_u8)
4206 return info->app->canonical_name_u8;
4208 if (info->app->verbs->len > 0 &&
4209 (shverb = _verb_idx (info->app->verbs, 0))->executable_basename != NULL)
4210 return shverb->executable_basename;
4216 g_win32_app_info_get_name (GAppInfo *appinfo)
4218 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4220 if (info->app && info->app->pretty_name_u8)
4221 return info->app->pretty_name_u8;
4222 else if (info->app && info->app->canonical_name_u8)
4223 return info->app->canonical_name_u8;
4225 return P_("Unnamed");
4229 g_win32_app_info_get_display_name (GAppInfo *appinfo)
4231 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4235 if (info->app->localized_pretty_name_u8)
4236 return info->app->localized_pretty_name_u8;
4237 else if (info->app->pretty_name_u8)
4238 return info->app->pretty_name_u8;
4241 return g_win32_app_info_get_name (appinfo);
4245 g_win32_app_info_get_description (GAppInfo *appinfo)
4247 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4249 if (info->app == NULL)
4252 return info->app->description_u8;
4256 g_win32_app_info_get_executable (GAppInfo *appinfo)
4258 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4260 if (info->app == NULL)
4263 if (info->app->verbs->len > 0 && !info->app->is_uwp)
4264 return _verb_idx (info->app->verbs, 0)->executable;
4270 g_win32_app_info_get_commandline (GAppInfo *appinfo)
4272 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4274 if (info->app == NULL)
4277 if (info->app->verbs->len > 0 && !info->app->is_uwp)
4278 return _verb_idx (info->app->verbs, 0)->command_utf8;
4284 g_win32_app_info_get_icon (GAppInfo *appinfo)
4286 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4288 if (info->app == NULL)
4291 return info->app->icon;
4294 typedef struct _file_or_uri {
4300 expand_macro_single (char macro, file_or_uri *obj)
4302 char *result = NULL;
4319 /* TODO: handle 'l' and 'd' differently (longname and desktop name) */
4321 result = g_strdup (obj->uri);
4323 result = g_strdup (obj->file);
4328 result = g_shell_quote (obj->uri);
4333 result = g_shell_quote (obj->file);
4341 expand_macro (char macro,
4343 GWin32AppInfo *info,
4344 GList **stat_obj_list,
4347 GList *objs = *obj_list;
4349 gboolean result = FALSE;
4351 g_return_val_if_fail (exec != NULL, FALSE);
4354 Legend: (from http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
4355 %* - replace with all parameters
4356 %~ - replace with all parameters starting with and following the second parameter
4357 %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).
4358 %<n> (where N is 2 - 9), replace with the nth parameter
4361 %i - IDList stored in a shared memory handle is passed here.
4362 %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.
4363 %d - desktop absolute parsing name of the first parameter (for items that don't have file system paths)
4364 %v - for verbs that are none implies all, if there is no parameter passed this is the working directory
4365 %w - the working directory
4377 for (o = *stat_obj_list, i = 0;
4378 macro == '~' && o && i < 2;
4381 for (; o; o = o->next)
4383 expanded = expand_macro_single (macro, o->data);
4387 if (o != *stat_obj_list)
4388 g_string_append (exec, " ");
4390 g_string_append (exec, expanded);
4411 expanded = expand_macro_single (macro, o->data);
4415 if (o != *stat_obj_list)
4416 g_string_append (exec, " ");
4418 g_string_append (exec, expanded);
4471 for (o = *stat_obj_list, i = 0; o && i < n; o = o->next, i++);
4475 expanded = expand_macro_single (macro, o->data);
4479 if (o != *stat_obj_list)
4480 g_string_append (exec, " ");
4482 g_string_append (exec, expanded);
4501 expanded = g_get_current_dir ();
4502 g_string_append (exec, expanded);
4509 expanded = expand_macro_single (macro, objs->data);
4513 g_string_append (exec, expanded);
4526 expanded = expand_macro_single (macro, objs->data);
4530 g_string_append (exec, expanded);
4537 if (objs != NULL && expanded)
4538 g_string_append_c (exec, ' ');
4544 if (info->app && info->app->localized_pretty_name_u8)
4546 expanded = g_shell_quote (info->app->localized_pretty_name_u8);
4547 g_string_append (exec, expanded);
4552 case 'm': /* deprecated */
4553 case 'n': /* deprecated */
4554 case 'N': /* deprecated */
4555 /*case 'd': *//* deprecated */
4556 case 'D': /* deprecated */
4560 g_string_append_c (exec, '%');
4570 expand_application_parameters (GWin32AppInfo *info,
4571 const gchar *exec_line,
4577 GList *obj_list = *objs;
4578 GList **stat_obj_list = objs;
4579 const char *p = exec_line;
4580 GString *expanded_exec;
4584 expanded_exec = g_string_new (NULL);
4589 if (p[0] == '%' && p[1] != '\0')
4591 if (expand_macro (p[1],
4593 info, stat_obj_list,
4600 g_string_append_c (expanded_exec, *p);
4605 /* No file substitutions */
4606 if (obj_list == *objs && obj_list != NULL && !res)
4608 /* If there is no macro default to %f. This is also what KDE does */
4609 g_string_append_c (expanded_exec, ' ');
4610 expand_macro ('f', expanded_exec, info, stat_obj_list, objs);
4613 /* Replace '\\' with '/', because g_shell_parse_argv considers them
4614 * to be escape sequences.
4616 for (a_char = expanded_exec->str;
4617 a_char <= &expanded_exec->str[expanded_exec->len];
4620 if (*a_char == '\\')
4624 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
4625 g_string_free (expanded_exec, TRUE);
4631 get_appath_for_exe (const gchar *exe_basename)
4633 GWin32RegistryKey *apppath_key = NULL;
4634 GWin32RegistryValueType val_type;
4635 gchar *appath = NULL;
4637 gchar *key_path = g_strdup_printf ("HKEY_LOCAL_MACHINE\\"
4643 "%s", exe_basename);
4645 apppath_key = g_win32_registry_key_new (key_path, NULL);
4646 g_clear_pointer (&key_path, g_free);
4648 if (apppath_key == NULL)
4651 got_value = g_win32_registry_key_get_value (apppath_key,
4660 g_object_unref (apppath_key);
4663 val_type == G_WIN32_REGISTRY_VALUE_STR)
4666 g_clear_pointer (&appath, g_free);
4673 g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
4675 IShellItemArray *items,
4676 GWin32AppInfoShellVerb *shverb,
4680 IApplicationActivationManager* paam = NULL;
4681 gboolean result = TRUE;
4684 hr = CoCreateInstance (&CLSID_ApplicationActivationManager, NULL, CLSCTX_INPROC_SERVER, &IID_IApplicationActivationManager, (void **) &paam);
4687 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4688 "Failed to create ApplicationActivationManager: 0x%lx", hr);
4693 hr = IApplicationActivationManager_ActivateApplication (paam, (const wchar_t *) info->app->canonical_name, NULL, AO_NONE, &pid);
4695 hr = IApplicationActivationManager_ActivateForFile (paam, (const wchar_t *) info->app->canonical_name, items, shverb->verb_name, &pid);
4697 hr = IApplicationActivationManager_ActivateForProtocol (paam, (const wchar_t *) info->app->canonical_name, items, &pid);
4701 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4702 "The app %s failed to launch: 0x%lx",
4703 g_win32_appinfo_application_get_some_name (info->app), hr);
4707 IApplicationActivationManager_Release (paam);
4714 g_win32_app_info_launch_internal (GWin32AppInfo *info,
4715 GList *objs, /* non-UWP only */
4716 gboolean for_files, /* UWP only */
4717 IShellItemArray *items, /* UWP only */
4718 GAppLaunchContext *launch_context,
4719 GSpawnFlags spawn_flags,
4722 gboolean completed = FALSE;
4723 char **argv, **envp;
4725 const gchar *command;
4727 GWin32AppInfoShellVerb *shverb;
4729 g_return_val_if_fail (info != NULL, FALSE);
4730 g_return_val_if_fail (info->app != NULL, FALSE);
4735 if (!info->app->is_uwp &&
4736 info->handler != NULL &&
4737 info->handler->verbs->len > 0)
4738 shverb = _verb_idx (info->handler->verbs, 0);
4739 else if (info->app->verbs->len > 0)
4740 shverb = _verb_idx (info->app->verbs, 0);
4744 if (info->app->is_uwp || info->handler == NULL)
4745 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4746 P_("The app ‘%s’ in the application object has no verbs"),
4747 g_win32_appinfo_application_get_some_name (info->app));
4748 else if (info->handler->verbs->len == 0)
4749 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4750 P_("The app ‘%s’ and the handler ‘%s’ in the application object have no verbs"),
4751 g_win32_appinfo_application_get_some_name (info->app),
4752 info->handler->handler_id_folded);
4757 if (info->app->is_uwp)
4758 return g_win32_app_info_launch_uwp_internal (info,
4765 envp = g_app_launch_context_get_environment (launch_context);
4767 envp = g_get_environ ();
4769 g_assert (shverb->command_utf8 != NULL);
4770 command = shverb->command_utf8;
4771 apppath = get_appath_for_exe (shverb->executable_basename);
4778 for (p = envp, p_index = 0; p[0]; p++, p_index++)
4779 if ((p[0][0] == 'p' || p[0][0] == 'P') &&
4780 (p[0][1] == 'a' || p[0][1] == 'A') &&
4781 (p[0][2] == 't' || p[0][2] == 'T') &&
4782 (p[0][3] == 'h' || p[0][3] == 'H') &&
4789 new_envp = g_new (char *, g_strv_length (envp) + 2);
4790 new_envp[0] = g_strdup_printf ("PATH=%s", apppath);
4792 for (p_index = 0; p_index <= g_strv_length (envp); p_index++)
4793 new_envp[1 + p_index] = envp[p_index];
4804 if (p_path[0] != '\0')
4805 envp[p_index] = g_strdup_printf ("PATH=%s%c%s",
4807 G_SEARCHPATH_SEPARATOR,
4810 envp[p_index] = g_strdup_printf ("PATH=%s", apppath);
4812 g_free (&p_path[-5]);
4820 if (!expand_application_parameters (info,
4828 if (!g_spawn_async (NULL,
4838 if (launch_context != NULL)
4840 GVariantBuilder builder;
4841 GVariant *platform_data;
4843 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
4844 g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
4846 platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
4847 g_signal_emit_by_name (launch_context, "launched", info, platform_data);
4848 g_variant_unref (platform_data);
4854 while (objs != NULL);
4866 free_file_or_uri (gpointer ptr)
4868 file_or_uri *obj = ptr;
4876 g_win32_app_supports_uris (GWin32AppInfoApplication *app)
4878 gssize num_of_uris_supported;
4883 num_of_uris_supported = (gssize) g_hash_table_size (app->supported_urls);
4885 if (g_hash_table_lookup (app->supported_urls, "file"))
4886 num_of_uris_supported -= 1;
4888 return num_of_uris_supported > 0;
4893 g_win32_app_info_supports_uris (GAppInfo *appinfo)
4895 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4897 if (info->app == NULL)
4900 return g_win32_app_supports_uris (info->app);
4905 g_win32_app_info_supports_files (GAppInfo *appinfo)
4907 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
4909 if (info->app == NULL)
4912 return g_hash_table_size (info->app->supported_exts) > 0;
4916 static IShellItemArray *
4917 make_item_array (gboolean for_files,
4918 GList *files_or_uris,
4921 ITEMIDLIST **item_ids;
4922 IShellItemArray *items;
4928 count = g_list_length (files_or_uris);
4931 item_ids = g_new (ITEMIDLIST*, count);
4933 for (i = 0, p = files_or_uris; p != NULL; p = p->next, i++)
4935 wchar_t *file_or_uri_utf16;
4938 file_or_uri_utf16 = g_utf8_to_utf16 ((gchar *) p->data, -1, NULL, NULL, error);
4940 file_or_uri_utf16 = g_utf8_to_utf16 (g_file_peek_path (G_FILE (p->data)), -1, NULL, NULL, error);
4942 if (file_or_uri_utf16 == NULL)
4951 len = wcslen (file_or_uri_utf16);
4952 /* Filenames *MUST* use single backslashes, else the call
4953 * will fail. First convert all slashes to backslashes,
4954 * then remove duplicates.
4956 for (c = file_or_uri_utf16; for_files && *c != 0; c++)
4961 for (len_tail = 0, c = &file_or_uri_utf16[len - 1];
4962 for_files && c > file_or_uri_utf16;
4965 if (c[0] != L'\\' || c[-1] != L'\\')
4968 memmove (&c[-1], &c[0], len_tail * sizeof (wchar_t));
4972 hr = SHParseDisplayName (file_or_uri_utf16, NULL, &item_ids[i], 0, NULL);
4973 g_free (file_or_uri_utf16);
4977 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4978 "File or URI `%S' cannot be parsed by SHParseDisplayName: 0x%lx", file_or_uri_utf16, hr);
4985 hr = SHCreateShellItemArrayFromIDLists (count, (const ITEMIDLIST **) item_ids, &items);
4988 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
4989 "SHCreateShellItemArrayFromIDLists() failed: 0x%lx", hr);
4996 for (i = 0; i < count; i++)
4997 CoTaskMemFree (item_ids[i]);
5006 g_win32_app_info_launch_uris (GAppInfo *appinfo,
5008 GAppLaunchContext *launch_context,
5011 gboolean res = FALSE;
5014 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
5016 if (info->app != NULL && info->app->is_uwp)
5018 IShellItemArray *items = NULL;
5022 items = make_item_array (FALSE, uris, error);
5027 res = g_win32_app_info_launch_internal (info, NULL, FALSE, items, launch_context, 0, error);
5030 IShellItemArray_Release (items);
5035 do_files = g_win32_app_info_supports_files (appinfo);
5041 obj = g_new0 (file_or_uri, 1);
5048 file = g_file_new_for_uri (uris->data);
5049 path = g_file_get_path (file);
5051 g_object_unref (file);
5054 obj->uri = g_strdup (uris->data);
5056 objs = g_list_prepend (objs, obj);
5060 objs = g_list_reverse (objs);
5062 res = g_win32_app_info_launch_internal (info,
5067 G_SPAWN_SEARCH_PATH,
5070 g_list_free_full (objs, free_file_or_uri);
5076 g_win32_app_info_launch (GAppInfo *appinfo,
5078 GAppLaunchContext *launch_context,
5081 gboolean res = FALSE;
5084 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
5086 if (info->app != NULL && info->app->is_uwp)
5088 IShellItemArray *items = NULL;
5092 items = make_item_array (TRUE, files, error);
5097 res = g_win32_app_info_launch_internal (info, NULL, TRUE, items, launch_context, 0, error);
5100 IShellItemArray_Release (items);
5105 do_uris = g_win32_app_info_supports_uris (appinfo);
5111 obj = g_new0 (file_or_uri, 1);
5112 obj->file = g_file_get_path (G_FILE (files->data));
5115 obj->uri = g_file_get_uri (G_FILE (files->data));
5117 objs = g_list_prepend (objs, obj);
5118 files = files->next;
5121 objs = g_list_reverse (objs);
5123 res = g_win32_app_info_launch_internal (info,
5128 G_SPAWN_SEARCH_PATH,
5131 g_list_free_full (objs, free_file_or_uri);
5136 static const char **
5137 g_win32_app_info_get_supported_types (GAppInfo *appinfo)
5139 GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
5141 return (const char**) info->supported_types;
5145 g_app_info_create_from_commandline (const char *commandline,
5146 const char *application_name,
5147 GAppInfoCreateFlags flags,
5150 GWin32AppInfo *info;
5151 GWin32AppInfoApplication *app;
5152 gunichar2 *app_command;
5154 g_return_val_if_fail (commandline, NULL);
5156 app_command = g_utf8_to_utf16 (commandline, -1, NULL, NULL, NULL);
5158 if (app_command == NULL)
5161 info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
5162 app = g_object_new (G_TYPE_WIN32_APPINFO_APPLICATION, NULL);
5164 app->no_open_with = FALSE;
5165 app->user_specific = FALSE;
5166 app->default_app = FALSE;
5168 if (application_name)
5170 app->canonical_name = g_utf8_to_utf16 (application_name,
5175 app->canonical_name_u8 = g_strdup (application_name);
5176 app->canonical_name_folded = g_utf8_casefold (application_name, -1);
5188 g_clear_pointer (&app_command, g_free);
5190 info->handler = NULL;
5192 return G_APP_INFO (info);
5195 /* GAppInfo interface init */
5198 g_win32_app_info_iface_init (GAppInfoIface *iface)
5200 iface->dup = g_win32_app_info_dup;
5201 iface->equal = g_win32_app_info_equal;
5202 iface->get_id = g_win32_app_info_get_id;
5203 iface->get_name = g_win32_app_info_get_name;
5204 iface->get_description = g_win32_app_info_get_description;
5205 iface->get_executable = g_win32_app_info_get_executable;
5206 iface->get_icon = g_win32_app_info_get_icon;
5207 iface->launch = g_win32_app_info_launch;
5208 iface->supports_uris = g_win32_app_info_supports_uris;
5209 iface->supports_files = g_win32_app_info_supports_files;
5210 iface->launch_uris = g_win32_app_info_launch_uris;
5211 /* iface->should_show = g_win32_app_info_should_show;*/
5212 /* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
5213 /* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
5214 /* iface->add_supports_type = g_win32_app_info_add_supports_type;*/
5215 /* iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
5216 /* iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
5217 /* iface->can_delete = g_win32_app_info_can_delete;*/
5218 /* iface->do_delete = g_win32_app_info_delete;*/
5219 iface->get_commandline = g_win32_app_info_get_commandline;
5220 iface->get_display_name = g_win32_app_info_get_display_name;
5221 /* iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
5222 iface->get_supported_types = g_win32_app_info_get_supported_types;
5226 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
5228 GWin32AppInfoURLSchema *scheme = NULL;
5231 GWin32AppInfoShellVerb *shverb;
5233 scheme_down = g_utf8_casefold (uri_scheme, -1);
5238 if (strcmp (scheme_down, "file") == 0)
5240 g_free (scheme_down);
5245 gio_win32_appinfo_init (TRUE);
5246 g_mutex_lock (&gio_win32_appinfo_mutex);
5248 g_set_object (&scheme, g_hash_table_lookup (urls, scheme_down));
5249 g_free (scheme_down);
5251 g_mutex_unlock (&gio_win32_appinfo_mutex);
5255 if (scheme != NULL &&
5256 scheme->chosen_handler != NULL &&
5257 scheme->chosen_handler->verbs->len > 0 &&
5258 (shverb = _verb_idx (scheme->chosen_handler->verbs, 0))->app != NULL)
5259 result = g_win32_app_info_new_from_app (shverb->app,
5260 scheme->chosen_handler);
5262 g_clear_object (&scheme);
5268 g_app_info_get_default_for_type (const char *content_type,
5269 gboolean must_support_uris)
5271 GWin32AppInfoFileExtension *ext = NULL;
5274 GWin32AppInfoShellVerb *shverb;
5276 ext_down = g_utf8_casefold (content_type, -1);
5281 gio_win32_appinfo_init (TRUE);
5282 g_mutex_lock (&gio_win32_appinfo_mutex);
5284 /* Assuming that "content_type" is a file extension, not a MIME type */
5285 g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
5288 g_mutex_unlock (&gio_win32_appinfo_mutex);
5295 if (ext->chosen_handler != NULL &&
5296 ext->chosen_handler->verbs->len > 0 &&
5297 (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL &&
5298 (!must_support_uris ||
5299 g_win32_app_supports_uris (shverb->app)))
5300 result = g_win32_app_info_new_from_app (shverb->app,
5301 ext->chosen_handler);
5304 GHashTableIter iter;
5305 GWin32AppInfoHandler *handler;
5307 g_hash_table_iter_init (&iter, ext->handlers);
5309 while (result == NULL &&
5310 g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
5312 if (handler->verbs->len == 0)
5315 shverb = _verb_idx (handler->verbs, 0);
5318 (!must_support_uris ||
5319 g_win32_app_supports_uris (shverb->app)))
5320 result = g_win32_app_info_new_from_app (shverb->app, handler);
5324 g_clear_object (&ext);
5330 g_app_info_get_all (void)
5332 GHashTableIter iter;
5338 gio_win32_appinfo_init (TRUE);
5339 g_mutex_lock (&gio_win32_appinfo_mutex);
5342 g_hash_table_iter_init (&iter, apps_by_id);
5343 while (g_hash_table_iter_next (&iter, NULL, &value))
5344 apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
5346 g_mutex_unlock (&gio_win32_appinfo_mutex);
5349 for (apps_i = apps; apps_i; apps_i = apps_i->next)
5350 infos = g_list_prepend (infos,
5351 g_win32_app_info_new_from_app (apps_i->data, NULL));
5353 g_list_free_full (apps, g_object_unref);
5359 g_app_info_get_all_for_type (const char *content_type)
5361 GWin32AppInfoFileExtension *ext = NULL;
5363 GWin32AppInfoHandler *handler;
5364 GHashTableIter iter;
5365 GHashTable *apps = NULL;
5367 GWin32AppInfoShellVerb *shverb;
5369 ext_down = g_utf8_casefold (content_type, -1);
5374 gio_win32_appinfo_init (TRUE);
5375 g_mutex_lock (&gio_win32_appinfo_mutex);
5377 /* Assuming that "content_type" is a file extension, not a MIME type */
5378 g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
5381 g_mutex_unlock (&gio_win32_appinfo_mutex);
5387 /* Used as a set to ensure uniqueness */
5388 apps = g_hash_table_new (g_direct_hash, g_direct_equal);
5390 if (ext->chosen_handler != NULL &&
5391 ext->chosen_handler->verbs->len > 0 &&
5392 (shverb = _verb_idx (ext->chosen_handler->verbs, 0))->app != NULL)
5394 g_hash_table_add (apps, shverb->app);
5395 result = g_list_prepend (result,
5396 g_win32_app_info_new_from_app (shverb->app,
5397 ext->chosen_handler));
5400 g_hash_table_iter_init (&iter, ext->handlers);
5402 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &handler))
5406 for (vi = 0; vi < handler->verbs->len; vi++)
5408 shverb = _verb_idx (handler->verbs, vi);
5410 if (shverb->app == NULL ||
5411 g_hash_table_contains (apps, shverb->app))
5414 g_hash_table_add (apps, shverb->app);
5415 result = g_list_prepend (result,
5416 g_win32_app_info_new_from_app (shverb->app,
5421 g_clear_object (&ext);
5422 result = g_list_reverse (result);
5423 g_hash_table_unref (apps);
5429 g_app_info_get_fallback_for_type (const gchar *content_type)
5431 /* TODO: fix this once gcontenttype support is improved */
5432 return g_app_info_get_all_for_type (content_type);
5436 g_app_info_get_recommended_for_type (const gchar *content_type)
5438 /* TODO: fix this once gcontenttype support is improved */
5439 return g_app_info_get_all_for_type (content_type);
5443 g_app_info_reset_type_associations (const char *content_type)