mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
Merge branch 'g-win32-app-info-launch-uris-async' into 'main'
GWin32AppInfo: Implement launch_uris_async Closes gtk#4400 See merge request GNOME/glib!2760
This commit is contained in:
commit
e1e9bc9429
@ -651,7 +651,9 @@ g_app_info_supports_files (GAppInfo *appinfo)
|
|||||||
* Launches the application. This passes the @uris to the launched application
|
* Launches the application. This passes the @uris to the launched application
|
||||||
* as arguments, using the optional @context to get information
|
* as arguments, using the optional @context to get information
|
||||||
* about the details of the launcher (like what screen it is on).
|
* about the details of the launcher (like what screen it is on).
|
||||||
* On error, @error will be set accordingly.
|
* On error, @error will be set accordingly. If the application only supports
|
||||||
|
* one URI per invocation as part of their command-line, multiple instances
|
||||||
|
* of the application will be spawned.
|
||||||
*
|
*
|
||||||
* To launch the application without arguments pass a %NULL @uris list.
|
* To launch the application without arguments pass a %NULL @uris list.
|
||||||
*
|
*
|
||||||
@ -1379,6 +1381,10 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
|
|||||||
* fails. The startup notification id is provided, so that the launcher
|
* fails. The startup notification id is provided, so that the launcher
|
||||||
* can cancel the startup notification.
|
* can cancel the startup notification.
|
||||||
*
|
*
|
||||||
|
* Because a launch operation may involve spawning multiple instances of the
|
||||||
|
* target application, you should expect this signal to be emitted multiple
|
||||||
|
* times, one for each spawned instance.
|
||||||
|
*
|
||||||
* Since: 2.36
|
* Since: 2.36
|
||||||
*/
|
*/
|
||||||
signals[LAUNCH_FAILED] = g_signal_new (I_("launch-failed"),
|
signals[LAUNCH_FAILED] = g_signal_new (I_("launch-failed"),
|
||||||
@ -1409,6 +1415,10 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
|
|||||||
* It is guaranteed that this signal is followed by either a #GAppLaunchContext::launched or
|
* It is guaranteed that this signal is followed by either a #GAppLaunchContext::launched or
|
||||||
* #GAppLaunchContext::launch-failed signal.
|
* #GAppLaunchContext::launch-failed signal.
|
||||||
*
|
*
|
||||||
|
* Because a launch operation may involve spawning multiple instances of the
|
||||||
|
* target application, you should expect this signal to be emitted multiple
|
||||||
|
* times, one for each spawned instance.
|
||||||
|
*
|
||||||
* Since: 2.72
|
* Since: 2.72
|
||||||
*/
|
*/
|
||||||
signals[LAUNCH_STARTED] = g_signal_new (I_("launch-started"),
|
signals[LAUNCH_STARTED] = g_signal_new (I_("launch-started"),
|
||||||
@ -1430,7 +1440,13 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
|
|||||||
* @platform_data: additional platform-specific data for this launch
|
* @platform_data: additional platform-specific data for this launch
|
||||||
*
|
*
|
||||||
* The #GAppLaunchContext::launched signal is emitted when a #GAppInfo is successfully
|
* The #GAppLaunchContext::launched signal is emitted when a #GAppInfo is successfully
|
||||||
* launched. The @platform_data is an GVariant dictionary mapping
|
* launched.
|
||||||
|
*
|
||||||
|
* Because a launch operation may involve spawning multiple instances of the
|
||||||
|
* target application, you should expect this signal to be emitted multiple
|
||||||
|
* times, one time for each spawned instance.
|
||||||
|
*
|
||||||
|
* The @platform_data is an GVariant dictionary mapping
|
||||||
* strings to variants (ie `a{sv}`), which contains additional,
|
* strings to variants (ie `a{sv}`), which contains additional,
|
||||||
* platform-specific data about this launch. On UNIX, at least the
|
* platform-specific data about this launch. On UNIX, at least the
|
||||||
* `pid` and `startup-notification-id` keys will be present.
|
* `pid` and `startup-notification-id` keys will be present.
|
||||||
|
@ -3261,9 +3261,8 @@ g_desktop_app_info_launch_uris (GAppInfo *appinfo,
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
GAppInfo *appinfo;
|
GList *uris; /* (element-type utf8) (owned) (nullable) */
|
||||||
GList *uris;
|
GAppLaunchContext *context; /* (owned) (nullable) */
|
||||||
GAppLaunchContext *context;
|
|
||||||
} LaunchUrisData;
|
} LaunchUrisData;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -3280,13 +3279,13 @@ launch_uris_with_dbus_cb (GObject *object,
|
|||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
GTask *task = G_TASK (user_data);
|
GTask *task = G_TASK (user_data);
|
||||||
GError *error = NULL;
|
GError *local_error = NULL;
|
||||||
|
|
||||||
g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
|
g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &local_error);
|
||||||
if (error != NULL)
|
if (local_error != NULL)
|
||||||
{
|
{
|
||||||
g_dbus_error_strip_remote_error (error);
|
g_dbus_error_strip_remote_error (local_error);
|
||||||
g_task_return_error (task, g_steal_pointer (&error));
|
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
g_task_return_boolean (task, TRUE);
|
g_task_return_boolean (task, TRUE);
|
||||||
@ -3316,7 +3315,7 @@ launch_uris_bus_get_cb (GObject *object,
|
|||||||
LaunchUrisData *data = g_task_get_task_data (task);
|
LaunchUrisData *data = g_task_get_task_data (task);
|
||||||
GCancellable *cancellable = g_task_get_cancellable (task);
|
GCancellable *cancellable = g_task_get_cancellable (task);
|
||||||
GDBusConnection *session_bus;
|
GDBusConnection *session_bus;
|
||||||
GError *error = NULL;
|
GError *local_error = NULL;
|
||||||
|
|
||||||
session_bus = g_bus_get_finish (result, NULL);
|
session_bus = g_bus_get_finish (result, NULL);
|
||||||
|
|
||||||
@ -3342,10 +3341,10 @@ launch_uris_bus_get_cb (GObject *object,
|
|||||||
data->uris, data->context,
|
data->uris, data->context,
|
||||||
_SPAWN_FLAGS_DEFAULT, NULL,
|
_SPAWN_FLAGS_DEFAULT, NULL,
|
||||||
NULL, NULL, NULL, -1, -1, -1,
|
NULL, NULL, NULL, -1, -1, -1,
|
||||||
&error);
|
&local_error);
|
||||||
if (error != NULL)
|
if (local_error != NULL)
|
||||||
{
|
{
|
||||||
g_task_return_error (task, g_steal_pointer (&error));
|
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||||
g_object_unref (task);
|
g_object_unref (task);
|
||||||
}
|
}
|
||||||
else if (session_bus)
|
else if (session_bus)
|
||||||
@ -3379,7 +3378,7 @@ g_desktop_app_info_launch_uris_async (GAppInfo *appinfo,
|
|||||||
|
|
||||||
data = g_new0 (LaunchUrisData, 1);
|
data = g_new0 (LaunchUrisData, 1);
|
||||||
data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
|
data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
|
||||||
data->context = (context != NULL) ? g_object_ref (context) : NULL;
|
g_set_object (&data->context, context);
|
||||||
g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
|
g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
|
||||||
|
|
||||||
g_bus_get (G_BUS_TYPE_SESSION, cancellable, launch_uris_bus_get_cb, task);
|
g_bus_get (G_BUS_TYPE_SESSION, cancellable, launch_uris_bus_get_cb, task);
|
||||||
@ -3923,40 +3922,40 @@ static void
|
|||||||
run_update_command (char *command,
|
run_update_command (char *command,
|
||||||
char *subdir)
|
char *subdir)
|
||||||
{
|
{
|
||||||
char *argv[3] = {
|
char *argv[3] = {
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
GPid pid = 0;
|
GPid pid = 0;
|
||||||
GError *error = NULL;
|
GError *local_error = NULL;
|
||||||
|
|
||||||
argv[0] = command;
|
argv[0] = command;
|
||||||
argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
|
argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
|
||||||
|
|
||||||
if (g_spawn_async ("/", argv,
|
if (g_spawn_async ("/", argv,
|
||||||
NULL, /* envp */
|
NULL, /* envp */
|
||||||
G_SPAWN_SEARCH_PATH |
|
G_SPAWN_SEARCH_PATH |
|
||||||
G_SPAWN_STDOUT_TO_DEV_NULL |
|
G_SPAWN_STDOUT_TO_DEV_NULL |
|
||||||
G_SPAWN_STDERR_TO_DEV_NULL |
|
G_SPAWN_STDERR_TO_DEV_NULL |
|
||||||
G_SPAWN_DO_NOT_REAP_CHILD,
|
G_SPAWN_DO_NOT_REAP_CHILD,
|
||||||
NULL, NULL, /* No setup function */
|
NULL, NULL, /* No setup function */
|
||||||
&pid,
|
&pid,
|
||||||
&error))
|
&local_error))
|
||||||
g_child_watch_add (pid, update_program_done, NULL);
|
g_child_watch_add (pid, update_program_done, NULL);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* If we get an error at this point, it's quite likely the user doesn't
|
/* If we get an error at this point, it's quite likely the user doesn't
|
||||||
* have an installed copy of either 'update-mime-database' or
|
* have an installed copy of either 'update-mime-database' or
|
||||||
* 'update-desktop-database'. I don't think we want to popup an error
|
* 'update-desktop-database'. I don't think we want to popup an error
|
||||||
* dialog at this point, so we just do a g_warning to give the user a
|
* dialog at this point, so we just do a g_warning to give the user a
|
||||||
* chance of debugging it.
|
* chance of debugging it.
|
||||||
*/
|
*/
|
||||||
g_warning ("%s", error->message);
|
g_warning ("%s", local_error->message);
|
||||||
g_error_free (error);
|
g_error_free (local_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free (argv[1]);
|
g_free (argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -4685,43 +4685,334 @@ get_appath_for_exe (const gchar *exe_basename)
|
|||||||
return appath;
|
return appath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* GDesktopAppInfo::launch_uris_async emits all GAppLaunchContext's signals
|
||||||
|
* on the main thread.
|
||||||
|
*
|
||||||
|
* We do the same: when g_win32_app_info_launch_uris_impl has a non-NULL
|
||||||
|
* from_task argument we schedule the signal emissions on the main loop,
|
||||||
|
* taking care not to emit signals after the task itself is completed
|
||||||
|
* (see g_task_get_completed).
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GAppLaunchContext *context; /* (owned) */
|
||||||
|
GWin32AppInfo *info; /* (owned) */
|
||||||
|
} EmitLaunchStartedData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_launch_started_data_free (EmitLaunchStartedData *data)
|
||||||
|
{
|
||||||
|
g_clear_object (&data->context);
|
||||||
|
g_clear_object (&data->info);
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
emit_launch_started_cb (EmitLaunchStartedData *data)
|
||||||
|
{
|
||||||
|
g_signal_emit_by_name (data->context, "launch-started", data->info, NULL);
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_launch_started (GAppLaunchContext *context,
|
||||||
|
GWin32AppInfo *info,
|
||||||
|
GTask *from_task)
|
||||||
|
{
|
||||||
|
if (!context || !info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!from_task)
|
||||||
|
g_signal_emit_by_name (context, "launch-started", info, NULL);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitLaunchStartedData *data;
|
||||||
|
|
||||||
|
data = g_new (EmitLaunchStartedData, 1);
|
||||||
|
data->context = g_object_ref (context);
|
||||||
|
data->info = g_object_ref (info);
|
||||||
|
|
||||||
|
g_main_context_invoke_full (g_task_get_context (from_task),
|
||||||
|
g_task_get_priority (from_task),
|
||||||
|
G_SOURCE_FUNC (emit_launch_started_cb),
|
||||||
|
g_steal_pointer (&data),
|
||||||
|
(GDestroyNotify) emit_launch_started_data_free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GAppLaunchContext *context; /* (owned) */
|
||||||
|
GWin32AppInfo *info; /* (owned) */
|
||||||
|
GPid pid; /* (owned) */
|
||||||
|
} EmitLaunchedData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_launched_data_free (EmitLaunchedData *data)
|
||||||
|
{
|
||||||
|
g_clear_object (&data->context);
|
||||||
|
g_clear_object (&data->info);
|
||||||
|
g_spawn_close_pid (data->pid);
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GVariant*
|
||||||
|
make_platform_data (GPid pid)
|
||||||
|
{
|
||||||
|
GVariantBuilder builder;
|
||||||
|
|
||||||
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
||||||
|
/* pid handles are never bigger than 2^24 as per
|
||||||
|
* https://docs.microsoft.com/en-us/windows/win32/sysinfo/kernel-objects,
|
||||||
|
* so truncating to `int32` is valid.
|
||||||
|
* The gsize cast is to silence a compiler warning
|
||||||
|
* about conversion from pointer to integer of
|
||||||
|
* different size. */
|
||||||
|
g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gsize) pid));
|
||||||
|
|
||||||
|
return g_variant_ref_sink (g_variant_builder_end (&builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
emit_launched_cb (EmitLaunchedData *data)
|
||||||
|
{
|
||||||
|
|
||||||
|
GVariant *platform_data = make_platform_data (data->pid);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (data->context, "launched", data->info, platform_data);
|
||||||
|
g_variant_unref (platform_data);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_launched (GAppLaunchContext *context,
|
||||||
|
GWin32AppInfo *info,
|
||||||
|
GPid *pid,
|
||||||
|
GTask *from_task)
|
||||||
|
{
|
||||||
|
if (!context || !info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!from_task)
|
||||||
|
{
|
||||||
|
GVariant *platform_data = make_platform_data (*pid);
|
||||||
|
g_signal_emit_by_name (context, "launched", info, platform_data);
|
||||||
|
g_variant_unref (platform_data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitLaunchedData *data;
|
||||||
|
|
||||||
|
data = g_new (EmitLaunchedData, 1);
|
||||||
|
data->context = g_object_ref (context);
|
||||||
|
data->info = g_object_ref (info);
|
||||||
|
data->pid = *pid;
|
||||||
|
|
||||||
|
g_main_context_invoke_full (g_task_get_context (from_task),
|
||||||
|
g_task_get_priority (from_task),
|
||||||
|
G_SOURCE_FUNC (emit_launched_cb),
|
||||||
|
g_steal_pointer (&data),
|
||||||
|
(GDestroyNotify) emit_launched_data_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GAppLaunchContext *context; /* (owned) */
|
||||||
|
GWin32AppInfo *info; /* (owned) */
|
||||||
|
} EmitLaunchFailedData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_launch_failed_data_free (EmitLaunchFailedData *data)
|
||||||
|
{
|
||||||
|
g_clear_object (&data->context);
|
||||||
|
g_clear_object (&data->info);
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
emit_launch_failed_cb (EmitLaunchFailedData *data)
|
||||||
|
{
|
||||||
|
g_signal_emit_by_name (data->context, "launch-failed", data->info, NULL);
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_launch_failed (GAppLaunchContext *context,
|
||||||
|
GWin32AppInfo *info,
|
||||||
|
GTask *from_task)
|
||||||
|
{
|
||||||
|
if (!context || !info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!from_task)
|
||||||
|
g_signal_emit_by_name (context, "launch-failed", info, NULL);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitLaunchFailedData *data;
|
||||||
|
|
||||||
|
data = g_new (EmitLaunchFailedData, 1);
|
||||||
|
data->context = g_object_ref (context);
|
||||||
|
data->info = g_object_ref (info);
|
||||||
|
|
||||||
|
g_main_context_invoke_full (g_task_get_context (from_task),
|
||||||
|
g_task_get_priority (from_task),
|
||||||
|
G_SOURCE_FUNC (emit_launch_failed_cb),
|
||||||
|
g_steal_pointer (&data),
|
||||||
|
(GDestroyNotify) emit_launch_failed_data_free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
|
g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
|
||||||
gboolean for_files,
|
gboolean for_files,
|
||||||
IShellItemArray *items,
|
IShellItemArray *items,
|
||||||
GWin32AppInfoShellVerb *shverb,
|
GWin32AppInfoShellVerb *shverb,
|
||||||
|
GAppLaunchContext *launch_context,
|
||||||
|
GTask *from_task,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
DWORD pid;
|
|
||||||
IApplicationActivationManager* paam = NULL;
|
IApplicationActivationManager* paam = NULL;
|
||||||
gboolean result = TRUE;
|
gboolean com_initialized = FALSE;
|
||||||
|
gboolean result = FALSE;
|
||||||
|
DWORD process_id = 0;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
const wchar_t *app_canonical_name = (const wchar_t *) info->app->canonical_name;
|
||||||
|
|
||||||
hr = CoCreateInstance (&CLSID_ApplicationActivationManager, NULL, CLSCTX_INPROC_SERVER, &IID_IApplicationActivationManager, (void **) &paam);
|
/* ApplicationActivationManager threading model is both,
|
||||||
|
* prefer the multithreaded apartment type, as we don't
|
||||||
|
* need anything of the STA here. */
|
||||||
|
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
|
||||||
|
if (SUCCEEDED (hr))
|
||||||
|
com_initialized = TRUE;
|
||||||
|
else if (hr != RPC_E_CHANGED_MODE)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to initialize the COM support library for the thread: 0x%lx", hr);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It's best to instantiate ApplicationActivationManager out-of-proc,
|
||||||
|
* as documented on MSDN:
|
||||||
|
*
|
||||||
|
* An IApplicationActivationManager object creates a thread in its
|
||||||
|
* host process to serve any activated event arguments objects
|
||||||
|
* (LaunchActivatedEventArgs, FileActivatedEventArgs, and Protocol-
|
||||||
|
* ActivatedEventArgs) that are passed to the app. If the calling
|
||||||
|
* process is long-lived, you can create this object in-proc,
|
||||||
|
* based on the assumption that the event arguments will exist long
|
||||||
|
* enough for the target app to use them.
|
||||||
|
* However, if the calling process is spawned only to launch the
|
||||||
|
* target app, it should create the IApplicationActivationManager
|
||||||
|
* object out-of-process, by using CLSCTX_LOCAL_SERVER. This causes
|
||||||
|
* the object to be created in a Dllhost instance that automatically
|
||||||
|
* manages the object's lifetime based on outstanding references to
|
||||||
|
* the activated event argument objects.
|
||||||
|
*/
|
||||||
|
hr = CoCreateInstance (&CLSID_ApplicationActivationManager, NULL,
|
||||||
|
CLSCTX_LOCAL_SERVER,
|
||||||
|
&IID_IApplicationActivationManager, (void **) &paam);
|
||||||
if (FAILED (hr))
|
if (FAILED (hr))
|
||||||
{
|
{
|
||||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
"Failed to create ApplicationActivationManager: 0x%lx", hr);
|
"Failed to create ApplicationActivationManager: 0x%lx", hr);
|
||||||
return FALSE;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit_launch_started (launch_context, info, from_task);
|
||||||
|
|
||||||
|
/* The Activate methods return a process identifier (PID), so we should consider
|
||||||
|
* those methods as potentially blocking */
|
||||||
if (items == NULL)
|
if (items == NULL)
|
||||||
hr = IApplicationActivationManager_ActivateApplication (paam, (const wchar_t *) info->app->canonical_name, NULL, AO_NONE, &pid);
|
hr = IApplicationActivationManager_ActivateApplication (paam,
|
||||||
|
app_canonical_name,
|
||||||
|
NULL, AO_NONE,
|
||||||
|
&process_id);
|
||||||
else if (for_files)
|
else if (for_files)
|
||||||
hr = IApplicationActivationManager_ActivateForFile (paam, (const wchar_t *) info->app->canonical_name, items, shverb->verb_name, &pid);
|
hr = IApplicationActivationManager_ActivateForFile (paam,
|
||||||
|
app_canonical_name,
|
||||||
|
items, shverb->verb_name,
|
||||||
|
&process_id);
|
||||||
else
|
else
|
||||||
hr = IApplicationActivationManager_ActivateForProtocol (paam, (const wchar_t *) info->app->canonical_name, items, &pid);
|
hr = IApplicationActivationManager_ActivateForProtocol (paam,
|
||||||
|
app_canonical_name,
|
||||||
|
items,
|
||||||
|
&process_id);
|
||||||
|
|
||||||
if (FAILED (hr))
|
if (FAILED (hr))
|
||||||
{
|
{
|
||||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
"The app %s failed to launch: 0x%lx",
|
"The app %s failed to launch: 0x%lx",
|
||||||
g_win32_appinfo_application_get_some_name (info->app), hr);
|
g_win32_appinfo_application_get_some_name (info->app), hr);
|
||||||
result = FALSE;
|
|
||||||
|
emit_launch_failed (launch_context, info, from_task);
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
else if (launch_context)
|
||||||
|
{
|
||||||
|
DWORD access_rights = 0;
|
||||||
|
HANDLE process_handle = NULL;
|
||||||
|
|
||||||
|
/* Unfortunately, there's a race condition here.
|
||||||
|
* ApplicationActivationManager methods return a process ID, but it
|
||||||
|
* keeps no open HANDLE to the spawned process internally (tested
|
||||||
|
* on Windows 10 21H2). So we cannot guarantee that by the time
|
||||||
|
* OpenProcess is called, process ID still referes to the spawned
|
||||||
|
* process. Anyway hitting such case is extremely unlikely.
|
||||||
|
*
|
||||||
|
* https://docs.microsoft.com/en-us/answers/questions/942879/
|
||||||
|
* iapplicationactivationmanager-race-condition.html
|
||||||
|
*
|
||||||
|
* Maybe we could make use of the WinRT APIs to activate UWP apps,
|
||||||
|
* instead? */
|
||||||
|
|
||||||
|
/* As documented on MSDN, the handle returned by CreateProcess has
|
||||||
|
* PROCESS_ALL_ACCESS rights. First try passing PROCESS_ALL_ACCESS
|
||||||
|
* to have the same access rights as the non-UWP code-path; should
|
||||||
|
* that fail with ERROR_ACCESS_DENIED error code, retry using safe
|
||||||
|
* access rights */
|
||||||
|
access_rights = PROCESS_ALL_ACCESS;
|
||||||
|
|
||||||
|
process_handle = OpenProcess (access_rights, FALSE, process_id);
|
||||||
|
|
||||||
|
if (!process_handle && GetLastError () == ERROR_ACCESS_DENIED)
|
||||||
|
{
|
||||||
|
DWORD access_rights = PROCESS_QUERY_LIMITED_INFORMATION |
|
||||||
|
SYNCHRONIZE;
|
||||||
|
|
||||||
|
process_handle = OpenProcess (access_rights, FALSE, process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process_handle)
|
||||||
|
{
|
||||||
|
g_warning ("OpenProcess failed with error code %" G_GUINT32_FORMAT,
|
||||||
|
(guint32) GetLastError ());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit the launched signal regardless if we have the process
|
||||||
|
* HANDLE or NULL */
|
||||||
|
emit_launched (launch_context, info, (GPid*) &process_handle, from_task);
|
||||||
|
|
||||||
|
g_spawn_close_pid ((GPid) process_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
IApplicationActivationManager_Release (paam);
|
result = TRUE;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if (paam)
|
||||||
|
{
|
||||||
|
IApplicationActivationManager_Release (paam);
|
||||||
|
paam = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (com_initialized)
|
||||||
|
{
|
||||||
|
CoUninitialize ();
|
||||||
|
com_initialized = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -4734,6 +5025,7 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
IShellItemArray *items, /* UWP only */
|
IShellItemArray *items, /* UWP only */
|
||||||
GAppLaunchContext *launch_context,
|
GAppLaunchContext *launch_context,
|
||||||
GSpawnFlags spawn_flags,
|
GSpawnFlags spawn_flags,
|
||||||
|
GTask *from_task,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
gboolean completed = FALSE;
|
gboolean completed = FALSE;
|
||||||
@ -4742,6 +5034,7 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
const gchar *command;
|
const gchar *command;
|
||||||
gchar *apppath;
|
gchar *apppath;
|
||||||
GWin32AppInfoShellVerb *shverb;
|
GWin32AppInfoShellVerb *shverb;
|
||||||
|
GPid pid = NULL;
|
||||||
|
|
||||||
g_return_val_if_fail (info != NULL, FALSE);
|
g_return_val_if_fail (info != NULL, FALSE);
|
||||||
g_return_val_if_fail (info->app != NULL, FALSE);
|
g_return_val_if_fail (info->app != NULL, FALSE);
|
||||||
@ -4776,6 +5069,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
for_files,
|
for_files,
|
||||||
items,
|
items,
|
||||||
shverb,
|
shverb,
|
||||||
|
launch_context,
|
||||||
|
from_task,
|
||||||
error);
|
error);
|
||||||
|
|
||||||
if (launch_context)
|
if (launch_context)
|
||||||
@ -4832,7 +5127,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
GPid pid;
|
if (from_task && g_task_return_error_if_cancelled (from_task))
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (!expand_application_parameters (info,
|
if (!expand_application_parameters (info,
|
||||||
command,
|
command,
|
||||||
@ -4842,6 +5138,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
error))
|
error))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
emit_launch_started (launch_context, info, from_task);
|
||||||
|
|
||||||
if (!g_spawn_async (NULL,
|
if (!g_spawn_async (NULL,
|
||||||
argv,
|
argv,
|
||||||
envp,
|
envp,
|
||||||
@ -4851,28 +5149,16 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
NULL,
|
NULL,
|
||||||
&pid,
|
&pid,
|
||||||
error))
|
error))
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (launch_context != NULL)
|
|
||||||
{
|
{
|
||||||
GVariantBuilder builder;
|
emit_launch_failed (launch_context, info, from_task);
|
||||||
GVariant *platform_data;
|
|
||||||
|
|
||||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
goto out;
|
||||||
/* pid handles are never bigger than 2^24 as per
|
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/sysinfo/kernel-objects,
|
|
||||||
* so truncating to `int32` is valid.
|
|
||||||
* The gsize cast is to silence a compiler warning
|
|
||||||
* about conversion from pointer to integer of
|
|
||||||
* different size. */
|
|
||||||
g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gsize) pid));
|
|
||||||
|
|
||||||
platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
|
|
||||||
g_signal_emit_by_name (launch_context, "launched", info, platform_data);
|
|
||||||
g_variant_unref (platform_data);
|
|
||||||
}
|
}
|
||||||
|
else if (launch_context)
|
||||||
|
emit_launched (launch_context, info, &pid, from_task);
|
||||||
|
|
||||||
g_spawn_close_pid (pid);
|
g_spawn_close_pid (pid);
|
||||||
|
pid = NULL;
|
||||||
g_strfreev (argv);
|
g_strfreev (argv);
|
||||||
argv = NULL;
|
argv = NULL;
|
||||||
}
|
}
|
||||||
@ -4880,7 +5166,8 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
|
|
||||||
completed = TRUE;
|
completed = TRUE;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
g_spawn_close_pid (pid);
|
||||||
g_strfreev (argv);
|
g_strfreev (argv);
|
||||||
g_strfreev (envp);
|
g_strfreev (envp);
|
||||||
|
|
||||||
@ -5028,10 +5315,11 @@ make_item_array (gboolean for_files,
|
|||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
g_win32_app_info_launch_uris_impl (GAppInfo *appinfo,
|
||||||
GList *uris,
|
GList *uris,
|
||||||
GAppLaunchContext *launch_context,
|
GAppLaunchContext *launch_context,
|
||||||
GError **error)
|
GTask *from_task,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
gboolean res = FALSE;
|
gboolean res = FALSE;
|
||||||
gboolean do_files;
|
gboolean do_files;
|
||||||
@ -5049,7 +5337,7 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = g_win32_app_info_launch_internal (info, NULL, FALSE, items, launch_context, 0, error);
|
res = g_win32_app_info_launch_internal (info, NULL, FALSE, items, launch_context, 0, from_task, error);
|
||||||
|
|
||||||
if (items != NULL)
|
if (items != NULL)
|
||||||
IShellItemArray_Release (items);
|
IShellItemArray_Release (items);
|
||||||
@ -5090,6 +5378,7 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
|||||||
NULL,
|
NULL,
|
||||||
launch_context,
|
launch_context,
|
||||||
G_SPAWN_SEARCH_PATH,
|
G_SPAWN_SEARCH_PATH,
|
||||||
|
from_task,
|
||||||
error);
|
error);
|
||||||
|
|
||||||
g_list_free_full (objs, free_file_or_uri);
|
g_list_free_full (objs, free_file_or_uri);
|
||||||
@ -5097,6 +5386,80 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_win32_app_info_launch_uris (GAppInfo *appinfo,
|
||||||
|
GList *uris,
|
||||||
|
GAppLaunchContext *launch_context,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_win32_app_info_launch_uris_impl (appinfo, uris, launch_context, NULL, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GList *uris; /* (element-type utf8) (owned) (nullable) */
|
||||||
|
GAppLaunchContext *context; /* (owned) (nullable) */
|
||||||
|
} LaunchUrisData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
launch_uris_data_free (LaunchUrisData *data)
|
||||||
|
{
|
||||||
|
g_clear_object (&data->context);
|
||||||
|
g_list_free_full (data->uris, g_free);
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
launch_uris_async_thread (GTask *task,
|
||||||
|
gpointer source_object,
|
||||||
|
gpointer task_data,
|
||||||
|
GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
GAppInfo *appinfo = G_APP_INFO (source_object);
|
||||||
|
LaunchUrisData *data = task_data;
|
||||||
|
GError *local_error = NULL;
|
||||||
|
gboolean succeeded;
|
||||||
|
|
||||||
|
succeeded = g_win32_app_info_launch_uris_impl (appinfo, data->uris, data->context, task, &local_error);
|
||||||
|
if (succeeded)
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
else if (!g_task_had_error (task))
|
||||||
|
g_task_return_error (task, g_steal_pointer (&local_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_win32_app_info_launch_uris_async (GAppInfo *appinfo,
|
||||||
|
GList *uris,
|
||||||
|
GAppLaunchContext *context,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task;
|
||||||
|
LaunchUrisData *data;
|
||||||
|
|
||||||
|
task = g_task_new (appinfo, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, g_win32_app_info_launch_uris_async);
|
||||||
|
|
||||||
|
data = g_new0 (LaunchUrisData, 1);
|
||||||
|
data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
|
||||||
|
g_set_object (&data->context, context);
|
||||||
|
g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
|
||||||
|
|
||||||
|
g_task_run_in_thread (task, launch_uris_async_thread);
|
||||||
|
g_object_unref (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_win32_app_info_launch_uris_finish (GAppInfo *appinfo,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (g_task_is_valid (result, appinfo), FALSE);
|
||||||
|
|
||||||
|
return g_task_propagate_boolean (G_TASK (result), error);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
g_win32_app_info_should_show (GAppInfo *appinfo)
|
g_win32_app_info_should_show (GAppInfo *appinfo)
|
||||||
{
|
{
|
||||||
@ -5128,7 +5491,7 @@ g_win32_app_info_launch (GAppInfo *appinfo,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = g_win32_app_info_launch_internal (info, NULL, TRUE, items, launch_context, 0, error);
|
res = g_win32_app_info_launch_internal (info, NULL, TRUE, items, launch_context, 0, NULL, error);
|
||||||
|
|
||||||
if (items != NULL)
|
if (items != NULL)
|
||||||
IShellItemArray_Release (items);
|
IShellItemArray_Release (items);
|
||||||
@ -5160,6 +5523,7 @@ g_win32_app_info_launch (GAppInfo *appinfo,
|
|||||||
NULL,
|
NULL,
|
||||||
launch_context,
|
launch_context,
|
||||||
G_SPAWN_SEARCH_PATH,
|
G_SPAWN_SEARCH_PATH,
|
||||||
|
NULL,
|
||||||
error);
|
error);
|
||||||
|
|
||||||
g_list_free_full (objs, free_file_or_uri);
|
g_list_free_full (objs, free_file_or_uri);
|
||||||
@ -5242,6 +5606,8 @@ g_win32_app_info_iface_init (GAppInfoIface *iface)
|
|||||||
iface->supports_uris = g_win32_app_info_supports_uris;
|
iface->supports_uris = g_win32_app_info_supports_uris;
|
||||||
iface->supports_files = g_win32_app_info_supports_files;
|
iface->supports_files = g_win32_app_info_supports_files;
|
||||||
iface->launch_uris = g_win32_app_info_launch_uris;
|
iface->launch_uris = g_win32_app_info_launch_uris;
|
||||||
|
iface->launch_uris_async = g_win32_app_info_launch_uris_async;
|
||||||
|
iface->launch_uris_finish = g_win32_app_info_launch_uris_finish;
|
||||||
iface->should_show = g_win32_app_info_should_show;
|
iface->should_show = g_win32_app_info_should_show;
|
||||||
/* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
|
/* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
|
||||||
/* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
|
/* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
|
||||||
|
Loading…
Reference in New Issue
Block a user