2007-11-26 17:13:05 +01:00
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright ( C ) 2006 - 2007 Red Hat , Inc .
2014-08-29 10:53:35 +02:00
* Copyright ( C ) 2014 Р у с л а н И ж б у л а т о в
2007-11-26 17:13:05 +01:00
*
2022-05-18 10:12:45 +02:00
* SPDX - License - Identifier : LGPL - 2.1 - or - later
*
2007-11-26 17:13:05 +01:00
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2017-05-27 18:21:30 +02:00
* version 2.1 of the License , or ( at your option ) any later version .
2007-11-26 17:13:05 +01:00
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General
2014-01-23 12:58:29 +01:00
* Public License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2007-11-26 17:13:05 +01:00
*
2014-08-29 10:53:35 +02:00
* Authors : Alexander Larsson < alexl @ redhat . com >
* Р у с л а н И ж б у л а т о в < lrn1986 @ gmail . com >
2007-11-26 17:13:05 +01:00
*/
2008-06-22 17:10:51 +02:00
# include "config.h"
2007-11-26 17:13:05 +01:00
2020-06-04 20:05:22 +02:00
# define COBJMACROS
2007-11-26 17:13:05 +01:00
# include <string.h>
2012-07-04 03:14:41 +02:00
# include "gcontenttype.h"
2007-11-26 17:13:05 +01:00
# include "gwin32appinfo.h"
2008-07-02 03:52:37 +02:00
# include "gappinfo.h"
2007-11-26 17:13:05 +01:00
# include "gioerror.h"
2007-12-08 13:01:06 +01:00
# include "gfile.h"
2007-11-26 17:13:05 +01:00
# include <glib/gstdio.h>
# include "glibintl.h"
2014-08-29 10:53:35 +02:00
# include <gio/gwin32registrykey.h>
2020-06-04 20:05:22 +02:00
# include <shlobj.h>
/* Contains the definitions from shlobj.h that are
* guarded as Windows8 - or - newer and are unavailable
* to GLib , being only Windows7 - or - newer .
*/
# include "gwin32api-application-activation-manager.h"
2007-11-26 17:13:05 +01:00
# include <windows.h>
2020-12-15 16:27:06 +01:00
/* For SHLoadIndirectString() */
# include <shlwapi.h>
2007-11-26 17:13:05 +01:00
2019-12-29 12:35:49 +01:00
# include <glib/gstdioprivate.h>
2020-10-04 00:44:19 +02:00
# include "giowin32-priv.h"
2019-12-29 12:35:49 +01:00
# include "glib-private.h"
2014-08-29 10:53:35 +02:00
/* We need to watch 8 places:
* 0 ) HKEY_CURRENT_USER \ \ Software \ \ Microsoft \ \ Windows \ \ Shell \ \ Associations \ \ UrlAssociations
* ( anything below that key )
* On change : re - enumerate subkeys , read their values .
* 1 ) HKEY_CURRENT_USER \ \ Software \ \ Microsoft \ \ Windows \ \ CurrentVersion \ \ Explorer \ \ FileExts
* ( anything below that key )
* On change : re - enumerate subkeys
* 2 ) HKEY_CURRENT_USER \ \ Software \ \ Clients ( anything below that key )
* On change : re - read the whole hierarchy of handlers
* 3 ) HKEY_LOCAL_MACHINE \ \ Software \ \ Clients ( anything below that key )
* On change : re - read the whole hierarchy of handlers
* 4 ) HKEY_LOCAL_MACHINE \ \ Software \ \ RegisteredApplications ( values of that key )
* On change : re - read the value list of registered applications
* 5 ) HKEY_CURRENT_USER \ \ Software \ \ RegisteredApplications ( values of that key )
* On change : re - read the value list of registered applications
* 6 ) HKEY_CLASSES_ROOT \ \ Applications ( anything below that key )
* On change : re - read the whole hierarchy of apps
* 7 ) HKEY_CLASSES_ROOT ( only its subkeys )
* On change : re - enumerate subkeys , try to filter out wrong names .
*
2019-12-29 12:35:49 +01:00
*
* About verbs . A registry key ( the name of that key is known as ProgID )
* can contain a " shell " subkey , which can then contain a number of verb
* subkeys ( the most common being the " open " verb ) , and each of these
* contains a " command " subkey , which has a default string value that
* is the command to be run .
* Most ProgIDs are in HKEY_CLASSES_ROOT , but some are nested deeper in
* the registry ( such as HKEY_CURRENT_USER \ \ Software \ \ < softwarename > ) .
*
* Verb selection works like this ( according to https : //docs.microsoft.com/en-us/windows/win32/shell/context ):
* 1 ) If " open " verb is available , that verb is used .
* 2 ) If the Shell subkey has a default string value , and if a verb subkey
* with that name exists , that verb is used .
* 3 ) The first subkey found in the list of verb subkeys is used .
* 4 ) The " openwith " verb is used
*
* Testing suggests that Windows never reaches the point 4 in any realistic
* circumstances . If a " command " subkey is missing for a verb , or if it has
* an empty string as its default value , the app launch fails
* ( the " openwith " verb is not used , even if it ' s present ) .
* If the command is present , but is not valid ( runs nonexisting executable ,
* for example ) , then other verbs are not checked .
* It seems that when the documentation said " openwith verb " , it meant
* that Windows invokes the default " Open with... " dialog ( it does not
* look at the " openwith " verb subkey , even if it ' s there ) .
* If a verb subkey that is supposed to be used is present , but it lacks
* a command subkey , an error message is shown and nothing else happens .
2014-08-29 10:53:35 +02:00
*/
2019-12-29 12:35:49 +01:00
# define _verb_idx(array,index) ((GWin32AppInfoShellVerb *) g_ptr_array_index (array, index))
# define _lookup_by_verb(array, verb, dst, itemtype) do { \
gsize _index ; \
itemtype * _v ; \
for ( _index = 0 ; array & & _index < array - > len ; _index + + ) \
{ \
_v = ( itemtype * ) g_ptr_array_index ( array , _index ) ; \
if ( _wcsicmp ( _v - > verb_name , ( verb ) ) = = 0 ) \
{ \
* ( dst ) = _v ; \
break ; \
} \
} \
if ( array = = NULL | | _index > = array - > len ) \
* ( dst ) = NULL ; \
} while ( 0 )
# define _verb_lookup(array, verb, dst) _lookup_by_verb (array, verb, dst, GWin32AppInfoShellVerb)
/* Because with subcommands a verb would have
* a name like " foo \\ bar " , but the key its command
* should be looked for is " shell \\ foo \\ shell \\ bar \\ command "
*/
typedef struct _reg_verb {
gunichar2 * name ;
gunichar2 * shellpath ;
} reg_verb ;
2014-08-29 10:53:35 +02:00
typedef struct _GWin32AppInfoURLSchema GWin32AppInfoURLSchema ;
typedef struct _GWin32AppInfoFileExtension GWin32AppInfoFileExtension ;
2019-12-29 12:35:49 +01:00
typedef struct _GWin32AppInfoShellVerb GWin32AppInfoShellVerb ;
2014-08-29 10:53:35 +02:00
typedef struct _GWin32AppInfoHandler GWin32AppInfoHandler ;
typedef struct _GWin32AppInfoApplication GWin32AppInfoApplication ;
typedef struct _GWin32AppInfoURLSchemaClass GWin32AppInfoURLSchemaClass ;
typedef struct _GWin32AppInfoFileExtensionClass GWin32AppInfoFileExtensionClass ;
2019-12-29 12:35:49 +01:00
typedef struct _GWin32AppInfoShellVerbClass GWin32AppInfoShellVerbClass ;
2014-08-29 10:53:35 +02:00
typedef struct _GWin32AppInfoHandlerClass GWin32AppInfoHandlerClass ;
typedef struct _GWin32AppInfoApplicationClass GWin32AppInfoApplicationClass ;
struct _GWin32AppInfoURLSchemaClass
{
GObjectClass parent_class ;
} ;
struct _GWin32AppInfoFileExtensionClass
{
GObjectClass parent_class ;
} ;
struct _GWin32AppInfoHandlerClass
{
GObjectClass parent_class ;
} ;
struct _GWin32AppInfoApplicationClass
{
GObjectClass parent_class ;
} ;
2019-12-29 12:35:49 +01:00
struct _GWin32AppInfoShellVerbClass
{
GObjectClass parent_class ;
} ;
2014-08-29 10:53:35 +02:00
struct _GWin32AppInfoURLSchema {
GObject parent_instance ;
/* url schema (stuff before ':') */
gunichar2 * schema ;
/* url schema (stuff before ':'), in UTF-8 */
gchar * schema_u8 ;
/* url schema (stuff before ':'), in UTF-8, folded */
2019-12-29 12:35:49 +01:00
gchar * schema_u8_folded ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
/* Handler currently selected for this schema. Can be NULL. */
2014-08-29 10:53:35 +02:00
GWin32AppInfoHandler * chosen_handler ;
2019-12-29 12:35:49 +01:00
/* Maps folded handler IDs -> to GWin32AppInfoHandlers for this schema.
* Includes the chosen handler , if any .
*/
2014-08-29 10:53:35 +02:00
GHashTable * handlers ;
} ;
struct _GWin32AppInfoHandler {
GObject parent_instance ;
2019-12-29 12:35:49 +01:00
/* Usually a class name in HKCR */
2014-08-29 10:53:35 +02:00
gunichar2 * handler_id ;
2020-06-04 17:13:33 +02:00
/* Registry object obtained by opening @handler_id.
* Can be used to watch this handler .
* May be % NULL ( for fake handlers that we made up ) .
*/
2014-08-29 10:53:35 +02:00
GWin32RegistryKey * key ;
2019-12-29 12:35:49 +01:00
/* @handler_id, in UTF-8, folded */
2014-08-29 10:53:35 +02:00
gchar * handler_id_folded ;
2019-12-29 12:35:49 +01:00
/* Icon of the application for this handler */
GIcon * icon ;
/* Verbs that this handler supports */
GPtrArray * verbs ; /* of GWin32AppInfoShellVerb */
2020-01-28 06:12:21 +01:00
/* AppUserModelID for a UWP application. When this is not NULL,
* this handler launches a UWP application .
* UWP applications are launched using a COM interface and have no commandlines ,
* and the verbs will reflect that too .
*/
gunichar2 * uwp_aumid ;
2019-12-29 12:35:49 +01:00
} ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
struct _GWin32AppInfoShellVerb {
GObject parent_instance ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
/* The verb that is used to invoke this handler. */
gunichar2 * verb_name ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
/* User-friendly (localized) verb name. */
gchar * verb_displayname ;
2014-08-29 10:53:35 +02:00
2020-01-28 06:12:21 +01:00
/* %TRUE if this verb is for a UWP app.
* It means that @ command , @ executable and @ dll_function are % NULL .
*/
gboolean is_uwp ;
2019-12-29 12:35:49 +01:00
/* shell/verb/command */
gunichar2 * command ;
/* Same as @command, but in UTF-8 */
gchar * command_utf8 ;
2014-08-29 10:53:35 +02:00
/* Executable of the program (UTF-8) */
gchar * executable ;
2019-12-29 12:35:49 +01:00
/* Executable of the program (for matching, in folded form; UTF-8) */
gchar * executable_folded ;
2014-08-29 10:53:35 +02:00
/* Pointer to a location within @executable */
gchar * executable_basename ;
2019-11-29 17:48:32 +01:00
/* If not NULL, then @executable and its derived fields contain the name
* of a DLL file ( without the name of the function that rundll32 . exe should
* invoke ) , and this field contains the name of the function to be invoked .
* The application is then invoked as ' rundll32 . exe " dll_path " , dll_function other_arguments . . . ' .
*/
gchar * dll_function ;
2019-12-29 12:35:49 +01:00
/* The application that is linked to this verb. */
2014-08-29 10:53:35 +02:00
GWin32AppInfoApplication * app ;
} ;
struct _GWin32AppInfoFileExtension {
GObject parent_instance ;
/* File extension (with leading '.') */
gunichar2 * extension ;
/* File extension (with leading '.'), in UTF-8 */
gchar * extension_u8 ;
2019-12-29 12:35:49 +01:00
/* handler currently selected for this extension. Can be NULL. */
2014-08-29 10:53:35 +02:00
GWin32AppInfoHandler * chosen_handler ;
2019-12-29 12:35:49 +01:00
/* Maps folded handler IDs -> to GWin32AppInfoHandlers for this extension.
* Includes the chosen handler , if any .
*/
2014-08-29 10:53:35 +02:00
GHashTable * handlers ;
} ;
struct _GWin32AppInfoApplication {
GObject parent_instance ;
2019-12-29 12:35:49 +01:00
/* Canonical name (used for key names).
* For applications tracked by id this is the root registry
* key path for the application .
* For applications tracked by executable name this is the
* basename of the executable .
2020-06-04 17:13:33 +02:00
* For UWP apps this is the AppUserModelID .
2019-12-29 12:35:49 +01:00
* 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
* all that a commandline happen to give us ) .
*/
2014-08-29 10:53:35 +02:00
gunichar2 * canonical_name ;
2019-12-29 12:35:49 +01:00
/* @canonical_name, in UTF-8 */
2014-08-29 10:53:35 +02:00
gchar * canonical_name_u8 ;
2019-12-29 12:35:49 +01:00
/* @canonical_name, in UTF-8, folded */
2014-08-29 10:53:35 +02:00
gchar * canonical_name_folded ;
/* Human-readable name in English. Can be NULL */
gunichar2 * pretty_name ;
/* Human-readable name in English, UTF-8. Can be NULL */
gchar * pretty_name_u8 ;
/* Human-readable name in user's language. Can be NULL */
gunichar2 * localized_pretty_name ;
/* Human-readable name in user's language, UTF-8. Can be NULL */
gchar * localized_pretty_name_u8 ;
/* Description, could be in user's language. Can be NULL */
gunichar2 * description ;
/* Description, could be in user's language, UTF-8. Can be NULL */
gchar * description_u8 ;
2019-12-29 12:35:49 +01:00
/* Verbs that this application supports */
GPtrArray * verbs ; /* of GWin32AppInfoShellVerb */
2019-11-29 17:48:32 +01:00
2014-08-29 10:53:35 +02:00
/* Explicitly supported URLs, hashmap from map-owned gchar ptr (schema,
2019-12-29 12:35:49 +01:00
* UTF - 8 , folded ) - > to a GWin32AppInfoHandler
2014-08-29 10:53:35 +02:00
* Schema can be used as a key in the urls hashmap .
*/
GHashTable * supported_urls ;
/* Explicitly supported extensions, hashmap from map-owned gchar ptr
2019-12-29 12:35:49 +01:00
* ( . extension , UTF - 8 , folded ) - > to a GWin32AppInfoHandler
2014-08-29 10:53:35 +02:00
* Extension can be used as a key in the extensions hashmap .
*/
GHashTable * supported_exts ;
/* Icon of the application (remember, handler can have its own icon too) */
GIcon * icon ;
/* Set to TRUE to prevent this app from appearing in lists of apps for
* opening files . This will not prevent it from appearing in lists of apps
* just for running , or lists of apps for opening exts / urls for which this
* app reports explicit support .
*/
gboolean no_open_with ;
/* Set to TRUE for applications from HKEY_CURRENT_USER.
* Give them priority over applications from HKEY_LOCAL_MACHINE , when all
* other things are equal .
*/
gboolean user_specific ;
/* Set to TRUE for applications that are machine-wide defaults (i.e. default
* browser ) */
gboolean default_app ;
2020-01-28 06:12:21 +01:00
2020-06-04 17:13:33 +02:00
/* Set to TRUE for UWP applications */
gboolean is_uwp ;
2014-08-29 10:53:35 +02:00
} ;
# define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ())
# define G_WIN32_APPINFO_URL_SCHEMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_URL_SCHEMA, GWin32AppInfoURLSchema))
# define G_TYPE_WIN32_APPINFO_FILE_EXTENSION (g_win32_appinfo_file_extension_get_type ())
# define G_WIN32_APPINFO_FILE_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_FILE_EXTENSION, GWin32AppInfoFileExtension))
# define G_TYPE_WIN32_APPINFO_HANDLER (g_win32_appinfo_handler_get_type ())
# define G_WIN32_APPINFO_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_HANDLER, GWin32AppInfoHandler))
# define G_TYPE_WIN32_APPINFO_APPLICATION (g_win32_appinfo_application_get_type ())
# define G_WIN32_APPINFO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_APPLICATION, GWin32AppInfoApplication))
2019-12-29 12:35:49 +01:00
# define G_TYPE_WIN32_APPINFO_SHELL_VERB (g_win32_appinfo_shell_verb_get_type ())
# define G_WIN32_APPINFO_SHELL_VERB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_WIN32_APPINFO_SHELL_VERB, GWin32AppInfoShellVerb))
2014-08-29 10:53:35 +02:00
GType g_win32_appinfo_url_schema_get_type ( void ) G_GNUC_CONST ;
GType g_win32_appinfo_file_extension_get_type ( void ) G_GNUC_CONST ;
2019-12-29 12:35:49 +01:00
GType g_win32_appinfo_shell_verb_get_type ( void ) G_GNUC_CONST ;
2014-08-29 10:53:35 +02:00
GType g_win32_appinfo_handler_get_type ( void ) G_GNUC_CONST ;
GType g_win32_appinfo_application_get_type ( void ) G_GNUC_CONST ;
G_DEFINE_TYPE ( GWin32AppInfoURLSchema , g_win32_appinfo_url_schema , G_TYPE_OBJECT )
G_DEFINE_TYPE ( GWin32AppInfoFileExtension , g_win32_appinfo_file_extension , G_TYPE_OBJECT )
2019-12-29 12:35:49 +01:00
G_DEFINE_TYPE ( GWin32AppInfoShellVerb , g_win32_appinfo_shell_verb , G_TYPE_OBJECT )
2014-08-29 10:53:35 +02:00
G_DEFINE_TYPE ( GWin32AppInfoHandler , g_win32_appinfo_handler , G_TYPE_OBJECT )
G_DEFINE_TYPE ( GWin32AppInfoApplication , g_win32_appinfo_application , G_TYPE_OBJECT )
static void
g_win32_appinfo_url_schema_dispose ( GObject * object )
{
GWin32AppInfoURLSchema * url = G_WIN32_APPINFO_URL_SCHEMA ( object ) ;
g_clear_pointer ( & url - > schema , g_free ) ;
g_clear_pointer ( & url - > schema_u8 , g_free ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & url - > schema_u8_folded , g_free ) ;
2014-08-29 10:53:35 +02:00
g_clear_object ( & url - > chosen_handler ) ;
g_clear_pointer ( & url - > handlers , g_hash_table_destroy ) ;
G_OBJECT_CLASS ( g_win32_appinfo_url_schema_parent_class ) - > dispose ( object ) ;
}
static void
g_win32_appinfo_handler_dispose ( GObject * object )
{
GWin32AppInfoHandler * handler = G_WIN32_APPINFO_HANDLER ( object ) ;
g_clear_pointer ( & handler - > handler_id , g_free ) ;
g_clear_pointer ( & handler - > handler_id_folded , g_free ) ;
g_clear_object ( & handler - > key ) ;
g_clear_object ( & handler - > icon ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & handler - > verbs , g_ptr_array_unref ) ;
2020-01-28 06:12:21 +01:00
g_clear_pointer ( & handler - > uwp_aumid , g_free ) ;
2014-08-29 10:53:35 +02:00
G_OBJECT_CLASS ( g_win32_appinfo_handler_parent_class ) - > dispose ( object ) ;
}
static void
g_win32_appinfo_file_extension_dispose ( GObject * object )
{
GWin32AppInfoFileExtension * ext = G_WIN32_APPINFO_FILE_EXTENSION ( object ) ;
g_clear_pointer ( & ext - > extension , g_free ) ;
g_clear_pointer ( & ext - > extension_u8 , g_free ) ;
g_clear_object ( & ext - > chosen_handler ) ;
g_clear_pointer ( & ext - > handlers , g_hash_table_destroy ) ;
G_OBJECT_CLASS ( g_win32_appinfo_file_extension_parent_class ) - > dispose ( object ) ;
}
2019-12-29 12:35:49 +01:00
static void
g_win32_appinfo_shell_verb_dispose ( GObject * object )
{
GWin32AppInfoShellVerb * shverb = G_WIN32_APPINFO_SHELL_VERB ( object ) ;
g_clear_pointer ( & shverb - > verb_name , g_free ) ;
g_clear_pointer ( & shverb - > verb_displayname , g_free ) ;
g_clear_pointer ( & shverb - > command , g_free ) ;
g_clear_pointer ( & shverb - > command_utf8 , g_free ) ;
g_clear_pointer ( & shverb - > executable_folded , g_free ) ;
g_clear_pointer ( & shverb - > executable , g_free ) ;
g_clear_pointer ( & shverb - > dll_function , g_free ) ;
g_clear_object ( & shverb - > app ) ;
G_OBJECT_CLASS ( g_win32_appinfo_shell_verb_parent_class ) - > dispose ( object ) ;
}
2014-08-29 10:53:35 +02:00
static void
g_win32_appinfo_application_dispose ( GObject * object )
{
GWin32AppInfoApplication * app = G_WIN32_APPINFO_APPLICATION ( object ) ;
g_clear_pointer ( & app - > canonical_name_u8 , g_free ) ;
g_clear_pointer ( & app - > canonical_name_folded , g_free ) ;
g_clear_pointer ( & app - > canonical_name , g_free ) ;
g_clear_pointer ( & app - > pretty_name , g_free ) ;
g_clear_pointer ( & app - > localized_pretty_name , g_free ) ;
g_clear_pointer ( & app - > description , g_free ) ;
g_clear_pointer ( & app - > pretty_name_u8 , g_free ) ;
g_clear_pointer ( & app - > localized_pretty_name_u8 , g_free ) ;
g_clear_pointer ( & app - > description_u8 , g_free ) ;
g_clear_pointer ( & app - > supported_urls , g_hash_table_destroy ) ;
g_clear_pointer ( & app - > supported_exts , g_hash_table_destroy ) ;
g_clear_object ( & app - > icon ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & app - > verbs , g_ptr_array_unref ) ;
2014-08-29 10:53:35 +02:00
G_OBJECT_CLASS ( g_win32_appinfo_application_parent_class ) - > dispose ( object ) ;
}
2019-12-29 12:35:49 +01:00
static const gchar *
g_win32_appinfo_application_get_some_name ( GWin32AppInfoApplication * app )
{
if ( app - > localized_pretty_name_u8 )
return app - > localized_pretty_name_u8 ;
if ( app - > pretty_name_u8 )
return app - > pretty_name_u8 ;
return app - > canonical_name_u8 ;
}
2014-08-29 10:53:35 +02:00
static void
g_win32_appinfo_url_schema_class_init ( GWin32AppInfoURLSchemaClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > dispose = g_win32_appinfo_url_schema_dispose ;
}
static void
g_win32_appinfo_file_extension_class_init ( GWin32AppInfoFileExtensionClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > dispose = g_win32_appinfo_file_extension_dispose ;
}
2019-12-29 12:35:49 +01:00
static void
g_win32_appinfo_shell_verb_class_init ( GWin32AppInfoShellVerbClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > dispose = g_win32_appinfo_shell_verb_dispose ;
}
2014-08-29 10:53:35 +02:00
static void
g_win32_appinfo_handler_class_init ( GWin32AppInfoHandlerClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > dispose = g_win32_appinfo_handler_dispose ;
}
static void
g_win32_appinfo_application_class_init ( GWin32AppInfoApplicationClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > dispose = g_win32_appinfo_application_dispose ;
}
static void
g_win32_appinfo_url_schema_init ( GWin32AppInfoURLSchema * self )
{
self - > handlers = g_hash_table_new_full ( g_str_hash ,
g_str_equal ,
g_free ,
g_object_unref ) ;
}
2019-12-29 12:35:49 +01:00
static void
g_win32_appinfo_shell_verb_init ( GWin32AppInfoShellVerb * self )
{
}
2014-08-29 10:53:35 +02:00
static void
g_win32_appinfo_file_extension_init ( GWin32AppInfoFileExtension * self )
{
self - > handlers = g_hash_table_new_full ( g_str_hash ,
g_str_equal ,
g_free ,
g_object_unref ) ;
}
static void
g_win32_appinfo_handler_init ( GWin32AppInfoHandler * self )
{
2019-12-29 12:35:49 +01:00
self - > verbs = g_ptr_array_new_with_free_func ( g_object_unref ) ;
2014-08-29 10:53:35 +02:00
}
static void
g_win32_appinfo_application_init ( GWin32AppInfoApplication * self )
{
self - > supported_urls = g_hash_table_new_full ( g_str_hash ,
g_str_equal ,
g_free ,
g_object_unref ) ;
self - > supported_exts = g_hash_table_new_full ( g_str_hash ,
g_str_equal ,
g_free ,
g_object_unref ) ;
2019-12-29 12:35:49 +01:00
self - > verbs = g_ptr_array_new_with_free_func ( g_object_unref ) ;
2014-08-29 10:53:35 +02:00
}
2020-10-04 00:44:19 +02:00
/* The AppInfo threadpool that does asynchronous AppInfo tree rebuilds */
static GThreadPool * gio_win32_appinfo_threadpool ;
/* This mutex is held by a thread that reads or writes the AppInfo tree.
* ( tree object references can be obtained and later read without
* holding this mutex , since objects are practically immutable ) .
*/
static GMutex gio_win32_appinfo_mutex ;
/* Any thread wanting to access AppInfo can wait on this condition */
static GCond gio_win32_appinfo_cond ;
/* Increased to indicate that AppInfo tree does needs to be rebuilt.
* AppInfo thread checks this to see if it needs to
* do a tree re - build . If the value changes during a rebuild ,
* another rebuild is triggered after that .
* Other threads check this to see if they need
* to wait for a tree re - build to finish .
*/
static gint gio_win32_appinfo_update_counter = 0 ;
2014-08-29 10:53:35 +02:00
/* Map of owned ".ext" (with '.', UTF-8, folded)
* to GWin32AppInfoFileExtension ptr
*/
static GHashTable * extensions = NULL ;
/* Map of owned "schema" (without ':', UTF-8, folded)
* to GWin32AppInfoURLSchema ptr
*/
static GHashTable * urls = NULL ;
/* Map of owned "appID" (UTF-8, folded) to
2019-12-29 12:35:49 +01:00
* a GWin32AppInfoApplication
2014-08-29 10:53:35 +02:00
*/
static GHashTable * apps_by_id = NULL ;
/* Map of owned "app.exe" (UTF-8, folded) to
2019-12-29 12:35:49 +01:00
* a GWin32AppInfoApplication .
2014-08-29 10:53:35 +02:00
* This map and its values are separate from apps_by_id . The fact that an app
* with known ID has the same executable [ base ] name as an app in this map does
* not mean that they are the same application .
*/
static GHashTable * apps_by_exe = NULL ;
2019-12-29 12:35:49 +01:00
/* Map of owned "path:\to\app.exe" (UTF-8, folded) to
* a GWin32AppInfoApplication .
* The app objects in this map are fake - they are linked to
* handlers that do not have any apps associated with them .
*/
static GHashTable * fake_apps = NULL ;
2014-08-29 10:53:35 +02:00
/* Map of owned "handler id" (UTF-8, folded)
2019-12-29 12:35:49 +01:00
* to a GWin32AppInfoHandler
2014-08-29 10:53:35 +02:00
*/
static GHashTable * handlers = NULL ;
2020-12-15 16:27:06 +01:00
/* Temporary (only exists while the registry is being scanned) table
* that maps GWin32RegistryKey objects ( keeps a ref ) to owned AUMId wchar strings .
*/
static GHashTable * uwp_handler_table = NULL ;
2014-08-29 10:53:35 +02:00
/* Watch this whole subtree */
static GWin32RegistryKey * url_associations_key ;
/* Watch this whole subtree */
static GWin32RegistryKey * file_exts_key ;
/* Watch this whole subtree */
static GWin32RegistryKey * user_clients_key ;
/* Watch this whole subtree */
static GWin32RegistryKey * system_clients_key ;
/* Watch this key */
static GWin32RegistryKey * user_registered_apps_key ;
/* Watch this key */
static GWin32RegistryKey * system_registered_apps_key ;
/* Watch this whole subtree */
static GWin32RegistryKey * applications_key ;
/* Watch this key */
static GWin32RegistryKey * classes_root_key ;
# define URL_ASSOCIATIONS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
# define USER_CHOICE L"\\UserChoice"
# define OPEN_WITH_PROGIDS L"\\OpenWithProgids"
# define FILE_EXTS L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"
# define HKCR L"HKEY_CLASSES_ROOT\\"
# define HKCU L"HKEY_CURRENT_USER\\"
# define HKLM L"HKEY_LOCAL_MACHINE\\"
# define REG_PATH_MAX 256
# define REG_PATH_MAX_SIZE (REG_PATH_MAX * sizeof (gunichar2))
2019-11-29 17:48:32 +01:00
/* for g_wcsdup(),
* _g_win32_extract_executable ( ) ,
* _g_win32_fixup_broken_microsoft_rundll_commandline ( )
*/
# include "giowin32-private.c"
2020-06-04 17:13:33 +02:00
/* for g_win32_package_parser_enum_packages() */
# include "gwin32packageparser.h"
2014-08-29 10:53:35 +02:00
static void
2019-12-29 12:35:49 +01:00
read_handler_icon ( GWin32RegistryKey * key ,
2014-08-29 10:53:35 +02:00
GIcon * * icon_out )
{
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * icon_key ;
GWin32RegistryValueType default_type ;
gchar * default_value ;
g_assert ( icon_out ) ;
2014-08-29 10:53:35 +02:00
* icon_out = NULL ;
2019-12-29 12:35:49 +01:00
icon_key = g_win32_registry_key_get_child_w ( key , L " DefaultIcon " , NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( icon_key = = NULL )
return ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( g_win32_registry_key_get_value ( icon_key ,
NULL ,
TRUE ,
" " ,
& default_type ,
( gpointer * ) & default_value ,
NULL ,
NULL ) )
{
2020-12-15 16:27:06 +01:00
/* TODO: For UWP handlers this string is usually in @{...} form,
* see grab_registry_string ( ) below . Right now this
* string is read as - is and the icon would silently fail to load .
* Also , right now handler icon is not used anywhere
* ( only app icon is used ) .
*/
2019-12-29 12:35:49 +01:00
if ( default_type = = G_WIN32_REGISTRY_VALUE_STR & &
default_value [ 0 ] ! = ' \0 ' )
* icon_out = g_themed_icon_new ( default_value ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & default_value , g_free ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_object_unref ( icon_key ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
static void
reg_verb_free ( gpointer p )
{
if ( p = = NULL )
return ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_free ( ( ( reg_verb * ) p ) - > name ) ;
g_free ( ( ( reg_verb * ) p ) - > shellpath ) ;
g_free ( p ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
# define is_open(x) ( \
( ( x ) [ 0 ] = = L ' o ' | | ( x ) [ 0 ] = = L ' O ' ) & & \
( ( x ) [ 1 ] = = L ' p ' | | ( x ) [ 1 ] = = L ' P ' ) & & \
( ( x ) [ 2 ] = = L ' e ' | | ( x ) [ 2 ] = = L ' E ' ) & & \
( ( x ) [ 3 ] = = L ' n ' | | ( x ) [ 3 ] = = L ' N ' ) & & \
( ( x ) [ 4 ] = = L ' \0 ' ) \
)
/* default verb (if any) comes first,
* then " open " , then the rest of the verbs
* are sorted alphabetically
*/
static gint
compare_verbs ( gconstpointer a ,
gconstpointer b ,
gpointer user_data )
{
const reg_verb * ca = ( const reg_verb * ) a ;
const reg_verb * cb = ( const reg_verb * ) b ;
const gunichar2 * def = ( const gunichar2 * ) user_data ;
gboolean is_open_ca ;
gboolean is_open_cb ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( def ! = NULL )
{
if ( _wcsicmp ( ca - > name , def ) = = 0 )
return - 1 ;
2020-12-17 17:43:23 +01:00
else if ( _wcsicmp ( cb - > name , def ) = = 0 )
return 1 ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
is_open_ca = is_open ( ca - > name ) ;
is_open_cb = is_open ( cb - > name ) ;
if ( is_open_ca & & ! is_open_cb )
return - 1 ;
2020-12-17 17:43:23 +01:00
else if ( is_open_ca & & ! is_open_cb )
return 1 ;
2019-12-29 12:35:49 +01:00
return _wcsicmp ( ca - > name , cb - > name ) ;
2014-08-29 10:53:35 +02:00
}
static gboolean build_registry_path ( gunichar2 * output , gsize output_size , . . . ) G_GNUC_NULL_TERMINATED ;
static gboolean build_registry_pathv ( gunichar2 * output , gsize output_size , va_list components ) ;
static GWin32RegistryKey * _g_win32_registry_key_build_and_new_w ( GError * * error , . . . ) G_GNUC_NULL_TERMINATED ;
2019-12-29 12:35:49 +01:00
/* Called by process_verbs_commands.
* @ verb is a verb name
* @ command_line is the commandline of that verb
* @ command_line_utf8 is the UTF - 8 version of @ command_line
* @ verb_displayname is the prettier display name of the verb ( might be NULL )
* @ verb_is_preferred is TRUE if the verb is the preferred one
* @ invent_new_verb_name is TRUE when the verb should be added
* even if a verb with such
* name already exists ( in which case
* a new name is invented ) , unless
* the existing verb runs exactly the same
* commandline .
*/
typedef void ( * verb_command_func ) ( gpointer handler_data1 ,
gpointer handler_data2 ,
const gunichar2 * verb ,
const gunichar2 * command_line ,
const gchar * command_line_utf8 ,
const gchar * verb_displayname ,
gboolean verb_is_preferred ,
gboolean invent_new_verb_name ) ;
static gunichar2 * decide_which_id_to_use ( const gunichar2 * program_id ,
GWin32RegistryKey * * return_key ,
gchar * * return_handler_id_u8 ,
2020-01-28 04:40:39 +01:00
gchar * * return_handler_id_u8_folded ,
gunichar2 * * return_uwp_aumid ) ;
2019-12-29 12:35:49 +01:00
static GWin32AppInfoURLSchema * get_schema_object ( const gunichar2 * schema ,
const gchar * schema_u8 ,
const gchar * schema_u8_folded ) ;
static GWin32AppInfoHandler * get_handler_object ( const gchar * handler_id_u8_folded ,
GWin32RegistryKey * handler_key ,
2020-01-28 06:12:21 +01:00
const gunichar2 * handler_id ,
const gunichar2 * uwp_aumid ) ;
2019-12-29 12:35:49 +01:00
static GWin32AppInfoFileExtension * get_ext_object ( const gunichar2 * ext ,
const gchar * ext_u8 ,
const gchar * ext_u8_folded ) ;
static void process_verbs_commands ( GList * verbs ,
const reg_verb * preferred_verb ,
const gunichar2 * path_to_progid ,
const gunichar2 * progid ,
gboolean autoprefer_first_verb ,
verb_command_func handler ,
gpointer handler_data1 ,
gpointer handler_data2 ) ;
static void handler_add_verb ( gpointer handler_data1 ,
gpointer handler_data2 ,
const gunichar2 * verb ,
const gunichar2 * command_line ,
const gchar * command_line_utf8 ,
const gchar * verb_displayname ,
gboolean verb_is_preferred ,
gboolean invent_new_verb_name ) ;
2020-06-04 17:13:33 +02:00
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 ) ;
2014-08-29 10:53:35 +02:00
/* output_size is in *bytes*, not gunichar2s! */
static gboolean
build_registry_path ( gunichar2 * output , gsize output_size , . . . )
{
va_list ap ;
gboolean result ;
va_start ( ap , output_size ) ;
result = build_registry_pathv ( output , output_size , ap ) ;
va_end ( ap ) ;
return result ;
}
/* output_size is in *bytes*, not gunichar2s! */
static gboolean
build_registry_pathv ( gunichar2 * output , gsize output_size , va_list components )
{
va_list lentest ;
gunichar2 * p ;
gunichar2 * component ;
gsize length ;
if ( output = = NULL )
return FALSE ;
2022-10-21 15:48:47 +02:00
va_copy ( lentest , components ) ;
2014-08-29 10:53:35 +02:00
for ( length = 0 , component = va_arg ( lentest , gunichar2 * ) ;
component ! = NULL ;
component = va_arg ( lentest , gunichar2 * ) )
{
length + = wcslen ( component ) ;
}
va_end ( lentest ) ;
if ( ( length > = REG_PATH_MAX_SIZE ) | |
( length * sizeof ( gunichar2 ) > = output_size ) )
return FALSE ;
output [ 0 ] = L ' \0 ' ;
for ( p = output , component = va_arg ( components , gunichar2 * ) ;
component ! = NULL ;
component = va_arg ( components , gunichar2 * ) )
{
length = wcslen ( component ) ;
wcscat ( p , component ) ;
p + = length ;
}
return TRUE ;
}
static GWin32RegistryKey *
_g_win32_registry_key_build_and_new_w ( GError * * error , . . . )
{
va_list ap ;
gunichar2 key_path [ REG_PATH_MAX_SIZE + 1 ] ;
GWin32RegistryKey * key ;
va_start ( ap , error ) ;
key = NULL ;
if ( build_registry_pathv ( key_path , sizeof ( key_path ) , ap ) )
key = g_win32_registry_key_new_w ( key_path , error ) ;
va_end ( ap ) ;
return key ;
}
2019-12-29 12:35:49 +01:00
/* Gets the list of shell verbs (a GList of reg_verb, put into @verbs)
* from the @ program_id_key .
* If one of the verbs should be preferred ,
* a pointer to this verb ( in the GList ) will be
* put into @ preferred_verb .
* Does not automatically assume that the first verb
* is preferred ( when no other preferences exist ) .
* @ verbname_prefix is prefixed to the name of the verb
* ( this is used for subcommands ) and is initially an
* empty string .
* @ verbshell_prefix is the subkey of @ program_id_key
* that contains the verbs . It is " Shell " initially ,
* but grows with recursive invocations ( for subcommands ) .
2020-06-04 17:13:33 +02:00
* @ 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 .
2019-12-29 12:35:49 +01:00
* Returns TRUE on success , FALSE on failure .
2019-11-29 17:48:32 +01:00
*/
2014-08-29 10:53:35 +02:00
static gboolean
2019-12-29 12:35:49 +01:00
get_verbs ( GWin32RegistryKey * program_id_key ,
const reg_verb * * preferred_verb ,
GList * * verbs ,
const gunichar2 * verbname_prefix ,
2020-06-04 17:13:33 +02:00
const gunichar2 * verbshell_prefix ,
gboolean * is_uwp )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GWin32RegistrySubkeyIter iter ;
2014-08-29 10:53:35 +02:00
GWin32RegistryKey * key ;
GWin32RegistryValueType val_type ;
2019-12-29 12:35:49 +01:00
gunichar2 * default_verb ;
gsize verbshell_prefix_len ;
gsize verbname_prefix_len ;
GList * i ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_assert ( program_id_key & & verbs & & preferred_verb ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
* verbs = NULL ;
* preferred_verb = NULL ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
key = g_win32_registry_key_get_child_w ( program_id_key ,
verbshell_prefix ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( key = = NULL )
return FALSE ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ! g_win32_registry_subkey_iter_init ( & iter , key , NULL ) )
2014-08-29 10:53:35 +02:00
{
g_object_unref ( key ) ;
2019-12-29 12:35:49 +01:00
return FALSE ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
verbshell_prefix_len = g_utf16_len ( verbshell_prefix ) ;
verbname_prefix_len = g_utf16_len ( verbname_prefix ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
while ( g_win32_registry_subkey_iter_next ( & iter , TRUE , NULL ) )
{
const gunichar2 * name ;
gsize name_len ;
GWin32RegistryKey * subkey ;
gboolean has_subcommands ;
const reg_verb * tmp ;
GWin32RegistryValueType subc_type ;
reg_verb * rverb ;
const gunichar2 * shell = L " Shell " ;
const gsize shell_len = g_utf16_len ( shell ) ;
if ( ! g_win32_registry_subkey_iter_get_name_w ( & iter , & name , & name_len , NULL ) )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
subkey = g_win32_registry_key_get_child_w ( key ,
name ,
NULL ) ;
2021-09-21 16:09:23 +02:00
/* We may not have the required access rights to open the child key */
if ( subkey = = NULL )
continue ;
2019-12-29 12:35:49 +01:00
/* The key we're looking at is "<some_root>/Shell/<this_key>",
* where " Shell " is verbshell_prefix .
* If it has a value named ' Subcommands ' ( doesn ' t matter what its data is ) ,
* it means that this key has its own Shell subkey , the subkeys
* of which are shell commands ( i . e . < some_root > / Shell / < this_key > / Shell / < some_other_keys > ) .
* To handle that , create new , extended nameprefix and shellprefix ,
* and call the function recursively .
* name prefix " " - > " <this_key_name> \\ "
* shell prefix " Shell " - > " Shell \\ <this_key_name> \\ Shell "
* The root , program_id_key , remains the same in all invocations .
* Essentially , we ' re flattening the command tree into a list .
*/
has_subcommands = FALSE ;
2020-06-04 17:13:33 +02:00
if ( ( is_uwp = = NULL | | ! ( * is_uwp ) ) & & /* Assume UWP apps don't have subcommands */
g_win32_registry_key_get_value_w ( subkey ,
2019-12-29 12:35:49 +01:00
NULL ,
TRUE ,
L " Subcommands " ,
& subc_type ,
NULL ,
NULL ,
NULL ) & &
subc_type = = G_WIN32_REGISTRY_VALUE_STR )
{
2020-06-04 17:13:33 +02:00
gboolean dummy = FALSE ;
2020-12-23 02:19:31 +01:00
gunichar2 * new_nameprefix = g_new ( gunichar2 , verbname_prefix_len + name_len + 1 + 1 ) ;
gunichar2 * new_shellprefix = g_new ( gunichar2 , verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1 ) ;
2019-12-29 12:35:49 +01:00
memcpy ( & new_shellprefix [ 0 ] , verbshell_prefix , verbshell_prefix_len * sizeof ( gunichar2 ) ) ;
new_shellprefix [ verbshell_prefix_len ] = L ' \\ ' ;
memcpy ( & new_shellprefix [ verbshell_prefix_len + 1 ] , name , name_len * sizeof ( gunichar2 ) ) ;
new_shellprefix [ verbshell_prefix_len + 1 + name_len ] = L ' \\ ' ;
memcpy ( & new_shellprefix [ verbshell_prefix_len + 1 + name_len + 1 ] , shell , shell_len * sizeof ( gunichar2 ) ) ;
new_shellprefix [ verbshell_prefix_len + 1 + name_len + 1 + shell_len ] = 0 ;
memcpy ( & new_nameprefix [ 0 ] , verbname_prefix , verbname_prefix_len * sizeof ( gunichar2 ) ) ;
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 ;
2020-06-04 17:13:33 +02:00
has_subcommands = get_verbs ( program_id_key , & tmp , verbs , new_nameprefix , new_shellprefix , & dummy ) ;
2019-12-29 12:35:49 +01:00
g_free ( new_shellprefix ) ;
g_free ( new_nameprefix ) ;
}
2019-11-29 17:48:32 +01:00
2019-12-29 12:35:49 +01:00
/* Presence of subcommands means that this key itself is not a command-key */
if ( has_subcommands )
2020-06-04 17:13:33 +02:00
{
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 ) ;
2019-12-29 12:35:49 +01:00
/* 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 ( ) .
* The name of the verb is also saved .
* verbname_prefix is prefixed to the verb name ( it ' s either an empty string
* or already ends with a ' \\ ' , so no extra separators needed ) .
* verbshell_prefix is prefixed to the verb key path ( this one needs a separator ,
* because it never has one - all verbshell prefixes end with " Shell " , not " Shell \\ " )
*/
rverb = g_new0 ( reg_verb , 1 ) ;
2020-12-23 02:19:31 +01:00
rverb - > name = g_new ( gunichar2 , verbname_prefix_len + name_len + 1 ) ;
2019-12-29 12:35:49 +01:00
memcpy ( & rverb - > name [ 0 ] , verbname_prefix , verbname_prefix_len * sizeof ( gunichar2 ) ) ;
memcpy ( & rverb - > name [ verbname_prefix_len ] , name , name_len * sizeof ( gunichar2 ) ) ;
rverb - > name [ verbname_prefix_len + name_len ] = 0 ;
2020-12-23 02:19:31 +01:00
rverb - > shellpath = g_new ( gunichar2 , verbshell_prefix_len + 1 + name_len + 1 ) ;
2019-12-29 12:35:49 +01:00
memcpy ( & rverb - > shellpath [ 0 ] , verbshell_prefix , verbshell_prefix_len * sizeof ( gunichar2 ) ) ;
memcpy ( & rverb - > shellpath [ verbshell_prefix_len ] , L " \\ " , sizeof ( gunichar2 ) ) ;
memcpy ( & rverb - > shellpath [ verbshell_prefix_len + 1 ] , name , name_len * sizeof ( gunichar2 ) ) ;
rverb - > shellpath [ verbshell_prefix_len + 1 + name_len ] = 0 ;
* verbs = g_list_append ( * verbs , rverb ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
g_win32_registry_subkey_iter_clear ( & iter ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( * verbs = = NULL )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
g_object_unref ( key ) ;
2019-11-29 17:48:32 +01:00
2014-08-29 10:53:35 +02:00
return FALSE ;
}
2019-12-29 12:35:49 +01:00
default_verb = NULL ;
2019-11-29 17:48:32 +01:00
2019-12-29 12:35:49 +01:00
if ( g_win32_registry_key_get_value_w ( key ,
NULL ,
TRUE ,
L " " ,
& val_type ,
( void * * ) & default_verb ,
NULL ,
NULL ) & &
( val_type ! = G_WIN32_REGISTRY_VALUE_STR | |
g_utf16_len ( default_verb ) < = 0 ) )
g_clear_pointer ( & default_verb , g_free ) ;
g_object_unref ( key ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
/* Only sort at the top level */
if ( verbname_prefix [ 0 ] = = 0 )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
* verbs = g_list_sort_with_data ( * verbs , compare_verbs , default_verb ) ;
for ( i = * verbs ; default_verb & & * preferred_verb = = NULL & & i ; i = i - > next )
if ( _wcsicmp ( default_verb , ( ( const reg_verb * ) i - > data ) - > name ) = = 0 )
* preferred_verb = ( const reg_verb * ) i - > data ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & default_verb , g_free ) ;
2014-08-29 10:53:35 +02:00
return TRUE ;
}
2019-12-29 12:35:49 +01:00
/* Grabs a URL association (from HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
* or from an application with Capabilities , or just a schema subkey in HKCR ) .
* @ program_id is a ProgID of the handler for the URL .
* @ schema is the schema for the URL .
* @ schema_u8 and @ schema_u8_folded are UTF - 8 and folded UTF - 8
* respectively .
* @ app is the app to which the URL handler belongs ( can be NULL ) .
* @ is_user_choice is TRUE if this association is clearly preferred
*/
2014-08-29 10:53:35 +02:00
static void
2019-12-29 12:35:49 +01:00
get_url_association ( const gunichar2 * program_id ,
const gunichar2 * schema ,
const gchar * schema_u8 ,
const gchar * schema_u8_folded ,
GWin32AppInfoApplication * app ,
gboolean is_user_choice )
2014-08-29 10:53:35 +02:00
{
GWin32AppInfoURLSchema * schema_rec ;
GWin32AppInfoHandler * handler_rec ;
2019-12-29 12:35:49 +01:00
gunichar2 * handler_id ;
GList * verbs ;
const reg_verb * preferred_verb ;
gchar * handler_id_u8 ;
gchar * handler_id_u8_folded ;
2020-01-28 04:40:39 +01:00
gunichar2 * uwp_aumid ;
2020-06-04 17:13:33 +02:00
gboolean is_uwp ;
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * handler_key ;
if ( ( handler_id = decide_which_id_to_use ( program_id ,
& handler_key ,
& handler_id_u8 ,
2020-01-28 04:40:39 +01:00
& handler_id_u8_folded ,
& uwp_aumid ) ) = = NULL )
2014-08-29 10:53:35 +02:00
return ;
2020-06-04 17:13:33 +02:00
is_uwp = uwp_aumid ! = NULL ;
if ( ! get_verbs ( handler_key , & preferred_verb , & verbs , L " " , L " Shell " , & is_uwp ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
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 ) ;
2020-06-04 17:13:33 +02:00
g_clear_pointer ( & uwp_aumid , g_free ) ;
2019-12-29 12:35:49 +01:00
2014-08-29 10:53:35 +02:00
return ;
}
2020-06-04 17:13:33 +02:00
if ( ! is_uwp & & uwp_aumid ! = NULL )
g_clear_pointer ( & uwp_aumid , g_free ) ;
2019-12-29 12:35:49 +01:00
schema_rec = get_schema_object ( schema ,
schema_u8 ,
schema_u8_folded ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
handler_rec = get_handler_object ( handler_id_u8_folded ,
handler_key ,
2020-01-28 06:12:21 +01:00
handler_id ,
uwp_aumid ) ;
2019-12-29 12:35:49 +01:00
if ( is_user_choice | | schema_rec - > chosen_handler = = NULL )
g_set_object ( & schema_rec - > chosen_handler , handler_rec ) ;
g_hash_table_insert ( schema_rec - > handlers ,
g_strdup ( handler_id_u8_folded ) ,
g_object_ref ( handler_rec ) ) ;
g_clear_object ( & handler_key ) ;
if ( app )
g_hash_table_insert ( app - > supported_urls ,
g_strdup ( schema_rec - > schema_u8_folded ) ,
g_object_ref ( handler_rec ) ) ;
2020-01-28 06:12:21 +01:00
if ( uwp_aumid = = NULL )
process_verbs_commands ( g_steal_pointer ( & verbs ) ,
preferred_verb ,
HKCR ,
handler_id ,
TRUE ,
handler_add_verb ,
handler_rec ,
app ) ;
2020-06-04 17:13:33 +02:00
else
process_uwp_verbs ( g_steal_pointer ( & verbs ) ,
preferred_verb ,
HKCR ,
handler_id ,
TRUE ,
handler_rec ,
app ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & handler_id_u8 , g_free ) ;
g_clear_pointer ( & handler_id_u8_folded , g_free ) ;
g_clear_pointer ( & handler_id , g_free ) ;
2020-01-28 06:12:21 +01:00
g_clear_pointer ( & uwp_aumid , g_free ) ;
2019-12-29 12:35:49 +01:00
}
/* Grabs a file extension association (from HKCR\.ext or similar).
* @ program_id is a ProgID of the handler for the extension .
* @ file_extension is the extension ( with the leading ' . ' )
* @ app is the app to which the extension handler belongs ( can be NULL ) .
* @ is_user_choice is TRUE if this is clearly the preferred association
*/
static void
get_file_ext ( const gunichar2 * program_id ,
const gunichar2 * file_extension ,
GWin32AppInfoApplication * app ,
gboolean is_user_choice )
{
GWin32AppInfoHandler * handler_rec ;
gunichar2 * handler_id ;
const reg_verb * preferred_verb ;
GList * verbs ;
gchar * handler_id_u8 ;
gchar * handler_id_u8_folded ;
2020-01-28 04:40:39 +01:00
gunichar2 * uwp_aumid ;
2020-06-04 17:13:33 +02:00
gboolean is_uwp ;
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * handler_key ;
GWin32AppInfoFileExtension * file_extn ;
gchar * file_extension_u8 ;
gchar * file_extension_u8_folded ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ( handler_id = decide_which_id_to_use ( program_id ,
& handler_key ,
& handler_id_u8 ,
2020-01-28 04:40:39 +01:00
& handler_id_u8_folded ,
& uwp_aumid ) ) = = NULL )
2019-12-29 12:35:49 +01:00
return ;
if ( ! g_utf16_to_utf8_and_fold ( file_extension ,
- 1 ,
& file_extension_u8 ,
& file_extension_u8_folded ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & handler_id , g_free ) ;
g_clear_pointer ( & handler_id_u8 , g_free ) ;
g_clear_pointer ( & handler_id_u8_folded , g_free ) ;
2020-01-28 06:12:21 +01:00
g_clear_pointer ( & uwp_aumid , g_free ) ;
2019-12-29 12:35:49 +01:00
g_clear_object ( & handler_key ) ;
2014-08-29 10:53:35 +02:00
return ;
}
2020-06-04 17:13:33 +02:00
is_uwp = uwp_aumid ! = NULL ;
if ( ! get_verbs ( handler_key , & preferred_verb , & verbs , L " " , L " Shell " , & is_uwp ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
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 ) ;
g_clear_pointer ( & file_extension_u8 , g_free ) ;
g_clear_pointer ( & file_extension_u8_folded , g_free ) ;
2020-06-04 17:13:33 +02:00
g_clear_pointer ( & uwp_aumid , g_free ) ;
2019-12-29 12:35:49 +01:00
2014-08-29 10:53:35 +02:00
return ;
}
2020-06-04 17:13:33 +02:00
if ( ! is_uwp & & uwp_aumid ! = NULL )
g_clear_pointer ( & uwp_aumid , g_free ) ;
2019-12-29 12:35:49 +01:00
file_extn = get_ext_object ( file_extension , file_extension_u8 , file_extension_u8_folded ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
handler_rec = get_handler_object ( handler_id_u8_folded ,
handler_key ,
2020-01-28 06:12:21 +01:00
handler_id ,
uwp_aumid ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( is_user_choice | | file_extn - > chosen_handler = = NULL )
g_set_object ( & file_extn - > chosen_handler , handler_rec ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_hash_table_insert ( file_extn - > handlers ,
g_strdup ( handler_id_u8_folded ) ,
g_object_ref ( handler_rec ) ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( app )
g_hash_table_insert ( app - > supported_exts ,
g_strdup ( file_extension_u8_folded ) ,
2014-08-29 10:53:35 +02:00
g_object_ref ( handler_rec ) ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & file_extension_u8 , g_free ) ;
g_clear_pointer ( & file_extension_u8_folded , g_free ) ;
g_clear_object ( & handler_key ) ;
2020-01-28 06:12:21 +01:00
if ( uwp_aumid = = NULL )
process_verbs_commands ( g_steal_pointer ( & verbs ) ,
preferred_verb ,
HKCR ,
handler_id ,
TRUE ,
handler_add_verb ,
handler_rec ,
app ) ;
2020-06-04 17:13:33 +02:00
else
process_uwp_verbs ( g_steal_pointer ( & verbs ) ,
preferred_verb ,
HKCR ,
handler_id ,
TRUE ,
handler_rec ,
app ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & handler_id , g_free ) ;
g_clear_pointer ( & handler_id_u8 , g_free ) ;
g_clear_pointer ( & handler_id_u8_folded , g_free ) ;
2020-01-28 06:12:21 +01:00
g_clear_pointer ( & uwp_aumid , g_free ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
/* Returns either a @program_id or the string from
* the default value of the program_id key ( which is a name
* of a proxy class ) , or NULL .
* Does not check that proxy represents a valid
* record , just checks that it exists .
* Can return the class key ( HKCR / program_id or HKCR / proxy_id ) .
* Can convert returned value to UTF - 8 and fold it .
*/
static gunichar2 *
decide_which_id_to_use ( const gunichar2 * program_id ,
GWin32RegistryKey * * return_key ,
gchar * * return_handler_id_u8 ,
2020-01-28 04:40:39 +01:00
gchar * * return_handler_id_u8_folded ,
gunichar2 * * return_uwp_aumid )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * key ;
2020-01-28 04:40:39 +01:00
GWin32RegistryKey * uwp_key ;
2014-08-29 10:53:35 +02:00
GWin32RegistryValueType val_type ;
gunichar2 * proxy_id ;
2019-12-29 12:35:49 +01:00
gunichar2 * return_id ;
2020-01-28 04:40:39 +01:00
gunichar2 * uwp_aumid ;
2019-12-29 12:35:49 +01:00
gboolean got_value ;
gchar * handler_id_u8 ;
gchar * handler_id_u8_folded ;
g_assert ( program_id ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( return_key )
* return_key = NULL ;
2014-08-29 10:53:35 +02:00
2020-01-28 04:40:39 +01:00
if ( return_uwp_aumid )
* return_uwp_aumid = NULL ;
2019-12-29 12:35:49 +01:00
key = g_win32_registry_key_get_child_w ( classes_root_key , program_id , NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( key = = NULL )
return NULL ;
2014-08-29 10:53:35 +02:00
2020-01-28 04:40:39 +01:00
/* Check for UWP first */
uwp_aumid = NULL ;
uwp_key = g_win32_registry_key_get_child_w ( key , L " Application " , NULL ) ;
if ( uwp_key ! = NULL )
{
got_value = g_win32_registry_key_get_value_w ( uwp_key ,
NULL ,
TRUE ,
L " AppUserModelID " ,
& val_type ,
( void * * ) & uwp_aumid ,
NULL ,
NULL ) ;
if ( got_value & & val_type ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & uwp_aumid , g_free ) ;
2020-12-15 16:27:06 +01:00
/* Other values in the Application key contain useful information
* ( description , name , icon ) , but it ' s inconvenient to read
* it here ( we don ' t have an app object * yet * ) . Store the key
* in a table instead , and look at it later .
*/
2020-01-28 04:40:39 +01:00
if ( uwp_aumid = = NULL )
g_debug ( " ProgramID %S looks like a UWP application, but isn't " ,
program_id ) ;
2020-12-15 16:27:06 +01:00
else
g_hash_table_insert ( uwp_handler_table , g_object_ref ( uwp_key ) , g_wcsdup ( uwp_aumid , - 1 ) ) ;
2020-01-28 04:40:39 +01:00
g_object_unref ( uwp_key ) ;
}
/* Then check for proxy */
2019-12-29 12:35:49 +01:00
proxy_id = NULL ;
2020-01-28 04:40:39 +01:00
if ( uwp_aumid = = NULL )
{
got_value = g_win32_registry_key_get_value_w ( key ,
NULL ,
TRUE ,
L " " ,
& val_type ,
( void * * ) & proxy_id ,
NULL ,
NULL ) ;
if ( got_value & & val_type ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & proxy_id , g_free ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
return_id = NULL ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( proxy_id )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * proxy_key ;
proxy_key = g_win32_registry_key_get_child_w ( classes_root_key , proxy_id , NULL ) ;
if ( proxy_key )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
if ( return_key )
* return_key = g_steal_pointer ( & proxy_key ) ;
g_clear_object ( & proxy_key ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
return_id = g_steal_pointer ( & proxy_id ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & proxy_id , g_free ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ( return_handler_id_u8 | |
return_handler_id_u8_folded ) & &
! g_utf16_to_utf8_and_fold ( return_id = = NULL ? program_id : return_id ,
- 1 ,
& handler_id_u8 ,
& handler_id_u8_folded ) )
{
g_clear_object ( & key ) ;
if ( return_key )
g_clear_object ( return_key ) ;
g_clear_pointer ( & return_id , g_free ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
return NULL ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( return_handler_id_u8 )
* return_handler_id_u8 = g_steal_pointer ( & handler_id_u8 ) ;
2020-11-18 16:19:51 +01:00
g_clear_pointer ( & handler_id_u8 , g_free ) ;
2019-12-29 12:35:49 +01:00
if ( return_handler_id_u8_folded )
* return_handler_id_u8_folded = g_steal_pointer ( & handler_id_u8_folded ) ;
2020-11-18 16:19:51 +01:00
g_clear_pointer ( & handler_id_u8_folded , g_free ) ;
2020-01-28 04:40:39 +01:00
if ( return_uwp_aumid )
* return_uwp_aumid = g_steal_pointer ( & uwp_aumid ) ;
g_clear_pointer ( & uwp_aumid , g_free ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( return_id = = NULL & & return_key )
* return_key = g_steal_pointer ( & key ) ;
g_clear_object ( & key ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( return_id = = NULL )
return g_wcsdup ( program_id , - 1 ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
return return_id ;
}
/* Grabs the command for each verb from @verbs,
* and invokes @ handler for it . Consumes @ verbs .
* @ path_to_progid and @ progid are concatenated to
* produce a path to the key where Shell / verb / command
* subkeys are looked up .
* @ preferred_verb , if not NULL , will be used to inform
* the @ handler that a verb is preferred .
* @ autoprefer_first_verb will automatically make the first
* verb to be preferred , if @ preferred_verb is NULL .
* @ handler_data1 and @ handler_data2 are passed to @ handler as - is .
*/
static void
process_verbs_commands ( GList * verbs ,
const reg_verb * preferred_verb ,
const gunichar2 * path_to_progid ,
const gunichar2 * progid ,
gboolean autoprefer_first_verb ,
verb_command_func handler ,
gpointer handler_data1 ,
gpointer handler_data2 )
{
GList * i ;
gboolean got_value ;
g_assert ( handler ! = NULL ) ;
g_assert ( verbs ! = NULL ) ;
g_assert ( progid ! = NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
for ( i = verbs ; i ; i = i - > next )
{
const reg_verb * verb = ( const reg_verb * ) i - > data ;
GWin32RegistryKey * key ;
GWin32RegistryKey * verb_key ;
gunichar2 * command_value ;
gchar * command_value_utf8 ;
GWin32RegistryValueType val_type ;
gunichar2 * verb_displayname ;
gchar * verb_displayname_u8 ;
key = _g_win32_registry_key_build_and_new_w ( NULL , path_to_progid , progid ,
L " \\ " , verb - > shellpath , L " \\ command " , NULL ) ;
if ( key = = NULL )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
g_debug ( " %S%S \\ shell \\ %S does not have a \" command \" subkey " ,
path_to_progid , progid , verb - > shellpath ) ;
continue ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
command_value = NULL ;
got_value = g_win32_registry_key_get_value_w ( key ,
NULL ,
TRUE ,
L " " ,
& val_type ,
( void * * ) & command_value ,
NULL ,
NULL ) ;
g_clear_object ( & key ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ! got_value | |
val_type ! = G_WIN32_REGISTRY_VALUE_STR | |
( command_value_utf8 = g_utf16_to_utf8 ( command_value ,
- 1 ,
NULL ,
NULL ,
NULL ) ) = = NULL )
{
g_clear_pointer ( & command_value , g_free ) ;
continue ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
verb_displayname = NULL ;
verb_displayname_u8 = NULL ;
verb_key = _g_win32_registry_key_build_and_new_w ( NULL , path_to_progid , progid ,
L " \\ " , verb - > shellpath , NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( verb_key )
{
gsize verb_displayname_len ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
got_value = g_win32_registry_key_get_value_w ( verb_key ,
g_win32_registry_get_os_dirs_w ( ) ,
TRUE ,
L " MUIVerb " ,
& val_type ,
( void * * ) & verb_displayname ,
& verb_displayname_len ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( got_value & &
val_type = = G_WIN32_REGISTRY_VALUE_STR & &
verb_displayname_len > sizeof ( gunichar2 ) )
verb_displayname_u8 = g_utf16_to_utf8 ( verb_displayname , - 1 , NULL , NULL , NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & verb_displayname , g_free ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( verb_displayname_u8 = = NULL )
{
got_value = g_win32_registry_key_get_value_w ( verb_key ,
NULL ,
TRUE ,
L " " ,
& val_type ,
( void * * ) & verb_displayname ,
& verb_displayname_len ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( got_value & &
val_type = = G_WIN32_REGISTRY_VALUE_STR & &
verb_displayname_len > sizeof ( gunichar2 ) )
verb_displayname_u8 = g_utf16_to_utf8 ( verb_displayname , - 1 , NULL , NULL , NULL ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & verb_displayname , g_free ) ;
g_clear_object ( & verb_key ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
handler ( handler_data1 , handler_data2 , verb - > name , command_value , command_value_utf8 ,
verb_displayname_u8 ,
( preferred_verb & & _wcsicmp ( verb - > name , preferred_verb - > name ) = = 0 ) | |
( ! preferred_verb & & autoprefer_first_verb & & i = = verbs ) ,
FALSE ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & command_value , g_free ) ;
g_clear_pointer ( & command_value_utf8 , g_free ) ;
g_clear_pointer ( & verb_displayname_u8 , g_free ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
g_list_free_full ( verbs , reg_verb_free ) ;
}
2020-06-04 17:13:33 +02:00
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 ;
}
2021-08-02 16:19:35 +02:00
acid = NULL ;
2020-06-04 17:13:33 +02:00
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 ) ;
}
2019-12-29 12:35:49 +01:00
/* Looks up a schema object identified by
* @ schema_u8_folded in the urls hash table .
* If such object doesn ' t exist ,
* creates it and puts it into the urls hash table .
* Returns the object .
*/
static GWin32AppInfoURLSchema *
get_schema_object ( const gunichar2 * schema ,
const gchar * schema_u8 ,
const gchar * schema_u8_folded )
{
GWin32AppInfoURLSchema * schema_rec ;
schema_rec = g_hash_table_lookup ( urls , schema_u8_folded ) ;
if ( schema_rec ! = NULL )
return schema_rec ;
schema_rec = g_object_new ( G_TYPE_WIN32_APPINFO_URL_SCHEMA , NULL ) ;
schema_rec - > schema = g_wcsdup ( schema , - 1 ) ;
schema_rec - > schema_u8 = g_strdup ( schema_u8 ) ;
schema_rec - > schema_u8_folded = g_strdup ( schema_u8_folded ) ;
g_hash_table_insert ( urls , g_strdup ( schema_rec - > schema_u8_folded ) , schema_rec ) ;
return schema_rec ;
}
/* Looks up a handler object identified by
* @ handler_id_u8_folded in the handlers hash table .
* If such object doesn ' t exist ,
* creates it and puts it into the handlers hash table .
* Returns the object .
*/
static GWin32AppInfoHandler *
get_handler_object ( const gchar * handler_id_u8_folded ,
GWin32RegistryKey * handler_key ,
2020-01-28 06:12:21 +01:00
const gunichar2 * handler_id ,
const gunichar2 * uwp_aumid )
2019-12-29 12:35:49 +01:00
{
GWin32AppInfoHandler * handler_rec ;
handler_rec = g_hash_table_lookup ( handlers , handler_id_u8_folded ) ;
if ( handler_rec ! = NULL )
return handler_rec ;
handler_rec = g_object_new ( G_TYPE_WIN32_APPINFO_HANDLER , NULL ) ;
2020-06-04 17:13:33 +02:00
if ( handler_key )
handler_rec - > key = g_object_ref ( handler_key ) ;
2019-12-29 12:35:49 +01:00
handler_rec - > handler_id = g_wcsdup ( handler_id , - 1 ) ;
handler_rec - > handler_id_folded = g_strdup ( handler_id_u8_folded ) ;
2020-01-28 06:12:21 +01:00
if ( uwp_aumid )
handler_rec - > uwp_aumid = g_wcsdup ( uwp_aumid , - 1 ) ;
2020-06-04 17:13:33 +02:00
if ( handler_key )
read_handler_icon ( handler_key , & handler_rec - > icon ) ;
2019-12-29 12:35:49 +01:00
g_hash_table_insert ( handlers , g_strdup ( handler_id_u8_folded ) , handler_rec ) ;
return handler_rec ;
}
static void
handler_add_verb ( gpointer handler_data1 ,
gpointer handler_data2 ,
const gunichar2 * verb ,
const gunichar2 * command_line ,
const gchar * command_line_utf8 ,
const gchar * verb_displayname ,
gboolean verb_is_preferred ,
gboolean invent_new_verb_name )
{
GWin32AppInfoHandler * handler_rec = ( GWin32AppInfoHandler * ) handler_data1 ;
GWin32AppInfoApplication * app_rec = ( GWin32AppInfoApplication * ) handler_data2 ;
2022-01-19 17:43:39 +01:00
GWin32AppInfoShellVerb * shverb = NULL ;
2019-12-29 12:35:49 +01:00
_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 - > command = g_wcsdup ( command_line , - 1 ) ;
shverb - > command_utf8 = g_strdup ( command_line_utf8 ) ;
2020-01-28 06:12:21 +01:00
shverb - > is_uwp = FALSE ; /* This function is for non-UWP verbs only */
2019-12-29 12:35:49 +01:00
if ( app_rec )
shverb - > app = g_object_ref ( app_rec ) ;
_g_win32_extract_executable ( shverb - > command ,
& shverb - > executable ,
& shverb - > executable_basename ,
& shverb - > executable_folded ,
NULL ,
& shverb - > dll_function ) ;
if ( shverb - > dll_function ! = NULL )
_g_win32_fixup_broken_microsoft_rundll_commandline ( shverb - > command ) ;
if ( ! verb_is_preferred )
g_ptr_array_add ( handler_rec - > verbs , shverb ) ;
else
g_ptr_array_insert ( handler_rec - > verbs , 0 , shverb ) ;
}
/* Tries to generate a new name for a verb that looks
* like " verb (%x) " , where % x is an integer in range of [ 0 ; 255 ) .
* On success puts new verb ( and new verb displayname ) into
* @ new_verb and @ new_displayname and return TRUE .
* On failure puts NULL into both and returns FALSE .
*/
static gboolean
generate_new_verb_name ( GPtrArray * verbs ,
const gunichar2 * verb ,
const gchar * verb_displayname ,
gunichar2 * * new_verb ,
gchar * * new_displayname )
{
gsize counter ;
2022-01-19 17:43:39 +01:00
GWin32AppInfoShellVerb * shverb = NULL ;
2019-12-29 12:35:49 +01:00
gsize orig_len = g_utf16_len ( verb ) ;
gsize new_verb_name_len = orig_len + strlen ( " () " ) + 2 + 1 ;
2020-12-23 02:19:31 +01:00
gunichar2 * new_verb_name = g_new ( gunichar2 , new_verb_name_len ) ;
2019-12-29 12:35:49 +01:00
* new_verb = NULL ;
* new_displayname = NULL ;
memcpy ( new_verb_name , verb , orig_len * sizeof ( gunichar2 ) ) ;
for ( counter = 0 ; counter < 255 ; counter + + )
{
2020-11-20 19:02:39 +01:00
_snwprintf ( & new_verb_name [ orig_len ] , new_verb_name_len , L " (%zx) " , counter ) ;
2019-12-29 12:35:49 +01:00
_verb_lookup ( verbs , new_verb_name , & shverb ) ;
if ( shverb = = NULL )
{
* new_verb = new_verb_name ;
if ( verb_displayname ! = NULL )
* new_displayname = g_strdup_printf ( " %s (%zx) " , verb_displayname , counter ) ;
return TRUE ;
}
}
return FALSE ;
}
static void
app_add_verb ( gpointer handler_data1 ,
gpointer handler_data2 ,
const gunichar2 * verb ,
const gunichar2 * command_line ,
const gchar * command_line_utf8 ,
const gchar * verb_displayname ,
gboolean verb_is_preferred ,
gboolean invent_new_verb_name )
{
gunichar2 * new_verb = NULL ;
gchar * new_displayname = NULL ;
GWin32AppInfoApplication * app_rec = ( GWin32AppInfoApplication * ) handler_data2 ;
2022-01-19 17:43:39 +01:00
GWin32AppInfoShellVerb * shverb = NULL ;
2019-12-29 12:35:49 +01:00
_verb_lookup ( app_rec - > verbs , verb , & shverb ) ;
/* Special logic for fake apps - do our best to
* collate all possible verbs in the app ,
* including the verbs that have the same name but
* different commandlines , in which case a new
* verb name has to be invented .
*/
if ( shverb ! = NULL )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
gsize vi ;
if ( ! invent_new_verb_name )
return ;
for ( vi = 0 ; vi < app_rec - > verbs - > len ; vi + + )
{
GWin32AppInfoShellVerb * app_verb ;
app_verb = _verb_idx ( app_rec - > verbs , vi ) ;
if ( _wcsicmp ( command_line , app_verb - > command ) = = 0 )
break ;
}
if ( vi < app_rec - > verbs - > len | |
! generate_new_verb_name ( app_rec - > verbs ,
verb ,
verb_displayname ,
& new_verb ,
& new_displayname ) )
return ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
shverb = g_object_new ( G_TYPE_WIN32_APPINFO_SHELL_VERB , NULL ) ;
if ( new_verb = = NULL )
shverb - > verb_name = g_wcsdup ( verb , - 1 ) ;
else
shverb - > verb_name = g_steal_pointer ( & new_verb ) ;
if ( new_displayname = = NULL )
shverb - > verb_displayname = g_strdup ( verb_displayname ) ;
else
shverb - > verb_displayname = g_steal_pointer ( & new_displayname ) ;
shverb - > command = g_wcsdup ( command_line , - 1 ) ;
shverb - > command_utf8 = g_strdup ( command_line_utf8 ) ;
shverb - > app = g_object_ref ( app_rec ) ;
_g_win32_extract_executable ( shverb - > command ,
& shverb - > executable ,
& shverb - > executable_basename ,
& shverb - > executable_folded ,
NULL ,
& shverb - > dll_function ) ;
if ( shverb - > dll_function ! = NULL )
_g_win32_fixup_broken_microsoft_rundll_commandline ( shverb - > command ) ;
if ( ! verb_is_preferred )
g_ptr_array_add ( app_rec - > verbs , shverb ) ;
else
g_ptr_array_insert ( app_rec - > verbs , 0 , shverb ) ;
}
2020-06-04 17:13:33 +02:00
static void
uwp_app_add_verb ( GWin32AppInfoApplication * app_rec ,
const gunichar2 * verb ,
const gchar * verb_displayname )
{
2022-01-19 17:43:39 +01:00
GWin32AppInfoShellVerb * shverb = NULL ;
2020-06-04 17:13:33 +02:00
_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 )
{
2022-01-19 17:43:39 +01:00
GWin32AppInfoShellVerb * shverb = NULL ;
2020-06-04 17:13:33 +02:00
_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 ) ;
}
2019-12-29 12:35:49 +01:00
/* Looks up a file extension object identified by
* @ ext_u8_folded in the extensions hash table .
* If such object doesn ' t exist ,
* creates it and puts it into the extensions hash table .
* Returns the object .
*/
static GWin32AppInfoFileExtension *
get_ext_object ( const gunichar2 * ext ,
const gchar * ext_u8 ,
const gchar * ext_u8_folded )
{
GWin32AppInfoFileExtension * file_extn ;
if ( g_hash_table_lookup_extended ( extensions ,
ext_u8_folded ,
NULL ,
( void * * ) & file_extn ) )
return file_extn ;
file_extn = g_object_new ( G_TYPE_WIN32_APPINFO_FILE_EXTENSION , NULL ) ;
file_extn - > extension = g_wcsdup ( ext , - 1 ) ;
file_extn - > extension_u8 = g_strdup ( ext_u8 ) ;
g_hash_table_insert ( extensions , g_strdup ( ext_u8_folded ) , file_extn ) ;
return file_extn ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
/* Iterates over HKCU\\Software\\Clients or HKLM\\Software\\Clients,
* ( depending on @ user_registry being TRUE or FALSE ) ,
* collecting applications listed there .
* Puts the path to the client key for each client into @ priority_capable_apps
* ( only for clients with file or URL associations ) .
*/
2014-08-29 10:53:35 +02:00
static void
collect_capable_apps_from_clients ( GPtrArray * capable_apps ,
GPtrArray * priority_capable_apps ,
gboolean user_registry )
{
GWin32RegistryKey * clients ;
GWin32RegistrySubkeyIter clients_iter ;
2019-12-29 12:35:49 +01:00
const gunichar2 * client_type_name ;
2014-08-29 10:53:35 +02:00
gsize client_type_name_len ;
if ( user_registry )
clients =
g_win32_registry_key_new_w ( L " HKEY_CURRENT_USER \\ Software \\ Clients " ,
NULL ) ;
else
clients =
g_win32_registry_key_new_w ( L " HKEY_LOCAL_MACHINE \\ Software \\ Clients " ,
NULL ) ;
if ( clients = = NULL )
return ;
if ( ! g_win32_registry_subkey_iter_init ( & clients_iter , clients , NULL ) )
{
g_object_unref ( clients ) ;
return ;
}
while ( g_win32_registry_subkey_iter_next ( & clients_iter , TRUE , NULL ) )
{
GWin32RegistrySubkeyIter subkey_iter ;
GWin32RegistryKey * system_client_type ;
GWin32RegistryValueType default_type ;
2019-01-07 11:25:46 +01:00
gunichar2 * default_value = NULL ;
2019-12-29 12:35:49 +01:00
const gunichar2 * client_name ;
2014-08-29 10:53:35 +02:00
gsize client_name_len ;
if ( ! g_win32_registry_subkey_iter_get_name_w ( & clients_iter ,
& client_type_name ,
& client_type_name_len ,
NULL ) )
continue ;
system_client_type = g_win32_registry_key_get_child_w ( clients ,
client_type_name ,
NULL ) ;
if ( system_client_type = = NULL )
continue ;
if ( g_win32_registry_key_get_value_w ( system_client_type ,
2020-01-27 00:28:19 +01:00
NULL ,
2014-08-29 10:53:35 +02:00
TRUE ,
L " " ,
& default_type ,
( gpointer * ) & default_value ,
NULL ,
NULL ) )
{
if ( default_type ! = G_WIN32_REGISTRY_VALUE_STR | |
default_value [ 0 ] = = L ' \0 ' )
g_clear_pointer ( & default_value , g_free ) ;
}
if ( ! g_win32_registry_subkey_iter_init ( & subkey_iter ,
system_client_type ,
NULL ) )
{
g_clear_pointer ( & default_value , g_free ) ;
g_object_unref ( system_client_type ) ;
continue ;
}
while ( g_win32_registry_subkey_iter_next ( & subkey_iter , TRUE , NULL ) )
{
GWin32RegistryKey * system_client ;
GWin32RegistryKey * system_client_assoc ;
gboolean add ;
gunichar2 * keyname ;
if ( ! g_win32_registry_subkey_iter_get_name_w ( & subkey_iter ,
& client_name ,
& client_name_len ,
NULL ) )
continue ;
system_client = g_win32_registry_key_get_child_w ( system_client_type ,
client_name ,
NULL ) ;
if ( system_client = = NULL )
continue ;
add = FALSE ;
system_client_assoc = g_win32_registry_key_get_child_w ( system_client ,
L " Capabilities \\ FileAssociations " ,
NULL ) ;
if ( system_client_assoc ! = NULL )
{
add = TRUE ;
g_object_unref ( system_client_assoc ) ;
}
else
{
system_client_assoc = g_win32_registry_key_get_child_w ( system_client ,
L " Capabilities \\ UrlAssociations " ,
NULL ) ;
if ( system_client_assoc ! = NULL )
{
add = TRUE ;
g_object_unref ( system_client_assoc ) ;
}
}
if ( add )
{
keyname = g_wcsdup ( g_win32_registry_key_get_path_w ( system_client ) , - 1 ) ;
if ( default_value & & wcscmp ( default_value , client_name ) = = 0 )
g_ptr_array_add ( priority_capable_apps , keyname ) ;
else
g_ptr_array_add ( capable_apps , keyname ) ;
}
g_object_unref ( system_client ) ;
}
g_win32_registry_subkey_iter_clear ( & subkey_iter ) ;
g_clear_pointer ( & default_value , g_free ) ;
g_object_unref ( system_client_type ) ;
}
g_win32_registry_subkey_iter_clear ( & clients_iter ) ;
g_object_unref ( clients ) ;
}
2019-12-29 12:35:49 +01:00
/* Iterates over HKCU\\Software\\RegisteredApplications or HKLM\\Software\\RegisteredApplications,
* ( depending on @ user_registry being TRUE or FALSE ) ,
* collecting applications listed there .
* Puts the path to the app key for each app into @ capable_apps .
*/
2014-08-29 10:53:35 +02:00
static void
collect_capable_apps_from_registered_apps ( GPtrArray * capable_apps ,
gboolean user_registry )
{
GWin32RegistryValueIter iter ;
2019-12-29 12:35:49 +01:00
const gunichar2 * reg_path ;
2014-08-29 10:53:35 +02:00
gunichar2 * value_data ;
gsize value_data_size ;
GWin32RegistryValueType value_type ;
GWin32RegistryKey * registered_apps ;
if ( user_registry )
2019-12-29 12:35:49 +01:00
reg_path = L " HKEY_CURRENT_USER \\ Software \\ RegisteredApplications " ;
2014-08-29 10:53:35 +02:00
else
2019-12-29 12:35:49 +01:00
reg_path = L " HKEY_LOCAL_MACHINE \\ Software \\ RegisteredApplications " ;
registered_apps =
g_win32_registry_key_new_w ( reg_path , NULL ) ;
2014-08-29 10:53:35 +02:00
if ( ! registered_apps )
return ;
if ( ! g_win32_registry_value_iter_init ( & iter , registered_apps , NULL ) )
{
g_object_unref ( registered_apps ) ;
2019-12-29 12:35:49 +01:00
2014-08-29 10:53:35 +02:00
return ;
}
while ( g_win32_registry_value_iter_next ( & iter , TRUE , NULL ) )
{
gunichar2 possible_location [ REG_PATH_MAX_SIZE + 1 ] ;
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * location ;
gunichar2 * p ;
2014-08-29 10:53:35 +02:00
if ( ( ! g_win32_registry_value_iter_get_value_type ( & iter ,
& value_type ,
NULL ) ) | |
( value_type ! = G_WIN32_REGISTRY_VALUE_STR ) | |
( ! g_win32_registry_value_iter_get_data_w ( & iter , TRUE ,
( void * * ) & value_data ,
& value_data_size ,
NULL ) ) | |
( value_data_size < sizeof ( gunichar2 ) ) | |
( value_data [ 0 ] = = L ' \0 ' ) )
continue ;
if ( ! build_registry_path ( possible_location , sizeof ( possible_location ) ,
user_registry ? HKCU : HKLM , value_data , NULL ) )
continue ;
location = g_win32_registry_key_new_w ( possible_location , NULL ) ;
2019-12-29 12:35:49 +01:00
if ( location = = NULL )
continue ;
p = wcsrchr ( possible_location , L ' \\ ' ) ;
if ( p )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
* p = L ' \0 ' ;
2014-08-29 10:53:35 +02:00
g_ptr_array_add ( capable_apps , g_wcsdup ( possible_location , - 1 ) ) ;
}
2019-12-29 12:35:49 +01:00
g_object_unref ( location ) ;
2014-08-29 10:53:35 +02:00
}
g_win32_registry_value_iter_clear ( & iter ) ;
g_object_unref ( registered_apps ) ;
}
2019-12-29 12:35:49 +01:00
/* Looks up an app object identified by
* @ canonical_name_folded in the @ app_hashmap .
* If such object doesn ' t exist ,
* creates it and puts it into the @ app_hashmap .
* Returns the object .
*/
static GWin32AppInfoApplication *
get_app_object ( GHashTable * app_hashmap ,
const gunichar2 * canonical_name ,
const gchar * canonical_name_u8 ,
const gchar * canonical_name_folded ,
gboolean user_specific ,
2020-06-04 17:13:33 +02:00
gboolean default_app ,
gboolean is_uwp )
2019-12-29 12:35:49 +01:00
{
GWin32AppInfoApplication * app ;
app = g_hash_table_lookup ( app_hashmap , canonical_name_folded ) ;
if ( app ! = NULL )
return app ;
app = g_object_new ( G_TYPE_WIN32_APPINFO_APPLICATION , NULL ) ;
app - > canonical_name = g_wcsdup ( canonical_name , - 1 ) ;
app - > canonical_name_u8 = g_strdup ( canonical_name_u8 ) ;
app - > canonical_name_folded = g_strdup ( canonical_name_folded ) ;
app - > no_open_with = FALSE ;
app - > user_specific = user_specific ;
app - > default_app = default_app ;
2020-06-04 17:13:33 +02:00
app - > is_uwp = is_uwp ;
2019-12-29 12:35:49 +01:00
g_hash_table_insert ( app_hashmap ,
g_strdup ( canonical_name_folded ) ,
app ) ;
return app ;
}
/* Grabs an application that has Capabilities.
* @ app_key_path is the path to the application key
* ( which must have a " Capabilities " subkey ) .
* @ default_app is TRUE if the app has priority
*/
2014-08-29 10:53:35 +02:00
static void
2019-12-29 12:35:49 +01:00
read_capable_app ( const gunichar2 * app_key_path ,
gboolean user_specific ,
gboolean default_app )
2014-08-29 10:53:35 +02:00
{
GWin32AppInfoApplication * app ;
2019-12-29 12:35:49 +01:00
gchar * canonical_name_u8 = NULL ;
gchar * canonical_name_folded = NULL ;
gchar * app_key_path_u8 = NULL ;
gchar * app_key_path_u8_folded = NULL ;
GWin32RegistryKey * appkey = NULL ;
2014-08-29 10:53:35 +02:00
gunichar2 * fallback_friendly_name ;
GWin32RegistryValueType vtype ;
gboolean success ;
gunichar2 * friendly_name ;
gunichar2 * description ;
gunichar2 * narrow_application_name ;
gunichar2 * icon_source ;
GWin32RegistryKey * capabilities ;
GWin32RegistryKey * default_icon_key ;
GWin32RegistryKey * associations ;
2019-12-29 12:35:49 +01:00
const reg_verb * preferred_verb ;
GList * verbs = NULL ;
2020-12-15 08:55:28 +01:00
gboolean verbs_in_root_key = TRUE ;
2019-12-29 12:35:49 +01:00
appkey = NULL ;
capabilities = NULL ;
if ( ! g_utf16_to_utf8_and_fold ( app_key_path ,
- 1 ,
& canonical_name_u8 ,
& canonical_name_folded ) | |
! g_utf16_to_utf8_and_fold ( app_key_path ,
- 1 ,
& app_key_path_u8 ,
& 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 | |
2020-12-15 08:55:28 +01:00
! ( get_verbs ( appkey , & preferred_verb , & verbs , L " " , L " Shell " , NULL ) | |
( verbs_in_root_key = FALSE ) | |
get_verbs ( capabilities , & preferred_verb , & verbs , L " " , L " Shell " , NULL ) ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & canonical_name_u8 , g_free ) ;
g_clear_pointer ( & canonical_name_folded , g_free ) ;
g_clear_object ( & appkey ) ;
2020-12-17 18:01:44 +01:00
g_clear_object ( & capabilities ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & app_key_path_u8 , g_free ) ;
g_clear_pointer ( & app_key_path_u8_folded , g_free ) ;
2014-08-29 10:53:35 +02:00
return ;
}
2019-12-29 12:35:49 +01:00
app = get_app_object ( apps_by_id ,
app_key_path ,
canonical_name_u8 ,
canonical_name_folded ,
user_specific ,
2020-06-04 17:13:33 +02:00
default_app ,
FALSE ) ;
2019-12-29 12:35:49 +01:00
process_verbs_commands ( g_steal_pointer ( & verbs ) ,
preferred_verb ,
L " " , /* [ab]use the fact that two strings are simply concatenated */
2020-12-15 08:55:28 +01:00
verbs_in_root_key ? app_key_path : g_win32_registry_key_get_path_w ( capabilities ) ,
2019-12-29 12:35:49 +01:00
FALSE ,
app_add_verb ,
app ,
app ) ;
2014-08-29 10:53:35 +02:00
fallback_friendly_name = NULL ;
success = g_win32_registry_key_get_value_w ( appkey ,
2020-01-27 00:28:19 +01:00
NULL ,
2014-08-29 10:53:35 +02:00
TRUE ,
L " " ,
& vtype ,
( void * * ) & fallback_friendly_name ,
NULL ,
NULL ) ;
if ( success & & vtype ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & fallback_friendly_name , g_free ) ;
2019-12-29 12:35:49 +01:00
if ( fallback_friendly_name & &
app - > pretty_name = = NULL )
2014-08-29 10:53:35 +02:00
{
app - > pretty_name = g_wcsdup ( fallback_friendly_name , - 1 ) ;
g_clear_pointer ( & app - > pretty_name_u8 , g_free ) ;
app - > pretty_name_u8 = g_utf16_to_utf8 ( fallback_friendly_name ,
- 1 ,
NULL ,
NULL ,
NULL ) ;
}
friendly_name = NULL ;
success = g_win32_registry_key_get_value_w ( capabilities ,
2020-01-27 00:42:31 +01:00
g_win32_registry_get_os_dirs_w ( ) ,
2014-08-29 10:53:35 +02:00
TRUE ,
L " LocalizedString " ,
& vtype ,
( void * * ) & friendly_name ,
NULL ,
NULL ) ;
2019-12-29 12:35:49 +01:00
if ( success & &
vtype ! = G_WIN32_REGISTRY_VALUE_STR )
2014-08-29 10:53:35 +02:00
g_clear_pointer ( & friendly_name , g_free ) ;
2019-12-29 12:35:49 +01:00
if ( friendly_name & &
app - > localized_pretty_name = = NULL )
2014-08-29 10:53:35 +02:00
{
app - > localized_pretty_name = g_wcsdup ( friendly_name , - 1 ) ;
g_clear_pointer ( & app - > localized_pretty_name_u8 , g_free ) ;
app - > localized_pretty_name_u8 = g_utf16_to_utf8 ( friendly_name ,
- 1 ,
NULL ,
NULL ,
NULL ) ;
}
description = NULL ;
success = g_win32_registry_key_get_value_w ( capabilities ,
2020-01-27 00:42:31 +01:00
g_win32_registry_get_os_dirs_w ( ) ,
2014-08-29 10:53:35 +02:00
TRUE ,
L " ApplicationDescription " ,
& vtype ,
( void * * ) & description ,
NULL ,
NULL ) ;
if ( success & & vtype ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & description , g_free ) ;
if ( description & & app - > description = = NULL )
{
app - > description = g_wcsdup ( description , - 1 ) ;
g_clear_pointer ( & app - > description_u8 , g_free ) ;
app - > description_u8 = g_utf16_to_utf8 ( description , - 1 , NULL , NULL , NULL ) ;
}
default_icon_key = g_win32_registry_key_get_child_w ( appkey ,
L " DefaultIcon " ,
NULL ) ;
icon_source = NULL ;
if ( default_icon_key ! = NULL )
{
success = g_win32_registry_key_get_value_w ( default_icon_key ,
2020-01-27 00:28:19 +01:00
NULL ,
2014-08-29 10:53:35 +02:00
TRUE ,
L " " ,
& vtype ,
( void * * ) & icon_source ,
NULL ,
NULL ) ;
2019-12-29 12:35:49 +01:00
if ( success & &
vtype ! = G_WIN32_REGISTRY_VALUE_STR )
2014-08-29 10:53:35 +02:00
g_clear_pointer ( & icon_source , g_free ) ;
g_object_unref ( default_icon_key ) ;
}
if ( icon_source = = NULL )
{
success = g_win32_registry_key_get_value_w ( capabilities ,
2020-01-27 00:28:19 +01:00
NULL ,
2014-08-29 10:53:35 +02:00
TRUE ,
L " ApplicationIcon " ,
& vtype ,
( void * * ) & icon_source ,
NULL ,
NULL ) ;
2019-12-29 12:35:49 +01:00
if ( success & &
vtype ! = G_WIN32_REGISTRY_VALUE_STR )
2014-08-29 10:53:35 +02:00
g_clear_pointer ( & icon_source , g_free ) ;
}
2019-12-29 12:35:49 +01:00
if ( icon_source & &
app - > icon = = NULL )
2014-08-29 10:53:35 +02:00
{
gchar * name = g_utf16_to_utf8 ( icon_source , - 1 , NULL , NULL , NULL ) ;
app - > icon = g_themed_icon_new ( name ) ;
g_free ( name ) ;
}
narrow_application_name = NULL ;
success = g_win32_registry_key_get_value_w ( capabilities ,
2020-01-27 00:42:31 +01:00
g_win32_registry_get_os_dirs_w ( ) ,
2014-08-29 10:53:35 +02:00
TRUE ,
L " ApplicationName " ,
& vtype ,
( void * * ) & narrow_application_name ,
NULL ,
NULL ) ;
if ( success & & vtype ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & narrow_application_name , g_free ) ;
2019-12-29 12:35:49 +01:00
if ( narrow_application_name & &
app - > localized_pretty_name = = NULL )
{
app - > localized_pretty_name = g_wcsdup ( narrow_application_name , - 1 ) ;
g_clear_pointer ( & app - > localized_pretty_name_u8 , g_free ) ;
app - > localized_pretty_name_u8 = g_utf16_to_utf8 ( narrow_application_name ,
- 1 ,
NULL ,
NULL ,
NULL ) ;
}
2014-08-29 10:53:35 +02:00
associations = g_win32_registry_key_get_child_w ( capabilities ,
L " FileAssociations " ,
NULL ) ;
if ( associations ! = NULL )
{
GWin32RegistryValueIter iter ;
if ( g_win32_registry_value_iter_init ( & iter , associations , NULL ) )
{
gunichar2 * file_extension ;
gunichar2 * extension_handler ;
gsize file_extension_len ;
gsize extension_handler_size ;
GWin32RegistryValueType value_type ;
while ( g_win32_registry_value_iter_next ( & iter , TRUE , NULL ) )
{
if ( ( ! g_win32_registry_value_iter_get_value_type ( & iter ,
& value_type ,
NULL ) ) | |
( value_type ! = G_WIN32_REGISTRY_VALUE_STR ) | |
( ! g_win32_registry_value_iter_get_name_w ( & iter ,
& file_extension ,
& file_extension_len ,
NULL ) ) | |
( file_extension_len < = 0 ) | |
( file_extension [ 0 ] ! = L ' . ' ) | |
( ! g_win32_registry_value_iter_get_data_w ( & iter , TRUE ,
( void * * ) & extension_handler ,
& extension_handler_size ,
NULL ) ) | |
( extension_handler_size < sizeof ( gunichar2 ) ) | |
( extension_handler [ 0 ] = = L ' \0 ' ) )
continue ;
2019-12-29 12:35:49 +01:00
get_file_ext ( extension_handler , file_extension , app , FALSE ) ;
2014-08-29 10:53:35 +02:00
}
g_win32_registry_value_iter_clear ( & iter ) ;
}
g_object_unref ( associations ) ;
}
associations = g_win32_registry_key_get_child_w ( capabilities , L " URLAssociations " , NULL ) ;
if ( associations ! = NULL )
{
GWin32RegistryValueIter iter ;
if ( g_win32_registry_value_iter_init ( & iter , associations , NULL ) )
{
gunichar2 * url_schema ;
gunichar2 * schema_handler ;
gsize url_schema_len ;
gsize schema_handler_size ;
GWin32RegistryValueType value_type ;
while ( g_win32_registry_value_iter_next ( & iter , TRUE , NULL ) )
{
2022-01-19 17:43:39 +01:00
gchar * schema_u8 = NULL ;
gchar * schema_u8_folded = NULL ;
2014-08-29 10:53:35 +02:00
if ( ( ! g_win32_registry_value_iter_get_value_type ( & iter ,
& value_type ,
NULL ) ) | |
( ( value_type ! = G_WIN32_REGISTRY_VALUE_STR ) & &
( value_type ! = G_WIN32_REGISTRY_VALUE_EXPAND_STR ) ) | |
( ! g_win32_registry_value_iter_get_name_w ( & iter ,
& url_schema ,
& url_schema_len ,
NULL ) ) | |
( url_schema_len < = 0 ) | |
( url_schema [ 0 ] = = L ' \0 ' ) | |
( ! g_win32_registry_value_iter_get_data_w ( & iter , TRUE ,
( void * * ) & schema_handler ,
& schema_handler_size ,
NULL ) ) | |
( schema_handler_size < sizeof ( gunichar2 ) ) | |
( schema_handler [ 0 ] = = L ' \0 ' ) )
continue ;
2019-12-29 12:35:49 +01:00
if ( g_utf16_to_utf8_and_fold ( url_schema ,
url_schema_len ,
& schema_u8 ,
& schema_u8_folded ) )
get_url_association ( schema_handler , url_schema , schema_u8 , schema_u8_folded , app , FALSE ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & schema_u8 , g_free ) ;
g_clear_pointer ( & schema_u8_folded , g_free ) ;
2014-08-29 10:53:35 +02:00
}
g_win32_registry_value_iter_clear ( & iter ) ;
}
g_object_unref ( associations ) ;
}
g_clear_pointer ( & fallback_friendly_name , g_free ) ;
g_clear_pointer ( & description , g_free ) ;
g_clear_pointer ( & icon_source , g_free ) ;
g_clear_pointer ( & narrow_application_name , g_free ) ;
g_object_unref ( appkey ) ;
g_object_unref ( capabilities ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & app_key_path_u8 , g_free ) ;
g_clear_pointer ( & app_key_path_u8_folded , g_free ) ;
g_clear_pointer ( & canonical_name_u8 , g_free ) ;
g_clear_pointer ( & canonical_name_folded , g_free ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
/* Iterates over subkeys in HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\
* and calls get_url_association ( ) for each one that has a user - chosen handler .
*/
2014-08-29 10:53:35 +02:00
static void
read_urls ( GWin32RegistryKey * url_associations )
{
GWin32RegistrySubkeyIter url_iter ;
if ( url_associations = = NULL )
return ;
if ( ! g_win32_registry_subkey_iter_init ( & url_iter , url_associations , NULL ) )
return ;
while ( g_win32_registry_subkey_iter_next ( & url_iter , TRUE , NULL ) )
{
2019-12-29 12:35:49 +01:00
gchar * schema_u8 = NULL ;
gchar * schema_u8_folded = NULL ;
const gunichar2 * url_schema = NULL ;
gunichar2 * program_id = NULL ;
GWin32RegistryKey * user_choice = NULL ;
gsize url_schema_len ;
GWin32RegistryValueType val_type ;
if ( g_win32_registry_subkey_iter_get_name_w ( & url_iter ,
& url_schema ,
& url_schema_len ,
NULL ) & &
g_utf16_to_utf8_and_fold ( url_schema ,
url_schema_len ,
& schema_u8 ,
& schema_u8_folded ) & &
( user_choice = _g_win32_registry_key_build_and_new_w ( NULL , URL_ASSOCIATIONS ,
url_schema , USER_CHOICE ,
NULL ) ) ! = NULL & &
g_win32_registry_key_get_value_w ( user_choice ,
NULL ,
TRUE ,
L " Progid " ,
& val_type ,
( void * * ) & program_id ,
NULL ,
NULL ) & &
val_type = = G_WIN32_REGISTRY_VALUE_STR )
get_url_association ( program_id , url_schema , schema_u8 , schema_u8_folded , NULL , TRUE ) ;
g_clear_pointer ( & program_id , g_free ) ;
g_clear_pointer ( & user_choice , g_object_unref ) ;
g_clear_pointer ( & schema_u8 , g_free ) ;
g_clear_pointer ( & schema_u8_folded , g_free ) ;
2014-08-29 10:53:35 +02:00
}
g_win32_registry_subkey_iter_clear ( & url_iter ) ;
}
2019-12-29 12:35:49 +01:00
/* Reads an application that is only registered by the basename of its
* executable ( and doesn ' t have Capabilities subkey ) .
* @ incapable_app is the registry key for the app .
* @ app_exe_basename is the basename of its executable .
*/
2014-08-29 10:53:35 +02:00
static void
2019-12-29 12:35:49 +01:00
read_incapable_app ( GWin32RegistryKey * incapable_app ,
const gunichar2 * app_exe_basename ,
const gchar * app_exe_basename_u8 ,
const gchar * app_exe_basename_u8_folded )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GWin32RegistryValueIter sup_iter ;
GWin32AppInfoApplication * app ;
GList * verbs ;
const reg_verb * preferred_verb ;
gunichar2 * friendly_app_name ;
gboolean success ;
GWin32RegistryValueType vtype ;
gboolean no_open_with ;
GWin32RegistryKey * default_icon_key ;
gunichar2 * icon_source ;
GIcon * icon = NULL ;
GWin32RegistryKey * supported_key ;
2014-08-29 10:53:35 +02:00
2020-06-04 17:13:33 +02:00
if ( ! get_verbs ( incapable_app , & preferred_verb , & verbs , L " " , L " Shell " , NULL ) )
2014-08-29 10:53:35 +02:00
return ;
2019-12-29 12:35:49 +01:00
app = get_app_object ( apps_by_exe ,
app_exe_basename ,
app_exe_basename_u8 ,
app_exe_basename_u8_folded ,
FALSE ,
2020-06-04 17:13:33 +02:00
FALSE ,
2019-12-29 12:35:49 +01:00
FALSE ) ;
process_verbs_commands ( g_steal_pointer ( & verbs ) ,
preferred_verb ,
L " HKEY_CLASSES_ROOT \\ Applications \\ " ,
app_exe_basename ,
TRUE ,
app_add_verb ,
app ,
app ) ;
friendly_app_name = NULL ;
success = g_win32_registry_key_get_value_w ( incapable_app ,
g_win32_registry_get_os_dirs_w ( ) ,
2014-08-29 10:53:35 +02:00
TRUE ,
2019-12-29 12:35:49 +01:00
L " FriendlyAppName " ,
2014-08-29 10:53:35 +02:00
& vtype ,
2019-12-29 12:35:49 +01:00
( void * * ) & friendly_app_name ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
2019-12-29 12:35:49 +01:00
if ( success & & vtype ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & friendly_app_name , g_free ) ;
no_open_with = g_win32_registry_key_get_value_w ( incapable_app ,
NULL ,
TRUE ,
L " NoOpenWith " ,
& vtype ,
NULL ,
NULL ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
default_icon_key =
g_win32_registry_key_get_child_w ( incapable_app ,
L " DefaultIcon " ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
icon_source = NULL ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( default_icon_key ! = NULL )
{
success =
g_win32_registry_key_get_value_w ( default_icon_key ,
NULL ,
TRUE ,
L " " ,
& vtype ,
( void * * ) & icon_source ,
NULL ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( success & & vtype ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & icon_source , g_free ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_object_unref ( default_icon_key ) ;
}
2019-11-29 17:48:32 +01:00
2019-12-29 12:35:49 +01:00
if ( icon_source )
{
gchar * name = g_utf16_to_utf8 ( icon_source , - 1 , NULL , NULL , NULL ) ;
if ( name ! = NULL )
icon = g_themed_icon_new ( name ) ;
g_free ( name ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
app - > no_open_with = no_open_with ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( friendly_app_name & &
app - > localized_pretty_name = = NULL )
{
app - > localized_pretty_name = g_wcsdup ( friendly_app_name , - 1 ) ;
g_clear_pointer ( & app - > localized_pretty_name_u8 , g_free ) ;
app - > localized_pretty_name_u8 =
g_utf16_to_utf8 ( friendly_app_name , - 1 , NULL , NULL , NULL ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( icon & & app - > icon = = NULL )
app - > icon = g_object_ref ( icon ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
supported_key =
g_win32_registry_key_get_child_w ( incapable_app ,
L " SupportedTypes " ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( supported_key & &
g_win32_registry_value_iter_init ( & sup_iter , supported_key , NULL ) )
{
gunichar2 * ext_name ;
gsize ext_name_len ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
while ( g_win32_registry_value_iter_next ( & sup_iter , TRUE , NULL ) )
{
if ( ( ! g_win32_registry_value_iter_get_name_w ( & sup_iter ,
& ext_name ,
& ext_name_len ,
NULL ) ) | |
( ext_name_len < = 0 ) | |
( ext_name [ 0 ] ! = L ' . ' ) )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
get_file_ext ( ext_name , ext_name , app , FALSE ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
g_win32_registry_value_iter_clear ( & sup_iter ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_object ( & supported_key ) ;
g_free ( friendly_app_name ) ;
g_free ( icon_source ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_object ( & icon ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
/* Iterates over subkeys of HKEY_CLASSES_ROOT\\Applications
* and calls read_incapable_app ( ) for each one .
*/
static void
read_exeapps ( void )
{
2022-01-19 17:43:39 +01:00
GWin32RegistryKey * local_applications_key ;
2019-12-29 12:35:49 +01:00
GWin32RegistrySubkeyIter app_iter ;
2014-08-29 10:53:35 +02:00
2022-01-19 17:43:39 +01:00
local_applications_key =
2019-12-29 12:35:49 +01:00
g_win32_registry_key_new_w ( L " HKEY_CLASSES_ROOT \\ Applications " , NULL ) ;
2014-08-29 10:53:35 +02:00
2022-01-19 17:43:39 +01:00
if ( local_applications_key = = NULL )
2019-12-29 12:35:49 +01:00
return ;
2014-08-29 10:53:35 +02:00
2022-01-19 17:43:39 +01:00
if ( ! g_win32_registry_subkey_iter_init ( & app_iter , local_applications_key , NULL ) )
2019-12-29 12:35:49 +01:00
{
2022-01-19 17:43:39 +01:00
g_object_unref ( local_applications_key ) ;
2019-12-29 12:35:49 +01:00
return ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
while ( g_win32_registry_subkey_iter_next ( & app_iter , TRUE , NULL ) )
{
const gunichar2 * app_exe_basename ;
gsize app_exe_basename_len ;
GWin32RegistryKey * incapable_app ;
gchar * app_exe_basename_u8 ;
gchar * app_exe_basename_u8_folded ;
if ( ! g_win32_registry_subkey_iter_get_name_w ( & app_iter ,
& app_exe_basename ,
& app_exe_basename_len ,
NULL ) | |
! g_utf16_to_utf8_and_fold ( app_exe_basename ,
app_exe_basename_len ,
& app_exe_basename_u8 ,
& app_exe_basename_u8_folded ) )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
incapable_app =
2022-01-19 17:43:39 +01:00
g_win32_registry_key_get_child_w ( local_applications_key ,
2019-12-29 12:35:49 +01:00
app_exe_basename ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( incapable_app ! = NULL )
read_incapable_app ( incapable_app ,
app_exe_basename ,
app_exe_basename_u8 ,
app_exe_basename_u8_folded ) ;
2014-08-29 10:53:35 +02:00
g_clear_object ( & incapable_app ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & app_exe_basename_u8 , g_free ) ;
g_clear_pointer ( & app_exe_basename_u8_folded , g_free ) ;
2014-08-29 10:53:35 +02:00
}
g_win32_registry_subkey_iter_clear ( & app_iter ) ;
2022-01-19 17:43:39 +01:00
g_object_unref ( local_applications_key ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
/* Iterates over subkeys of HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\
* and calls get_file_ext ( ) for each associated handler
* ( starting with user - chosen handler , if any )
*/
2014-08-29 10:53:35 +02:00
static void
read_exts ( GWin32RegistryKey * file_exts )
{
GWin32RegistrySubkeyIter ext_iter ;
2019-12-29 12:35:49 +01:00
const gunichar2 * file_extension ;
2014-08-29 10:53:35 +02:00
gsize file_extension_len ;
if ( file_exts = = NULL )
return ;
if ( ! g_win32_registry_subkey_iter_init ( & ext_iter , file_exts , NULL ) )
return ;
while ( g_win32_registry_subkey_iter_next ( & ext_iter , TRUE , NULL ) )
{
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * open_with_progids ;
gunichar2 * program_id ;
GWin32RegistryValueIter iter ;
gunichar2 * value_name ;
gsize value_name_len ;
GWin32RegistryValueType value_type ;
GWin32RegistryKey * user_choice ;
2014-08-29 10:53:35 +02:00
if ( ! g_win32_registry_subkey_iter_get_name_w ( & ext_iter ,
& file_extension ,
& file_extension_len ,
NULL ) )
continue ;
2019-12-29 12:35:49 +01:00
program_id = NULL ;
user_choice = _g_win32_registry_key_build_and_new_w ( NULL , FILE_EXTS , file_extension ,
USER_CHOICE , NULL ) ;
if ( user_choice & &
g_win32_registry_key_get_value_w ( user_choice ,
NULL ,
TRUE ,
L " Progid " ,
& value_type ,
( void * * ) & program_id ,
NULL ,
NULL ) & &
value_type = = G_WIN32_REGISTRY_VALUE_STR )
{
/* Note: program_id could be "ProgramID" or "Applications\\program.exe".
* The code still works , but handler_id might have a backslash
* in it - that might trip us up later on .
* Even though in that case this is logically an " application "
* registry entry , we don ' t treat it in any special way .
* We do scan that registry branch anyway , just not here .
*/
get_file_ext ( program_id , file_extension , NULL , TRUE ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_object ( & user_choice ) ;
g_clear_pointer ( & program_id , g_free ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
open_with_progids = _g_win32_registry_key_build_and_new_w ( NULL , FILE_EXTS ,
file_extension ,
OPEN_WITH_PROGIDS ,
NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( open_with_progids = = NULL )
2014-08-29 10:53:35 +02:00
continue ;
2019-12-29 12:35:49 +01:00
if ( ! g_win32_registry_value_iter_init ( & iter , open_with_progids , NULL ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
g_clear_object ( & open_with_progids ) ;
continue ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
while ( g_win32_registry_value_iter_next ( & iter , TRUE , NULL ) )
{
if ( ! g_win32_registry_value_iter_get_name_w ( & iter , & value_name ,
& value_name_len ,
NULL ) | |
( value_name_len = = 0 ) )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
get_file_ext ( value_name , file_extension , NULL , FALSE ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
g_win32_registry_value_iter_clear ( & iter ) ;
g_clear_object ( & open_with_progids ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
g_win32_registry_subkey_iter_clear ( & ext_iter ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
/* Iterates over subkeys in HKCR, calls
* get_file_ext ( ) for any subkey that starts with " . " ,
* or get_url_association ( ) for any subkey that could
* be a URL schema and has a " URL Protocol " value .
*/
2014-08-29 10:53:35 +02:00
static void
2019-12-29 12:35:49 +01:00
read_classes ( GWin32RegistryKey * classes_root )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GWin32RegistrySubkeyIter class_iter ;
const gunichar2 * class_name ;
gsize class_name_len ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( classes_root = = NULL )
return ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ! g_win32_registry_subkey_iter_init ( & class_iter , classes_root , NULL ) )
return ;
while ( g_win32_registry_subkey_iter_next ( & class_iter , TRUE , NULL ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
if ( ( ! g_win32_registry_subkey_iter_get_name_w ( & class_iter ,
& class_name ,
& class_name_len ,
NULL ) ) | |
( class_name_len < = 1 ) )
2014-08-29 10:53:35 +02:00
continue ;
2019-12-29 12:35:49 +01:00
if ( class_name [ 0 ] = = L ' . ' )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GWin32RegistryKey * class_key ;
GWin32RegistryValueIter iter ;
GWin32RegistryKey * open_with_progids ;
gunichar2 * value_name ;
gsize value_name_len ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
/* Read the data from the HKCR\\.ext (usually proxied
* to another HKCR subkey )
*/
get_file_ext ( class_name , class_name , NULL , FALSE ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
class_key = g_win32_registry_key_get_child_w ( classes_root , class_name , NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( class_key = = NULL )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
open_with_progids = g_win32_registry_key_get_child_w ( class_key , L " OpenWithProgids " , NULL ) ;
g_clear_object ( & class_key ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( open_with_progids = = NULL )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ! g_win32_registry_value_iter_init ( & iter , open_with_progids , NULL ) )
{
g_clear_object ( & open_with_progids ) ;
continue ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
/* Read the data for other handlers for this extension */
while ( g_win32_registry_value_iter_next ( & iter , TRUE , NULL ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
if ( ! g_win32_registry_value_iter_get_name_w ( & iter , & value_name ,
& value_name_len ,
NULL ) | |
( value_name_len = = 0 ) )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
get_file_ext ( value_name , class_name , NULL , FALSE ) ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_win32_registry_value_iter_clear ( & iter ) ;
g_clear_object ( & open_with_progids ) ;
}
else
{
gsize i ;
GWin32RegistryKey * class_key ;
gboolean success ;
GWin32RegistryValueType vtype ;
gchar * schema_u8 ;
gchar * schema_u8_folded ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
for ( i = 0 ; i < class_name_len ; i + + )
if ( ! iswalpha ( class_name [ i ] ) )
break ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( i ! = class_name_len )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
class_key = g_win32_registry_key_get_child_w ( classes_root , class_name , NULL ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( class_key = = NULL )
2014-08-29 10:53:35 +02:00
continue ;
2019-12-29 12:35:49 +01:00
success = g_win32_registry_key_get_value_w ( class_key ,
NULL ,
TRUE ,
L " URL Protocol " ,
& vtype ,
NULL ,
NULL ,
NULL ) ;
g_clear_object ( & class_key ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ! success | |
vtype ! = G_WIN32_REGISTRY_VALUE_STR )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ! g_utf16_to_utf8_and_fold ( class_name , - 1 , & schema_u8 , & schema_u8_folded ) )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
get_url_association ( class_name , class_name , schema_u8 , schema_u8_folded , NULL , FALSE ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & schema_u8 , g_free ) ;
g_clear_pointer ( & schema_u8_folded , g_free ) ;
2014-08-29 10:53:35 +02:00
}
}
2019-12-29 12:35:49 +01:00
g_win32_registry_subkey_iter_clear ( & class_iter ) ;
2014-08-29 10:53:35 +02:00
}
2019-12-29 12:35:49 +01:00
/* Iterates over all handlers and over all apps,
* and links handler verbs to apps if a handler
* runs the same executable as one of the app verbs .
*/
2014-08-29 10:53:35 +02:00
static void
2019-12-29 12:35:49 +01:00
link_handlers_to_unregistered_apps ( void )
2014-08-29 10:53:35 +02:00
{
GHashTableIter iter ;
2019-12-29 12:35:49 +01:00
GHashTableIter app_iter ;
GWin32AppInfoHandler * handler ;
gchar * handler_id_fld ;
2014-08-29 10:53:35 +02:00
GWin32AppInfoApplication * app ;
2019-12-29 12:35:49 +01:00
gchar * canonical_name_fld ;
gchar * appexe_fld_basename ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_hash_table_iter_init ( & iter , handlers ) ;
2014-08-29 10:53:35 +02:00
while ( g_hash_table_iter_next ( & iter ,
2019-12-29 12:35:49 +01:00
( gpointer * ) & handler_id_fld ,
( gpointer * ) & handler ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
gsize vi ;
2014-08-29 10:53:35 +02:00
2020-01-28 06:12:21 +01:00
if ( handler - > uwp_aumid ! = NULL )
continue ;
2019-12-29 12:35:49 +01:00
for ( vi = 0 ; vi < handler - > verbs - > len ; vi + + )
{
GWin32AppInfoShellVerb * handler_verb ;
const gchar * handler_exe_basename ;
enum
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
SH_UNKNOWN ,
GOT_SH_INFO ,
ERROR_GETTING_SH_INFO ,
} have_stat_handler = SH_UNKNOWN ;
2022-01-19 17:43:39 +01:00
GWin32PrivateStat handler_verb_exec_info = { 0 } ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
handler_verb = _verb_idx ( handler - > verbs , vi ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( handler_verb - > app ! = NULL )
continue ;
2023-03-09 14:21:43 +01:00
if ( handler_verb - > executable_folded = = NULL )
continue ;
2019-12-29 12:35:49 +01:00
handler_exe_basename = g_utf8_find_basename ( handler_verb - > executable_folded , - 1 ) ;
g_hash_table_iter_init ( & app_iter , apps_by_id ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
while ( g_hash_table_iter_next ( & app_iter ,
( gpointer * ) & canonical_name_fld ,
( gpointer * ) & app ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GWin32AppInfoShellVerb * app_verb ;
gsize ai ;
2014-08-29 10:53:35 +02:00
2020-06-04 17:13:33 +02:00
if ( app - > is_uwp )
continue ;
2019-12-29 12:35:49 +01:00
for ( ai = 0 ; ai < app - > verbs - > len ; ai + + )
{
GWin32PrivateStat app_verb_exec_info ;
const gchar * app_exe_basename ;
app_verb = _verb_idx ( app - > verbs , ai ) ;
2014-08-29 10:53:35 +02:00
2023-03-09 14:21:43 +01:00
if ( app_verb - > executable_folded = = NULL )
continue ;
2019-12-29 12:35:49 +01:00
app_exe_basename = g_utf8_find_basename ( app_verb - > executable_folded , - 1 ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
/* First check that the executable paths are identical */
if ( g_strcmp0 ( app_verb - > executable_folded , handler_verb - > executable_folded ) ! = 0 )
{
/* If not, check the basenames. If they are different, don't bother
* with further checks .
*/
if ( g_strcmp0 ( app_exe_basename , handler_exe_basename ) ! = 0 )
continue ;
/* Get filesystem IDs for both files.
* For the handler that is attempted only once .
*/
if ( have_stat_handler = = SH_UNKNOWN )
{
if ( GLIB_PRIVATE_CALL ( g_win32_stat_utf8 ) ( handler_verb - > executable_folded ,
& handler_verb_exec_info ) = = 0 )
have_stat_handler = GOT_SH_INFO ;
else
have_stat_handler = ERROR_GETTING_SH_INFO ;
}
if ( have_stat_handler ! = GOT_SH_INFO | |
( GLIB_PRIVATE_CALL ( g_win32_stat_utf8 ) ( app_verb - > executable_folded ,
& app_verb_exec_info ) ! = 0 ) | |
app_verb_exec_info . file_index ! = handler_verb_exec_info . file_index )
continue ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
handler_verb - > app = g_object_ref ( app ) ;
break ;
2014-08-29 10:53:35 +02:00
}
}
2019-12-29 12:35:49 +01:00
if ( handler_verb - > app ! = NULL )
continue ;
g_hash_table_iter_init ( & app_iter , apps_by_exe ) ;
while ( g_hash_table_iter_next ( & app_iter ,
( gpointer * ) & appexe_fld_basename ,
( gpointer * ) & app ) )
2014-08-29 10:53:35 +02:00
{
2020-06-04 17:13:33 +02:00
if ( app - > is_uwp )
continue ;
2019-12-29 12:35:49 +01:00
/* Use basename because apps_by_exe only has basenames */
if ( g_strcmp0 ( handler_exe_basename , appexe_fld_basename ) ! = 0 )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
handler_verb - > app = g_object_ref ( app ) ;
break ;
}
2014-08-29 10:53:35 +02:00
}
}
}
2019-12-29 12:35:49 +01:00
/* Finds all .ext and schema: handler verbs that have no app linked to them,
* creates a " fake app " object and links these verbs to these
* objects . Objects are identified by the full path to
* the executable being run , thus multiple different invocations
* get grouped in a more - or - less natural way .
* The iteration goes separately over . ext and schema : handlers
* ( instead of the global handlers hashmap ) to allow us to
* put the handlers into supported_urls or supported_exts as
* needed ( handler objects themselves have no knowledge of extensions
* and / or URLs they are associated with ) .
*/
2014-08-29 10:53:35 +02:00
static void
2019-12-29 12:35:49 +01:00
link_handlers_to_fake_apps ( void )
2014-08-29 10:53:35 +02:00
{
GHashTableIter iter ;
2019-12-29 12:35:49 +01:00
GHashTableIter handler_iter ;
gchar * extension_utf8_folded ;
GWin32AppInfoFileExtension * file_extn ;
gchar * handler_id_fld ;
2014-08-29 10:53:35 +02:00
GWin32AppInfoHandler * handler ;
2019-12-29 12:35:49 +01:00
gchar * url_utf8_folded ;
GWin32AppInfoURLSchema * schema ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_hash_table_iter_init ( & iter , extensions ) ;
2014-08-29 10:53:35 +02:00
while ( g_hash_table_iter_next ( & iter ,
2019-12-29 12:35:49 +01:00
( gpointer * ) & extension_utf8_folded ,
( gpointer * ) & file_extn ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
g_hash_table_iter_init ( & handler_iter , file_extn - > handlers ) ;
while ( g_hash_table_iter_next ( & handler_iter ,
( gpointer * ) & handler_id_fld ,
( gpointer * ) & handler ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
gsize vi ;
2014-08-29 10:53:35 +02:00
2020-01-28 06:12:21 +01:00
if ( handler - > uwp_aumid ! = NULL )
continue ;
2019-12-29 12:35:49 +01:00
for ( vi = 0 ; vi < handler - > verbs - > len ; vi + + )
{
GWin32AppInfoShellVerb * handler_verb ;
GWin32AppInfoApplication * app ;
gunichar2 * exename_utf16 ;
handler_verb = _verb_idx ( handler - > verbs , vi ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( handler_verb - > app ! = NULL )
continue ;
2019-11-29 17:49:01 +01:00
2019-12-29 12:35:49 +01:00
exename_utf16 = g_utf8_to_utf16 ( handler_verb - > executable , - 1 , NULL , NULL , NULL ) ;
if ( exename_utf16 = = NULL )
continue ;
2019-11-29 17:49:01 +01:00
2019-12-29 12:35:49 +01:00
app = get_app_object ( fake_apps ,
exename_utf16 ,
handler_verb - > executable ,
handler_verb - > executable_folded ,
FALSE ,
2020-06-04 17:13:33 +02:00
FALSE ,
2019-12-29 12:35:49 +01:00
FALSE ) ;
g_clear_pointer ( & exename_utf16 , g_free ) ;
handler_verb - > app = g_object_ref ( app ) ;
app_add_verb ( app ,
app ,
handler_verb - > verb_name ,
handler_verb - > command ,
handler_verb - > command_utf8 ,
handler_verb - > verb_displayname ,
TRUE ,
TRUE ) ;
g_hash_table_insert ( app - > supported_exts ,
g_strdup ( extension_utf8_folded ) ,
g_object_ref ( handler ) ) ;
}
}
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_hash_table_iter_init ( & iter , urls ) ;
while ( g_hash_table_iter_next ( & iter ,
( gpointer * ) & url_utf8_folded ,
( gpointer * ) & schema ) )
{
g_hash_table_iter_init ( & handler_iter , schema - > handlers ) ;
while ( g_hash_table_iter_next ( & handler_iter ,
( gpointer * ) & handler_id_fld ,
( gpointer * ) & handler ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
gsize vi ;
2014-08-29 10:53:35 +02:00
2020-01-28 06:12:21 +01:00
if ( handler - > uwp_aumid ! = NULL )
continue ;
2019-12-29 12:35:49 +01:00
for ( vi = 0 ; vi < handler - > verbs - > len ; vi + + )
{
GWin32AppInfoShellVerb * handler_verb ;
GWin32AppInfoApplication * app ;
gchar * command_utf8_folded ;
handler_verb = _verb_idx ( handler - > verbs , vi ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( handler_verb - > app ! = NULL )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
command_utf8_folded = g_utf8_casefold ( handler_verb - > command_utf8 , - 1 ) ;
app = get_app_object ( fake_apps ,
handler_verb - > command ,
handler_verb - > command_utf8 ,
command_utf8_folded ,
FALSE ,
2020-06-04 17:13:33 +02:00
FALSE ,
2019-12-29 12:35:49 +01:00
FALSE ) ;
g_clear_pointer ( & command_utf8_folded , g_free ) ;
handler_verb - > app = g_object_ref ( app ) ;
app_add_verb ( app ,
app ,
handler_verb - > verb_name ,
handler_verb - > command ,
handler_verb - > command_utf8 ,
handler_verb - > verb_displayname ,
TRUE ,
TRUE ) ;
g_hash_table_insert ( app - > supported_urls ,
g_strdup ( url_utf8_folded ) ,
g_object_ref ( handler ) ) ;
}
}
2014-08-29 10:53:35 +02:00
}
}
2020-06-04 17:13:33 +02:00
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 ,
2022-12-23 17:36:22 +01:00
const gunichar2 * display_name ,
2020-06-04 17:13:33 +02:00
const gunichar2 * app_user_model_id ,
gboolean show_in_applist ,
GPtrArray * supported_extgroups ,
GPtrArray * supported_protocols )
{
2021-05-14 14:30:11 +02:00
guint i , i_verb , i_ext ;
2020-06-04 17:13:33 +02:00
gint extensions_considered ;
GWin32AppInfoApplication * app ;
gchar * app_user_model_id_u8 ;
gchar * app_user_model_id_u8_folded ;
GHashTableIter iter ;
2022-01-19 17:43:39 +01:00
GWin32AppInfoHandler * ext_handler ;
2020-12-15 17:59:59 +01:00
GWin32AppInfoHandler * url ;
2020-06-04 17:13:33 +02:00
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 ) ;
2022-12-23 17:36:22 +01:00
if ( ! app - > pretty_name & & ! app - > pretty_name_u8 & & display_name )
{
char * display_name_u8 = g_utf16_to_utf8 ( display_name , - 1 , NULL , NULL , NULL ) ;
app - > pretty_name = g_wcsdup ( display_name , - 1 ) ;
app - > pretty_name_u8 = g_steal_pointer ( & display_name_u8 ) ;
}
2020-06-04 17:13:33 +02:00
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 ) ) ;
}
2020-12-15 17:59:59 +01:00
if ( file_extn - > chosen_handler = = NULL )
g_set_object ( & file_extn - > chosen_handler , handler_rec ) ;
2020-06-04 17:13:33 +02:00
/* 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 .
*/
2022-01-19 17:43:39 +01:00
while ( g_hash_table_iter_next ( & iter , NULL , ( gpointer * ) & ext_handler ) )
2020-06-04 17:13:33 +02:00
{
2021-05-14 14:30:11 +02:00
guint i_hverb ;
2020-06-04 17:13:33 +02:00
2022-01-19 17:43:39 +01:00
if ( ! ext_handler )
2020-06-04 17:13:33 +02:00
continue ;
2022-01-19 17:43:39 +01:00
for ( i_hverb = 0 ; i_hverb < ext_handler - > verbs - > len ; i_hverb + + )
2020-06-04 17:13:33 +02:00
{
GWin32AppInfoShellVerb * handler_verb ;
2022-01-19 17:43:39 +01:00
handler_verb = _verb_idx ( ext_handler - > verbs , i_hverb ) ;
2020-06-04 17:13:33 +02:00
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 )
2022-05-17 12:14:21 +02:00
g_debug ( " Unexpectedly, UWP app `%S' (AUMId `%s') supports %d extensions but has no verbs " ,
full_package_name , app_user_model_id_u8 , extensions_considered ) ;
2020-06-04 17:13:33 +02:00
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 ) ;
2020-12-15 17:59:59 +01:00
2020-06-04 17:13:33 +02:00
g_hash_table_insert ( schema_rec - > handlers ,
g_strdup ( app_user_model_id_u8_folded ) ,
g_object_ref ( handler_rec ) ) ;
}
2020-12-15 17:59:59 +01:00
if ( schema_rec - > chosen_handler = = NULL )
g_set_object ( & schema_rec - > chosen_handler , handler_rec ) ;
/* Technically, UWP apps don't use verbs for URIs,
* but we only store an app field in verbs ,
* so each UWP URI handler has to have one .
* Let ' s call it " open " .
*/
uwp_handler_add_verb ( handler_rec ,
app ,
L " open " ,
NULL ,
TRUE ) ;
2020-06-04 17:13:33 +02:00
g_hash_table_insert ( app - > supported_urls ,
g_steal_pointer ( & proto_u8_folded ) ,
g_object_ref ( handler_rec ) ) ;
}
2020-12-15 17:59:59 +01:00
g_hash_table_iter_init ( & iter , app - > supported_urls ) ;
while ( g_hash_table_iter_next ( & iter , NULL , ( gpointer * ) & url ) )
{
2021-05-14 14:30:11 +02:00
guint i_hverb ;
2020-12-15 17:59:59 +01:00
if ( ! url )
continue ;
for ( i_hverb = 0 ; i_hverb < url - > verbs - > len ; i_hverb + + )
{
GWin32AppInfoShellVerb * handler_verb ;
handler_verb = _verb_idx ( url - > 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 ) ;
}
}
2020-06-04 17:13:33 +02:00
g_free ( app_user_model_id_u8 ) ;
g_free ( app_user_model_id_u8_folded ) ;
return TRUE ;
}
2014-08-29 10:53:35 +02:00
2020-12-15 16:27:06 +01:00
/* Calls SHLoadIndirectString() in a loop to resolve
* a string in @ { . . . } format ( also supports other indirect
* strings , but we aren ' t using it for those ) .
* Consumes the input , but may return it unmodified
* ( not an indirect string ) . May return % NULL ( the string
* is indirect , but the OS failed to load it ) .
*/
static gunichar2 *
resolve_string ( gunichar2 * at_string )
{
HRESULT hr ;
gunichar2 * result = NULL ;
gsize result_size ;
/* This value is arbitrary */
const gsize reasonable_size_limit = 8192 ;
if ( at_string = = NULL | | at_string [ 0 ] ! = L ' @ ' )
return at_string ;
/* In case of a no-op @at_string will be copied into the output,
* buffer so allocate at least that much .
*/
result_size = wcslen ( at_string ) + 1 ;
while ( TRUE )
{
result = g_renew ( gunichar2 , result , result_size ) ;
/* Since there's no built-in way to detect too small buffer size,
* we do so by putting a sentinel at the end of the buffer .
* If it ' s 0 ( result is always 0 - terminated , even if the buffer
* is too small ) , then try larger buffer .
*/
result [ result_size - 1 ] = 0xff ;
/* This string accepts size in characters, not bytes. */
hr = SHLoadIndirectString ( at_string , result , result_size , NULL ) ;
if ( ! SUCCEEDED ( hr ) )
{
g_free ( result ) ;
g_free ( at_string ) ;
return NULL ;
}
else if ( result [ result_size - 1 ] ! = 0 | |
result_size > = reasonable_size_limit )
{
/* Now that the length is known, allocate the exact amount */
gunichar2 * copy = g_wcsdup ( result , - 1 ) ;
g_free ( result ) ;
g_free ( at_string ) ;
return copy ;
}
result_size * = 2 ;
}
g_assert_not_reached ( ) ;
return at_string ;
}
static void
grab_registry_string ( GWin32RegistryKey * handler_appkey ,
const gunichar2 * value_name ,
gunichar2 * * destination ,
gchar * * destination_u8 )
{
gunichar2 * value ;
gsize value_size ;
GWin32RegistryValueType vtype ;
const gunichar2 * ms_resource_prefix = L " ms-resource: " ;
gsize ms_resource_prefix_len = wcslen ( ms_resource_prefix ) ;
/* Right now this function is not used without destination,
* enforce this . destination_u8 is optional .
*/
g_assert ( destination ! = NULL ) ;
if ( * destination ! = NULL )
return ;
2021-08-02 16:19:35 +02:00
value = NULL ;
2020-12-15 16:27:06 +01:00
if ( g_win32_registry_key_get_value_w ( handler_appkey ,
NULL ,
TRUE ,
value_name ,
& vtype ,
( void * * ) & value ,
& value_size ,
NULL ) & &
vtype ! = G_WIN32_REGISTRY_VALUE_STR )
g_clear_pointer ( & value , g_free ) ;
/* There's no way for us to resolve "ms-resource:..." strings */
if ( value ! = NULL & &
value_size > = ms_resource_prefix_len & &
memcmp ( value ,
ms_resource_prefix ,
ms_resource_prefix_len * sizeof ( gunichar2 ) ) = = 0 )
g_clear_pointer ( & value , g_free ) ;
if ( value = = NULL )
return ;
* destination = resolve_string ( g_steal_pointer ( & value ) ) ;
if ( * destination = = NULL )
return ;
if ( destination_u8 )
* destination_u8 = g_utf16_to_utf8 ( * destination , - 1 , NULL , NULL , NULL ) ;
}
static void
read_uwp_handler_info ( void )
{
GHashTableIter iter ;
GWin32RegistryKey * handler_appkey ;
gunichar2 * aumid ;
g_hash_table_iter_init ( & iter , uwp_handler_table ) ;
while ( g_hash_table_iter_next ( & iter , ( gpointer * ) & handler_appkey , ( gpointer * ) & aumid ) )
{
gchar * aumid_u8_folded ;
GWin32AppInfoApplication * app ;
if ( ! g_utf16_to_utf8_and_fold ( aumid ,
- 1 ,
NULL ,
& aumid_u8_folded ) )
continue ;
app = g_hash_table_lookup ( apps_by_id , aumid_u8_folded ) ;
g_clear_pointer ( & aumid_u8_folded , g_free ) ;
if ( app = = NULL )
continue ;
grab_registry_string ( handler_appkey , L " ApplicationDescription " , & app - > description , & app - > description_u8 ) ;
grab_registry_string ( handler_appkey , L " ApplicationName " , & app - > localized_pretty_name , & app - > localized_pretty_name_u8 ) ;
/* TODO: ApplicationIcon value (usually also @{...}) resolves into
* an image ( PNG only ? ) with implicit multiple variants ( scale , size , etc ) .
*/
}
}
2014-08-29 10:53:35 +02:00
static void
update_registry_data ( void )
{
guint i ;
GPtrArray * capable_apps_keys ;
GPtrArray * user_capable_apps_keys ;
GPtrArray * priority_capable_apps_keys ;
GWin32RegistryKey * url_associations ;
GWin32RegistryKey * file_exts ;
GWin32RegistryKey * classes_root ;
2020-06-04 17:13:33 +02:00
DWORD collect_start , collect_end , alloc_end , capable_end , url_end , ext_end , exeapp_end , classes_end , uwp_end , postproc_end ;
GError * error = NULL ;
2014-08-29 10:53:35 +02:00
url_associations =
g_win32_registry_key_new_w ( L " HKEY_CURRENT_USER \\ Software \\ Microsoft \\ Windows \\ Shell \\ Associations \\ UrlAssociations " ,
NULL ) ;
file_exts =
g_win32_registry_key_new_w ( L " HKEY_CURRENT_USER \\ Software \\ Microsoft \\ Windows \\ CurrentVersion \\ Explorer \\ FileExts " ,
NULL ) ;
classes_root = g_win32_registry_key_new_w ( L " HKEY_CLASSES_ROOT " , NULL ) ;
capable_apps_keys = g_ptr_array_new_with_free_func ( g_free ) ;
user_capable_apps_keys = g_ptr_array_new_with_free_func ( g_free ) ;
priority_capable_apps_keys = g_ptr_array_new_with_free_func ( g_free ) ;
g_clear_pointer ( & apps_by_id , g_hash_table_destroy ) ;
g_clear_pointer ( & apps_by_exe , g_hash_table_destroy ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & fake_apps , g_hash_table_destroy ) ;
2014-08-29 10:53:35 +02:00
g_clear_pointer ( & urls , g_hash_table_destroy ) ;
g_clear_pointer ( & extensions , g_hash_table_destroy ) ;
g_clear_pointer ( & handlers , g_hash_table_destroy ) ;
collect_start = GetTickCount ( ) ;
collect_capable_apps_from_clients ( capable_apps_keys ,
priority_capable_apps_keys ,
FALSE ) ;
collect_capable_apps_from_clients ( user_capable_apps_keys ,
priority_capable_apps_keys ,
TRUE ) ;
collect_capable_apps_from_registered_apps ( user_capable_apps_keys ,
TRUE ) ;
collect_capable_apps_from_registered_apps ( capable_apps_keys ,
FALSE ) ;
collect_end = GetTickCount ( ) ;
apps_by_id =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , g_object_unref ) ;
apps_by_exe =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , g_object_unref ) ;
2019-12-29 12:35:49 +01:00
fake_apps =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , g_object_unref ) ;
2014-08-29 10:53:35 +02:00
urls =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , g_object_unref ) ;
extensions =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , g_object_unref ) ;
handlers =
g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , g_object_unref ) ;
2020-12-15 16:27:06 +01:00
uwp_handler_table =
g_hash_table_new_full ( g_direct_hash , g_direct_equal , g_object_unref , g_free ) ;
2014-08-29 10:53:35 +02:00
alloc_end = GetTickCount ( ) ;
for ( i = 0 ; i < priority_capable_apps_keys - > len ; i + + )
read_capable_app ( g_ptr_array_index ( priority_capable_apps_keys , i ) ,
TRUE ,
TRUE ) ;
for ( i = 0 ; i < user_capable_apps_keys - > len ; i + + )
read_capable_app ( g_ptr_array_index ( user_capable_apps_keys , i ) ,
TRUE ,
FALSE ) ;
for ( i = 0 ; i < capable_apps_keys - > len ; i + + )
read_capable_app ( g_ptr_array_index ( capable_apps_keys , i ) ,
FALSE ,
FALSE ) ;
capable_end = GetTickCount ( ) ;
read_urls ( url_associations ) ;
url_end = GetTickCount ( ) ;
read_exts ( file_exts ) ;
ext_end = GetTickCount ( ) ;
read_exeapps ( ) ;
exeapp_end = GetTickCount ( ) ;
read_classes ( classes_root ) ;
classes_end = GetTickCount ( ) ;
2020-06-04 17:13:33 +02:00
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 ) ;
}
2020-12-15 16:27:06 +01:00
read_uwp_handler_info ( ) ;
2020-06-04 17:13:33 +02:00
uwp_end = GetTickCount ( ) ;
2014-08-29 10:53:35 +02:00
link_handlers_to_unregistered_apps ( ) ;
2019-12-29 12:35:49 +01:00
link_handlers_to_fake_apps ( ) ;
2014-08-29 10:53:35 +02:00
postproc_end = GetTickCount ( ) ;
g_debug ( " Collecting capable appnames: %lums \n "
" Allocating hashtables:...... %lums \n "
" Reading capable apps: %lums \n "
" Reading URL associations:... %lums \n "
" Reading extension assocs: %lums \n "
" Reading exe-only apps:...... %lums \n "
" Reading classes: %lums \n "
2020-06-04 17:13:33 +02:00
" Reading UWP apps: %lums \n "
2014-08-29 10:53:35 +02:00
" Postprocessing:..............%lums \n "
2019-12-29 12:35:49 +01:00
" TOTAL: %lums " ,
2014-08-29 10:53:35 +02:00
collect_end - collect_start ,
alloc_end - collect_end ,
capable_end - alloc_end ,
url_end - capable_end ,
ext_end - url_end ,
exeapp_end - ext_end ,
classes_end - exeapp_end ,
2020-06-04 17:13:33 +02:00
uwp_end - classes_end ,
postproc_end - uwp_end ,
2014-08-29 10:53:35 +02:00
postproc_end - collect_start ) ;
g_clear_object ( & classes_root ) ;
g_clear_object ( & url_associations ) ;
g_clear_object ( & file_exts ) ;
g_ptr_array_free ( capable_apps_keys , TRUE ) ;
g_ptr_array_free ( user_capable_apps_keys , TRUE ) ;
g_ptr_array_free ( priority_capable_apps_keys , TRUE ) ;
2020-12-15 16:27:06 +01:00
g_hash_table_unref ( uwp_handler_table ) ;
2014-08-29 10:53:35 +02:00
return ;
}
2021-07-31 13:01:06 +02:00
static void
watch_keys ( void ) ;
2020-10-04 00:44:19 +02:00
/* This function is called when any of our registry watchers detect
* changes in the registry .
*/
static void
keys_updated ( GWin32RegistryKey * key ,
gpointer user_data )
{
2021-07-31 13:01:06 +02:00
watch_keys ( ) ;
2020-10-04 00:44:19 +02:00
/* Indicate the tree as not up-to-date, push a new job for the AppInfo thread */
g_atomic_int_inc ( & gio_win32_appinfo_update_counter ) ;
/* We don't use the data pointer, but it must be non-NULL */
g_thread_pool_push ( gio_win32_appinfo_threadpool , ( gpointer ) keys_updated , NULL ) ;
}
2014-08-29 10:53:35 +02:00
static void
watch_keys ( void )
{
if ( url_associations_key )
g_win32_registry_key_watch ( url_associations_key ,
TRUE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
if ( file_exts_key )
g_win32_registry_key_watch ( file_exts_key ,
TRUE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
if ( user_clients_key )
g_win32_registry_key_watch ( user_clients_key ,
TRUE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
if ( system_clients_key )
g_win32_registry_key_watch ( system_clients_key ,
TRUE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
if ( applications_key )
g_win32_registry_key_watch ( applications_key ,
TRUE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
if ( user_registered_apps_key )
g_win32_registry_key_watch ( user_registered_apps_key ,
TRUE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
if ( system_registered_apps_key )
g_win32_registry_key_watch ( system_registered_apps_key ,
TRUE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
if ( classes_root_key )
g_win32_registry_key_watch ( classes_root_key ,
FALSE ,
G_WIN32_REGISTRY_WATCH_NAME |
G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
G_WIN32_REGISTRY_WATCH_VALUES ,
2020-10-04 00:44:19 +02:00
keys_updated ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ) ;
}
2020-10-04 00:44:19 +02:00
/* This is the main function of the AppInfo thread */
2014-08-29 10:53:35 +02:00
static void
2020-10-04 00:44:19 +02:00
gio_win32_appinfo_thread_func ( gpointer data ,
gpointer user_data )
{
gint saved_counter ;
g_mutex_lock ( & gio_win32_appinfo_mutex ) ;
saved_counter = g_atomic_int_get ( & gio_win32_appinfo_update_counter ) ;
if ( saved_counter > 0 )
update_registry_data ( ) ;
/* If the counter didn't change while we were working, then set it to zero.
* Otherwise we need to rebuild the tree again , so keep it greater than zero .
* Numeric value doesn ' t matter - even if we ' re asked to rebuild N times ,
* we just need to rebuild once , and as long as there were no new rebuild
* requests while we were working , we ' re done .
*/
if ( g_atomic_int_compare_and_exchange ( & gio_win32_appinfo_update_counter ,
saved_counter ,
0 ) )
g_cond_broadcast ( & gio_win32_appinfo_cond ) ;
g_mutex_unlock ( & gio_win32_appinfo_mutex ) ;
}
/* Initializes Windows AppInfo. Creates the registry watchers,
* the AppInfo thread , and initiates an update of the AppInfo tree .
* Called with do_wait = ` FALSE ` at startup to prevent it from
* blocking until the tree is updated . All subsequent calls
* from everywhere else are made with do_wait = ` TRUE ` , blocking
* until the tree is re - built ( if needed ) .
*/
void
gio_win32_appinfo_init ( gboolean do_wait )
2014-08-29 10:53:35 +02:00
{
2015-08-31 19:48:22 +02:00
static gsize initialized ;
2014-08-29 10:53:35 +02:00
if ( g_once_init_enter ( & initialized ) )
{
2021-03-24 17:36:42 +01:00
HMODULE gio_dll_extra ;
2014-08-29 10:53:35 +02:00
url_associations_key =
g_win32_registry_key_new_w ( L " HKEY_CURRENT_USER \\ Software \\ Microsoft \\ Windows \\ Shell \\ Associations \\ UrlAssociations " ,
NULL ) ;
file_exts_key =
g_win32_registry_key_new_w ( L " HKEY_CURRENT_USER \\ Software \\ Microsoft \\ Windows \\ CurrentVersion \\ Explorer \\ FileExts " ,
NULL ) ;
user_clients_key =
g_win32_registry_key_new_w ( L " HKEY_CURRENT_USER \\ Software \\ Clients " ,
NULL ) ;
system_clients_key =
g_win32_registry_key_new_w ( L " HKEY_LOCAL_MACHINE \\ Software \\ Clients " ,
NULL ) ;
applications_key =
g_win32_registry_key_new_w ( L " HKEY_CLASSES_ROOT \\ Applications " ,
NULL ) ;
user_registered_apps_key =
g_win32_registry_key_new_w ( L " HKEY_CURRENT_USER \\ Software \\ RegisteredApplications " ,
NULL ) ;
system_registered_apps_key =
g_win32_registry_key_new_w ( L " HKEY_LOCAL_MACHINE \\ Software \\ RegisteredApplications " ,
NULL ) ;
classes_root_key =
g_win32_registry_key_new_w ( L " HKEY_CLASSES_ROOT " ,
NULL ) ;
watch_keys ( ) ;
2020-10-04 00:44:19 +02:00
/* We don't really require an exclusive pool, but the implementation
* details might cause the g_thread_pool_push ( ) call below to block
* if the pool is not exclusive ( specifically - for POSIX threads backend
* lacking thread scheduler settings ) .
*/
gio_win32_appinfo_threadpool = g_thread_pool_new ( gio_win32_appinfo_thread_func ,
NULL ,
1 ,
TRUE ,
NULL ) ;
g_mutex_init ( & gio_win32_appinfo_mutex ) ;
g_cond_init ( & gio_win32_appinfo_cond ) ;
g_atomic_int_set ( & gio_win32_appinfo_update_counter , 1 ) ;
/* Trigger initial tree build. Fake data pointer. */
g_thread_pool_push ( gio_win32_appinfo_threadpool , ( gpointer ) keys_updated , NULL ) ;
2021-03-24 17:36:42 +01:00
/* Increment the DLL refcount */
2023-10-05 09:21:08 +02:00
GetModuleHandleEx ( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN ,
( LPCWSTR ) gio_win32_appinfo_init ,
& gio_dll_extra ) ;
2021-03-24 17:36:42 +01:00
/* gio DLL cannot be unloaded now */
2014-08-29 10:53:35 +02:00
g_once_init_leave ( & initialized , TRUE ) ;
}
2020-10-04 00:44:19 +02:00
if ( ! do_wait )
return ;
2020-12-11 16:18:18 +01:00
/* Previously, we checked each of the watched keys here.
* Now we just look at the update counter , because each key
* has a change callback keys_updated , which increments this counter .
2020-10-04 00:44:19 +02:00
*/
2020-12-11 16:18:18 +01:00
if ( g_atomic_int_get ( & gio_win32_appinfo_update_counter ) > 0 )
2014-08-29 10:53:35 +02:00
{
2020-10-04 00:44:19 +02:00
g_mutex_lock ( & gio_win32_appinfo_mutex ) ;
while ( g_atomic_int_get ( & gio_win32_appinfo_update_counter ) > 0 )
g_cond_wait ( & gio_win32_appinfo_cond , & gio_win32_appinfo_mutex ) ;
g_mutex_unlock ( & gio_win32_appinfo_mutex ) ;
2014-08-29 10:53:35 +02:00
}
}
static void g_win32_app_info_iface_init ( GAppInfoIface * iface ) ;
struct _GWin32AppInfo
{
GObject parent_instance ;
/*<private>*/
gchar * * supported_types ;
GWin32AppInfoApplication * app ;
GWin32AppInfoHandler * handler ;
guint startup_notify : 1 ;
} ;
G_DEFINE_TYPE_WITH_CODE ( GWin32AppInfo , g_win32_app_info , G_TYPE_OBJECT ,
G_IMPLEMENT_INTERFACE ( G_TYPE_APP_INFO ,
g_win32_app_info_iface_init ) )
static void
g_win32_app_info_finalize ( GObject * object )
{
GWin32AppInfo * info ;
info = G_WIN32_APP_INFO ( object ) ;
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & info - > supported_types , g_strfreev ) ;
2014-08-29 10:53:35 +02:00
g_clear_object ( & info - > app ) ;
g_clear_object ( & info - > handler ) ;
G_OBJECT_CLASS ( g_win32_app_info_parent_class ) - > finalize ( object ) ;
}
static void
g_win32_app_info_class_init ( GWin32AppInfoClass * klass )
{
GObjectClass * gobject_class = G_OBJECT_CLASS ( klass ) ;
gobject_class - > finalize = g_win32_app_info_finalize ;
}
static void
g_win32_app_info_init ( GWin32AppInfo * local )
{
}
static GAppInfo *
g_win32_app_info_new_from_app ( GWin32AppInfoApplication * app ,
GWin32AppInfoHandler * handler )
{
GWin32AppInfo * new_info ;
GHashTableIter iter ;
gpointer ext ;
int i ;
new_info = g_object_new ( G_TYPE_WIN32_APP_INFO , NULL ) ;
new_info - > app = g_object_ref ( app ) ;
2020-10-04 00:44:19 +02:00
gio_win32_appinfo_init ( TRUE ) ;
g_mutex_lock ( & gio_win32_appinfo_mutex ) ;
2014-08-29 10:53:35 +02:00
i = 0 ;
g_hash_table_iter_init ( & iter , new_info - > app - > supported_exts ) ;
while ( g_hash_table_iter_next ( & iter , & ext , NULL ) )
{
if ( ext )
i + = 1 ;
}
2020-12-23 02:19:31 +01:00
new_info - > supported_types = g_new ( gchar * , i + 1 ) ;
2014-08-29 10:53:35 +02:00
i = 0 ;
g_hash_table_iter_init ( & iter , new_info - > app - > supported_exts ) ;
while ( g_hash_table_iter_next ( & iter , & ext , NULL ) )
{
if ( ! ext )
continue ;
2019-12-29 12:35:49 +01:00
new_info - > supported_types [ i ] = g_strdup ( ( gchar * ) ext ) ;
2014-08-29 10:53:35 +02:00
i + = 1 ;
}
2020-10-04 00:44:19 +02:00
g_mutex_unlock ( & gio_win32_appinfo_mutex ) ;
2014-08-29 10:53:35 +02:00
new_info - > supported_types [ i ] = NULL ;
2015-06-24 12:07:15 +02:00
new_info - > handler = handler ? g_object_ref ( handler ) : NULL ;
2014-08-29 10:53:35 +02:00
return G_APP_INFO ( new_info ) ;
}
static GAppInfo *
g_win32_app_info_dup ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
GWin32AppInfo * new_info ;
new_info = g_object_new ( G_TYPE_WIN32_APP_INFO , NULL ) ;
if ( info - > app )
new_info - > app = g_object_ref ( info - > app ) ;
if ( info - > handler )
new_info - > handler = g_object_ref ( info - > handler ) ;
new_info - > startup_notify = info - > startup_notify ;
if ( info - > supported_types )
{
int i ;
for ( i = 0 ; info - > supported_types [ i ] ; i + + )
break ;
2020-12-23 02:19:31 +01:00
new_info - > supported_types = g_new ( gchar * , i + 1 ) ;
2014-08-29 10:53:35 +02:00
for ( i = 0 ; info - > supported_types [ i ] ; i + + )
new_info - > supported_types [ i ] = g_strdup ( info - > supported_types [ i ] ) ;
new_info - > supported_types [ i ] = NULL ;
}
return G_APP_INFO ( new_info ) ;
}
static gboolean
g_win32_app_info_equal ( GAppInfo * appinfo1 ,
GAppInfo * appinfo2 )
{
2019-12-29 12:35:49 +01:00
GWin32AppInfoShellVerb * shverb1 = NULL ;
GWin32AppInfoShellVerb * shverb2 = NULL ;
2014-08-29 10:53:35 +02:00
GWin32AppInfo * info1 = G_WIN32_APP_INFO ( appinfo1 ) ;
GWin32AppInfo * info2 = G_WIN32_APP_INFO ( appinfo2 ) ;
2019-12-29 12:35:49 +01:00
GWin32AppInfoApplication * app1 = info1 - > app ;
GWin32AppInfoApplication * app2 = info2 - > app ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( app1 = = NULL | |
app2 = = NULL )
2014-08-29 10:53:35 +02:00
return info1 = = info2 ;
2019-12-29 12:35:49 +01:00
if ( app1 - > canonical_name_folded ! = NULL & &
app2 - > canonical_name_folded ! = NULL )
return ( g_strcmp0 ( app1 - > canonical_name_folded ,
app2 - > canonical_name_folded ) ) = = 0 ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( app1 - > verbs - > len > 0 & &
app2 - > verbs - > len > 0 )
{
shverb1 = _verb_idx ( app1 - > verbs , 0 ) ;
shverb2 = _verb_idx ( app2 - > verbs , 0 ) ;
if ( shverb1 - > executable_folded ! = NULL & &
shverb2 - > executable_folded ! = NULL )
return ( g_strcmp0 ( shverb1 - > executable_folded ,
shverb2 - > executable_folded ) ) = = 0 ;
}
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
return app1 = = app2 ;
2014-08-29 10:53:35 +02:00
}
static const char *
g_win32_app_info_get_id ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
2019-12-29 12:35:49 +01:00
GWin32AppInfoShellVerb * shverb ;
2014-08-29 10:53:35 +02:00
if ( info - > app = = NULL )
return NULL ;
if ( info - > app - > canonical_name_u8 )
return info - > app - > canonical_name_u8 ;
2019-12-29 12:35:49 +01:00
if ( info - > app - > verbs - > len > 0 & &
( shverb = _verb_idx ( info - > app - > verbs , 0 ) ) - > executable_basename ! = NULL )
return shverb - > executable_basename ;
2014-08-29 10:53:35 +02:00
return NULL ;
}
static const char *
g_win32_app_info_get_name ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
2020-12-15 08:57:28 +01:00
if ( info - > app & & info - > app - > pretty_name_u8 )
return info - > app - > pretty_name_u8 ;
else if ( info - > app & & info - > app - > canonical_name_u8 )
2014-08-29 10:53:35 +02:00
return info - > app - > canonical_name_u8 ;
else
2023-11-29 14:39:58 +01:00
return _ ( " Unnamed " ) ;
2014-08-29 10:53:35 +02:00
}
static const char *
g_win32_app_info_get_display_name ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
if ( info - > app )
{
if ( info - > app - > localized_pretty_name_u8 )
return info - > app - > localized_pretty_name_u8 ;
else if ( info - > app - > pretty_name_u8 )
return info - > app - > pretty_name_u8 ;
}
return g_win32_app_info_get_name ( appinfo ) ;
}
static const char *
g_win32_app_info_get_description ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
if ( info - > app = = NULL )
return NULL ;
return info - > app - > description_u8 ;
}
static const char *
g_win32_app_info_get_executable ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
if ( info - > app = = NULL )
return NULL ;
2020-06-04 17:22:08 +02:00
if ( info - > app - > verbs - > len > 0 & & ! info - > app - > is_uwp )
2019-12-29 12:35:49 +01:00
return _verb_idx ( info - > app - > verbs , 0 ) - > executable ;
return NULL ;
2014-08-29 10:53:35 +02:00
}
static const char *
g_win32_app_info_get_commandline ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
if ( info - > app = = NULL )
return NULL ;
2020-06-04 17:22:08 +02:00
if ( info - > app - > verbs - > len > 0 & & ! info - > app - > is_uwp )
2019-12-29 12:35:49 +01:00
return _verb_idx ( info - > app - > verbs , 0 ) - > command_utf8 ;
return NULL ;
2014-08-29 10:53:35 +02:00
}
static GIcon *
g_win32_app_info_get_icon ( GAppInfo * appinfo )
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
if ( info - > app = = NULL )
return NULL ;
return info - > app - > icon ;
}
typedef struct _file_or_uri {
gchar * uri ;
gchar * file ;
} file_or_uri ;
static char *
expand_macro_single ( char macro , file_or_uri * obj )
{
char * result = NULL ;
switch ( macro )
{
case ' * ' :
case ' 0 ' :
case ' 1 ' :
case ' l ' :
case ' d ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' :
/* TODO: handle 'l' and 'd' differently (longname and desktop name) */
2022-12-21 13:57:29 +01:00
if ( obj - > file )
{
result = g_strdup ( obj - > file ) ;
}
else if ( obj - > uri )
{
const char * prefix = " file:/// " ;
const size_t prefix_len = strlen ( prefix ) ;
2023-04-05 13:50:23 +02:00
if ( g_str_has_prefix ( obj - > uri , prefix ) & & obj - > uri [ prefix_len ] ! = 0 )
2022-12-21 13:57:29 +01:00
{
GFile * file = g_file_new_for_uri ( obj - > uri ) ;
result = g_file_get_path ( file ) ;
g_object_unref ( file ) ;
}
if ( ! result )
result = g_strdup ( obj - > uri ) ;
}
2014-08-29 10:53:35 +02:00
break ;
case ' u ' :
case ' U ' :
if ( obj - > uri )
result = g_shell_quote ( obj - > uri ) ;
break ;
case ' f ' :
case ' F ' :
if ( obj - > file )
result = g_shell_quote ( obj - > file ) ;
break ;
}
return result ;
}
static gboolean
expand_macro ( char macro ,
GString * exec ,
GWin32AppInfo * info ,
GList * * stat_obj_list ,
GList * * obj_list )
{
GList * objs = * obj_list ;
char * expanded ;
gboolean result = FALSE ;
g_return_val_if_fail ( exec ! = NULL , FALSE ) ;
/*
Legend : ( from http : //msdn.microsoft.com/en-us/library/windows/desktop/cc144101%28v=vs.85%29.aspx)
% * - replace with all parameters
% ~ - replace with all parameters starting with and following the second parameter
% 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 ) .
% < n > ( where N is 2 - 9 ) , replace with the nth parameter
% s - show command
% h - hotkey value
% i - IDList stored in a shared memory handle is passed here .
% 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 .
% d - desktop absolute parsing name of the first parameter ( for items that don ' t have file system paths )
% v - for verbs that are none implies all , if there is no parameter passed this is the working directory
% w - the working directory
*/
switch ( macro )
{
case ' * ' :
case ' ~ ' :
if ( * stat_obj_list )
{
gint i ;
GList * o ;
for ( o = * stat_obj_list , i = 0 ;
macro = = ' ~ ' & & o & & i < 2 ;
o = o - > next , i + + ) ;
for ( ; o ; o = o - > next )
{
expanded = expand_macro_single ( macro , o - > data ) ;
if ( expanded )
{
if ( o ! = * stat_obj_list )
g_string_append ( exec , " " ) ;
g_string_append ( exec , expanded ) ;
g_free ( expanded ) ;
}
}
objs = NULL ;
result = TRUE ;
}
break ;
case ' 0 ' :
case ' 1 ' :
case ' l ' :
case ' d ' :
if ( * stat_obj_list )
{
GList * o ;
o = * stat_obj_list ;
if ( o )
{
expanded = expand_macro_single ( macro , o - > data ) ;
if ( expanded )
{
if ( o ! = * stat_obj_list )
g_string_append ( exec , " " ) ;
g_string_append ( exec , expanded ) ;
g_free ( expanded ) ;
}
}
if ( objs )
objs = objs - > next ;
result = TRUE ;
}
break ;
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' :
if ( * stat_obj_list )
{
gint i ;
GList * o ;
2022-01-19 17:43:39 +01:00
gint n = 0 ;
2014-08-29 10:53:35 +02:00
switch ( macro )
{
case ' 2 ' :
n = 2 ;
break ;
case ' 3 ' :
n = 3 ;
break ;
case ' 4 ' :
n = 4 ;
break ;
case ' 5 ' :
n = 5 ;
break ;
case ' 6 ' :
n = 6 ;
break ;
case ' 7 ' :
n = 7 ;
break ;
case ' 8 ' :
n = 8 ;
break ;
case ' 9 ' :
n = 9 ;
break ;
}
for ( o = * stat_obj_list , i = 0 ; o & & i < n ; o = o - > next , i + + ) ;
if ( o )
{
expanded = expand_macro_single ( macro , o - > data ) ;
if ( expanded )
{
if ( o ! = * stat_obj_list )
g_string_append ( exec , " " ) ;
2007-11-28 13:39:07 +01:00
2014-08-29 10:53:35 +02:00
g_string_append ( exec , expanded ) ;
g_free ( expanded ) ;
}
}
result = TRUE ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( objs )
objs = NULL ;
}
break ;
case ' s ' :
break ;
case ' h ' :
break ;
case ' i ' :
break ;
case ' v ' :
break ;
case ' w ' :
expanded = g_get_current_dir ( ) ;
g_string_append ( exec , expanded ) ;
g_free ( expanded ) ;
break ;
case ' u ' :
case ' f ' :
if ( objs )
{
expanded = expand_macro_single ( macro , objs - > data ) ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( expanded )
{
g_string_append ( exec , expanded ) ;
g_free ( expanded ) ;
}
objs = objs - > next ;
result = TRUE ;
}
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
break ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
case ' U ' :
case ' F ' :
while ( objs )
{
expanded = expand_macro_single ( macro , objs - > data ) ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( expanded )
{
g_string_append ( exec , expanded ) ;
g_free ( expanded ) ;
}
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
objs = objs - > next ;
result = TRUE ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( objs ! = NULL & & expanded )
g_string_append_c ( exec , ' ' ) ;
}
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
break ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
case ' c ' :
if ( info - > app & & info - > app - > localized_pretty_name_u8 )
{
expanded = g_shell_quote ( info - > app - > localized_pretty_name_u8 ) ;
g_string_append ( exec , expanded ) ;
g_free ( expanded ) ;
}
break ;
2008-06-16 11:54:04 +02:00
2014-08-29 10:53:35 +02:00
case ' m ' : /* deprecated */
case ' n ' : /* deprecated */
case ' N ' : /* deprecated */
/*case 'd': */ /* deprecated */
case ' D ' : /* deprecated */
break ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
case ' % ' :
g_string_append_c ( exec , ' % ' ) ;
break ;
}
* obj_list = objs ;
return result ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
static gboolean
expand_application_parameters ( GWin32AppInfo * info ,
const gchar * exec_line ,
GList * * objs ,
int * argc ,
char * * * argv ,
GError * * error )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
GList * obj_list = * objs ;
GList * * stat_obj_list = objs ;
const char * p = exec_line ;
GString * expanded_exec ;
gboolean res ;
gchar * a_char ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
expanded_exec = g_string_new ( NULL ) ;
res = FALSE ;
while ( * p )
{
if ( p [ 0 ] = = ' % ' & & p [ 1 ] ! = ' \0 ' )
{
if ( expand_macro ( p [ 1 ] ,
expanded_exec ,
info , stat_obj_list ,
objs ) )
res = TRUE ;
p + + ;
}
2007-11-26 17:13:05 +01:00
else
2014-08-29 10:53:35 +02:00
g_string_append_c ( expanded_exec , * p ) ;
p + + ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
/* No file substitutions */
if ( obj_list = = * objs & & obj_list ! = NULL & & ! res )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
/* If there is no macro default to %f. This is also what KDE does */
g_string_append_c ( expanded_exec , ' ' ) ;
expand_macro ( ' f ' , expanded_exec , info , stat_obj_list , objs ) ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
/* Replace '\\' with '/', because g_shell_parse_argv considers them
* to be escape sequences .
*/
for ( a_char = expanded_exec - > str ;
a_char < = & expanded_exec - > str [ expanded_exec - > len ] ;
a_char + + )
{
if ( * a_char = = ' \\ ' )
* a_char = ' / ' ;
}
res = g_shell_parse_argv ( expanded_exec - > str , argc , argv , error ) ;
g_string_free ( expanded_exec , TRUE ) ;
return res ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
static gchar *
2019-12-29 12:35:49 +01:00
get_appath_for_exe ( const gchar * exe_basename )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
GWin32RegistryKey * apppath_key = NULL ;
GWin32RegistryValueType val_type ;
2019-12-29 12:35:49 +01:00
gchar * appath = NULL ;
2014-08-29 10:53:35 +02:00
gboolean got_value ;
2019-12-29 12:35:49 +01:00
gchar * key_path = g_strdup_printf ( " HKEY_LOCAL_MACHINE \\ "
" SOFTWARE \\ "
" Microsoft \\ "
" Windows \\ "
" CurrentVersion \\ "
" App Paths \\ "
" %s " , exe_basename ) ;
2007-11-26 17:13:05 +01:00
2019-12-29 12:35:49 +01:00
apppath_key = g_win32_registry_key_new ( key_path , NULL ) ;
g_clear_pointer ( & key_path , g_free ) ;
2014-08-29 10:53:35 +02:00
if ( apppath_key = = NULL )
return NULL ;
2019-12-29 12:35:49 +01:00
got_value = g_win32_registry_key_get_value ( apppath_key ,
NULL ,
TRUE ,
" Path " ,
& val_type ,
( void * * ) & appath ,
NULL ,
NULL ) ;
2014-08-29 10:53:35 +02:00
g_object_unref ( apppath_key ) ;
2019-12-29 12:35:49 +01:00
if ( got_value & &
val_type = = G_WIN32_REGISTRY_VALUE_STR )
return appath ;
2014-08-29 10:53:35 +02:00
g_clear_pointer ( & appath , g_free ) ;
2019-12-29 12:35:49 +01:00
return appath ;
2007-11-26 17:13:05 +01:00
}
2022-07-21 16:23:56 +02:00
/* 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 ) ;
2022-09-02 12:35:17 +02:00
g_spawn_close_pid ( * pid ) ;
2022-07-21 16:23:56 +02:00
}
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 ) ;
}
2022-09-02 12:35:17 +02:00
* pid = NULL ;
2022-07-21 16:23:56 +02:00
}
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 ) ;
}
}
2014-08-29 10:53:35 +02:00
2022-12-22 22:01:21 +01:00
typedef enum
{
/* PLAIN: just open the application, without arguments of any kind
* 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
g_win32_app_info_launch_uwp_single ( IApplicationActivationManager * app_activation_manager ,
UwpActivationType activation_type ,
IShellItemArray * items ,
const wchar_t * verb ,
GWin32AppInfo * info ,
GAppLaunchContext * launch_context ,
GTask * from_task ,
GError * * error )
{
const wchar_t * canonical_name = ( const wchar_t * ) info - > app - > canonical_name ;
DWORD process_id = 0 ;
HRESULT hr = S_OK ;
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 */
switch ( activation_type )
{
case UWP_ACTIVATION_TYPE_PLAIN :
g_assert ( items = = NULL ) ;
hr = IApplicationActivationManager_ActivateApplication ( app_activation_manager ,
canonical_name ,
NULL , AO_NONE ,
& process_id ) ;
break ;
case UWP_ACTIVATION_TYPE_PROTOCOL :
g_assert ( items ! = NULL ) ;
hr = IApplicationActivationManager_ActivateForProtocol ( app_activation_manager ,
canonical_name ,
items ,
& 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 ) )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" The app %s failed to launch: 0x%lx " ,
g_win32_appinfo_application_get_some_name ( info - > app ) , hr ) ;
emit_launch_failed ( launch_context , info , from_task ) ;
return FALSE ;
}
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 21 H2 ) . 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 ) ;
}
return TRUE ;
}
2022-12-23 13:45:10 +01:00
static gboolean
g_win32_app_info_supports_files ( GAppInfo * appinfo ) ;
2022-12-22 16:35:14 +01:00
static IShellItemArray *
make_item_array ( gboolean for_files ,
GList * objs ,
GError * * error ) ;
2022-12-23 13:45:10 +01:00
static inline GList
make_single_entry_list ( gpointer data )
{
GList l = { NULL , NULL , NULL } ;
l . data = data ;
return l ;
}
2020-06-04 20:05:22 +02:00
static gboolean
g_win32_app_info_launch_uwp_internal ( GWin32AppInfo * info ,
gboolean for_files ,
2022-12-22 16:35:14 +01:00
GList * objs , /* (element-type file_or_uri) */
2020-06-04 20:05:22 +02:00
GWin32AppInfoShellVerb * shverb ,
2022-07-21 16:23:56 +02:00
GAppLaunchContext * launch_context ,
GTask * from_task ,
2020-06-04 20:05:22 +02:00
GError * * error )
{
2022-12-22 22:01:21 +01:00
IApplicationActivationManager * paam = NULL ;
2022-07-14 14:25:05 +02:00
gboolean com_initialized = FALSE ;
gboolean result = FALSE ;
2020-06-04 20:05:22 +02:00
HRESULT hr ;
2022-12-22 16:35:14 +01:00
2022-07-14 14:25:05 +02:00
/* 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 ;
}
2020-06-04 20:05:22 +02:00
2022-07-27 15:15:35 +02:00
/* 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 .
*/
2022-07-14 14:25:05 +02:00
hr = CoCreateInstance ( & CLSID_ApplicationActivationManager , NULL ,
2022-07-27 15:15:35 +02:00
CLSCTX_LOCAL_SERVER ,
2022-07-14 14:25:05 +02:00
& IID_IApplicationActivationManager , ( void * * ) & paam ) ;
2020-06-04 20:05:22 +02:00
if ( FAILED ( hr ) )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" Failed to create ApplicationActivationManager: 0x%lx " , hr ) ;
2022-07-14 14:25:05 +02:00
goto cleanup ;
2020-06-04 20:05:22 +02:00
}
2022-12-22 22:01:21 +01:00
if ( ! objs )
{
result = g_win32_app_info_launch_uwp_single ( paam , UWP_ACTIVATION_TYPE_PLAIN , NULL , NULL ,
info , launch_context , from_task , error ) ;
}
2020-06-04 20:05:22 +02:00
else if ( for_files )
{
2022-12-22 22:01:21 +01:00
IShellItemArray * items = make_item_array ( TRUE , objs , error ) ;
2022-07-21 16:23:56 +02:00
2022-12-22 22:01:21 +01:00
if ( ! items )
goto cleanup ;
2022-07-21 16:23:56 +02:00
2022-12-22 22:01:21 +01:00
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 ) ;
2022-07-14 14:25:05 +02:00
}
2022-12-22 22:01:21 +01:00
else
2022-07-21 16:23:56 +02:00
{
2022-12-23 13:45:10 +01:00
gboolean supports_files ;
gboolean supports_file_uris ;
2022-12-22 22:01:21 +01:00
gboolean outcome = TRUE ;
GList * l ;
2022-07-21 16:23:56 +02:00
2022-12-23 13:45:10 +01:00
supports_files = g_win32_app_info_supports_files ( G_APP_INFO ( info ) ) ;
supports_file_uris = info - > app & &
info - > app - > supported_urls & &
g_hash_table_lookup ( info - > app - > supported_urls , " file " ) ;
2022-12-22 22:01:21 +01:00
for ( l = objs ; l ! = NULL ; l = l - > next )
{
2022-12-23 13:45:10 +01:00
file_or_uri * obj = ( file_or_uri * ) l - > data ;
GList single = make_single_entry_list ( obj ) ;
IShellItemArray * item = NULL ;
UwpActivationType type ;
/* Most UWP applications support opening files but do not support
* the file : // protocol in URI's. That's because the UWP platform
* has a specific activation for files ( see FileActivatedEventArgs )
* which is different from protocol activation . Here we check for
* that . */
if ( ! supports_file_uris & & supports_files & & obj - > file )
{
type = UWP_ACTIVATION_TYPE_FILE ;
item = make_item_array ( TRUE , & single , error ) ;
}
else
{
type = UWP_ACTIVATION_TYPE_PROTOCOL ;
item = make_item_array ( FALSE , & single , error ) ;
}
2022-07-21 16:23:56 +02:00
2022-12-22 22:01:21 +01:00
if ( ! item )
{
outcome = FALSE ;
continue ;
}
2022-07-21 16:23:56 +02:00
2022-12-23 13:45:10 +01:00
if ( ! g_win32_app_info_launch_uwp_single ( paam , type ,
2022-12-22 22:01:21 +01:00
item , shverb - > verb_name , info ,
launch_context , from_task , error ) )
outcome = FALSE ;
2022-07-21 16:23:56 +02:00
2022-12-22 22:01:21 +01:00
IShellItemArray_Release ( item ) ;
2022-07-21 16:23:56 +02:00
}
2022-12-22 22:01:21 +01:00
result = outcome ;
2022-07-21 16:23:56 +02:00
}
2022-07-14 14:25:05 +02:00
cleanup :
if ( paam )
{
IApplicationActivationManager_Release ( paam ) ;
paam = NULL ;
2020-06-04 20:05:22 +02:00
}
2022-07-14 14:25:05 +02:00
if ( com_initialized )
{
CoUninitialize ( ) ;
com_initialized = FALSE ;
}
2020-06-04 20:05:22 +02:00
return result ;
}
2007-11-26 17:13:05 +01:00
static gboolean
2014-08-29 10:53:35 +02:00
g_win32_app_info_launch_internal ( GWin32AppInfo * info ,
2022-12-22 16:35:14 +01:00
GList * objs , /* (element-type file_or_uri) */
2020-06-04 20:05:22 +02:00
gboolean for_files , /* UWP only */
2014-08-29 10:53:35 +02:00
GAppLaunchContext * launch_context ,
GSpawnFlags spawn_flags ,
2022-07-21 15:57:15 +02:00
GTask * from_task ,
2014-08-29 10:53:35 +02:00
GError * * error )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
gboolean completed = FALSE ;
char * * argv , * * envp ;
int argc ;
2019-12-29 12:35:49 +01:00
const gchar * command ;
2014-08-29 10:53:35 +02:00
gchar * apppath ;
2019-12-29 12:35:49 +01:00
GWin32AppInfoShellVerb * shverb ;
2022-07-21 16:23:56 +02:00
GPid pid = NULL ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
g_return_val_if_fail ( info ! = NULL , FALSE ) ;
g_return_val_if_fail ( info - > app ! = NULL , FALSE ) ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
argv = NULL ;
2019-12-29 12:35:49 +01:00
shverb = NULL ;
2007-11-26 17:13:05 +01:00
2020-06-04 20:05:22 +02:00
if ( ! info - > app - > is_uwp & &
info - > handler ! = NULL & &
2019-12-29 12:35:49 +01:00
info - > handler - > verbs - > len > 0 )
shverb = _verb_idx ( info - > handler - > verbs , 0 ) ;
else if ( info - > app - > verbs - > len > 0 )
shverb = _verb_idx ( info - > app - > verbs , 0 ) ;
2007-11-26 17:13:05 +01:00
2019-12-29 12:35:49 +01:00
if ( shverb = = NULL )
2014-08-29 10:53:35 +02:00
{
2020-06-04 20:05:22 +02:00
if ( info - > app - > is_uwp | | info - > handler = = NULL )
2019-12-29 12:35:49 +01:00
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
2023-11-29 14:39:58 +01:00
_ ( " The app ‘ %s’ in the application object has no verbs " ) ,
2019-12-29 12:35:49 +01:00
g_win32_appinfo_application_get_some_name ( info - > app ) ) ;
else if ( info - > handler - > verbs - > len = = 0 )
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
2023-11-29 14:39:58 +01:00
_ ( " The app ‘ %s’ and the handler ‘ %s’ in the application object have no verbs " ) ,
2019-12-29 12:35:49 +01:00
g_win32_appinfo_application_get_some_name ( info - > app ) ,
info - > handler - > handler_id_folded ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
return FALSE ;
}
2014-08-29 10:53:35 +02:00
2020-06-04 20:05:22 +02:00
if ( info - > app - > is_uwp )
return g_win32_app_info_launch_uwp_internal ( info ,
for_files ,
2022-12-22 16:35:14 +01:00
objs ,
2020-06-04 20:05:22 +02:00
shverb ,
2022-07-21 16:23:56 +02:00
launch_context ,
from_task ,
2020-06-04 20:05:22 +02:00
error ) ;
if ( launch_context )
envp = g_app_launch_context_get_environment ( launch_context ) ;
else
envp = g_get_environ ( ) ;
2019-12-29 12:35:49 +01:00
g_assert ( shverb - > command_utf8 ! = NULL ) ;
command = shverb - > command_utf8 ;
apppath = get_appath_for_exe ( shverb - > executable_basename ) ;
2014-08-29 10:53:35 +02:00
if ( apppath )
{
gchar * * p ;
2021-05-14 14:30:11 +02:00
gsize p_index ;
2014-08-29 10:53:35 +02:00
for ( p = envp , p_index = 0 ; p [ 0 ] ; p + + , p_index + + )
if ( ( p [ 0 ] [ 0 ] = = ' p ' | | p [ 0 ] [ 0 ] = = ' P ' ) & &
( p [ 0 ] [ 1 ] = = ' a ' | | p [ 0 ] [ 1 ] = = ' A ' ) & &
( p [ 0 ] [ 2 ] = = ' t ' | | p [ 0 ] [ 2 ] = = ' T ' ) & &
( p [ 0 ] [ 3 ] = = ' h ' | | p [ 0 ] [ 3 ] = = ' H ' ) & &
( p [ 0 ] [ 4 ] = = ' = ' ) )
break ;
if ( p [ 0 ] = = NULL )
{
gchar * * new_envp ;
new_envp = g_new ( char * , g_strv_length ( envp ) + 2 ) ;
new_envp [ 0 ] = g_strdup_printf ( " PATH=%s " , apppath ) ;
for ( p_index = 0 ; p_index < = g_strv_length ( envp ) ; p_index + + )
new_envp [ 1 + p_index ] = envp [ p_index ] ;
g_free ( envp ) ;
envp = new_envp ;
}
else
{
gchar * p_path ;
p_path = & p [ 0 ] [ 5 ] ;
if ( p_path [ 0 ] ! = ' \0 ' )
envp [ p_index ] = g_strdup_printf ( " PATH=%s%c%s " ,
apppath ,
G_SEARCHPATH_SEPARATOR ,
p_path ) ;
else
envp [ p_index ] = g_strdup_printf ( " PATH=%s " , apppath ) ;
g_free ( & p_path [ - 5 ] ) ;
}
}
do
{
2022-08-12 19:08:38 +02:00
if ( from_task & & g_task_return_error_if_cancelled ( from_task ) )
goto out ;
2014-08-29 10:53:35 +02:00
if ( ! expand_application_parameters ( info ,
command ,
& objs ,
& argc ,
& argv ,
error ) )
goto out ;
2022-07-21 16:23:56 +02:00
emit_launch_started ( launch_context , info , from_task ) ;
2014-08-29 10:53:35 +02:00
if ( ! g_spawn_async ( NULL ,
argv ,
envp ,
2022-07-28 12:21:50 +02:00
spawn_flags |
G_SPAWN_DO_NOT_REAP_CHILD ,
2014-08-29 10:53:35 +02:00
NULL ,
NULL ,
& pid ,
error ) )
{
2022-07-21 16:23:56 +02:00
emit_launch_failed ( launch_context , info , from_task ) ;
goto out ;
2014-08-29 10:53:35 +02:00
}
2022-07-21 16:23:56 +02:00
else if ( launch_context )
emit_launched ( launch_context , info , & pid , from_task ) ;
2014-08-29 10:53:35 +02:00
2022-07-28 12:21:50 +02:00
g_spawn_close_pid ( pid ) ;
2022-07-21 16:23:56 +02:00
pid = NULL ;
2014-08-29 10:53:35 +02:00
g_strfreev ( argv ) ;
argv = NULL ;
}
while ( objs ! = NULL ) ;
completed = TRUE ;
2022-07-21 16:23:56 +02:00
out :
g_spawn_close_pid ( pid ) ;
2014-08-29 10:53:35 +02:00
g_strfreev ( argv ) ;
g_strfreev ( envp ) ;
return completed ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
static void
free_file_or_uri ( gpointer ptr )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
file_or_uri * obj = ptr ;
g_free ( obj - > file ) ;
g_free ( obj - > uri ) ;
g_free ( obj ) ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
static gboolean
g_win32_app_supports_uris ( GWin32AppInfoApplication * app )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
gssize num_of_uris_supported ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( app = = NULL )
return FALSE ;
num_of_uris_supported = ( gssize ) g_hash_table_size ( app - > supported_urls ) ;
if ( g_hash_table_lookup ( app - > supported_urls , " file " ) )
num_of_uris_supported - = 1 ;
return num_of_uris_supported > 0 ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
2007-11-26 17:13:05 +01:00
static gboolean
2014-08-29 10:53:35 +02:00
g_win32_app_info_supports_uris ( GAppInfo * appinfo )
2007-11-26 17:13:05 +01:00
{
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
2014-08-29 10:53:35 +02:00
if ( info - > app = = NULL )
return FALSE ;
2011-10-15 22:59:59 +02:00
2014-08-29 10:53:35 +02:00
return g_win32_app_supports_uris ( info - > app ) ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
2007-11-26 17:13:05 +01:00
static gboolean
2014-08-29 10:53:35 +02:00
g_win32_app_info_supports_files ( GAppInfo * appinfo )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
if ( info - > app = = NULL )
return FALSE ;
return g_hash_table_size ( info - > app - > supported_exts ) > 0 ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
2020-06-04 20:05:22 +02:00
static IShellItemArray *
make_item_array ( gboolean for_files ,
2022-12-22 16:35:14 +01:00
GList * objs , /* (element-type file_or_uri) */
2020-06-04 20:05:22 +02:00
GError * * error )
{
ITEMIDLIST * * item_ids ;
IShellItemArray * items ;
GList * p ;
gsize count ;
gsize i ;
HRESULT hr ;
2022-12-22 16:35:14 +01:00
count = g_list_length ( objs ) ;
2020-06-04 20:05:22 +02:00
items = NULL ;
item_ids = g_new ( ITEMIDLIST * , count ) ;
2022-12-22 16:35:14 +01:00
for ( i = 0 , p = objs ; p ! = NULL ; p = p - > next , i + + )
2020-06-04 20:05:22 +02:00
{
2022-12-22 16:35:14 +01:00
file_or_uri * obj = ( file_or_uri * ) p - > data ;
2020-06-04 20:05:22 +02:00
wchar_t * file_or_uri_utf16 ;
if ( ! for_files )
2022-12-22 16:35:14 +01:00
file_or_uri_utf16 = g_utf8_to_utf16 ( obj - > uri , - 1 , NULL , NULL , error ) ;
2020-06-04 20:05:22 +02:00
else
2022-12-22 16:35:14 +01:00
file_or_uri_utf16 = g_utf8_to_utf16 ( obj - > file , - 1 , NULL , NULL , error ) ;
2020-06-04 20:05:22 +02:00
if ( file_or_uri_utf16 = = NULL )
break ;
if ( for_files )
{
wchar_t * c ;
gsize len ;
gsize len_tail ;
len = wcslen ( file_or_uri_utf16 ) ;
/* Filenames *MUST* use single backslashes, else the call
* will fail . First convert all slashes to backslashes ,
* then remove duplicates .
*/
for ( c = file_or_uri_utf16 ; for_files & & * c ! = 0 ; c + + )
{
if ( * c = = L ' / ' )
* c = L ' \\ ' ;
}
for ( len_tail = 0 , c = & file_or_uri_utf16 [ len - 1 ] ;
for_files & & c > file_or_uri_utf16 ;
c - - , len_tail + + )
{
if ( c [ 0 ] ! = L ' \\ ' | | c [ - 1 ] ! = L ' \\ ' )
continue ;
memmove ( & c [ - 1 ] , & c [ 0 ] , len_tail * sizeof ( wchar_t ) ) ;
}
}
hr = SHParseDisplayName ( file_or_uri_utf16 , NULL , & item_ids [ i ] , 0 , NULL ) ;
if ( FAILED ( hr ) )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
2022-12-21 15:01:45 +01:00
" File or URI `%S' cannot be parsed by SHParseDisplayName: 0x%lx " ,
file_or_uri_utf16 , hr ) ;
g_free ( file_or_uri_utf16 ) ;
2020-06-04 20:05:22 +02:00
break ;
}
2022-12-21 15:01:45 +01:00
g_free ( file_or_uri_utf16 ) ;
2020-06-04 20:05:22 +02:00
}
if ( i = = count )
{
hr = SHCreateShellItemArrayFromIDLists ( count , ( const ITEMIDLIST * * ) item_ids , & items ) ;
if ( FAILED ( hr ) )
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
" SHCreateShellItemArrayFromIDLists() failed: 0x%lx " , hr ) ;
items = NULL ;
}
}
count = i ;
for ( i = 0 ; i < count ; i + + )
CoTaskMemFree ( item_ids [ i ] ) ;
g_free ( item_ids ) ;
return items ;
}
2008-01-04 11:51:56 +01:00
static gboolean
2022-07-21 15:57:15 +02:00
g_win32_app_info_launch_uris_impl ( GAppInfo * appinfo ,
2022-12-22 16:35:14 +01:00
GList * uris , /* (element-type utf8) */
2022-07-21 15:57:15 +02:00
GAppLaunchContext * launch_context ,
GTask * from_task ,
GError * * error )
2008-01-04 11:51:56 +01:00
{
2014-08-29 10:53:35 +02:00
gboolean res = FALSE ;
gboolean do_files ;
2022-12-22 16:35:14 +01:00
GList * objs = NULL ;
2020-06-04 20:05:22 +02:00
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
2022-12-22 16:35:14 +01:00
gboolean is_uwp ;
2014-08-29 10:53:35 +02:00
do_files = g_win32_app_info_supports_files ( appinfo ) ;
while ( uris )
{
file_or_uri * obj ;
obj = g_new0 ( file_or_uri , 1 ) ;
if ( do_files )
{
GFile * file ;
gchar * path ;
file = g_file_new_for_uri ( uris - > data ) ;
path = g_file_get_path ( file ) ;
obj - > file = path ;
g_object_unref ( file ) ;
}
obj - > uri = g_strdup ( uris - > data ) ;
objs = g_list_prepend ( objs , obj ) ;
uris = uris - > next ;
}
objs = g_list_reverse ( objs ) ;
2022-12-22 16:35:14 +01:00
is_uwp = ( info - > app ! = NULL & & info - > app - > is_uwp ) ;
2020-06-04 20:05:22 +02:00
res = g_win32_app_info_launch_internal ( info ,
2014-08-29 10:53:35 +02:00
objs ,
2020-06-04 20:05:22 +02:00
FALSE ,
2014-08-29 10:53:35 +02:00
launch_context ,
2022-12-22 16:35:14 +01:00
is_uwp ?
0 :
2014-08-29 10:53:35 +02:00
G_SPAWN_SEARCH_PATH ,
2022-07-21 15:57:15 +02:00
from_task ,
2014-08-29 10:53:35 +02:00
error ) ;
g_list_free_full ( objs , free_file_or_uri ) ;
return res ;
2008-01-04 11:51:56 +01:00
}
2022-07-21 15:57:15 +02:00
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 ) ;
}
2022-07-21 15:42:55 +02:00
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 ;
2022-07-21 15:57:15 +02:00
succeeded = g_win32_app_info_launch_uris_impl ( appinfo , data - > uris , data - > context , task , & local_error ) ;
2022-07-21 15:42:55 +02:00
if ( succeeded )
g_task_return_boolean ( task , TRUE ) ;
2022-08-12 19:08:38 +02:00
else if ( ! g_task_had_error ( task ) )
2022-07-21 15:42:55 +02:00
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 ) ;
}
2021-11-24 15:00:39 +01:00
static gboolean
g_win32_app_info_should_show ( GAppInfo * appinfo )
{
/* FIXME: This is a placeholder implementation to avoid crashes
* for now . It can be made more specific to @ appinfo in future . */
return TRUE ;
}
2014-11-05 07:20:25 +01:00
static gboolean
g_win32_app_info_launch ( GAppInfo * appinfo ,
2022-12-22 16:35:14 +01:00
GList * files , /* (element-type GFile) */
2014-11-05 07:20:25 +01:00
GAppLaunchContext * launch_context ,
GError * * error )
{
2014-08-29 10:53:35 +02:00
gboolean res = FALSE ;
gboolean do_uris ;
2022-12-22 16:35:14 +01:00
GList * objs = NULL ;
2020-06-04 20:05:22 +02:00
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
2022-12-22 16:35:14 +01:00
gboolean is_uwp ;
2014-11-05 07:20:25 +01:00
2014-08-29 10:53:35 +02:00
do_uris = g_win32_app_info_supports_uris ( appinfo ) ;
2014-11-05 07:20:25 +01:00
2014-08-29 10:53:35 +02:00
while ( files )
{
file_or_uri * obj ;
obj = g_new0 ( file_or_uri , 1 ) ;
obj - > file = g_file_get_path ( G_FILE ( files - > data ) ) ;
2014-11-05 07:20:25 +01:00
2014-08-29 10:53:35 +02:00
if ( do_uris )
obj - > uri = g_file_get_uri ( G_FILE ( files - > data ) ) ;
2014-11-05 07:20:25 +01:00
2014-08-29 10:53:35 +02:00
objs = g_list_prepend ( objs , obj ) ;
files = files - > next ;
}
objs = g_list_reverse ( objs ) ;
2007-11-26 17:13:05 +01:00
2022-12-22 16:35:14 +01:00
is_uwp = ( info - > app ! = NULL & & info - > app - > is_uwp ) ;
2020-06-04 20:05:22 +02:00
res = g_win32_app_info_launch_internal ( info ,
2014-08-29 10:53:35 +02:00
objs ,
2020-06-04 20:05:22 +02:00
TRUE ,
2014-08-29 10:53:35 +02:00
launch_context ,
2022-12-22 16:35:14 +01:00
is_uwp ?
0 :
2014-08-29 10:53:35 +02:00
G_SPAWN_SEARCH_PATH ,
2022-07-21 15:57:15 +02:00
NULL ,
2014-08-29 10:53:35 +02:00
error ) ;
g_list_free_full ( objs , free_file_or_uri ) ;
return res ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
static const char * *
g_win32_app_info_get_supported_types ( GAppInfo * appinfo )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
GWin32AppInfo * info = G_WIN32_APP_INFO ( appinfo ) ;
return ( const char * * ) info - > supported_types ;
2007-11-26 17:13:05 +01:00
}
GAppInfo *
2007-11-30 06:11:25 +01:00
g_app_info_create_from_commandline ( const char * commandline ,
2014-08-29 10:53:35 +02:00
const char * application_name ,
GAppInfoCreateFlags flags ,
GError * * error )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
GWin32AppInfo * info ;
GWin32AppInfoApplication * app ;
2019-11-29 17:48:32 +01:00
gunichar2 * app_command ;
2014-08-29 10:53:35 +02:00
g_return_val_if_fail ( commandline , NULL ) ;
2019-11-29 17:48:32 +01:00
app_command = g_utf8_to_utf16 ( commandline , - 1 , NULL , NULL , NULL ) ;
if ( app_command = = NULL )
return NULL ;
2014-08-29 10:53:35 +02:00
2019-11-29 17:48:32 +01:00
info = g_object_new ( G_TYPE_WIN32_APP_INFO , NULL ) ;
2014-08-29 10:53:35 +02:00
app = g_object_new ( G_TYPE_WIN32_APPINFO_APPLICATION , NULL ) ;
2019-12-29 12:35:49 +01:00
app - > no_open_with = FALSE ;
app - > user_specific = FALSE ;
app - > default_app = FALSE ;
2019-11-29 17:48:32 +01:00
2014-08-29 10:53:35 +02:00
if ( application_name )
{
app - > canonical_name = g_utf8_to_utf16 ( application_name ,
- 1 ,
NULL ,
NULL ,
NULL ) ;
app - > canonical_name_u8 = g_strdup ( application_name ) ;
app - > canonical_name_folded = g_utf8_casefold ( application_name , - 1 ) ;
}
2019-12-29 12:35:49 +01:00
app_add_verb ( app ,
app ,
L " open " ,
app_command ,
commandline ,
" open " ,
TRUE ,
FALSE ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
g_clear_pointer ( & app_command , g_free ) ;
2014-08-29 10:53:35 +02:00
info - > app = app ;
info - > handler = NULL ;
return G_APP_INFO ( info ) ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
/* GAppInfo interface init */
2007-11-26 17:13:05 +01:00
static void
g_win32_app_info_iface_init ( GAppInfoIface * iface )
{
iface - > dup = g_win32_app_info_dup ;
iface - > equal = g_win32_app_info_equal ;
iface - > get_id = g_win32_app_info_get_id ;
iface - > get_name = g_win32_app_info_get_name ;
iface - > get_description = g_win32_app_info_get_description ;
iface - > get_executable = g_win32_app_info_get_executable ;
iface - > get_icon = g_win32_app_info_get_icon ;
iface - > launch = g_win32_app_info_launch ;
iface - > supports_uris = g_win32_app_info_supports_uris ;
2008-01-04 11:51:56 +01:00
iface - > supports_files = g_win32_app_info_supports_files ;
2007-11-26 17:13:05 +01:00
iface - > launch_uris = g_win32_app_info_launch_uris ;
2022-07-21 15:42:55 +02:00
iface - > launch_uris_async = g_win32_app_info_launch_uris_async ;
iface - > launch_uris_finish = g_win32_app_info_launch_uris_finish ;
2021-11-24 15:00:39 +01:00
iface - > should_show = g_win32_app_info_should_show ;
2014-08-29 10:53:35 +02:00
/* 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->add_supports_type = g_win32_app_info_add_supports_type;*/
/* iface->can_remove_supports_type = g_win32_app_info_can_remove_supports_type;*/
/* iface->remove_supports_type = g_win32_app_info_remove_supports_type;*/
/* iface->can_delete = g_win32_app_info_can_delete;*/
/* iface->do_delete = g_win32_app_info_delete;*/
iface - > get_commandline = g_win32_app_info_get_commandline ;
iface - > get_display_name = g_win32_app_info_get_display_name ;
/* iface->set_as_last_used_for_type = g_win32_app_info_set_as_last_used_for_type;*/
iface - > get_supported_types = g_win32_app_info_get_supported_types ;
2007-11-26 17:13:05 +01:00
}
2014-08-29 10:53:35 +02:00
GAppInfo *
g_app_info_get_default_for_uri_scheme ( const char * uri_scheme )
2007-11-26 17:13:05 +01:00
{
2019-12-29 12:35:49 +01:00
GWin32AppInfoURLSchema * scheme = NULL ;
2014-08-29 10:53:35 +02:00
char * scheme_down ;
GAppInfo * result ;
2019-12-29 12:35:49 +01:00
GWin32AppInfoShellVerb * shverb ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
scheme_down = g_utf8_casefold ( uri_scheme , - 1 ) ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( ! scheme_down )
return NULL ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( strcmp ( scheme_down , " file " ) = = 0 )
2007-11-26 17:13:05 +01:00
{
2014-08-29 10:53:35 +02:00
g_free ( scheme_down ) ;
2019-12-29 12:35:49 +01:00
2014-08-29 10:53:35 +02:00
return NULL ;
2007-11-26 17:13:05 +01:00
}
2020-10-04 00:44:19 +02:00
gio_win32_appinfo_init ( TRUE ) ;
g_mutex_lock ( & gio_win32_appinfo_mutex ) ;
2007-11-26 17:13:05 +01:00
2019-12-29 12:35:49 +01:00
g_set_object ( & scheme , g_hash_table_lookup ( urls , scheme_down ) ) ;
2014-08-29 10:53:35 +02:00
g_free ( scheme_down ) ;
2007-11-26 17:13:05 +01:00
2020-10-04 00:44:19 +02:00
g_mutex_unlock ( & gio_win32_appinfo_mutex ) ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
result = NULL ;
2007-11-26 17:13:05 +01:00
2014-08-29 10:53:35 +02:00
if ( scheme ! = NULL & &
scheme - > chosen_handler ! = NULL & &
2019-12-29 12:35:49 +01:00
scheme - > chosen_handler - > verbs - > len > 0 & &
( shverb = _verb_idx ( scheme - > chosen_handler - > verbs , 0 ) ) - > app ! = NULL )
result = g_win32_app_info_new_from_app ( shverb - > app ,
2014-08-29 10:53:35 +02:00
scheme - > chosen_handler ) ;
2010-11-19 10:39:33 +01:00
2019-12-29 12:35:49 +01:00
g_clear_object ( & scheme ) ;
2010-11-19 10:39:33 +01:00
2014-08-29 10:53:35 +02:00
return result ;
2007-11-26 17:13:05 +01:00
}
2014-11-05 07:18:25 +01:00
GAppInfo *
g_app_info_get_default_for_type ( const char * content_type ,
gboolean must_support_uris )
{
2019-12-29 12:35:49 +01:00
GWin32AppInfoFileExtension * ext = NULL ;
2014-08-29 10:53:35 +02:00
char * ext_down ;
GAppInfo * result ;
2019-12-29 12:35:49 +01:00
GWin32AppInfoShellVerb * shverb ;
2014-11-05 07:18:25 +01:00
2014-08-29 10:53:35 +02:00
ext_down = g_utf8_casefold ( content_type , - 1 ) ;
if ( ! ext_down )
return NULL ;
2020-10-04 00:44:19 +02:00
gio_win32_appinfo_init ( TRUE ) ;
g_mutex_lock ( & gio_win32_appinfo_mutex ) ;
2014-08-29 10:53:35 +02:00
/* Assuming that "content_type" is a file extension, not a MIME type */
2019-12-29 12:35:49 +01:00
g_set_object ( & ext , g_hash_table_lookup ( extensions , ext_down ) ) ;
2014-08-29 10:53:35 +02:00
g_free ( ext_down ) ;
2020-10-04 00:44:19 +02:00
g_mutex_unlock ( & gio_win32_appinfo_mutex ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ext = = NULL )
return NULL ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
result = NULL ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( ext - > chosen_handler ! = NULL & &
ext - > chosen_handler - > verbs - > len > 0 & &
( shverb = _verb_idx ( ext - > chosen_handler - > verbs , 0 ) ) - > app ! = NULL & &
( ! must_support_uris | |
g_win32_app_supports_uris ( shverb - > app ) ) )
result = g_win32_app_info_new_from_app ( shverb - > app ,
ext - > chosen_handler ) ;
else
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
GHashTableIter iter ;
GWin32AppInfoHandler * handler ;
g_hash_table_iter_init ( & iter , ext - > handlers ) ;
while ( result = = NULL & &
g_hash_table_iter_next ( & iter , NULL , ( gpointer * ) & handler ) )
2014-08-29 10:53:35 +02:00
{
2019-12-29 12:35:49 +01:00
if ( handler - > verbs - > len = = 0 )
continue ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
shverb = _verb_idx ( handler - > verbs , 0 ) ;
2014-08-29 10:53:35 +02:00
2019-12-29 12:35:49 +01:00
if ( shverb - > app & &
( ! must_support_uris | |
g_win32_app_supports_uris ( shverb - > app ) ) )
result = g_win32_app_info_new_from_app ( shverb - > app , handler ) ;
2014-08-29 10:53:35 +02:00
}
}
2019-12-29 12:35:49 +01:00
g_clear_object ( & ext ) ;
2014-08-29 10:53:35 +02:00
return result ;
2007-11-26 17:13:05 +01:00
}
GList *
g_app_info_get_all ( void )
{
2014-08-29 10:53:35 +02:00
GHashTableIter iter ;
gpointer value ;
2007-11-26 17:13:05 +01:00
GList * infos ;
2014-08-29 10:53:35 +02:00
GList * apps ;
GList * apps_i ;
2007-11-26 17:13:05 +01:00
2020-10-04 00:44:19 +02:00
gio_win32_appinfo_init ( TRUE ) ;
g_mutex_lock ( & gio_win32_appinfo_mutex ) ;
2014-08-29 10:53:35 +02:00
apps = NULL ;
g_hash_table_iter_init ( & iter , apps_by_id ) ;
while ( g_hash_table_iter_next ( & iter , NULL , & value ) )
apps = g_list_prepend ( apps , g_object_ref ( G_OBJECT ( value ) ) ) ;
2020-10-04 00:44:19 +02:00
g_mutex_unlock ( & gio_win32_appinfo_mutex ) ;
2007-11-26 17:13:05 +01:00
infos = NULL ;
2014-08-29 10:53:35 +02:00
for ( apps_i = apps ; apps_i ; apps_i = apps_i - > next )
infos = g_list_prepend ( infos ,
g_win32_app_info_new_from_app ( apps_i - > data , NULL ) ) ;
g_list_free_full ( apps , g_object_unref ) ;
return infos ;
2008-09-26 21:57:36 +02:00
}
2015-06-24 12:07:40 +02:00
GList *
g_app_info_get_all_for_type ( const char * content_type )
{
2019-12-29 12:35:49 +01:00
GWin32AppInfoFileExtension * ext = NULL ;
2015-06-24 12:07:40 +02:00
char * ext_down ;
GWin32AppInfoHandler * handler ;
GHashTableIter iter ;
2019-12-29 12:35:49 +01:00
GHashTable * apps = NULL ;
2015-06-24 12:07:40 +02:00
GList * result ;
2019-12-29 12:35:49 +01:00
GWin32AppInfoShellVerb * shverb ;
2015-06-24 12:07:40 +02:00
ext_down = g_utf8_casefold ( content_type , - 1 ) ;
if ( ! ext_down )
return NULL ;
2020-10-04 00:44:19 +02:00
gio_win32_appinfo_init ( TRUE ) ;
g_mutex_lock ( & gio_win32_appinfo_mutex ) ;
2015-06-24 12:07:40 +02:00
/* Assuming that "content_type" is a file extension, not a MIME type */
2019-12-29 12:35:49 +01:00
g_set_object ( & ext , g_hash_table_lookup ( extensions , ext_down ) ) ;
2015-06-24 12:07:40 +02:00
g_free ( ext_down ) ;
2020-10-04 00:44:19 +02:00
g_mutex_unlock ( & gio_win32_appinfo_mutex ) ;
2015-06-24 12:07:40 +02:00
if ( ext = = NULL )
return NULL ;
2019-12-29 12:35:49 +01:00
result = NULL ;
/* Used as a set to ensure uniqueness */
apps = g_hash_table_new ( g_direct_hash , g_direct_equal ) ;
2015-06-24 12:07:40 +02:00
if ( ext - > chosen_handler ! = NULL & &
2019-12-29 12:35:49 +01:00
ext - > chosen_handler - > verbs - > len > 0 & &
( shverb = _verb_idx ( ext - > chosen_handler - > verbs , 0 ) ) - > app ! = NULL )
{
g_hash_table_add ( apps , shverb - > app ) ;
result = g_list_prepend ( result ,
g_win32_app_info_new_from_app ( shverb - > app ,
ext - > chosen_handler ) ) ;
}
2015-06-24 12:07:40 +02:00
g_hash_table_iter_init ( & iter , ext - > handlers ) ;
while ( g_hash_table_iter_next ( & iter , NULL , ( gpointer * ) & handler ) )
{
2019-12-29 12:35:49 +01:00
gsize vi ;
2015-06-24 12:07:40 +02:00
2019-12-29 12:35:49 +01:00
for ( vi = 0 ; vi < handler - > verbs - > len ; vi + + )
{
shverb = _verb_idx ( handler - > verbs , vi ) ;
2015-06-24 12:07:40 +02:00
2019-12-29 12:35:49 +01:00
if ( shverb - > app = = NULL | |
g_hash_table_contains ( apps , shverb - > app ) )
continue ;
2015-06-24 12:07:40 +02:00
2019-12-29 12:35:49 +01:00
g_hash_table_add ( apps , shverb - > app ) ;
result = g_list_prepend ( result ,
g_win32_app_info_new_from_app ( shverb - > app ,
handler ) ) ;
}
}
2015-06-24 12:07:40 +02:00
2019-12-29 12:35:49 +01:00
g_clear_object ( & ext ) ;
2015-06-24 12:07:40 +02:00
result = g_list_reverse ( result ) ;
2019-12-29 12:35:49 +01:00
g_hash_table_unref ( apps ) ;
2015-06-24 12:07:40 +02:00
return result ;
}
GList *
g_app_info_get_fallback_for_type ( const gchar * content_type )
{
/* TODO: fix this once gcontenttype support is improved */
return g_app_info_get_all_for_type ( content_type ) ;
}
GList *
g_app_info_get_recommended_for_type ( const gchar * content_type )
{
/* TODO: fix this once gcontenttype support is improved */
return g_app_info_get_all_for_type ( content_type ) ;
}
2015-07-02 13:06:17 +02:00
void
g_app_info_reset_type_associations ( const char * content_type )
{
/* nothing to do */
}