mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 15:36:14 +01:00
GWin32AppInfo: Emit GAppLaunchContext signals for all code paths
This commit is contained in:
parent
f0ff6a6af0
commit
cff3e660c1
@ -4685,18 +4685,198 @@ 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)
|
||||||
{
|
{
|
||||||
IApplicationActivationManager* paam = NULL;
|
IApplicationActivationManager* paam = NULL;
|
||||||
gboolean com_initialized = FALSE;
|
gboolean com_initialized = FALSE;
|
||||||
gboolean result = FALSE;
|
gboolean result = FALSE;
|
||||||
DWORD pid = 0;
|
DWORD process_id = 0;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
const wchar_t *app_canonical_name = (const wchar_t *) info->app->canonical_name;
|
const wchar_t *app_canonical_name = (const wchar_t *) info->app->canonical_name;
|
||||||
|
|
||||||
@ -4723,31 +4903,83 @@ g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit_launch_started (launch_context, info, from_task);
|
||||||
|
|
||||||
/* The Activate methods return a process identifier (PID), so we should consider
|
/* The Activate methods return a process identifier (PID), so we should consider
|
||||||
* those methods as potentially blocking */
|
* those methods as potentially blocking */
|
||||||
if (items == NULL)
|
if (items == NULL)
|
||||||
hr = IApplicationActivationManager_ActivateApplication (paam,
|
hr = IApplicationActivationManager_ActivateApplication (paam,
|
||||||
app_canonical_name,
|
app_canonical_name,
|
||||||
NULL, AO_NONE,
|
NULL, AO_NONE,
|
||||||
&pid);
|
&process_id);
|
||||||
else if (for_files)
|
else if (for_files)
|
||||||
hr = IApplicationActivationManager_ActivateForFile (paam,
|
hr = IApplicationActivationManager_ActivateForFile (paam,
|
||||||
app_canonical_name,
|
app_canonical_name,
|
||||||
items, shverb->verb_name,
|
items, shverb->verb_name,
|
||||||
&pid);
|
&process_id);
|
||||||
else
|
else
|
||||||
hr = IApplicationActivationManager_ActivateForProtocol (paam,
|
hr = IApplicationActivationManager_ActivateForProtocol (paam,
|
||||||
app_canonical_name,
|
app_canonical_name,
|
||||||
items,
|
items,
|
||||||
&pid);
|
&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);
|
||||||
|
|
||||||
|
emit_launch_failed (launch_context, info, from_task);
|
||||||
|
|
||||||
goto cleanup;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
result = TRUE;
|
result = TRUE;
|
||||||
|
|
||||||
@ -4785,6 +5017,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);
|
||||||
@ -4819,6 +5052,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)
|
||||||
@ -4875,8 +5110,6 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
GPid pid;
|
|
||||||
|
|
||||||
if (!expand_application_parameters (info,
|
if (!expand_application_parameters (info,
|
||||||
command,
|
command,
|
||||||
&objs,
|
&objs,
|
||||||
@ -4885,6 +5118,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,
|
||||||
@ -4894,28 +5129,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;
|
||||||
}
|
}
|
||||||
@ -4924,6 +5147,7 @@ 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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user