GWin32AppInfo: Pass only one item to ActivateForProtocol()

IApplicationActivationManager::ActivateForProtocol() only uses the first
item from the IShellItemArray. When we have to activate multiple URI's,
call ActivateForProtocol() repeatedly in a loop, each time with one URI.

References:

 [1] - IApplicationActivationManager::ActivateForProtocol method [MSDN]
       https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-iapplicationactivationmanager-activateforprotocol
This commit is contained in:
Luca Bacci 2022-12-22 22:01:21 +01:00
parent 7bbbb1ee42
commit 02417db370

View File

@ -4880,94 +4880,68 @@ emit_launch_failed (GAppLaunchContext *context,
} }
} }
static IShellItemArray * typedef enum
make_item_array (gboolean for_files, {
GList *objs, /* PLAIN: just open the application, without arguments of any kind
GError **error); * corresponds to: LaunchActivatedEventArgs */
UWP_ACTIVATION_TYPE_PLAIN,
/* FILE: open the applications passing a set of files
* corresponds to: FileActivatedEventArgs */
UWP_ACTIVATION_TYPE_FILE,
/* PROTOCOL: open the application passing a URI which describe an
app activity
* corresponds to: ProtocolActivatedEventArgs */
UWP_ACTIVATION_TYPE_PROTOCOL,
} UwpActivationType;
static gboolean static gboolean
g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info, g_win32_app_info_launch_uwp_single (IApplicationActivationManager *app_activation_manager,
gboolean for_files, UwpActivationType activation_type,
GList *objs, /* (element-type file_or_uri) */ IShellItemArray *items,
GWin32AppInfoShellVerb *shverb, const wchar_t *verb,
GWin32AppInfo *info,
GAppLaunchContext *launch_context, GAppLaunchContext *launch_context,
GTask *from_task, GTask *from_task,
GError **error) GError **error)
{ {
IApplicationActivationManager* paam = NULL; const wchar_t *canonical_name = (const wchar_t *) info->app->canonical_name;
gboolean com_initialized = FALSE;
gboolean result = FALSE;
IShellItemArray *items = NULL;
DWORD process_id = 0; DWORD process_id = 0;
HRESULT hr; HRESULT hr = S_OK;
const wchar_t *app_canonical_name = (const wchar_t *) info->app->canonical_name;
if (objs)
{
items = make_item_array (for_files, objs, error);
if (!items)
goto cleanup;
}
/* 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))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to create ApplicationActivationManager: 0x%lx", hr);
goto cleanup;
}
emit_launch_started (launch_context, info, from_task); 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 (objs == NULL)
hr = IApplicationActivationManager_ActivateApplication (paam, switch (activation_type)
app_canonical_name, {
case UWP_ACTIVATION_TYPE_PLAIN:
g_assert (items == NULL);
hr = IApplicationActivationManager_ActivateApplication (app_activation_manager,
canonical_name,
NULL, AO_NONE, NULL, AO_NONE,
&process_id); &process_id);
else if (for_files) break;
hr = IApplicationActivationManager_ActivateForFile (paam, case UWP_ACTIVATION_TYPE_PROTOCOL:
app_canonical_name, g_assert (items != NULL);
items, shverb->verb_name,
&process_id); hr = IApplicationActivationManager_ActivateForProtocol (app_activation_manager,
else canonical_name,
hr = IApplicationActivationManager_ActivateForProtocol (paam,
app_canonical_name,
items, items,
&process_id); &process_id);
break;
case UWP_ACTIVATION_TYPE_FILE:
g_assert (items != NULL);
hr = IApplicationActivationManager_ActivateForFile (app_activation_manager,
canonical_name,
items, verb,
&process_id);
break;
}
if (FAILED (hr)) if (FAILED (hr))
{ {
@ -4977,9 +4951,10 @@ g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
emit_launch_failed (launch_context, info, from_task); emit_launch_failed (launch_context, info, from_task);
goto cleanup; return FALSE;
} }
else if (launch_context)
if (launch_context)
{ {
DWORD access_rights = 0; DWORD access_rights = 0;
HANDLE process_handle = NULL; HANDLE process_handle = NULL;
@ -5027,7 +5002,118 @@ g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
g_spawn_close_pid ((GPid) process_handle); g_spawn_close_pid ((GPid) process_handle);
} }
result = TRUE; return TRUE;
}
static IShellItemArray *
make_item_array (gboolean for_files,
GList *objs,
GError **error);
static gboolean
g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
gboolean for_files,
GList *objs, /* (element-type file_or_uri) */
GWin32AppInfoShellVerb *shverb,
GAppLaunchContext *launch_context,
GTask *from_task,
GError **error)
{
IApplicationActivationManager *paam = NULL;
gboolean com_initialized = FALSE;
gboolean result = FALSE;
HRESULT hr;
/* 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))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to create ApplicationActivationManager: 0x%lx", hr);
goto cleanup;
}
if (!objs)
{
result = g_win32_app_info_launch_uwp_single (paam, UWP_ACTIVATION_TYPE_PLAIN, NULL, NULL,
info, launch_context, from_task, error);
}
else if (for_files)
{
IShellItemArray *items = make_item_array (TRUE, objs, error);
if (!items)
goto cleanup;
result = g_win32_app_info_launch_uwp_single (paam, UWP_ACTIVATION_TYPE_FILE, items,
shverb->verb_name,
info, launch_context, from_task, error);
IShellItemArray_Release (items);
}
else
{
gboolean outcome = TRUE;
GList *l;
for (l = objs; l != NULL; l = l->next)
{
IShellItemArray *item;
GList single;
single.data = l->data;
single.prev = NULL;
single.next = NULL;
item = make_item_array (FALSE, &single, error);
if (!item)
{
outcome = FALSE;
continue;
}
if (!g_win32_app_info_launch_uwp_single (paam, UWP_ACTIVATION_TYPE_PROTOCOL,
item, shverb->verb_name, info,
launch_context, from_task, error))
outcome = FALSE;
IShellItemArray_Release (item);
}
result = outcome;
}
cleanup: cleanup:
@ -5043,12 +5129,6 @@ cleanup:
com_initialized = FALSE; com_initialized = FALSE;
} }
if (items)
{
IShellItemArray_Release (items);
items = NULL;
}
return result; return result;
} }