[glib: 8/19] GWin32AppInfo: Support getting information about UWP apps
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 8/19] GWin32AppInfo: Support getting information about UWP apps
- Date: Mon, 4 Jan 2021 12:55:59 +0000 (UTC)
commit 720b51032ca28a32d86b90846ccee1f91e045b4e
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date: Thu Jun 4 15:13:33 2020 +0000
GWin32AppInfo: Support getting information about UWP apps
gio/gwin32appinfo.c | 519 ++++++++++++++++++++++++++++++++++++++++++++++++----
gio/meson.build | 3 +
meson.build | 3 +
3 files changed, 487 insertions(+), 38 deletions(-)
---
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index 34180b40c..ea48e292b 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -181,7 +181,10 @@ struct _GWin32AppInfoHandler {
/* Usually a class name in HKCR */
gunichar2 *handler_id;
- /* Registry object obtained by opening @handler_id. Can be used to watch this handler. */
+ /* Registry object obtained by opening @handler_id.
+ * Can be used to watch this handler.
+ * May be %NULL (for fake handlers that we made up).
+ */
GWin32RegistryKey *key;
/* @handler_id, in UTF-8, folded */
@@ -267,6 +270,7 @@ struct _GWin32AppInfoApplication {
* key path for the application.
* For applications tracked by executable name this is the
* basename of the executable.
+ * For UWP apps this is the AppUserModelID.
* For fake applications this is the full filename of the
* executable (as far as it can be inferred from a command line,
* meaning that it can also be a basename, if that's
@@ -333,13 +337,8 @@ struct _GWin32AppInfoApplication {
* browser) */
gboolean default_app;
- /* At the moment we do not support getting UWP application info
- * from the OS. That is, if a file extension is associated with
- * a UWP app, we can launch it, but we can't read a list of
- * UWP apps and see which extensions are associated with each one
- * of them.
- * That is why there's no uwp_aumid field here.
- */
+ /* Set to TRUE for UWP applications */
+ gboolean is_uwp;
};
#define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ())
@@ -637,6 +636,9 @@ static GWin32RegistryKey *classes_root_key;
*/
#include "giowin32-private.c"
+/* for g_win32_package_parser_enum_packages() */
+#include "gwin32packageparser.h"
+
static void
read_handler_icon (GWin32RegistryKey *key,
GIcon **icon_out)
@@ -791,6 +793,20 @@ static void handler_add_verb (gpointer ha
gboolean verb_is_preferred,
gboolean invent_new_verb_name);
+static void process_uwp_verbs (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app);
+
+static void uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app,
+ const gunichar2 *verb,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred);
+
/* output_size is in *bytes*, not gunichar2s! */
static gboolean
build_registry_path (gunichar2 *output, gsize output_size, ...)
@@ -881,6 +897,12 @@ _g_win32_registry_key_build_and_new_w (GError **error, ...)
* @verbshell_prefix is the subkey of @program_id_key
* that contains the verbs. It is "Shell" initially,
* but grows with recursive invocations (for subcommands).
+ * @is_uwp points to a boolean, which
+ * indicates whether the function is being called for a UWP app.
+ * It might be switched from %TRUE to %FALSE on return,
+ * if the application turns out to not to be UWP on closer inspection.
+ * If the application is already known not to be UWP before the
+ * call, this pointer can be %NULL instead.
* Returns TRUE on success, FALSE on failure.
*/
static gboolean
@@ -888,7 +910,8 @@ get_verbs (GWin32RegistryKey *program_id_key,
const reg_verb **preferred_verb,
GList **verbs,
const gunichar2 *verbname_prefix,
- const gunichar2 *verbshell_prefix)
+ const gunichar2 *verbshell_prefix,
+ gboolean *is_uwp)
{
GWin32RegistrySubkeyIter iter;
GWin32RegistryKey *key;
@@ -953,7 +976,8 @@ get_verbs (GWin32RegistryKey *program_id_key,
* Essentially, we're flattening the command tree into a list.
*/
has_subcommands = FALSE;
- if (g_win32_registry_key_get_value_w (subkey,
+ if ((is_uwp == NULL || !(*is_uwp)) && /* Assume UWP apps don't have subcommands */
+ g_win32_registry_key_get_value_w (subkey,
NULL,
TRUE,
L"Subcommands",
@@ -963,6 +987,7 @@ get_verbs (GWin32RegistryKey *program_id_key,
NULL) &&
subc_type == G_WIN32_REGISTRY_VALUE_STR)
{
+ gboolean dummy = FALSE;
gunichar2 *new_nameprefix = g_malloc ((verbname_prefix_len + name_len + 1 + 1) * sizeof
(gunichar2));
gunichar2 *new_shellprefix = g_malloc ((verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1) *
sizeof (gunichar2));
memcpy (&new_shellprefix[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
@@ -976,16 +1001,38 @@ get_verbs (GWin32RegistryKey *program_id_key,
memcpy (&new_nameprefix[verbname_prefix_len], name, (name_len) * sizeof (gunichar2));
new_nameprefix[verbname_prefix_len + name_len] = L'\\';
new_nameprefix[verbname_prefix_len + name_len + 1] = 0;
- has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix);
+ has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix, &dummy);
g_free (new_shellprefix);
g_free (new_nameprefix);
}
- g_clear_object (&subkey);
-
/* Presence of subcommands means that this key itself is not a command-key */
if (has_subcommands)
- continue;
+ {
+ g_clear_object (&subkey);
+ continue;
+ }
+
+ if (is_uwp != NULL && *is_uwp &&
+ !g_win32_registry_key_get_value_w (subkey,
+ NULL,
+ TRUE,
+ L"ActivatableClassId",
+ &subc_type,
+ NULL,
+ NULL,
+ NULL))
+ {
+ /* We expected a UWP app, but it lacks ActivatableClassId
+ * on a verb, which means that it does not behave like
+ * a UWP app should (msedge being an example - it's UWP,
+ * but has its own launchable exe file and a simple ID),
+ * so we have to treat it like a normal app.
+ */
+ *is_uwp = FALSE;
+ }
+
+ g_clear_object (&subkey);
/* We don't look at the command sub-key and its value (the actual command line) here.
* We save the registry path instead, and use it later in process_verbs_commands().
@@ -1073,6 +1120,7 @@ get_url_association (const gunichar2 *program_id,
gchar *handler_id_u8;
gchar *handler_id_u8_folded;
gunichar2 *uwp_aumid;
+ gboolean is_uwp;
GWin32RegistryKey *handler_key;
if ((handler_id = decide_which_id_to_use (program_id,
@@ -1082,22 +1130,22 @@ get_url_association (const gunichar2 *program_id,
&uwp_aumid)) == NULL)
return;
- if (uwp_aumid != NULL)
- {
- verbs = NULL;
- preferred_verb = NULL;
- }
- else if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
+ is_uwp = uwp_aumid != NULL;
+
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp))
{
g_clear_pointer (&handler_id, g_free);
g_clear_pointer (&handler_id_u8, g_free);
g_clear_pointer (&handler_id_u8_folded, g_free);
g_clear_object (&handler_key);
- /* and uwp_aumid is already NULL */
+ g_clear_pointer (&uwp_aumid, g_free);
return;
}
+ if (!is_uwp && uwp_aumid != NULL)
+ g_clear_pointer (&uwp_aumid, g_free);
+
schema_rec = get_schema_object (schema,
schema_u8,
schema_u8_folded);
@@ -1121,7 +1169,6 @@ get_url_association (const gunichar2 *program_id,
g_strdup (schema_rec->schema_u8_folded),
g_object_ref (handler_rec));
- /* UWP apps get their verbs elsewhere */
if (uwp_aumid == NULL)
process_verbs_commands (g_steal_pointer (&verbs),
preferred_verb,
@@ -1131,6 +1178,15 @@ get_url_association (const gunichar2 *program_id,
handler_add_verb,
handler_rec,
app);
+ else
+ process_uwp_verbs (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_rec,
+ app);
+
g_clear_pointer (&handler_id_u8, g_free);
g_clear_pointer (&handler_id_u8_folded, g_free);
@@ -1157,6 +1213,7 @@ get_file_ext (const gunichar2 *program_id,
gchar *handler_id_u8;
gchar *handler_id_u8_folded;
gunichar2 *uwp_aumid;
+ gboolean is_uwp;
GWin32RegistryKey *handler_key;
GWin32AppInfoFileExtension *file_extn;
gchar *file_extension_u8;
@@ -1183,12 +1240,9 @@ get_file_ext (const gunichar2 *program_id,
return;
}
- if (uwp_aumid != NULL)
- {
- verbs = NULL;
- preferred_verb = NULL;
- }
- else if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
+ is_uwp = uwp_aumid != NULL;
+
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp))
{
g_clear_pointer (&handler_id, g_free);
g_clear_pointer (&handler_id_u8, g_free);
@@ -1196,11 +1250,14 @@ get_file_ext (const gunichar2 *program_id,
g_clear_object (&handler_key);
g_clear_pointer (&file_extension_u8, g_free);
g_clear_pointer (&file_extension_u8_folded, g_free);
- /* and uwp_aumid is already NULL */
+ g_clear_pointer (&uwp_aumid, g_free);
return;
}
+ if (!is_uwp && uwp_aumid != NULL)
+ g_clear_pointer (&uwp_aumid, g_free);
+
file_extn = get_ext_object (file_extension, file_extension_u8, file_extension_u8_folded);
handler_rec = get_handler_object (handler_id_u8_folded,
@@ -1224,7 +1281,6 @@ get_file_ext (const gunichar2 *program_id,
g_clear_pointer (&file_extension_u8_folded, g_free);
g_clear_object (&handler_key);
- /* UWP apps get their verbs elsewhere */
if (uwp_aumid == NULL)
process_verbs_commands (g_steal_pointer (&verbs),
preferred_verb,
@@ -1234,6 +1290,14 @@ get_file_ext (const gunichar2 *program_id,
handler_add_verb,
handler_rec,
app);
+ else
+ process_uwp_verbs (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_rec,
+ app);
g_clear_pointer (&handler_id, g_free);
g_clear_pointer (&handler_id_u8, g_free);
@@ -1505,6 +1569,75 @@ process_verbs_commands (GList *verbs,
g_list_free_full (verbs, reg_verb_free);
}
+static void
+process_uwp_verbs (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app)
+{
+ GList *i;
+
+ g_assert (verbs != NULL);
+
+ for (i = verbs; i; i = i->next)
+ {
+ const reg_verb *verb = (const reg_verb *) i->data;
+ GWin32RegistryKey *key;
+ gboolean got_value;
+ GWin32RegistryValueType val_type;
+ gunichar2 *acid;
+ gsize acid_len;
+
+ key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
+ L"\\", verb->shellpath, NULL);
+
+ if (key == NULL)
+ {
+ g_debug ("%S%S\\%S does not exist",
+ path_to_progid, progid, verb->shellpath);
+ continue;
+ }
+
+ got_value = g_win32_registry_key_get_value_w (key,
+ g_win32_registry_get_os_dirs_w (),
+ TRUE,
+ L"ActivatableClassId",
+ &val_type,
+ (void **) &acid,
+ &acid_len,
+ NULL);
+
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR &&
+ acid_len > sizeof (gunichar2))
+ {
+ /* TODO: default value of a shell subkey, if not empty,
+ * migh contain something like
@{Some.Identifier_1234.456.678.789_some_words?ms-resource://Arbitrary.Path/Pointing/Somewhere}
+ * and it might be possible to turn it into a nice displayname.
+ */
+ uwp_handler_add_verb (handler_rec,
+ app,
+ verb->name,
+ NULL,
+ (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) ||
+ (!preferred_verb && autoprefer_first_verb && i == verbs));
+ }
+ else
+ {
+ g_debug ("%S%S\\%S does not have an ActivatableClassId string value",
+ path_to_progid, progid, verb->shellpath);
+ }
+
+ g_clear_pointer (&acid, g_free);
+ g_clear_object (&key);
+ }
+
+ g_list_free_full (verbs, reg_verb_free);
+}
+
/* Looks up a schema object identified by
* @schema_u8_folded in the urls hash table.
* If such object doesn't exist,
@@ -1552,12 +1685,14 @@ get_handler_object (const gchar *handler_id_u8_folded,
return handler_rec;
handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
- handler_rec->key = g_object_ref (handler_key);
+ if (handler_key)
+ handler_rec->key = g_object_ref (handler_key);
handler_rec->handler_id = g_wcsdup (handler_id, -1);
handler_rec->handler_id_folded = g_strdup (handler_id_u8_folded);
if (uwp_aumid)
handler_rec->uwp_aumid = g_wcsdup (uwp_aumid, -1);
- read_handler_icon (handler_key, &handler_rec->icon);
+ if (handler_key)
+ read_handler_icon (handler_key, &handler_rec->icon);
g_hash_table_insert (handlers, g_strdup (handler_id_u8_folded), handler_rec);
return handler_rec;
@@ -1727,6 +1862,75 @@ app_add_verb (gpointer handler_data1,
g_ptr_array_insert (app_rec->verbs, 0, shverb);
}
+static void
+uwp_app_add_verb (GWin32AppInfoApplication *app_rec,
+ const gunichar2 *verb,
+ const gchar *verb_displayname)
+{
+ GWin32AppInfoShellVerb *shverb;
+
+ _verb_lookup (app_rec->verbs, verb, &shverb);
+
+ if (shverb != NULL)
+ return;
+
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ shverb->verb_name = g_wcsdup (verb, -1);
+ shverb->app = g_object_ref (app_rec);
+ shverb->verb_displayname = g_strdup (verb_displayname);
+
+ shverb->is_uwp = TRUE;
+
+ /* Strictly speaking, this is unnecessary, but
+ * let's make it clear that UWP verbs have no
+ * commands and executables.
+ */
+ shverb->command = NULL;
+ shverb->command_utf8 = NULL;
+ shverb->executable = NULL;
+ shverb->executable_basename = NULL;
+ shverb->executable_folded = NULL;
+ shverb->dll_function = NULL;
+
+ g_ptr_array_add (app_rec->verbs, shverb);
+}
+
+static void
+uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app,
+ const gunichar2 *verb,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred)
+{
+ GWin32AppInfoShellVerb *shverb;
+
+ _verb_lookup (handler_rec->verbs, verb, &shverb);
+
+ if (shverb != NULL)
+ return;
+
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ shverb->verb_name = g_wcsdup (verb, -1);
+ shverb->verb_displayname = g_strdup (verb_displayname);
+
+ shverb->is_uwp = TRUE;
+
+ if (app)
+ shverb->app = g_object_ref (app);
+
+ shverb->command = NULL;
+ shverb->command_utf8 = NULL;
+ shverb->executable = NULL;
+ shverb->executable_basename = NULL;
+ shverb->executable_folded = NULL;
+ shverb->dll_function = NULL;
+
+ if (!verb_is_preferred)
+ g_ptr_array_add (handler_rec->verbs, shverb);
+ else
+ g_ptr_array_insert (handler_rec->verbs, 0, shverb);
+}
+
/* Looks up a file extension object identified by
* @ext_u8_folded in the extensions hash table.
* If such object doesn't exist,
@@ -1990,7 +2194,8 @@ get_app_object (GHashTable *app_hashmap,
const gchar *canonical_name_u8,
const gchar *canonical_name_folded,
gboolean user_specific,
- gboolean default_app)
+ gboolean default_app,
+ gboolean is_uwp)
{
GWin32AppInfoApplication *app;
@@ -2006,6 +2211,7 @@ get_app_object (GHashTable *app_hashmap,
app->no_open_with = FALSE;
app->user_specific = user_specific;
app->default_app = default_app;
+ app->is_uwp = is_uwp;
g_hash_table_insert (app_hashmap,
g_strdup (canonical_name_folded),
app);
@@ -2055,7 +2261,7 @@ read_capable_app (const gunichar2 *app_key_path,
&app_key_path_u8_folded) ||
(appkey = g_win32_registry_key_new_w (app_key_path, NULL)) == NULL ||
(capabilities = g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL)) == NULL ||
- !get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell"))
+ !get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell", NULL))
{
g_clear_pointer (&canonical_name_u8, g_free);
g_clear_pointer (&canonical_name_folded, g_free);
@@ -2071,7 +2277,8 @@ read_capable_app (const gunichar2 *app_key_path,
canonical_name_u8,
canonical_name_folded,
user_specific,
- default_app);
+ default_app,
+ FALSE);
process_verbs_commands (g_steal_pointer (&verbs),
preferred_verb,
@@ -2419,7 +2626,7 @@ read_incapable_app (GWin32RegistryKey *incapable_app,
GIcon *icon = NULL;
GWin32RegistryKey *supported_key;
- if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell"))
+ if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell", NULL))
return;
app = get_app_object (apps_by_exe,
@@ -2427,6 +2634,7 @@ read_incapable_app (GWin32RegistryKey *incapable_app,
app_exe_basename_u8,
app_exe_basename_u8_folded,
FALSE,
+ FALSE,
FALSE);
process_verbs_commands (g_steal_pointer (&verbs),
@@ -2864,6 +3072,9 @@ link_handlers_to_unregistered_apps (void)
GWin32AppInfoShellVerb *app_verb;
gsize ai;
+ if (app->is_uwp)
+ continue;
+
for (ai = 0; ai < app->verbs->len; ai++)
{
GWin32PrivateStat app_verb_exec_info;
@@ -2914,6 +3125,9 @@ link_handlers_to_unregistered_apps (void)
(gpointer *) &appexe_fld_basename,
(gpointer *) &app))
{
+ if (app->is_uwp)
+ continue;
+
/* Use basename because apps_by_exe only has basenames */
if (g_strcmp0 (handler_exe_basename, appexe_fld_basename) != 0)
continue;
@@ -2982,6 +3196,7 @@ link_handlers_to_fake_apps (void)
handler_verb->executable,
handler_verb->executable_folded,
FALSE,
+ FALSE,
FALSE);
g_clear_pointer (&exename_utf16, g_free);
handler_verb->app = g_object_ref (app);
@@ -3032,6 +3247,7 @@ link_handlers_to_fake_apps (void)
handler_verb->command_utf8,
command_utf8_folded,
FALSE,
+ FALSE,
FALSE);
g_clear_pointer (&command_utf8_folded, g_free);
handler_verb->app = g_object_ref (app);
@@ -3052,6 +3268,222 @@ link_handlers_to_fake_apps (void)
}
}
+static GWin32AppInfoHandler *
+find_uwp_handler_for_ext (GWin32AppInfoFileExtension *file_extn,
+ const gunichar2 *app_user_model_id)
+{
+ GHashTableIter handler_iter;
+ gchar *handler_id_fld;
+ GWin32AppInfoHandler *handler;
+
+ g_hash_table_iter_init (&handler_iter, file_extn->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
+ {
+ if (handler->uwp_aumid == NULL)
+ continue;
+
+ if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static GWin32AppInfoHandler *
+find_uwp_handler_for_schema (GWin32AppInfoURLSchema *schema,
+ const gunichar2 *app_user_model_id)
+{
+ GHashTableIter handler_iter;
+ gchar *handler_id_fld;
+ GWin32AppInfoHandler *handler;
+
+ g_hash_table_iter_init (&handler_iter, schema->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
+ {
+ if (handler->uwp_aumid == NULL)
+ continue;
+
+ if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static gboolean
+uwp_package_cb (gpointer user_data,
+ const gunichar2 *full_package_name,
+ const gunichar2 *package_name,
+ const gunichar2 *app_user_model_id,
+ gboolean show_in_applist,
+ GPtrArray *supported_extgroups,
+ GPtrArray *supported_protocols)
+{
+ gint i, i_verb, i_ext;
+ gint extensions_considered;
+ GWin32AppInfoApplication *app;
+ gchar *app_user_model_id_u8;
+ gchar *app_user_model_id_u8_folded;
+ GHashTableIter iter;
+ GWin32AppInfoHandler *ext;
+
+ if (!g_utf16_to_utf8_and_fold (app_user_model_id,
+ -1,
+ &app_user_model_id_u8,
+ &app_user_model_id_u8_folded))
+ return TRUE;
+
+ app = get_app_object (apps_by_id,
+ app_user_model_id,
+ app_user_model_id_u8,
+ app_user_model_id_u8_folded,
+ TRUE,
+ FALSE,
+ TRUE);
+
+ extensions_considered = 0;
+
+ for (i = 0; i < supported_extgroups->len; i++)
+ {
+ GWin32PackageExtGroup *grp = (GWin32PackageExtGroup *) g_ptr_array_index (supported_extgroups, i);
+
+ extensions_considered += grp->extensions->len;
+
+ for (i_ext = 0; i_ext < grp->extensions->len; i_ext++)
+ {
+ wchar_t *ext = (wchar_t *) g_ptr_array_index (grp->extensions, i_ext);
+ gchar *ext_u8;
+ gchar *ext_u8_folded;
+ GWin32AppInfoFileExtension *file_extn;
+ GWin32AppInfoHandler *handler_rec;
+
+ if (!g_utf16_to_utf8_and_fold (ext,
+ -1,
+ &ext_u8,
+ &ext_u8_folded))
+ continue;
+
+ file_extn = get_ext_object (ext, ext_u8, ext_u8_folded);
+ g_free (ext_u8);
+ handler_rec = find_uwp_handler_for_ext (file_extn, app_user_model_id);
+
+ if (handler_rec == NULL)
+ {
+ /* Use AppUserModelId as the ID of the new fake handler */
+ handler_rec = get_handler_object (app_user_model_id_u8_folded,
+ NULL,
+ app_user_model_id,
+ app_user_model_id);
+ g_hash_table_insert (file_extn->handlers,
+ g_strdup (app_user_model_id_u8_folded),
+ g_object_ref (handler_rec));
+ }
+
+ /* This is somewhat wasteful, but for 100% correct handling
+ * we need to remember which extensions (handlers) support
+ * which verbs, and each handler gets its own copy of the
+ * verb object, since our design is handler-centric,
+ * not verb-centric. The app also gets a list of verbs,
+ * but without handlers it would have no idea which
+ * verbs can be used with which extensions.
+ */
+ for (i_verb = 0; i_verb < grp->verbs->len; i_verb++)
+ {
+ wchar_t *verb = NULL;
+
+ verb = (wchar_t *) g_ptr_array_index (grp->verbs, i_verb);
+ /* *_add_verb() functions are no-ops when a verb already exists,
+ * so we're free to call them as many times as we want.
+ */
+ uwp_handler_add_verb (handler_rec,
+ app,
+ verb,
+ NULL,
+ FALSE);
+ }
+
+ g_hash_table_insert (app->supported_exts,
+ g_steal_pointer (&ext_u8_folded),
+ g_object_ref (handler_rec));
+ }
+ }
+
+ g_hash_table_iter_init (&iter, app->supported_exts);
+
+ /* Pile up all handler verbs into the app too,
+ * for cases when we don't have a ref to a handler.
+ */
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &ext))
+ {
+ gint i_hverb;
+
+ if (!ext)
+ continue;
+
+ for (i_hverb = 0; i_hverb < ext->verbs->len; i_hverb++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+
+ handler_verb = _verb_idx (ext->verbs, i_hverb);
+ uwp_app_add_verb (app, handler_verb->verb_name, handler_verb->verb_displayname);
+ if (handler_verb->app == NULL && handler_verb->is_uwp)
+ handler_verb->app = g_object_ref (app);
+ }
+ }
+
+ if (app->verbs->len == 0 && extensions_considered > 0)
+ g_warning ("Unexpectedly, UWP app `%S' (AUMId `%s') supports %d extensions but has no verbs",
+ full_package_name, app_user_model_id_u8, extensions_considered);
+
+ for (i = 0; i < supported_protocols->len; i++)
+ {
+ wchar_t *proto = (wchar_t *) g_ptr_array_index (supported_protocols, i);
+ gchar *proto_u8;
+ gchar *proto_u8_folded;
+ GWin32AppInfoURLSchema *schema_rec;
+ GWin32AppInfoHandler *handler_rec;
+
+ if (!g_utf16_to_utf8_and_fold (proto,
+ -1,
+ &proto_u8,
+ &proto_u8_folded))
+ continue;
+
+ schema_rec = get_schema_object (proto,
+ proto_u8,
+ proto_u8_folded);
+
+ g_free (proto_u8);
+
+ handler_rec = find_uwp_handler_for_schema (schema_rec, app_user_model_id);
+
+ if (handler_rec == NULL)
+ {
+ /* Use AppUserModelId as the ID of the new fake handler */
+ handler_rec = get_handler_object (app_user_model_id_u8_folded,
+ NULL,
+ app_user_model_id,
+ app_user_model_id);
+ /* UWP URL handlers do not need any verbs */
+ g_hash_table_insert (schema_rec->handlers,
+ g_strdup (app_user_model_id_u8_folded),
+ g_object_ref (handler_rec));
+ }
+
+ g_hash_table_insert (app->supported_urls,
+ g_steal_pointer (&proto_u8_folded),
+ g_object_ref (handler_rec));
+ }
+
+ g_free (app_user_model_id_u8);
+ g_free (app_user_model_id_u8_folded);
+
+ return TRUE;
+}
static void
update_registry_data (void)
@@ -3063,7 +3495,8 @@ update_registry_data (void)
GWin32RegistryKey *url_associations;
GWin32RegistryKey *file_exts;
GWin32RegistryKey *classes_root;
- DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end,
postproc_end;
+ DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end,
uwp_end, postproc_end;
+ GError *error = NULL;
url_associations =
g_win32_registry_key_new_w
(L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
@@ -3133,6 +3566,14 @@ update_registry_data (void)
exeapp_end = GetTickCount ();
read_classes (classes_root);
classes_end = GetTickCount ();
+
+ if (!g_win32_package_parser_enum_packages (uwp_package_cb, NULL, &error))
+ {
+ g_debug ("Unable to get UWP apps: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ uwp_end = GetTickCount ();
link_handlers_to_unregistered_apps ();
link_handlers_to_fake_apps ();
postproc_end = GetTickCount ();
@@ -3144,6 +3585,7 @@ update_registry_data (void)
"Reading extension assocs: %lums\n"
"Reading exe-only apps:...... %lums\n"
"Reading classes: %lums\n"
+ "Reading UWP apps: %lums\n"
"Postprocessing:..............%lums\n"
"TOTAL: %lums",
collect_end - collect_start,
@@ -3153,7 +3595,8 @@ update_registry_data (void)
ext_end - url_end,
exeapp_end - ext_end,
classes_end - exeapp_end,
- postproc_end - classes_end,
+ uwp_end - classes_end,
+ postproc_end - uwp_end,
postproc_end - collect_start);
g_clear_object (&classes_root);
diff --git a/gio/meson.build b/gio/meson.build
index b452f071b..4b9dda143 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -430,6 +430,8 @@ else
cc.find_library('dnsapi'),
iphlpapi_dep,
winsock2]
+ platform_deps += uwp_gio_deps
+
win32_sources += files(
'gwin32registrykey.c',
'gwin32mount.c',
@@ -437,6 +439,7 @@ else
'gwin32inputstream.c',
'gwin32outputstream.c',
'gwin32file-sync-stream.c',
+ 'gwin32packageparser.c',
'gwin32networkmonitor.c',
'gwin32networkmonitor.h',
'gwin32notificationbackend.c',
diff --git a/meson.build b/meson.build
index e0b308a25..3725473c4 100644
--- a/meson.build
+++ b/meson.build
@@ -466,9 +466,12 @@ if host_system == 'windows'
glib_conf.set('G_WINAPI_ONLY_APP', true)
# We require Windows 10+ on WinRT
glib_conf.set('_WIN32_WINNT', '0x0A00')
+ uwp_gio_deps = [cc.find_library('shcore'),
+ cc.find_library('runtimeobject')]
else
# We require Windows 7+ on Win32
glib_conf.set('_WIN32_WINNT', '0x0601')
+ uwp_gio_deps = []
endif
endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]