diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build index acdd5ece7..4d0364819 100644 --- a/docs/reference/gio/meson.build +++ b/docs/reference/gio/meson.build @@ -85,8 +85,15 @@ if get_option('gtk_doc') 'gunixvolume.h', 'gunixvolumemonitor.h', 'gwin32networkmonitor.h', + 'gwin32api-application-activation-manager.h', + 'gwin32api-iterator.h', + 'gwin32api-misc.h', + 'gwin32api-package.h', + 'gwin32api-storage.h', 'gwin32appinfo.h', + 'gwin32file-sync-stream.h', 'gwin32mount.h', + 'gwin32packageparser.h', 'gwin32resolver.h', 'gwin32volumemonitor.h', 'thumbnail-verify.h', diff --git a/gio/gwin32api-application-activation-manager.h b/gio/gwin32api-application-activation-manager.h new file mode 100755 index 000000000..cf44b93e6 --- /dev/null +++ b/gio/gwin32api-application-activation-manager.h @@ -0,0 +1,126 @@ +#if NTDDI_VERSION < NTDDI_WIN8 +/* The following code is copied verbatim from MinGW-w64 shobjidl.h */ +/* + * IApplicationActivationManager interface + */ +typedef enum ACTIVATEOPTIONS { + AO_NONE = 0x0, + AO_DESIGNMODE = 0x1, + AO_NOERRORUI = 0x2, + AO_NOSPLASHSCREEN = 0x4 +} ACTIVATEOPTIONS; + +DEFINE_ENUM_FLAG_OPERATORS(ACTIVATEOPTIONS) + +#ifndef __IApplicationActivationManager_INTERFACE_DEFINED__ +#define __IApplicationActivationManager_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IApplicationActivationManager, 0x2e941141, 0x7f97, 0x4756, 0xba,0x1d, 0x9d,0xec,0xde,0x89,0x4a,0x3d); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("2e941141-7f97-4756-ba1d-9decde894a3d") +IApplicationActivationManager : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE ActivateApplication( + LPCWSTR appUserModelId, + LPCWSTR arguments, + ACTIVATEOPTIONS options, + DWORD *processId) = 0; + + virtual HRESULT STDMETHODCALLTYPE ActivateForFile( + LPCWSTR appUserModelId, + IShellItemArray *itemArray, + LPCWSTR verb, + DWORD *processId) = 0; + + virtual HRESULT STDMETHODCALLTYPE ActivateForProtocol( + LPCWSTR appUserModelId, + IShellItemArray *itemArray, + DWORD *processId) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IApplicationActivationManager, 0x2e941141, 0x7f97, 0x4756, 0xba,0x1d, 0x9d,0xec,0xde,0x89,0x4a,0x3d) +#endif +#else +typedef struct IApplicationActivationManagerVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IApplicationActivationManager *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IApplicationActivationManager *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IApplicationActivationManager *This); + + /*** IApplicationActivationManager methods ***/ + HRESULT (STDMETHODCALLTYPE *ActivateApplication)( + IApplicationActivationManager *This, + LPCWSTR appUserModelId, + LPCWSTR arguments, + ACTIVATEOPTIONS options, + DWORD *processId); + + HRESULT (STDMETHODCALLTYPE *ActivateForFile)( + IApplicationActivationManager *This, + LPCWSTR appUserModelId, + IShellItemArray *itemArray, + LPCWSTR verb, + DWORD *processId); + + HRESULT (STDMETHODCALLTYPE *ActivateForProtocol)( + IApplicationActivationManager *This, + LPCWSTR appUserModelId, + IShellItemArray *itemArray, + DWORD *processId); + + END_INTERFACE +} IApplicationActivationManagerVtbl; + +interface IApplicationActivationManager { + CONST_VTBL IApplicationActivationManagerVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IApplicationActivationManager_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IApplicationActivationManager_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IApplicationActivationManager_Release(This) (This)->lpVtbl->Release(This) +/*** IApplicationActivationManager methods ***/ +#define IApplicationActivationManager_ActivateApplication(This,appUserModelId,arguments,options,processId) (This)->lpVtbl->ActivateApplication(This,appUserModelId,arguments,options,processId) +#define IApplicationActivationManager_ActivateForFile(This,appUserModelId,itemArray,verb,processId) (This)->lpVtbl->ActivateForFile(This,appUserModelId,itemArray,verb,processId) +#define IApplicationActivationManager_ActivateForProtocol(This,appUserModelId,itemArray,processId) (This)->lpVtbl->ActivateForProtocol(This,appUserModelId,itemArray,processId) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IApplicationActivationManager_QueryInterface(IApplicationActivationManager* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IApplicationActivationManager_AddRef(IApplicationActivationManager* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IApplicationActivationManager_Release(IApplicationActivationManager* This) { + return This->lpVtbl->Release(This); +} +/*** IApplicationActivationManager methods ***/ +static FORCEINLINE HRESULT IApplicationActivationManager_ActivateApplication(IApplicationActivationManager* This,LPCWSTR appUserModelId,LPCWSTR arguments,ACTIVATEOPTIONS options,DWORD *processId) { + return This->lpVtbl->ActivateApplication(This,appUserModelId,arguments,options,processId); +} +static FORCEINLINE HRESULT IApplicationActivationManager_ActivateForFile(IApplicationActivationManager* This,LPCWSTR appUserModelId,IShellItemArray *itemArray,LPCWSTR verb,DWORD *processId) { + return This->lpVtbl->ActivateForFile(This,appUserModelId,itemArray,verb,processId); +} +static FORCEINLINE HRESULT IApplicationActivationManager_ActivateForProtocol(IApplicationActivationManager* This,LPCWSTR appUserModelId,IShellItemArray *itemArray,DWORD *processId) { + return This->lpVtbl->ActivateForProtocol(This,appUserModelId,itemArray,processId); +} +#endif +#endif + +#endif + + +#endif /* __IApplicationActivationManager_INTERFACE_DEFINED__ */ +#endif /* NTDDI_VERSION < NTDDI_WIN8 */ \ No newline at end of file diff --git a/gio/gwin32api-iterator.h b/gio/gwin32api-iterator.h new file mode 100755 index 000000000..d4df8a7b8 --- /dev/null +++ b/gio/gwin32api-iterator.h @@ -0,0 +1,125 @@ +typedef interface IIterator IIterator; +typedef interface IIterable IIterable; + +/* IIterator */ +typedef struct IIteratorVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IIterator *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IIterator *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IIterator *This); + + /*** IInspectable methods ***/ + HRESULT (STDMETHODCALLTYPE *GetIids)( + IIterator *This, + UINT32 *count, + IID **ids); + + HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)( + IIterator *This, + HSTRING *className); + + HRESULT (STDMETHODCALLTYPE *GetTrustLevel)( + IIterator *This, + TrustLevel *trustLevel); + + /*** IIterator methods ***/ + HRESULT (STDMETHODCALLTYPE *get_Current)( + IIterator *This, + IUnknown **current); + + HRESULT (STDMETHODCALLTYPE *get_HasCurrent)( + IIterator *This, + CHAR *hasCurrent); + + HRESULT (STDMETHODCALLTYPE *MoveNext)( + IIterator *This, + CHAR *hasCurrent); + + HRESULT (STDMETHODCALLTYPE *GetMany)( + IIterator *This, + UINT capacity, + void *value, + UINT *actual); + + END_INTERFACE +} IIteratorVtbl; + +interface IIterator { + CONST_VTBL IIteratorVtbl* lpVtbl; +}; + +/*** IUnknown methods ***/ +#define IIterator_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IIterator_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IIterator_Release(This) (This)->lpVtbl->Release(This) +/*** IInspectable methods ***/ +#define IIterator_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids) +#define IIterator_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name) +#define IIterator_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level) +/*** IIterator methods ***/ +#define IIterator_get_Current(This,current) (This)->lpVtbl->get_Current(This,current) +#define IIterator_get_HasCurrent(This,hasCurrent) (This)->lpVtbl->get_HasCurrent(This,hasCurrent) +#define IIterator_MoveNext(This,hasCurrent) (This)->lpVtbl->MoveNext(This,hasCurrent) +#define IIterator_GetMany(This,capacity,value,actual) (This)->lpVtbl->GetMany(This,capacity,value,actual) + +/* IIterable */ +typedef struct IIterableVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IIterable *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IIterable *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IIterable *This); + + /*** IInspectable methods ***/ + HRESULT (STDMETHODCALLTYPE *GetIids)( + IIterable *This, + UINT32 *count, + IID **ids); + + HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)( + IIterable *This, + HSTRING *className); + + HRESULT (STDMETHODCALLTYPE *GetTrustLevel)( + IIterable *This, + TrustLevel *trustLevel); + + /*** IIterable methods ***/ + HRESULT (STDMETHODCALLTYPE *First)( + IIterable *This, + IIterator **first); + + END_INTERFACE +} IIterableVtbl; + +interface IIterable { + CONST_VTBL IIterableVtbl* lpVtbl; +}; + +/*** IUnknown methods ***/ +#define IIterable_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IIterable_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IIterable_Release(This) (This)->lpVtbl->Release(This) +/*** IInspectable methods ***/ +#define IIterable_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids) +#define IIterable_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name) +#define IIterable_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level) +/*** IIterable methods ***/ +#define IIterable_First(This,retval) (This)->lpVtbl->First(This,retval) diff --git a/gio/gwin32api-misc.h b/gio/gwin32api-misc.h new file mode 100755 index 000000000..2b45d9abd --- /dev/null +++ b/gio/gwin32api-misc.h @@ -0,0 +1 @@ +typedef interface IProcessorArchitecture IProcessorArchitecture; diff --git a/gio/gwin32api-package.h b/gio/gwin32api-package.h new file mode 100755 index 000000000..9842a86fa --- /dev/null +++ b/gio/gwin32api-package.h @@ -0,0 +1,264 @@ +typedef interface IPackageManager IPackageManager; +typedef interface IPackage IPackage; +typedef interface IPackageId IPackageId; +typedef interface IPackageVersion IPackageVersion; + +DEFINE_GUID(IID_IPackageManager, 0x9A7D4B65, 0x5E8F, 0x4FC7, 0xA2, 0xE5, 0x7F, 0x69, 0x25, 0xCB, 0x8B, 0x53); +DEFINE_GUID(IID_IPackage, 0x163C792F, 0xBD75, 0x413C, 0xBF, 0x23, 0xB1, 0xFE, 0x7B, 0x95, 0xD8, 0x25); + +/* IPackageManager */ +typedef struct IPackageManagerVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IPackageManager *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IPackageManager *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IPackageManager *This); + + /*** IInspectable methods ***/ + HRESULT (STDMETHODCALLTYPE *GetIids)( + IPackageManager *This, + UINT32 *count, + IID **ids); + + HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)( + IPackageManager *This, + HSTRING *className); + + HRESULT (STDMETHODCALLTYPE *GetTrustLevel)( + IPackageManager *This, + TrustLevel *trustLevel); + + /*** IPackageManager methods ***/ + HRESULT (STDMETHODCALLTYPE *stub_AddPackageAsync)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_UpdatePackageAsync)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_RemovePackageAsync)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_StagePackageAsync)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_RegisterPackageAsync)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *FindPackages)( + IPackageManager *This, + IIterable **retval); + + HRESULT (STDMETHODCALLTYPE *FindPackagesByUserSecurityId)( + IPackageManager *This, + HSTRING userSecurityId, + IIterable **retval); + + HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByNamePublisher)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByUserSecurityIdNamePublisher)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_FindUsers)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_SetPackageState)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_FindPackageByPackageFullName)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_CleanupPackageForUserAsync)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByPackageFamilyName)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByUserSecurityIdPackageFamilyName)( + IPackageManager *This); + + HRESULT (STDMETHODCALLTYPE *stub_FindPackageByUserSecurityIdPackageFullName)( + IPackageManager *This); + + END_INTERFACE +} IPackageManagerVtbl; + +interface IPackageManager { + CONST_VTBL IPackageManagerVtbl* lpVtbl; +}; + +/*** IUnknown methods ***/ +#define IPackageManager_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IPackageManager_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IPackageManager_Release(This) (This)->lpVtbl->Release(This) +/*** IInspectable methods ***/ +#define IPackageManager_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids) +#define IPackageManager_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name) +#define IPackageManager_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level) +/*** IPackageManager methods ***/ +#define IPackageManager_FindPackages(This,retval) (This)->lpVtbl->FindPackages(This,retval) +#define IPackageManager_FindPackagesByUserSecurityId(This,userSecurityId,retval) (This)->lpVtbl->FindPackagesByUserSecurityId(This,userSecurityId,retval) + +/* IPackageId */ +typedef struct IPackageIdVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IPackageId *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IPackageId *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IPackageId *This); + + /*** IInspectable methods ***/ + HRESULT (STDMETHODCALLTYPE *GetIids)( + IPackageId *This, + UINT32 *count, + IID **ids); + + HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)( + IPackageId *This, + HSTRING *className); + + HRESULT (STDMETHODCALLTYPE *GetTrustLevel)( + IPackageId *This, + TrustLevel *trustLevel); + + /*** IPackageId methods ***/ + HRESULT (STDMETHODCALLTYPE *get_Name)( + IPackageId *This, + HSTRING *value); + + HRESULT (STDMETHODCALLTYPE *get_Version)( + IPackageId *This, + IPackageVersion *value); + + HRESULT (STDMETHODCALLTYPE *get_Architecture)( + IPackageId *This, + IProcessorArchitecture *value); + + HRESULT (STDMETHODCALLTYPE *get_ResourceId)( + IPackageId *This, + HSTRING *value); + + HRESULT (STDMETHODCALLTYPE *get_Publisher)( + IPackageId *This, + HSTRING *value); + + HRESULT (STDMETHODCALLTYPE *get_PublisherId)( + IPackageId *This, + HSTRING *value); + + HRESULT (STDMETHODCALLTYPE *get_FullName)( + IPackageId *This, + HSTRING *value); + + HRESULT (STDMETHODCALLTYPE *get_FamilyName)( + IPackageId *This, + HSTRING *value); + + END_INTERFACE +} IPackageIdVtbl; + +interface IPackageId { + CONST_VTBL IPackageIdVtbl* lpVtbl; +}; + +/*** IUnknown methods ***/ +#define IPackageId_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IPackageId_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IPackageId_Release(This) (This)->lpVtbl->Release(This) +/*** IInspectable methods ***/ +#define IPackageId_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids) +#define IPackageId_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name) +#define IPackageId_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level) +/*** IPackageId methods ***/ +#define IPackageId_get_Name(This,value) (This)->lpVtbl->get_Name(This,value) +#define IPackageId_get_Version(This,value) (This)->lpVtbl->get_Version(This,value) +#define IPackageId_get_Architecture(This,value) (This)->lpVtbl->get_Architecture(This,value) +#define IPackageId_get_ResourceId(This,value) (This)->lpVtbl->get_ResourceId(This,value) +#define IPackageId_get_Publisher(This,value) (This)->lpVtbl->get_Publisher(This,value) +#define IPackageId_get_PublisherId(This,value) (This)->lpVtbl->get_PublisherId(This,value) +#define IPackageId_get_FullName(This,value) (This)->lpVtbl->get_FullName(This,value) +#define IPackageId_get_FamilyName(This,value) (This)->lpVtbl->get_FamilyName(This,value) + +/* IPackage */ +typedef struct IPackageVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IPackage *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IPackage *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IPackage *This); + + /*** IInspectable methods ***/ + HRESULT (STDMETHODCALLTYPE *GetIids)( + IPackage *This, + UINT32 *count, + IID **ids); + + HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)( + IPackage *This, + HSTRING *className); + + HRESULT (STDMETHODCALLTYPE *GetTrustLevel)( + IPackage *This, + TrustLevel *trustLevel); + + /*** IPackage methods ***/ + HRESULT (STDMETHODCALLTYPE *get_Id)( + IPackage *This, + IPackageId **value); + + HRESULT (STDMETHODCALLTYPE *get_InstalledLocation)( + IPackage *This, + IUnknown **value); + + HRESULT (STDMETHODCALLTYPE *get_IsFramework)( + IPackage *This, + CHAR *value); + + HRESULT (STDMETHODCALLTYPE *get_Dependencies)( + IPackage *This, + void **value); + + END_INTERFACE +} IPackageVtbl; + +interface IPackage { + CONST_VTBL IPackageVtbl* lpVtbl; +}; + +/*** IUnknown methods ***/ +#define IPackage_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IPackage_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IPackage_Release(This) (This)->lpVtbl->Release(This) +/*** IInspectable methods ***/ +#define IPackage_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids) +#define IPackage_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name) +#define IPackage_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level) +/*** IPackage methods ***/ +#define IPackage_get_Id(This,value) (This)->lpVtbl->get_Id(This,value) +#define IPackage_get_InstalledLocation(This,value) (This)->lpVtbl->get_InstalledLocation(This,value) +#define IPackage_get_IsFramework(This,value) (This)->lpVtbl->get_IsFramework(This,value) +#define IPackage_get_Dependencies(This,value) (This)->lpVtbl->get_Dependencies(This,value) diff --git a/gio/gwin32api-storage.h b/gio/gwin32api-storage.h new file mode 100755 index 000000000..716a0a7a1 --- /dev/null +++ b/gio/gwin32api-storage.h @@ -0,0 +1,339 @@ +struct DateTime; + +typedef struct DateTime { + UINT64 UniversalTime; +} DateTime; + +/* The following is copied verbatim from MinGW-w64 windows.storage.h */ +enum StorageItemTypes; +enum FileAttributes; +enum NameCollisionOption; +enum StorageDeleteOption; + +typedef enum NameCollisionoption { + NameCollisionoption_GenerateUniqueName = 0, + NameCollisionoption_ReplaceExisting = 1, + NameCollisionoption_FailIfExists = 2 +} NameCollisionOption; + +typedef enum FileAttributes { + FileAttributes_Normal = 0, + FileAttributes_ReadOnly = 1, + FileAttributes_Directory = 2, + FileAttributes_Archive = 3, + FileAttributes_Temporary = 4 +} FileAttributes; + +typedef enum StorageItemTypes { + StorageItemTypes_None = 0, + StorageItemTypes_File = 1, + StorageItemTypes_Folder = 2 +} StorageItemTypes; + +typedef enum StorageDeleteOption { + StorageDeleteOption_Default = 0, + StorageDeleteOption_PermanentDelete = 1 +} StorageDeleteOption; + +#ifndef __IStorageItem_FWD_DEFINED__ +#define __IStorageItem_FWD_DEFINED__ +typedef interface IStorageItem IStorageItem; +#endif + +/* + * IStorageItem interface + */ +#ifndef __IStorageItem_INTERFACE_DEFINED__ +#define __IStorageItem_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IStorageItem, 0x4207a996, 0xca2f, 0x42f7, 0xbd,0xe8, 0x8b,0x10,0x45,0x7a,0x7f,0x30); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("4207a996-ca2f-42f7-bde8-8b10457a7f30") +IStorageItem : public IInspectable +{ + virtual HRESULT STDMETHODCALLTYPE RenameAsyncOverloadDefaultOptions( + HSTRING desiredName, + IInspectable **action) = 0; + + virtual HRESULT STDMETHODCALLTYPE RenameAsync( + HSTRING desiredName, + NameCollisionOption option, + IInspectable **action) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteAsyncOverloadDefaultOptions( + IInspectable **action) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteAsync( + StorageDeleteOption option, + IInspectable **action) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetBasicPropertiesAsync( + IInspectable **action) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Name( + HSTRING *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Path( + HSTRING *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Attributes( + FileAttributes *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_DateCreated( + DateTime *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsOfType( + StorageItemTypes itemType, + boolean *value) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IStorageItem, 0x4207a996, 0xca2f, 0x42f7, 0xbd,0xe8, 0x8b,0x10,0x45,0x7a,0x7f,0x30) +#endif +#else +typedef struct IStorageItemVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IStorageItem* This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IStorageItem* This); + + ULONG (STDMETHODCALLTYPE *Release)( + IStorageItem* This); + + /*** IInspectable methods ***/ + HRESULT (STDMETHODCALLTYPE *GetIids)( + IStorageItem* This, + ULONG *iidCount, + IID **iids); + + HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)( + IStorageItem* This, + HSTRING *className); + + HRESULT (STDMETHODCALLTYPE *GetTrustLevel)( + IStorageItem* This, + TrustLevel *trustLevel); + + /*** IStorageItem methods ***/ + HRESULT (STDMETHODCALLTYPE *RenameAsyncOverloadDefaultOptions)( + IStorageItem* This, + HSTRING desiredName, + IInspectable **action); + + HRESULT (STDMETHODCALLTYPE *RenameAsync)( + IStorageItem* This, + HSTRING desiredName, + NameCollisionOption option, + IInspectable **action); + + HRESULT (STDMETHODCALLTYPE *DeleteAsyncOverloadDefaultOptions)( + IStorageItem* This, + IInspectable **action); + + HRESULT (STDMETHODCALLTYPE *DeleteAsync)( + IStorageItem* This, + StorageDeleteOption option, + IInspectable **action); + + HRESULT (STDMETHODCALLTYPE *GetBasicPropertiesAsync)( + IStorageItem* This, + IInspectable **action); + + HRESULT (STDMETHODCALLTYPE *get_Name)( + IStorageItem* This, + HSTRING *value); + + HRESULT (STDMETHODCALLTYPE *get_Path)( + IStorageItem* This, + HSTRING *value); + + HRESULT (STDMETHODCALLTYPE *get_Attributes)( + IStorageItem* This, + FileAttributes *value); + + HRESULT (STDMETHODCALLTYPE *get_DateCreated)( + IStorageItem* This, + DateTime *value); + + HRESULT (STDMETHODCALLTYPE *IsOfType)( + IStorageItem* This, + StorageItemTypes itemType, + boolean *value); + + END_INTERFACE +} IStorageItemVtbl; +interface IStorageItem { + CONST_VTBL IStorageItemVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IStorageItem_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IStorageItem_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IStorageItem_Release(This) (This)->lpVtbl->Release(This) +/*** IInspectable methods ***/ +#define IStorageItem_GetIids(This,iidCount,iids) (This)->lpVtbl->GetIids(This,iidCount,iids) +#define IStorageItem_GetRuntimeClassName(This,className) (This)->lpVtbl->GetRuntimeClassName(This,className) +#define IStorageItem_GetTrustLevel(This,trustLevel) (This)->lpVtbl->GetTrustLevel(This,trustLevel) +/*** IStorageItem methods ***/ +#define IStorageItem_RenameAsyncOverloadDefaultOptions(This,desiredName,action) (This)->lpVtbl->RenameAsyncOverloadDefaultOptions(This,desiredName,action) +#define IStorageItem_RenameAsync(This,desiredName,option,action) (This)->lpVtbl->RenameAsync(This,desiredName,option,action) +#define IStorageItem_DeleteAsyncOverloadDefaultOptions(This,action) (This)->lpVtbl->DeleteAsyncOverloadDefaultOptions(This,action) +#define IStorageItem_DeleteAsync(This,option,action) (This)->lpVtbl->DeleteAsync(This,option,action) +#define IStorageItem_GetBasicPropertiesAsync(This,action) (This)->lpVtbl->GetBasicPropertiesAsync(This,action) +#define IStorageItem_get_Name(This,value) (This)->lpVtbl->get_Name(This,value) +#define IStorageItem_get_Path(This,value) (This)->lpVtbl->get_Path(This,value) +#define IStorageItem_get_Attributes(This,value) (This)->lpVtbl->get_Attributes(This,value) +#define IStorageItem_get_DateCreated(This,value) (This)->lpVtbl->get_DateCreated(This,value) +#define IStorageItem_IsOfType(This,itemType,value) (This)->lpVtbl->IsOfType(This,itemType,value) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IStorageItem_QueryInterface(IStorageItem* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IStorageItem_AddRef(IStorageItem* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IStorageItem_Release(IStorageItem* This) { + return This->lpVtbl->Release(This); +} +/*** IInspectable methods ***/ +static FORCEINLINE HRESULT IStorageItem_GetIids(IStorageItem* This,ULONG *iidCount,IID **iids) { + return This->lpVtbl->GetIids(This,iidCount,iids); +} +static FORCEINLINE HRESULT IStorageItem_GetRuntimeClassName(IStorageItem* This,HSTRING *className) { + return This->lpVtbl->GetRuntimeClassName(This,className); +} +static FORCEINLINE HRESULT IStorageItem_GetTrustLevel(IStorageItem* This,TrustLevel *trustLevel) { + return This->lpVtbl->GetTrustLevel(This,trustLevel); +} +/*** IStorageItem methods ***/ +static FORCEINLINE HRESULT IStorageItem_RenameAsyncOverloadDefaultOptions(IStorageItem* This,HSTRING desiredName,IInspectable **action) { + return This->lpVtbl->RenameAsyncOverloadDefaultOptions(This,desiredName,action); +} +static FORCEINLINE HRESULT IStorageItem_RenameAsync(IStorageItem* This,HSTRING desiredName,NameCollisionOption option,IInspectable **action) { + return This->lpVtbl->RenameAsync(This,desiredName,option,action); +} +static FORCEINLINE HRESULT IStorageItem_DeleteAsyncOverloadDefaultOptions(IStorageItem* This,IInspectable **action) { + return This->lpVtbl->DeleteAsyncOverloadDefaultOptions(This,action); +} +static FORCEINLINE HRESULT IStorageItem_DeleteAsync(IStorageItem* This,StorageDeleteOption option,IInspectable **action) { + return This->lpVtbl->DeleteAsync(This,option,action); +} +static FORCEINLINE HRESULT IStorageItem_GetBasicPropertiesAsync(IStorageItem* This,IInspectable **action) { + return This->lpVtbl->GetBasicPropertiesAsync(This,action); +} +static FORCEINLINE HRESULT IStorageItem_get_Name(IStorageItem* This,HSTRING *value) { + return This->lpVtbl->get_Name(This,value); +} +static FORCEINLINE HRESULT IStorageItem_get_Path(IStorageItem* This,HSTRING *value) { + return This->lpVtbl->get_Path(This,value); +} +static FORCEINLINE HRESULT IStorageItem_get_Attributes(IStorageItem* This,FileAttributes *value) { + return This->lpVtbl->get_Attributes(This,value); +} +static FORCEINLINE HRESULT IStorageItem_get_DateCreated(IStorageItem* This,DateTime *value) { + return This->lpVtbl->get_DateCreated(This,value); +} +static FORCEINLINE HRESULT IStorageItem_IsOfType(IStorageItem* This,StorageItemTypes itemType,boolean *value) { + return This->lpVtbl->IsOfType(This,itemType,value); +} +#endif +#endif + +#endif + +HRESULT STDMETHODCALLTYPE IStorageItem_RenameAsyncOverloadDefaultOptions_Proxy( + IStorageItem* This, + HSTRING desiredName, + IInspectable **action); +void __RPC_STUB IStorageItem_RenameAsyncOverloadDefaultOptions_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_RenameAsync_Proxy( + IStorageItem* This, + HSTRING desiredName, + NameCollisionOption option, + IInspectable **action); +void __RPC_STUB IStorageItem_RenameAsync_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_DeleteAsyncOverloadDefaultOptions_Proxy( + IStorageItem* This, + IInspectable **action); +void __RPC_STUB IStorageItem_DeleteAsyncOverloadDefaultOptions_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_DeleteAsync_Proxy( + IStorageItem* This, + StorageDeleteOption option, + IInspectable **action); +void __RPC_STUB IStorageItem_DeleteAsync_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_GetBasicPropertiesAsync_Proxy( + IStorageItem* This, + IInspectable **action); +void __RPC_STUB IStorageItem_GetBasicPropertiesAsync_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_get_Name_Proxy( + IStorageItem* This, + HSTRING *value); +void __RPC_STUB IStorageItem_get_Name_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_get_Path_Proxy( + IStorageItem* This, + HSTRING *value); +void __RPC_STUB IStorageItem_get_Path_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_get_Attributes_Proxy( + IStorageItem* This, + FileAttributes *value); +void __RPC_STUB IStorageItem_get_Attributes_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_get_DateCreated_Proxy( + IStorageItem* This, + DateTime *value); +void __RPC_STUB IStorageItem_get_DateCreated_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); +HRESULT STDMETHODCALLTYPE IStorageItem_IsOfType_Proxy( + IStorageItem* This, + StorageItemTypes itemType, + boolean *value); +void __RPC_STUB IStorageItem_IsOfType_Stub( + IRpcStubBuffer* This, + IRpcChannelBuffer* pRpcChannelBuffer, + PRPC_MESSAGE pRpcMessage, + DWORD* pdwStubPhase); + +#endif /* __IStorageItem_INTERFACE_DEFINED__ */ diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c index 8871acc9d..b12f9e092 100644 --- a/gio/gwin32appinfo.c +++ b/gio/gwin32appinfo.c @@ -22,6 +22,8 @@ #include "config.h" +#define COBJMACROS + #include #include "gcontenttype.h" @@ -32,8 +34,16 @@ #include #include "glibintl.h" #include +#include +/* 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" #include +/* For SHLoadIndirectString() */ +#include #include #include "giowin32-priv.h" @@ -181,7 +191,10 @@ struct _GWin32AppInfoHandler { /* Usually a class name in HKCR */ gunichar2 *handler_id; - /* Registry object obtained by opening @handler_id. Can be used to watch this handler. */ + /* Registry object obtained by opening @handler_id. + * Can be used to watch this handler. + * May be %NULL (for fake handlers that we made up). + */ GWin32RegistryKey *key; /* @handler_id, in UTF-8, folded */ @@ -192,6 +205,13 @@ struct _GWin32AppInfoHandler { /* Verbs that this handler supports */ GPtrArray *verbs; /* of GWin32AppInfoShellVerb */ + + /* 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; }; struct _GWin32AppInfoShellVerb { @@ -203,6 +223,11 @@ struct _GWin32AppInfoShellVerb { /* User-friendly (localized) verb name. */ gchar *verb_displayname; + /* %TRUE if this verb is for a UWP app. + * It means that @command, @executable and @dll_function are %NULL. + */ + gboolean is_uwp; + /* shell/verb/command */ gunichar2 *command; @@ -255,6 +280,7 @@ struct _GWin32AppInfoApplication { * key path for the application. * For applications tracked by executable name this is the * basename of the executable. + * For UWP apps this is the AppUserModelID. * For fake applications this is the full filename of the * executable (as far as it can be inferred from a command line, * meaning that it can also be a basename, if that's @@ -320,6 +346,9 @@ struct _GWin32AppInfoApplication { /* Set to TRUE for applications that are machine-wide defaults (i.e. default * browser) */ gboolean default_app; + + /* Set to TRUE for UWP applications */ + gboolean is_uwp; }; #define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ()) @@ -373,6 +402,7 @@ g_win32_appinfo_handler_dispose (GObject *object) g_clear_object (&handler->key); g_clear_object (&handler->icon); g_clear_pointer (&handler->verbs, g_ptr_array_unref); + g_clear_pointer (&handler->uwp_aumid, g_free); G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object); } @@ -576,6 +606,11 @@ static GHashTable *fake_apps = NULL; */ static GHashTable *handlers = NULL; +/* 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; + /* Watch this whole subtree */ static GWin32RegistryKey *url_associations_key; @@ -616,6 +651,9 @@ static GWin32RegistryKey *classes_root_key; */ #include "giowin32-private.c" +/* for g_win32_package_parser_enum_packages() */ +#include "gwin32packageparser.h" + static void read_handler_icon (GWin32RegistryKey *key, GIcon **icon_out) @@ -642,6 +680,12 @@ read_handler_icon (GWin32RegistryKey *key, NULL, NULL)) { + /* 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). + */ if (default_type == G_WIN32_REGISTRY_VALUE_STR && default_value[0] != '\0') *icon_out = g_themed_icon_new (default_value); @@ -689,18 +733,18 @@ compare_verbs (gconstpointer a, if (def != NULL) { if (_wcsicmp (ca->name, def) == 0) - return 1; - else if (_wcsicmp (cb->name, def) == 0) return -1; + else if (_wcsicmp (cb->name, def) == 0) + return 1; } is_open_ca = is_open (ca->name); is_open_cb = is_open (cb->name); if (is_open_ca && !is_open_cb) - return 1; - else if (is_open_ca && !is_open_cb) return -1; + else if (is_open_ca && !is_open_cb) + return 1; return _wcsicmp (ca->name, cb->name); } @@ -735,7 +779,8 @@ typedef void (*verb_command_func) (gpointer handler_data1, static gunichar2 * decide_which_id_to_use (const gunichar2 *program_id, GWin32RegistryKey **return_key, gchar **return_handler_id_u8, - gchar **return_handler_id_u8_folded); + gchar **return_handler_id_u8_folded, + gunichar2 **return_uwp_aumid); static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *schema, const gchar *schema_u8, @@ -743,7 +788,8 @@ static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *sche static GWin32AppInfoHandler * get_handler_object (const gchar *handler_id_u8_folded, GWin32RegistryKey *handler_key, - const gunichar2 *handler_id); + const gunichar2 *handler_id, + const gunichar2 *uwp_aumid); static GWin32AppInfoFileExtension *get_ext_object (const gunichar2 *ext, const gchar *ext_u8, @@ -768,6 +814,20 @@ static void handler_add_verb (gpointer ha gboolean verb_is_preferred, gboolean invent_new_verb_name); +static void process_uwp_verbs (GList *verbs, + const reg_verb *preferred_verb, + const gunichar2 *path_to_progid, + const gunichar2 *progid, + gboolean autoprefer_first_verb, + GWin32AppInfoHandler *handler_rec, + GWin32AppInfoApplication *app); + +static void uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec, + GWin32AppInfoApplication *app, + const gunichar2 *verb, + const gchar *verb_displayname, + gboolean verb_is_preferred); + /* output_size is in *bytes*, not gunichar2s! */ static gboolean build_registry_path (gunichar2 *output, gsize output_size, ...) @@ -858,6 +918,12 @@ _g_win32_registry_key_build_and_new_w (GError **error, ...) * @verbshell_prefix is the subkey of @program_id_key * that contains the verbs. It is "Shell" initially, * but grows with recursive invocations (for subcommands). + * @is_uwp points to a boolean, which + * indicates whether the function is being called for a UWP app. + * It might be switched from %TRUE to %FALSE on return, + * if the application turns out to not to be UWP on closer inspection. + * If the application is already known not to be UWP before the + * call, this pointer can be %NULL instead. * Returns TRUE on success, FALSE on failure. */ static gboolean @@ -865,7 +931,8 @@ get_verbs (GWin32RegistryKey *program_id_key, const reg_verb **preferred_verb, GList **verbs, const gunichar2 *verbname_prefix, - const gunichar2 *verbshell_prefix) + const gunichar2 *verbshell_prefix, + gboolean *is_uwp) { GWin32RegistrySubkeyIter iter; GWin32RegistryKey *key; @@ -930,7 +997,8 @@ get_verbs (GWin32RegistryKey *program_id_key, * Essentially, we're flattening the command tree into a list. */ has_subcommands = FALSE; - if (g_win32_registry_key_get_value_w (subkey, + if ((is_uwp == NULL || !(*is_uwp)) && /* Assume UWP apps don't have subcommands */ + g_win32_registry_key_get_value_w (subkey, NULL, TRUE, L"Subcommands", @@ -940,8 +1008,9 @@ get_verbs (GWin32RegistryKey *program_id_key, NULL) && subc_type == G_WIN32_REGISTRY_VALUE_STR) { - gunichar2 *new_nameprefix = g_malloc ((verbname_prefix_len + name_len + 1 + 1) * sizeof (gunichar2)); - gunichar2 *new_shellprefix = g_malloc ((verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1) * sizeof (gunichar2)); + gboolean dummy = FALSE; + 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); 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)); @@ -953,16 +1022,38 @@ get_verbs (GWin32RegistryKey *program_id_key, memcpy (&new_nameprefix[verbname_prefix_len], name, (name_len) * sizeof (gunichar2)); new_nameprefix[verbname_prefix_len + name_len] = L'\\'; new_nameprefix[verbname_prefix_len + name_len + 1] = 0; - has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix); + has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix, &dummy); g_free (new_shellprefix); g_free (new_nameprefix); } - g_clear_object (&subkey); - /* Presence of subcommands means that this key itself is not a command-key */ if (has_subcommands) - continue; + { + g_clear_object (&subkey); + continue; + } + + if (is_uwp != NULL && *is_uwp && + !g_win32_registry_key_get_value_w (subkey, + NULL, + TRUE, + L"ActivatableClassId", + &subc_type, + NULL, + NULL, + NULL)) + { + /* We expected a UWP app, but it lacks ActivatableClassId + * on a verb, which means that it does not behave like + * a UWP app should (msedge being an example - it's UWP, + * but has its own launchable exe file and a simple ID), + * so we have to treat it like a normal app. + */ + *is_uwp = FALSE; + } + + g_clear_object (&subkey); /* We don't look at the command sub-key and its value (the actual command line) here. * We save the registry path instead, and use it later in process_verbs_commands(). @@ -973,11 +1064,11 @@ get_verbs (GWin32RegistryKey *program_id_key, * because it never has one - all verbshell prefixes end with "Shell", not "Shell\\") */ rverb = g_new0 (reg_verb, 1); - rverb->name = g_malloc ((verbname_prefix_len + name_len + 1) * sizeof (gunichar2)); + rverb->name = g_new (gunichar2, verbname_prefix_len + name_len + 1); 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; - rverb->shellpath = g_malloc ((verbshell_prefix_len + 1 + name_len + 1) * sizeof (gunichar2)); + rverb->shellpath = g_new (gunichar2, verbshell_prefix_len + 1 + name_len + 1); 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)); @@ -1049,31 +1140,41 @@ get_url_association (const gunichar2 *program_id, const reg_verb *preferred_verb; gchar *handler_id_u8; gchar *handler_id_u8_folded; + gunichar2 *uwp_aumid; + gboolean is_uwp; GWin32RegistryKey *handler_key; if ((handler_id = decide_which_id_to_use (program_id, &handler_key, &handler_id_u8, - &handler_id_u8_folded)) == NULL) + &handler_id_u8_folded, + &uwp_aumid)) == NULL) return; - if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell")) + is_uwp = uwp_aumid != NULL; + + if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp)) { g_clear_pointer (&handler_id, g_free); g_clear_pointer (&handler_id_u8, g_free); g_clear_pointer (&handler_id_u8_folded, g_free); g_clear_object (&handler_key); + g_clear_pointer (&uwp_aumid, g_free); return; } + if (!is_uwp && uwp_aumid != NULL) + g_clear_pointer (&uwp_aumid, g_free); + schema_rec = get_schema_object (schema, schema_u8, schema_u8_folded); handler_rec = get_handler_object (handler_id_u8_folded, handler_key, - handler_id); + handler_id, + uwp_aumid); if (is_user_choice || schema_rec->chosen_handler == NULL) g_set_object (&schema_rec->chosen_handler, handler_rec); @@ -1089,18 +1190,29 @@ get_url_association (const gunichar2 *program_id, g_strdup (schema_rec->schema_u8_folded), g_object_ref (handler_rec)); - process_verbs_commands (g_steal_pointer (&verbs), - preferred_verb, - HKCR, - handler_id, - TRUE, - handler_add_verb, - handler_rec, - app); + if (uwp_aumid == NULL) + process_verbs_commands (g_steal_pointer (&verbs), + preferred_verb, + HKCR, + handler_id, + TRUE, + handler_add_verb, + handler_rec, + app); + else + process_uwp_verbs (g_steal_pointer (&verbs), + preferred_verb, + HKCR, + handler_id, + TRUE, + handler_rec, + app); + g_clear_pointer (&handler_id_u8, g_free); g_clear_pointer (&handler_id_u8_folded, g_free); g_clear_pointer (&handler_id, g_free); + g_clear_pointer (&uwp_aumid, g_free); } /* Grabs a file extension association (from HKCR\.ext or similar). @@ -1121,6 +1233,8 @@ get_file_ext (const gunichar2 *program_id, GList *verbs; gchar *handler_id_u8; gchar *handler_id_u8_folded; + gunichar2 *uwp_aumid; + gboolean is_uwp; GWin32RegistryKey *handler_key; GWin32AppInfoFileExtension *file_extn; gchar *file_extension_u8; @@ -1129,7 +1243,8 @@ get_file_ext (const gunichar2 *program_id, if ((handler_id = decide_which_id_to_use (program_id, &handler_key, &handler_id_u8, - &handler_id_u8_folded)) == NULL) + &handler_id_u8_folded, + &uwp_aumid)) == NULL) return; if (!g_utf16_to_utf8_and_fold (file_extension, @@ -1140,12 +1255,15 @@ get_file_ext (const gunichar2 *program_id, 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_pointer (&uwp_aumid, g_free); g_clear_object (&handler_key); return; } - if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell")) + is_uwp = uwp_aumid != NULL; + + if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp)) { g_clear_pointer (&handler_id, g_free); g_clear_pointer (&handler_id_u8, g_free); @@ -1153,15 +1271,20 @@ get_file_ext (const gunichar2 *program_id, g_clear_object (&handler_key); g_clear_pointer (&file_extension_u8, g_free); g_clear_pointer (&file_extension_u8_folded, g_free); + g_clear_pointer (&uwp_aumid, g_free); return; } + if (!is_uwp && uwp_aumid != NULL) + g_clear_pointer (&uwp_aumid, g_free); + file_extn = get_ext_object (file_extension, file_extension_u8, file_extension_u8_folded); handler_rec = get_handler_object (handler_id_u8_folded, handler_key, - handler_id); + handler_id, + uwp_aumid); if (is_user_choice || file_extn->chosen_handler == NULL) g_set_object (&file_extn->chosen_handler, handler_rec); @@ -1179,18 +1302,28 @@ get_file_ext (const gunichar2 *program_id, g_clear_pointer (&file_extension_u8_folded, g_free); g_clear_object (&handler_key); - process_verbs_commands (g_steal_pointer (&verbs), - preferred_verb, - HKCR, - handler_id, - TRUE, - handler_add_verb, - handler_rec, - app); + if (uwp_aumid == NULL) + process_verbs_commands (g_steal_pointer (&verbs), + preferred_verb, + HKCR, + handler_id, + TRUE, + handler_add_verb, + handler_rec, + app); + else + process_uwp_verbs (g_steal_pointer (&verbs), + preferred_verb, + HKCR, + handler_id, + TRUE, + handler_rec, + app); g_clear_pointer (&handler_id, g_free); g_clear_pointer (&handler_id_u8, g_free); g_clear_pointer (&handler_id_u8_folded, g_free); + g_clear_pointer (&uwp_aumid, g_free); } /* Returns either a @program_id or the string from @@ -1205,12 +1338,15 @@ static gunichar2 * decide_which_id_to_use (const gunichar2 *program_id, GWin32RegistryKey **return_key, gchar **return_handler_id_u8, - gchar **return_handler_id_u8_folded) + gchar **return_handler_id_u8_folded, + gunichar2 **return_uwp_aumid) { GWin32RegistryKey *key; + GWin32RegistryKey *uwp_key; GWin32RegistryValueType val_type; gunichar2 *proxy_id; gunichar2 *return_id; + gunichar2 *uwp_aumid; gboolean got_value; gchar *handler_id_u8; gchar *handler_id_u8_folded; @@ -1219,23 +1355,61 @@ decide_which_id_to_use (const gunichar2 *program_id, if (return_key) *return_key = NULL; - /* Check the proxy first */ + if (return_uwp_aumid) + *return_uwp_aumid = NULL; + key = g_win32_registry_key_get_child_w (classes_root_key, program_id, NULL); if (key == NULL) return NULL; + /* 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); + + /* 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. + */ + if (uwp_aumid == NULL) + g_debug ("ProgramID %S looks like a UWP application, but isn't", + program_id); + else + g_hash_table_insert (uwp_handler_table, g_object_ref (uwp_key), g_wcsdup (uwp_aumid, -1)); + + g_object_unref (uwp_key); + } + + /* Then check for proxy */ proxy_id = 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); + + 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); + } return_id = NULL; @@ -1273,8 +1447,13 @@ decide_which_id_to_use (const gunichar2 *program_id, if (return_handler_id_u8) *return_handler_id_u8 = g_steal_pointer (&handler_id_u8); + g_clear_pointer (&handler_id_u8, g_free); if (return_handler_id_u8_folded) *return_handler_id_u8_folded = g_steal_pointer (&handler_id_u8_folded); + g_clear_pointer (&handler_id_u8_folded, g_free); + if (return_uwp_aumid) + *return_uwp_aumid = g_steal_pointer (&uwp_aumid); + g_clear_pointer (&uwp_aumid, g_free); if (return_id == NULL && return_key) *return_key = g_steal_pointer (&key); @@ -1418,6 +1597,75 @@ process_verbs_commands (GList *verbs, g_list_free_full (verbs, reg_verb_free); } +static void +process_uwp_verbs (GList *verbs, + const reg_verb *preferred_verb, + const gunichar2 *path_to_progid, + const gunichar2 *progid, + gboolean autoprefer_first_verb, + GWin32AppInfoHandler *handler_rec, + GWin32AppInfoApplication *app) +{ + GList *i; + + g_assert (verbs != NULL); + + for (i = verbs; i; i = i->next) + { + const reg_verb *verb = (const reg_verb *) i->data; + GWin32RegistryKey *key; + gboolean got_value; + GWin32RegistryValueType val_type; + gunichar2 *acid; + gsize acid_len; + + key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid, + L"\\", verb->shellpath, NULL); + + if (key == NULL) + { + g_debug ("%S%S\\%S does not exist", + path_to_progid, progid, verb->shellpath); + continue; + } + + got_value = g_win32_registry_key_get_value_w (key, + g_win32_registry_get_os_dirs_w (), + TRUE, + L"ActivatableClassId", + &val_type, + (void **) &acid, + &acid_len, + NULL); + + if (got_value && + val_type == G_WIN32_REGISTRY_VALUE_STR && + acid_len > sizeof (gunichar2)) + { + /* TODO: default value of a shell subkey, if not empty, + * migh contain something like @{Some.Identifier_1234.456.678.789_some_words?ms-resource://Arbitrary.Path/Pointing/Somewhere} + * and it might be possible to turn it into a nice displayname. + */ + uwp_handler_add_verb (handler_rec, + app, + verb->name, + NULL, + (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) || + (!preferred_verb && autoprefer_first_verb && i == verbs)); + } + else + { + g_debug ("%S%S\\%S does not have an ActivatableClassId string value", + path_to_progid, progid, verb->shellpath); + } + + g_clear_pointer (&acid, g_free); + g_clear_object (&key); + } + + g_list_free_full (verbs, reg_verb_free); +} + /* Looks up a schema object identified by * @schema_u8_folded in the urls hash table. * If such object doesn't exist, @@ -1454,7 +1702,8 @@ get_schema_object (const gunichar2 *schema, static GWin32AppInfoHandler * get_handler_object (const gchar *handler_id_u8_folded, GWin32RegistryKey *handler_key, - const gunichar2 *handler_id) + const gunichar2 *handler_id, + const gunichar2 *uwp_aumid) { GWin32AppInfoHandler *handler_rec; @@ -1464,10 +1713,14 @@ get_handler_object (const gchar *handler_id_u8_folded, return handler_rec; handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL); - handler_rec->key = g_object_ref (handler_key); + if (handler_key) + handler_rec->key = g_object_ref (handler_key); handler_rec->handler_id = g_wcsdup (handler_id, -1); handler_rec->handler_id_folded = g_strdup (handler_id_u8_folded); - read_handler_icon (handler_key, &handler_rec->icon); + if (uwp_aumid) + handler_rec->uwp_aumid = g_wcsdup (uwp_aumid, -1); + if (handler_key) + read_handler_icon (handler_key, &handler_rec->icon); g_hash_table_insert (handlers, g_strdup (handler_id_u8_folded), handler_rec); return handler_rec; @@ -1497,6 +1750,7 @@ handler_add_verb (gpointer handler_data1, shverb->verb_displayname = g_strdup (verb_displayname); shverb->command = g_wcsdup (command_line, -1); shverb->command_utf8 = g_strdup (command_line_utf8); + shverb->is_uwp = FALSE; /* This function is for non-UWP verbs only */ if (app_rec) shverb->app = g_object_ref (app_rec); @@ -1533,7 +1787,7 @@ generate_new_verb_name (GPtrArray *verbs, GWin32AppInfoShellVerb *shverb; gsize orig_len = g_utf16_len (verb); gsize new_verb_name_len = orig_len + strlen (" ()") + 2 + 1; - gunichar2 *new_verb_name = g_malloc (new_verb_name_len * sizeof (gunichar2)); + gunichar2 *new_verb_name = g_new (gunichar2, new_verb_name_len); *new_verb = NULL; *new_displayname = NULL; @@ -1636,6 +1890,75 @@ app_add_verb (gpointer handler_data1, g_ptr_array_insert (app_rec->verbs, 0, shverb); } +static void +uwp_app_add_verb (GWin32AppInfoApplication *app_rec, + const gunichar2 *verb, + const gchar *verb_displayname) +{ + GWin32AppInfoShellVerb *shverb; + + _verb_lookup (app_rec->verbs, verb, &shverb); + + if (shverb != NULL) + return; + + shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL); + shverb->verb_name = g_wcsdup (verb, -1); + shverb->app = g_object_ref (app_rec); + shverb->verb_displayname = g_strdup (verb_displayname); + + shverb->is_uwp = TRUE; + + /* Strictly speaking, this is unnecessary, but + * let's make it clear that UWP verbs have no + * commands and executables. + */ + shverb->command = NULL; + shverb->command_utf8 = NULL; + shverb->executable = NULL; + shverb->executable_basename = NULL; + shverb->executable_folded = NULL; + shverb->dll_function = NULL; + + g_ptr_array_add (app_rec->verbs, shverb); +} + +static void +uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec, + GWin32AppInfoApplication *app, + const gunichar2 *verb, + const gchar *verb_displayname, + gboolean verb_is_preferred) +{ + GWin32AppInfoShellVerb *shverb; + + _verb_lookup (handler_rec->verbs, verb, &shverb); + + if (shverb != NULL) + return; + + shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL); + shverb->verb_name = g_wcsdup (verb, -1); + shverb->verb_displayname = g_strdup (verb_displayname); + + shverb->is_uwp = TRUE; + + if (app) + shverb->app = g_object_ref (app); + + shverb->command = NULL; + shverb->command_utf8 = NULL; + shverb->executable = NULL; + shverb->executable_basename = NULL; + shverb->executable_folded = NULL; + shverb->dll_function = NULL; + + if (!verb_is_preferred) + g_ptr_array_add (handler_rec->verbs, shverb); + else + g_ptr_array_insert (handler_rec->verbs, 0, shverb); +} + /* Looks up a file extension object identified by * @ext_u8_folded in the extensions hash table. * If such object doesn't exist, @@ -1899,7 +2222,8 @@ get_app_object (GHashTable *app_hashmap, const gchar *canonical_name_u8, const gchar *canonical_name_folded, gboolean user_specific, - gboolean default_app) + gboolean default_app, + gboolean is_uwp) { GWin32AppInfoApplication *app; @@ -1915,6 +2239,7 @@ get_app_object (GHashTable *app_hashmap, app->no_open_with = FALSE; app->user_specific = user_specific; app->default_app = default_app; + app->is_uwp = is_uwp; g_hash_table_insert (app_hashmap, g_strdup (canonical_name_folded), app); @@ -1950,6 +2275,7 @@ read_capable_app (const gunichar2 *app_key_path, GWin32RegistryKey *associations; const reg_verb *preferred_verb; GList *verbs = NULL; + gboolean verbs_in_root_key = TRUE; appkey = NULL; capabilities = NULL; @@ -1964,11 +2290,14 @@ read_capable_app (const gunichar2 *app_key_path, &app_key_path_u8_folded) || (appkey = g_win32_registry_key_new_w (app_key_path, NULL)) == NULL || (capabilities = g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL)) == NULL || - !get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell")) + !(get_verbs (appkey, &preferred_verb, &verbs, L"", L"Shell", NULL) || + (verbs_in_root_key = FALSE) || + get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell", NULL))) { g_clear_pointer (&canonical_name_u8, g_free); g_clear_pointer (&canonical_name_folded, g_free); g_clear_object (&appkey); + g_clear_object (&capabilities); g_clear_pointer (&app_key_path_u8, g_free); g_clear_pointer (&app_key_path_u8_folded, g_free); @@ -1980,12 +2309,13 @@ read_capable_app (const gunichar2 *app_key_path, canonical_name_u8, canonical_name_folded, user_specific, - default_app); + default_app, + FALSE); process_verbs_commands (g_steal_pointer (&verbs), preferred_verb, L"", /* [ab]use the fact that two strings are simply concatenated */ - g_win32_registry_key_get_path_w (capabilities), + verbs_in_root_key ? app_key_path : g_win32_registry_key_get_path_w (capabilities), FALSE, app_add_verb, app, @@ -2328,7 +2658,7 @@ read_incapable_app (GWin32RegistryKey *incapable_app, GIcon *icon = NULL; GWin32RegistryKey *supported_key; - if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell")) + if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell", NULL)) return; app = get_app_object (apps_by_exe, @@ -2336,6 +2666,7 @@ read_incapable_app (GWin32RegistryKey *incapable_app, app_exe_basename_u8, app_exe_basename_u8_folded, FALSE, + FALSE, FALSE); process_verbs_commands (g_steal_pointer (&verbs), @@ -2743,6 +3074,9 @@ link_handlers_to_unregistered_apps (void) { gsize vi; + if (handler->uwp_aumid != NULL) + continue; + for (vi = 0; vi < handler->verbs->len; vi++) { GWin32AppInfoShellVerb *handler_verb; @@ -2770,6 +3104,9 @@ link_handlers_to_unregistered_apps (void) GWin32AppInfoShellVerb *app_verb; gsize ai; + if (app->is_uwp) + continue; + for (ai = 0; ai < app->verbs->len; ai++) { GWin32PrivateStat app_verb_exec_info; @@ -2820,6 +3157,9 @@ link_handlers_to_unregistered_apps (void) (gpointer *) &appexe_fld_basename, (gpointer *) &app)) { + if (app->is_uwp) + continue; + /* Use basename because apps_by_exe only has basenames */ if (g_strcmp0 (handler_exe_basename, appexe_fld_basename) != 0) continue; @@ -2866,6 +3206,9 @@ link_handlers_to_fake_apps (void) { gsize vi; + if (handler->uwp_aumid != NULL) + continue; + for (vi = 0; vi < handler->verbs->len; vi++) { GWin32AppInfoShellVerb *handler_verb; @@ -2885,6 +3228,7 @@ link_handlers_to_fake_apps (void) handler_verb->executable, handler_verb->executable_folded, FALSE, + FALSE, FALSE); g_clear_pointer (&exename_utf16, g_free); handler_verb->app = g_object_ref (app); @@ -2916,6 +3260,9 @@ link_handlers_to_fake_apps (void) { gsize vi; + if (handler->uwp_aumid != NULL) + continue; + for (vi = 0; vi < handler->verbs->len; vi++) { GWin32AppInfoShellVerb *handler_verb; @@ -2932,6 +3279,7 @@ link_handlers_to_fake_apps (void) handler_verb->command_utf8, command_utf8_folded, FALSE, + FALSE, FALSE); g_clear_pointer (&command_utf8_folded, g_free); handler_verb->app = g_object_ref (app); @@ -2952,6 +3300,404 @@ link_handlers_to_fake_apps (void) } } +static GWin32AppInfoHandler * +find_uwp_handler_for_ext (GWin32AppInfoFileExtension *file_extn, + const gunichar2 *app_user_model_id) +{ + GHashTableIter handler_iter; + gchar *handler_id_fld; + GWin32AppInfoHandler *handler; + + g_hash_table_iter_init (&handler_iter, file_extn->handlers); + while (g_hash_table_iter_next (&handler_iter, + (gpointer *) &handler_id_fld, + (gpointer *) &handler)) + { + if (handler->uwp_aumid == NULL) + continue; + + if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0) + return handler; + } + + return NULL; +} + +static GWin32AppInfoHandler * +find_uwp_handler_for_schema (GWin32AppInfoURLSchema *schema, + const gunichar2 *app_user_model_id) +{ + GHashTableIter handler_iter; + gchar *handler_id_fld; + GWin32AppInfoHandler *handler; + + g_hash_table_iter_init (&handler_iter, schema->handlers); + while (g_hash_table_iter_next (&handler_iter, + (gpointer *) &handler_id_fld, + (gpointer *) &handler)) + { + if (handler->uwp_aumid == NULL) + continue; + + if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0) + return handler; + } + + return NULL; +} + +static gboolean +uwp_package_cb (gpointer user_data, + const gunichar2 *full_package_name, + const gunichar2 *package_name, + const gunichar2 *app_user_model_id, + gboolean show_in_applist, + GPtrArray *supported_extgroups, + GPtrArray *supported_protocols) +{ + gint i, i_verb, i_ext; + gint extensions_considered; + GWin32AppInfoApplication *app; + gchar *app_user_model_id_u8; + gchar *app_user_model_id_u8_folded; + GHashTableIter iter; + GWin32AppInfoHandler *ext; + GWin32AppInfoHandler *url; + + if (!g_utf16_to_utf8_and_fold (app_user_model_id, + -1, + &app_user_model_id_u8, + &app_user_model_id_u8_folded)) + return TRUE; + + app = get_app_object (apps_by_id, + app_user_model_id, + app_user_model_id_u8, + app_user_model_id_u8_folded, + TRUE, + FALSE, + TRUE); + + extensions_considered = 0; + + for (i = 0; i < supported_extgroups->len; i++) + { + GWin32PackageExtGroup *grp = (GWin32PackageExtGroup *) g_ptr_array_index (supported_extgroups, i); + + extensions_considered += grp->extensions->len; + + for (i_ext = 0; i_ext < grp->extensions->len; i_ext++) + { + wchar_t *ext = (wchar_t *) g_ptr_array_index (grp->extensions, i_ext); + gchar *ext_u8; + gchar *ext_u8_folded; + GWin32AppInfoFileExtension *file_extn; + GWin32AppInfoHandler *handler_rec; + + if (!g_utf16_to_utf8_and_fold (ext, + -1, + &ext_u8, + &ext_u8_folded)) + continue; + + file_extn = get_ext_object (ext, ext_u8, ext_u8_folded); + g_free (ext_u8); + handler_rec = find_uwp_handler_for_ext (file_extn, app_user_model_id); + + if (handler_rec == NULL) + { + /* Use AppUserModelId as the ID of the new fake handler */ + handler_rec = get_handler_object (app_user_model_id_u8_folded, + NULL, + app_user_model_id, + app_user_model_id); + g_hash_table_insert (file_extn->handlers, + g_strdup (app_user_model_id_u8_folded), + g_object_ref (handler_rec)); + } + + if (file_extn->chosen_handler == NULL) + g_set_object (&file_extn->chosen_handler, handler_rec); + + /* This is somewhat wasteful, but for 100% correct handling + * we need to remember which extensions (handlers) support + * which verbs, and each handler gets its own copy of the + * verb object, since our design is handler-centric, + * not verb-centric. The app also gets a list of verbs, + * but without handlers it would have no idea which + * verbs can be used with which extensions. + */ + for (i_verb = 0; i_verb < grp->verbs->len; i_verb++) + { + wchar_t *verb = NULL; + + verb = (wchar_t *) g_ptr_array_index (grp->verbs, i_verb); + /* *_add_verb() functions are no-ops when a verb already exists, + * so we're free to call them as many times as we want. + */ + uwp_handler_add_verb (handler_rec, + app, + verb, + NULL, + FALSE); + } + + g_hash_table_insert (app->supported_exts, + g_steal_pointer (&ext_u8_folded), + g_object_ref (handler_rec)); + } + } + + g_hash_table_iter_init (&iter, app->supported_exts); + + /* Pile up all handler verbs into the app too, + * for cases when we don't have a ref to a handler. + */ + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &ext)) + { + gint i_hverb; + + if (!ext) + continue; + + for (i_hverb = 0; i_hverb < ext->verbs->len; i_hverb++) + { + GWin32AppInfoShellVerb *handler_verb; + + handler_verb = _verb_idx (ext->verbs, i_hverb); + uwp_app_add_verb (app, handler_verb->verb_name, handler_verb->verb_displayname); + if (handler_verb->app == NULL && handler_verb->is_uwp) + handler_verb->app = g_object_ref (app); + } + } + + if (app->verbs->len == 0 && extensions_considered > 0) + g_warning ("Unexpectedly, UWP app `%S' (AUMId `%s') supports %d extensions but has no verbs", + full_package_name, app_user_model_id_u8, extensions_considered); + + for (i = 0; i < supported_protocols->len; i++) + { + wchar_t *proto = (wchar_t *) g_ptr_array_index (supported_protocols, i); + gchar *proto_u8; + gchar *proto_u8_folded; + GWin32AppInfoURLSchema *schema_rec; + GWin32AppInfoHandler *handler_rec; + + if (!g_utf16_to_utf8_and_fold (proto, + -1, + &proto_u8, + &proto_u8_folded)) + continue; + + schema_rec = get_schema_object (proto, + proto_u8, + proto_u8_folded); + + g_free (proto_u8); + + handler_rec = find_uwp_handler_for_schema (schema_rec, app_user_model_id); + + if (handler_rec == NULL) + { + /* Use AppUserModelId as the ID of the new fake handler */ + handler_rec = get_handler_object (app_user_model_id_u8_folded, + NULL, + app_user_model_id, + app_user_model_id); + + g_hash_table_insert (schema_rec->handlers, + g_strdup (app_user_model_id_u8_folded), + g_object_ref (handler_rec)); + } + + 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); + + g_hash_table_insert (app->supported_urls, + g_steal_pointer (&proto_u8_folded), + g_object_ref (handler_rec)); + } + + g_hash_table_iter_init (&iter, app->supported_urls); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &url)) + { + gint i_hverb; + + 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); + } + } + + g_free (app_user_model_id_u8); + g_free (app_user_model_id_u8_folded); + + return TRUE; +} + +/* 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; + + 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). + */ + } +} static void update_registry_data (void) @@ -2963,7 +3709,8 @@ update_registry_data (void) GWin32RegistryKey *url_associations; GWin32RegistryKey *file_exts; GWin32RegistryKey *classes_root; - DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, postproc_end; + DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, uwp_end, postproc_end; + GError *error = NULL; url_associations = g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations", @@ -3009,6 +3756,8 @@ update_registry_data (void) 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); + uwp_handler_table = + g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_free); alloc_end = GetTickCount (); for (i = 0; i < priority_capable_apps_keys->len; i++) @@ -3033,6 +3782,16 @@ update_registry_data (void) exeapp_end = GetTickCount (); read_classes (classes_root); classes_end = GetTickCount (); + + if (!g_win32_package_parser_enum_packages (uwp_package_cb, NULL, &error)) + { + g_debug ("Unable to get UWP apps: %s", error->message); + g_clear_error (&error); + } + + read_uwp_handler_info (); + + uwp_end = GetTickCount (); link_handlers_to_unregistered_apps (); link_handlers_to_fake_apps (); postproc_end = GetTickCount (); @@ -3044,6 +3803,7 @@ update_registry_data (void) "Reading extension assocs: %lums\n" "Reading exe-only apps:...... %lums\n" "Reading classes: %lums\n" + "Reading UWP apps: %lums\n" "Postprocessing:..............%lums\n" "TOTAL: %lums", collect_end - collect_start, @@ -3053,7 +3813,8 @@ update_registry_data (void) ext_end - url_end, exeapp_end - ext_end, classes_end - exeapp_end, - postproc_end - classes_end, + uwp_end - classes_end, + postproc_end - uwp_end, postproc_end - collect_start); g_clear_object (&classes_root); @@ -3062,6 +3823,7 @@ update_registry_data (void) 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); + g_hash_table_unref (uwp_handler_table); return; } @@ -3251,17 +4013,11 @@ gio_win32_appinfo_init (gboolean do_wait) if (!do_wait) return; - /* If any of the keys had a change, then we've already initiated - * a tree re-build in keys_updated(). Just wait for it to finish. + /* 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. */ - if ((url_associations_key && g_win32_registry_key_has_changed (url_associations_key)) || - (file_exts_key && g_win32_registry_key_has_changed (file_exts_key)) || - (user_clients_key && g_win32_registry_key_has_changed (user_clients_key)) || - (system_clients_key && g_win32_registry_key_has_changed (system_clients_key)) || - (applications_key && g_win32_registry_key_has_changed (applications_key)) || - (user_registered_apps_key && g_win32_registry_key_has_changed (user_registered_apps_key)) || - (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) || - (classes_root_key && g_win32_registry_key_has_changed (classes_root_key))) + if (g_atomic_int_get (&gio_win32_appinfo_update_counter) > 0) { g_mutex_lock (&gio_win32_appinfo_mutex); while (g_atomic_int_get (&gio_win32_appinfo_update_counter) > 0) @@ -3345,7 +4101,7 @@ g_win32_app_info_new_from_app (GWin32AppInfoApplication *app, i += 1; } - new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1)); + new_info->supported_types = g_new (gchar *, i + 1); i = 0; g_hash_table_iter_init (&iter, new_info->app->supported_exts); @@ -3392,7 +4148,7 @@ g_win32_app_info_dup (GAppInfo *appinfo) for (i = 0; info->supported_types[i]; i++) break; - new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1)); + new_info->supported_types = g_new (gchar *, i + 1); for (i = 0; info->supported_types[i]; i++) new_info->supported_types[i] = g_strdup (info->supported_types[i]); @@ -3461,7 +4217,9 @@ g_win32_app_info_get_name (GAppInfo *appinfo) { GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo); - if (info->app && info->app->canonical_name_u8) + if (info->app && info->app->pretty_name_u8) + return info->app->pretty_name_u8; + else if (info->app && info->app->canonical_name_u8) return info->app->canonical_name_u8; else return P_("Unnamed"); @@ -3502,7 +4260,7 @@ g_win32_app_info_get_executable (GAppInfo *appinfo) if (info->app == NULL) return NULL; - if (info->app->verbs->len > 0) + if (info->app->verbs->len > 0 && !info->app->is_uwp) return _verb_idx (info->app->verbs, 0)->executable; return NULL; @@ -3516,7 +4274,7 @@ g_win32_app_info_get_commandline (GAppInfo *appinfo) if (info->app == NULL) return NULL; - if (info->app->verbs->len > 0) + if (info->app->verbs->len > 0 && !info->app->is_uwp) return _verb_idx (info->app->verbs, 0)->command_utf8; return NULL; @@ -3911,9 +4669,52 @@ get_appath_for_exe (const gchar *exe_basename) } +static gboolean +g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info, + gboolean for_files, + IShellItemArray *items, + GWin32AppInfoShellVerb *shverb, + GError **error) +{ + DWORD pid; + IApplicationActivationManager* paam = NULL; + gboolean result = TRUE; + HRESULT hr; + + hr = CoCreateInstance (&CLSID_ApplicationActivationManager, NULL, CLSCTX_INPROC_SERVER, &IID_IApplicationActivationManager, (void **) &paam); + if (FAILED (hr)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to create ApplicationActivationManager: 0x%lx", hr); + return FALSE; + } + + if (items == NULL) + hr = IApplicationActivationManager_ActivateApplication (paam, (const wchar_t *) info->app->canonical_name, NULL, AO_NONE, &pid); + else if (for_files) + hr = IApplicationActivationManager_ActivateForFile (paam, (const wchar_t *) info->app->canonical_name, items, shverb->verb_name, &pid); + else + hr = IApplicationActivationManager_ActivateForProtocol (paam, (const wchar_t *) info->app->canonical_name, items, &pid); + + 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); + result = FALSE; + } + + IApplicationActivationManager_Release (paam); + + return result; +} + + static gboolean g_win32_app_info_launch_internal (GWin32AppInfo *info, - GList *objs, + GList *objs, /* non-UWP only */ + gboolean for_files, /* UWP only */ + IShellItemArray *items, /* UWP only */ GAppLaunchContext *launch_context, GSpawnFlags spawn_flags, GError **error) @@ -3929,15 +4730,10 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info, g_return_val_if_fail (info->app != NULL, FALSE); argv = NULL; - - if (launch_context) - envp = g_app_launch_context_get_environment (launch_context); - else - envp = g_get_environ (); - shverb = NULL; - if (info->handler != NULL && + if (!info->app->is_uwp && + info->handler != NULL && info->handler->verbs->len > 0) shverb = _verb_idx (info->handler->verbs, 0); else if (info->app->verbs->len > 0) @@ -3945,7 +4741,7 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info, if (shverb == NULL) { - if (info->handler == NULL) + if (info->app->is_uwp || info->handler == NULL) g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, P_("The app ‘%s’ in the application object has no verbs"), g_win32_appinfo_application_get_some_name (info->app)); @@ -3958,6 +4754,18 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info, return FALSE; } + if (info->app->is_uwp) + return g_win32_app_info_launch_uwp_internal (info, + for_files, + items, + shverb, + error); + + if (launch_context) + envp = g_app_launch_context_get_environment (launch_context); + else + envp = g_get_environ (); + g_assert (shverb->command_utf8 != NULL); command = shverb->command_utf8; apppath = get_appath_for_exe (shverb->executable_basename); @@ -4105,6 +4913,95 @@ g_win32_app_info_supports_files (GAppInfo *appinfo) } +static IShellItemArray * +make_item_array (gboolean for_files, + GList *files_or_uris, + GError **error) +{ + ITEMIDLIST **item_ids; + IShellItemArray *items; + GList *p; + gsize count; + gsize i; + HRESULT hr; + + count = g_list_length (files_or_uris); + + items = NULL; + item_ids = g_new (ITEMIDLIST*, count); + + for (i = 0, p = files_or_uris; p != NULL; p = p->next, i++) + { + wchar_t *file_or_uri_utf16; + + if (!for_files) + file_or_uri_utf16 = g_utf8_to_utf16 ((gchar *) p->data, -1, NULL, NULL, error); + else + file_or_uri_utf16 = g_utf8_to_utf16 (g_file_peek_path (G_FILE (p->data)), -1, NULL, NULL, error); + + 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); + g_free (file_or_uri_utf16); + + if (FAILED (hr)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "File or URI `%S' cannot be parsed by SHParseDisplayName: 0x%lx", file_or_uri_utf16, hr); + break; + } + } + + 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; +} + + static gboolean g_win32_app_info_launch_uris (GAppInfo *appinfo, GList *uris, @@ -4114,6 +5011,26 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo, gboolean res = FALSE; gboolean do_files; GList *objs; + GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo); + + if (info->app != NULL && info->app->is_uwp) + { + IShellItemArray *items = NULL; + + if (uris) + { + items = make_item_array (FALSE, uris, error); + if (items == NULL) + return res; + } + + res = g_win32_app_info_launch_internal (info, NULL, FALSE, items, launch_context, 0, error); + + if (items != NULL) + IShellItemArray_Release (items); + + return res; + } do_files = g_win32_app_info_supports_files (appinfo); @@ -4142,8 +5059,10 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo, objs = g_list_reverse (objs); - res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo), + res = g_win32_app_info_launch_internal (info, objs, + FALSE, + NULL, launch_context, G_SPAWN_SEARCH_PATH, error); @@ -4162,6 +5081,26 @@ g_win32_app_info_launch (GAppInfo *appinfo, gboolean res = FALSE; gboolean do_uris; GList *objs; + GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo); + + if (info->app != NULL && info->app->is_uwp) + { + IShellItemArray *items = NULL; + + if (files) + { + items = make_item_array (TRUE, files, error); + if (items == NULL) + return res; + } + + res = g_win32_app_info_launch_internal (info, NULL, TRUE, items, launch_context, 0, error); + + if (items != NULL) + IShellItemArray_Release (items); + + return res; + } do_uris = g_win32_app_info_supports_uris (appinfo); @@ -4181,8 +5120,10 @@ g_win32_app_info_launch (GAppInfo *appinfo, objs = g_list_reverse (objs); - res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo), + res = g_win32_app_info_launch_internal (info, objs, + TRUE, + NULL, launch_context, G_SPAWN_SEARCH_PATH, error); diff --git a/gio/gwin32file-sync-stream.c b/gio/gwin32file-sync-stream.c new file mode 100755 index 000000000..bc3b60694 --- /dev/null +++ b/gio/gwin32file-sync-stream.c @@ -0,0 +1,508 @@ +/* gwin32file-sync-stream.c - a simple IStream implementation + * + * Copyright 2020 Руслан Ижбулатов + * + * 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 + * version 2.1 of the License, or (at your option) any later version. + * + * 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 Public License + * along with this library; if not, see . + */ + +/* A COM object that implements an IStream backed by a file HANDLE. + * Works just like `SHCreateStreamOnFileEx()`, but does not + * support locking, and doesn't force us to link to libshlwapi. + * Only supports synchronous access. + */ +#include "config.h" +#define COBJMACROS +#define INITGUID +#include + +#include "gwin32file-sync-stream.h" + +static HRESULT STDMETHODCALLTYPE _file_sync_stream_query_interface (IStream *self_ptr, + REFIID ref_interface_guid, + LPVOID *output_object_ptr); +static ULONG STDMETHODCALLTYPE _file_sync_stream_release (IStream *self_ptr); +static ULONG STDMETHODCALLTYPE _file_sync_stream_add_ref (IStream *self_ptr); + +static HRESULT STDMETHODCALLTYPE _file_sync_stream_read (IStream *self_ptr, + void *output_data, + ULONG bytes_to_read, + ULONG *output_bytes_read); + +static HRESULT STDMETHODCALLTYPE _file_sync_stream_write (IStream *self_ptr, + const void *data, + ULONG bytes_to_write, + ULONG *output_bytes_written); + + +static HRESULT STDMETHODCALLTYPE _file_sync_stream_clone (IStream *self_ptr, + IStream **output_clone_ptr); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_commit (IStream *self_ptr, + DWORD commit_flags); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_copy_to (IStream *self_ptr, + IStream *output_stream, + ULARGE_INTEGER bytes_to_copy, + ULARGE_INTEGER *output_bytes_read, + ULARGE_INTEGER *output_bytes_written); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_lock_region (IStream *self_ptr, + ULARGE_INTEGER lock_offset, + ULARGE_INTEGER lock_bytes, + DWORD lock_type); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_revert (IStream *self_ptr); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_seek (IStream *self_ptr, + LARGE_INTEGER move_distance, + DWORD origin, + ULARGE_INTEGER *output_new_position); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_set_size (IStream *self_ptr, + ULARGE_INTEGER new_size); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_stat (IStream *self_ptr, + STATSTG *output_stat, + DWORD flags); +static HRESULT STDMETHODCALLTYPE _file_sync_stream_unlock_region (IStream *self_ptr, + ULARGE_INTEGER lock_offset, + ULARGE_INTEGER lock_bytes, + DWORD lock_type); + +static void _file_sync_stream_free (GWin32FileSyncStream *self); + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_query_interface (IStream *self_ptr, + REFIID ref_interface_guid, + LPVOID *output_object_ptr) +{ + *output_object_ptr = NULL; + + if (IsEqualGUID (ref_interface_guid, &IID_IUnknown)) + { + IUnknown_AddRef ((IUnknown *) self_ptr); + *output_object_ptr = self_ptr; + return S_OK; + } + else if (IsEqualGUID (ref_interface_guid, &IID_IStream)) + { + IStream_AddRef (self_ptr); + *output_object_ptr = self_ptr; + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE +_file_sync_stream_add_ref (IStream *self_ptr) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + + return ++self->ref_count; +} + +static ULONG STDMETHODCALLTYPE +_file_sync_stream_release (IStream *self_ptr) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + + int ref_count = --self->ref_count; + + if (ref_count == 0) + _file_sync_stream_free (self); + + return ref_count; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_read (IStream *self_ptr, + void *output_data, + ULONG bytes_to_read, + ULONG *output_bytes_read) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + DWORD bytes_read; + + if (!ReadFile (self->file_handle, output_data, bytes_to_read, &bytes_read, NULL)) + { + DWORD error = GetLastError (); + return __HRESULT_FROM_WIN32 (error); + } + + if (output_bytes_read) + *output_bytes_read = bytes_read; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_write (IStream *self_ptr, + const void *data, + ULONG bytes_to_write, + ULONG *output_bytes_written) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + DWORD bytes_written; + + if (!WriteFile (self->file_handle, data, bytes_to_write, &bytes_written, NULL)) + { + DWORD error = GetLastError (); + return __HRESULT_FROM_WIN32 (error); + } + + if (output_bytes_written) + *output_bytes_written = bytes_written; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_seek (IStream *self_ptr, + LARGE_INTEGER move_distance, + DWORD origin, + ULARGE_INTEGER *output_new_position) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + LARGE_INTEGER new_position; + DWORD move_method; + + switch (origin) + { + case STREAM_SEEK_SET: + move_method = FILE_BEGIN; + break; + case STREAM_SEEK_CUR: + move_method = FILE_CURRENT; + break; + case STREAM_SEEK_END: + move_method = FILE_END; + break; + default: + return E_INVALIDARG; + } + + if (!SetFilePointerEx (self->file_handle, move_distance, &new_position, move_method)) + { + DWORD error = GetLastError (); + return __HRESULT_FROM_WIN32 (error); + } + + (*output_new_position).QuadPart = new_position.QuadPart; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_set_size (IStream *self_ptr, + ULARGE_INTEGER new_size) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + FILE_END_OF_FILE_INFO info; + + info.EndOfFile.QuadPart = new_size.QuadPart; + + if (SetFileInformationByHandle (self->file_handle, FileEndOfFileInfo, &info, sizeof (info))) + { + DWORD error = GetLastError (); + return __HRESULT_FROM_WIN32 (error); + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_copy_to (IStream *self_ptr, + IStream *output_stream, + ULARGE_INTEGER bytes_to_copy, + ULARGE_INTEGER *output_bytes_read, + ULARGE_INTEGER *output_bytes_written) +{ + ULARGE_INTEGER counter; + ULARGE_INTEGER written_counter; + ULARGE_INTEGER read_counter; + + counter.QuadPart = bytes_to_copy.QuadPart; + written_counter.QuadPart = 0; + read_counter.QuadPart = 0; + + while (counter.QuadPart > 0) + { + HRESULT hr; + ULONG bytes_read; + ULONG bytes_written; + ULONG bytes_index; +#define _INTERNAL_BUFSIZE 1024 + BYTE buffer[_INTERNAL_BUFSIZE]; +#undef _INTERNAL_BUFSIZE + ULONG buffer_size = sizeof (buffer); + ULONG to_read = buffer_size; + + if (counter.QuadPart < buffer_size) + to_read = (ULONG) counter.QuadPart; + + /* Because MS SDK has a function IStream_Read() with 3 arguments */ + hr = self_ptr->lpVtbl->Read (self_ptr, buffer, to_read, &bytes_read); + if (!SUCCEEDED (hr)) + return hr; + + read_counter.QuadPart += bytes_read; + + if (bytes_read == 0) + break; + + bytes_index = 0; + + while (bytes_index < bytes_read) + { + /* Because MS SDK has a function IStream_Write() with 3 arguments */ + hr = output_stream->lpVtbl->Write (output_stream, &buffer[bytes_index], bytes_read - bytes_index, &bytes_written); + if (!SUCCEEDED (hr)) + return hr; + + if (bytes_written == 0) + return __HRESULT_FROM_WIN32 (ERROR_WRITE_FAULT); + + bytes_index += bytes_written; + written_counter.QuadPart += bytes_written; + } + } + + if (output_bytes_read) + output_bytes_read->QuadPart = read_counter.QuadPart; + if (output_bytes_written) + output_bytes_written->QuadPart = written_counter.QuadPart; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_commit (IStream *self_ptr, + DWORD commit_flags) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + + if (!FlushFileBuffers (self->file_handle)) + { + DWORD error = GetLastError (); + return __HRESULT_FROM_WIN32 (error); + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_revert (IStream *self_ptr) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_lock_region (IStream *self_ptr, + ULARGE_INTEGER lock_offset, + ULARGE_INTEGER lock_bytes, + DWORD lock_type) +{ + return STG_E_INVALIDFUNCTION; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_unlock_region (IStream *self_ptr, + ULARGE_INTEGER lock_offset, + ULARGE_INTEGER lock_bytes, + DWORD lock_type) +{ + return STG_E_INVALIDFUNCTION; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_stat (IStream *self_ptr, + STATSTG *output_stat, + DWORD flags) +{ + GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr; + BOOL get_name = FALSE; + FILE_BASIC_INFO bi; + FILE_STANDARD_INFO si; + + if (output_stat == NULL) + return STG_E_INVALIDPOINTER; + + switch (flags) + { + case STATFLAG_DEFAULT: + get_name = TRUE; + break; + case STATFLAG_NONAME: + get_name = FALSE; + break; + default: + return STG_E_INVALIDFLAG; + } + + if (!GetFileInformationByHandleEx (self->file_handle, FileBasicInfo, &bi, sizeof (bi)) || + !GetFileInformationByHandleEx (self->file_handle, FileStandardInfo, &si, sizeof (si))) + { + DWORD error = GetLastError (); + return __HRESULT_FROM_WIN32 (error); + } + + output_stat->type = STGTY_STREAM; + output_stat->mtime.dwLowDateTime = bi.LastWriteTime.LowPart; + output_stat->mtime.dwHighDateTime = bi.LastWriteTime.HighPart; + output_stat->ctime.dwLowDateTime = bi.CreationTime.LowPart; + output_stat->ctime.dwHighDateTime = bi.CreationTime.HighPart; + output_stat->atime.dwLowDateTime = bi.LastAccessTime.LowPart; + output_stat->atime.dwHighDateTime = bi.LastAccessTime.HighPart; + output_stat->grfLocksSupported = 0; + memset (&output_stat->clsid, 0, sizeof (CLSID)); + output_stat->grfStateBits = 0; + output_stat->reserved = 0; + output_stat->cbSize.QuadPart = si.EndOfFile.QuadPart; + output_stat->grfMode = self->stgm_mode; + + if (get_name) + { + DWORD tries; + wchar_t *buffer; + + /* Nothing in the documentation guarantees that the name + * won't change between two invocations (one - to get the + * buffer size, the other - to fill the buffer). + * Re-try up to 5 times in case the required buffer size + * doesn't match. + */ + for (tries = 5; tries > 0; tries--) + { + DWORD buffer_size; + DWORD buffer_size2; + DWORD error; + + buffer_size = GetFinalPathNameByHandleW (self->file_handle, NULL, 0, 0); + + if (buffer_size == 0) + { + DWORD error = GetLastError (); + return __HRESULT_FROM_WIN32 (error); + } + + buffer = CoTaskMemAlloc (buffer_size); + buffer[buffer_size - 1] = 0; + buffer_size2 = GetFinalPathNameByHandleW (self->file_handle, buffer, buffer_size, 0); + + if (buffer_size2 < buffer_size) + break; + + error = GetLastError (); + CoTaskMemFree (buffer); + if (buffer_size2 == 0) + return __HRESULT_FROM_WIN32 (error); + } + + if (tries == 0) + return __HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH); + + output_stat->pwcsName = buffer; + } + else + output_stat->pwcsName = NULL; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +_file_sync_stream_clone (IStream *self_ptr, + IStream **output_clone_ptr) +{ + return E_NOTIMPL; +} + +static IStreamVtbl _file_sync_stream_vtbl = { + _file_sync_stream_query_interface, + _file_sync_stream_add_ref, + _file_sync_stream_release, + _file_sync_stream_read, + _file_sync_stream_write, + _file_sync_stream_seek, + _file_sync_stream_set_size, + _file_sync_stream_copy_to, + _file_sync_stream_commit, + _file_sync_stream_revert, + _file_sync_stream_lock_region, + _file_sync_stream_unlock_region, + _file_sync_stream_stat, + _file_sync_stream_clone, +}; + +static void +_file_sync_stream_free (GWin32FileSyncStream *self) +{ + if (self->owns_handle) + CloseHandle (self->file_handle); + + g_free (self); +} + +/** + * g_win32_file_sync_stream_new: + * @handle: a Win32 HANDLE for a file. + * @owns_handle: %TRUE if newly-created stream owns the handle + * (and closes it when destroyed) + * @stgm_mode: a combination of [STGM constants](https://docs.microsoft.com/en-us/windows/win32/stg/stgm-constants) + * that specify the mode with which the stream + * is opened. + * @output_hresult: (out) (optional): a HRESULT from the internal COM calls. + * Will be `S_OK` on success. + * + * Creates an IStream object backed by a HANDLE. + * + * @stgm_mode should match the mode of the @handle, otherwise the stream might + * attempt to perform operations that the @handle does not allow. The implementation + * itself ignores these flags completely, they are only used to report + * the mode of the stream to third parties. + * + * The stream only does synchronous access and will never return `E_PENDING` on I/O. + * + * The returned stream object should be treated just like any other + * COM object, and released via `IUnknown_Release()`. + * its elements have been unreffed with g_object_unref(). + * + * Returns: (nullable) (transfer full): a new IStream object on success, %NULL on failure. + **/ +IStream * +g_win32_file_sync_stream_new (HANDLE file_handle, + gboolean owns_handle, + DWORD stgm_mode, + HRESULT *output_hresult) +{ + GWin32FileSyncStream *new_stream; + IStream *result; + HRESULT hr; + + new_stream = g_new0 (GWin32FileSyncStream, 1); + new_stream->self.lpVtbl = &_file_sync_stream_vtbl; + + hr = IUnknown_QueryInterface ((IUnknown *) new_stream, &IID_IStream, (void **) &result); + if (!SUCCEEDED (hr)) + { + g_free (new_stream); + + if (output_hresult) + *output_hresult = hr; + + return NULL; + } + + new_stream->stgm_mode = stgm_mode; + new_stream->file_handle = file_handle; + new_stream->owns_handle = owns_handle; + + if (output_hresult) + *output_hresult = S_OK; + + return result; +} diff --git a/gio/gwin32file-sync-stream.h b/gio/gwin32file-sync-stream.h new file mode 100755 index 000000000..8e7f5fd59 --- /dev/null +++ b/gio/gwin32file-sync-stream.h @@ -0,0 +1,44 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2020 Руслан Ижбулатов + * + * 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 + * version 2.1 of the License, or (at your option) any later version. + * + * 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 + * Public License along with this library; if not, see . + * + */ +#ifndef __G_WIN32_FILE_SYNC_STREAM_H__ +#define __G_WIN32_FILE_SYNC_STREAM_H__ + +#include + +#ifdef G_PLATFORM_WIN32 + +typedef struct _GWin32FileSyncStream GWin32FileSyncStream; + +struct _GWin32FileSyncStream +{ + IStream self; + ULONG ref_count; + HANDLE file_handle; + gboolean owns_handle; + DWORD stgm_mode; +}; + +IStream *g_win32_file_sync_stream_new (HANDLE file_handle, + gboolean owns_handle, + DWORD stgm_mode, + HRESULT *output_hresult); + +#endif /* G_PLATFORM_WIN32 */ + +#endif /* __G_WIN32_FILE_SYNC_STREAM_H__ */ \ No newline at end of file diff --git a/gio/gwin32packageparser.c b/gio/gwin32packageparser.c new file mode 100755 index 000000000..745f1f522 --- /dev/null +++ b/gio/gwin32packageparser.c @@ -0,0 +1,815 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2020 Руслан Ижбулатов + * + * 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 + * version 2.1 of the License, or (at your option) any later version. + * + * 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 + * Public License along with this library; if not, see . + * + */ + +/* Queries the system (Windows 8 or newer) for the list + * of UWP packages, parses their manifests and invokes + * a user-provided callback with the needed application + * info. + */ + +#include "config.h" + +#define COBJMACROS +#define INITGUID +#include +#include +#include +#include +#include + +#include "gwin32api-storage.h" +#include "gwin32api-misc.h" +#include "gwin32api-iterator.h" +#include "gwin32api-package.h" + +#include + +#include + +#include "gwin32file-sync-stream.h" +#include "gwin32packageparser.h" + +#ifdef G_WINAPI_ONLY_APP +#define LoadedRoActivateInstance RoActivateInstance +#define LoadedWindowsCreateStringReference WindowsCreateStringReference +#define LoadedWindowsDeleteString WindowsDeleteString +#define sax_WindowsGetStringRawBuffer WindowsGetStringRawBuffer +#define LoadedWindowsGetStringRawBuffer WindowsGetStringRawBuffer +#define sax_CreateXmlReader CreateXmlReader +#else +typedef HRESULT (WINAPI *RoActivateInstance_func)(HSTRING activatableClassId, IInspectable **instance); +typedef HRESULT (WINAPI *WindowsCreateStringReference_func)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string); +typedef HRESULT (WINAPI *WindowsDeleteString_func)(HSTRING string); +typedef PCWSTR (WINAPI *WindowsGetStringRawBuffer_func)(HSTRING string, UINT32 *length); +typedef HRESULT (STDAPICALLTYPE *CreateXmlReader_func)(REFIID riid, void **ppvObject, IMalloc *pMalloc); +#define sax_WindowsGetStringRawBuffer sax->WindowsGetStringRawBuffer +#define sax_CreateXmlReader sax->CreateXmlReader +#endif + +static gssize +g_utf16_len (const gunichar2 *str) +{ + gssize result; + + for (result = 0; str[0] != 0; str++, result++) + ; + + return result; +} + +static gunichar2 * +g_wcsdup (const gunichar2 *str, gssize str_len) +{ + gssize str_size; + + g_return_val_if_fail (str != NULL, NULL); + + if (str_len == -1) + str_len = g_utf16_len (str); + + g_assert (str_len <= G_MAXSIZE / sizeof (gunichar2) - 1); + str_size = (str_len + 1) * sizeof (gunichar2); + + return g_memdup (str, str_size); +} + +static BOOL +WIN32_FROM_HRESULT (HRESULT hresult, + DWORD *win32_error_code) +{ + if ((hresult & 0xFFFF0000) == MAKE_HRESULT (SEVERITY_ERROR, FACILITY_WIN32, 0) || + hresult == S_OK) + { + *win32_error_code = HRESULT_CODE (hresult); + return TRUE; + } + + return FALSE; +} + +static GIOErrorEnum +gio_error_from_hresult (HRESULT hresult) +{ + DWORD error; + + if (WIN32_FROM_HRESULT (hresult, &error)) + return g_io_error_from_errno (error); + + return G_IO_ERROR_FAILED; +} + +static void +free_extgroup (GWin32PackageExtGroup *g) +{ + g_ptr_array_unref (g->verbs); + g_ptr_array_unref (g->extensions); + g_free (g); +} + +struct _xml_sax_state +{ +#ifndef G_WINAPI_ONLY_APP + WindowsGetStringRawBuffer_func WindowsGetStringRawBuffer; + CreateXmlReader_func CreateXmlReader; +#endif + + GWin32PackageParserCallback callback; + gpointer user_data; + + const wchar_t *manifest_filename; + gsize package_index; + const wchar_t *wcs_full_name; + const wchar_t *wcs_name; + HSTRING package_family; + + gboolean applist; + gboolean exit_early; + + UINT64 in_package; + UINT64 in_applications; + UINT64 in_application; + UINT64 in_extensions; + UINT64 in_extension_protocol; + UINT64 in_extension_fta; + UINT64 in_fta_group; + UINT64 in_sfp; + UINT64 in_filetype; + UINT64 in_sv; + GPtrArray *supported_extensions; + GPtrArray *supported_protocols; + GPtrArray *supported_verbs; + GPtrArray *supported_extgroups; + wchar_t *application_usermodelid; +}; + +static gboolean parse_manifest_file (struct _xml_sax_state *sax); +static gboolean xml_parser_iteration (struct _xml_sax_state *sax, + IXmlReader *xml_reader); +static gboolean xml_parser_get_current_state (struct _xml_sax_state *sax, + IXmlReader *xml_reader, + const wchar_t **local_name, + const wchar_t **prefix, + const wchar_t **value); + +gboolean +g_win32_package_parser_enum_packages (GWin32PackageParserCallback callback, + gpointer user_data, + GError **error) +{ + gboolean result = TRUE; + HRESULT hr; + HSTRING packagemanager_name = NULL; + HSTRING_HEADER packageanager_name_header; + const wchar_t *packman_id = L"Windows.Management.Deployment.PackageManager"; + IInspectable *ii_pm = NULL; + IPackageManager *pm = NULL; + IIterable *packages_iterable = NULL; + IIterator *packages_iterator = NULL; + CHAR has_current; + gsize package_index; + const wchar_t *bslash_appmanifest = L"\\AppxManifest.xml"; + struct _xml_sax_state sax_stack; + struct _xml_sax_state *sax = &sax_stack; + +#ifndef G_WINAPI_ONLY_APP + HMODULE xmllite = NULL; + HMODULE combase = NULL; + HMODULE winrt = NULL; + RoActivateInstance_func LoadedRoActivateInstance; + WindowsCreateStringReference_func LoadedWindowsCreateStringReference; + WindowsDeleteString_func LoadedWindowsDeleteString; + WindowsGetStringRawBuffer_func LoadedWindowsGetStringRawBuffer; + CreateXmlReader_func LoadedCreateXmlReader; + + winrt = LoadLibraryW (L"api-ms-win-core-winrt-l1-1-0.dll"); + if (winrt == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "Failed to load api-ms-win-core-winrt-l1-1-0.dll"); + goto cleanup; + } + + combase = LoadLibraryW (L"combase.dll"); + if (combase == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "Failed to load combase.dll"); + goto cleanup; + } + + xmllite = LoadLibraryW (L"xmllite.dll"); + if (xmllite == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "Failed to load xmllite.dll"); + goto cleanup; + } + + LoadedCreateXmlReader = (CreateXmlReader_func) GetProcAddress (xmllite, "CreateXmlReader"); + if (LoadedCreateXmlReader == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "CreateXmlReader entry point is not found in xmllite.dll"); + goto cleanup; + } + + LoadedRoActivateInstance = (RoActivateInstance_func) GetProcAddress (winrt, "RoActivateInstance"); + if (LoadedRoActivateInstance == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "RoActivateInstance entry point is not found in api-ms-win-core-winrt-l1-1-0.dll"); + goto cleanup; + } + + LoadedWindowsCreateStringReference = (WindowsCreateStringReference_func) GetProcAddress (combase, "WindowsCreateStringReference"); + if (LoadedWindowsCreateStringReference == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "WindowsCreateStringReference entry point is not found in combase.dll"); + goto cleanup; + } + + LoadedWindowsDeleteString = (WindowsDeleteString_func) GetProcAddress (combase, "WindowsDeleteString"); + if (LoadedWindowsDeleteString == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "WindowsDeleteString entry point is not found in combase.dll"); + goto cleanup; + } + + LoadedWindowsGetStringRawBuffer = (WindowsGetStringRawBuffer_func) GetProcAddress (combase, "WindowsGetStringRawBuffer"); + if (LoadedWindowsGetStringRawBuffer == NULL) + { + result = FALSE; + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()), + "WindowsGetStringRawBuffer entry point is not found in combase.dll"); + goto cleanup; + } +#endif + + /* This essentially locks current GLib thread into apartment COM model. */ + hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); + /* Can return S_FALSE if COM is already initialized, + * which is not an error, and we still need to uninitialize after that. + */ + if (hr != S_OK && hr != S_FALSE) + { + result = FALSE; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, + "CoInitializeEx(COINIT_APARTMENTTHREADED) failed with code 0x%lx", hr); + goto cleanup; + } + +#define canned_com_error_handler(function_name_literal, where_to_go) \ + do \ + { \ + if (FAILED (hr)) \ + { \ + result = FALSE; \ + g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \ + function_name_literal " failed with code 0x%lx", hr); \ + goto where_to_go; \ + } \ + } while (0) + + hr = LoadedWindowsCreateStringReference (packman_id, wcslen (packman_id), &packageanager_name_header, &packagemanager_name); + canned_com_error_handler ("WindowsCreateStringReference()", cleanup); + + hr = LoadedRoActivateInstance (packagemanager_name, &ii_pm); + canned_com_error_handler ("RoActivateInstance()", cleanup); + + hr = IInspectable_QueryInterface (ii_pm, &IID_IPackageManager, (void**) &pm); + canned_com_error_handler ("IInspectable_QueryInterface()", cleanup); + + hr = IPackageManager_FindPackagesByUserSecurityId (pm, 0, &packages_iterable); + canned_com_error_handler ("IPackageManager_FindPackagesByUserSecurityId()", cleanup); + + hr = IIterable_First (packages_iterable, &packages_iterator); + canned_com_error_handler ("IIterable_First()", cleanup); + + hr = IIterator_get_HasCurrent (packages_iterator, &has_current); + canned_com_error_handler ("IIterator_get_HasCurrent()", cleanup); + + for (package_index = 0; SUCCEEDED (hr) && has_current; package_index++) + { + IUnknown *item = NULL; + IPackage *ipackage = NULL; + IPackageId *ipackageid = NULL; + IUnknown *package_install_location = NULL; + IStorageItem *storage_item = NULL; + HSTRING path = NULL; + HSTRING name = NULL; + HSTRING full_name = NULL; + HSTRING package_family = NULL; + size_t manifest_filename_size; + const wchar_t *wcs_path; + const wchar_t *wcs_full_name; + const wchar_t *wcs_name; + wchar_t *manifest_filename = NULL; + +#define canned_com_error_handler_pkg(function_name_literal, where_to_go) \ + do \ + { \ + if (FAILED (hr)) \ + { \ + result = FALSE; \ + g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \ + function_name_literal " for package #%zu failed with code 0x%lx", package_index, hr); \ + goto where_to_go; \ + } \ + } while (0) + + hr = IIterator_get_Current (packages_iterator, &item); + canned_com_error_handler_pkg ("IIterator_get_Current()", package_cleanup); + + hr = IUnknown_QueryInterface (item, &IID_IPackage, (void **) &ipackage); + canned_com_error_handler_pkg ("IUnknown_QueryInterface(IID_IPackage)", package_cleanup); + + hr = IPackage_get_Id (ipackage, &ipackageid); + canned_com_error_handler_pkg ("IPackage_get_Id()", package_cleanup); + + hr = IPackageId_get_FullName (ipackageid, &full_name); + canned_com_error_handler_pkg ("IPackageId_get_FullName()", package_cleanup); + + hr = IPackageId_get_Name (ipackageid, &name); + canned_com_error_handler_pkg ("IPackageId_get_Name()", package_cleanup); + + wcs_full_name = LoadedWindowsGetStringRawBuffer (full_name, NULL); + wcs_name = LoadedWindowsGetStringRawBuffer (name, NULL); + +#define canned_com_error_handler_pkg_named(function_name_literal, where_to_go) \ + do \ + { \ + if (FAILED (hr)) \ + { \ + result = FALSE; \ + g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \ + function_name_literal " for package #%zu (`%S') failed with code 0x%lx", package_index, wcs_full_name, hr); \ + goto where_to_go; \ + } \ + } while (0) + + hr = IPackage_get_InstalledLocation (ipackage, &package_install_location); + canned_com_error_handler_pkg_named ("IPackage_get_InstalledLocation()", package_cleanup); + + hr = IUnknown_QueryInterface (package_install_location, &IID_IStorageItem, (void **) &storage_item); + canned_com_error_handler_pkg_named ("IUnknown_QueryInterface(IID_IStorageItem)", package_cleanup); + + hr = IPackageId_get_FamilyName (ipackageid, &package_family); + canned_com_error_handler_pkg_named ("IPackageId_get_FamilyName()", package_cleanup); + + hr = IStorageItem_get_Path (storage_item, &path); + canned_com_error_handler_pkg_named ("IStorageItem_get_Path()", package_cleanup); + + wcs_path = LoadedWindowsGetStringRawBuffer (path, NULL); + manifest_filename_size = wcslen (wcs_path) + wcslen (bslash_appmanifest); + manifest_filename = g_new (wchar_t, manifest_filename_size + 1); + memcpy (manifest_filename, wcs_path, manifest_filename_size * sizeof (wchar_t)); + memcpy (&manifest_filename[wcslen (wcs_path)], bslash_appmanifest, (wcslen (bslash_appmanifest) + 1) * sizeof (wchar_t)); + + memset (sax, 0, sizeof (*sax)); + sax->callback = callback; + sax->user_data = user_data; + sax->manifest_filename = manifest_filename; + sax->package_index = package_index; + sax->wcs_full_name = wcs_full_name; + sax->wcs_name = wcs_name; + sax->package_family = package_family; + sax->applist = TRUE; + sax->exit_early = FALSE; +#ifndef G_WINAPI_ONLY_APP + sax->CreateXmlReader = LoadedCreateXmlReader; + sax->WindowsGetStringRawBuffer = LoadedWindowsGetStringRawBuffer; +#endif + /* Result isn't checked. If we fail to parse the manifest, + * just try the next package, no need to bail out. + */ + parse_manifest_file (sax); + + hr = IIterator_MoveNext (packages_iterator, &has_current); + canned_com_error_handler_pkg_named ("IIterator_MoveNext()", package_cleanup); + +#undef canned_com_error_handler_pkg_named +#undef canned_com_error_handler_pkg +#undef canned_com_error_handler + + package_cleanup: + g_clear_pointer (&manifest_filename, g_free); + + if (path) + LoadedWindowsDeleteString (path); + if (storage_item) + (void) IStorageItem_Release (storage_item); + if (package_install_location) + (void) IUnknown_Release (package_install_location); + if (ipackage) + (void) IPackage_Release (ipackage); + if (item) + (void) IUnknown_Release (item); + + if (package_family) + LoadedWindowsDeleteString (package_family); + if (name) + LoadedWindowsDeleteString (name); + if (full_name) + LoadedWindowsDeleteString (full_name); + + if (ipackageid) + (void) IPackageId_Release (ipackageid); + if (sax->exit_early) + break; + } + + cleanup: + if (packages_iterator) + (void) IIterator_Release (packages_iterator); + if (packages_iterable) + (void) IIterable_Release (packages_iterable); + if (pm) + (void) IPackageManager_Release (pm); + if (ii_pm) + (void) IInspectable_Release (ii_pm); + + CoUninitialize (); + +#ifndef G_WINAPI_ONLY_APP + if (xmllite) + (void) FreeLibrary (xmllite); + if (combase) + (void) FreeLibrary (combase); + if (winrt) + (void) FreeLibrary (winrt); +#endif + + return result; +} + +static gboolean +parse_manifest_file (struct _xml_sax_state *sax) +{ + HRESULT hr; + HANDLE file_handle = INVALID_HANDLE_VALUE; + IStream *file_stream = NULL; + gboolean result; + IXmlReader *xml_reader; + + file_handle = CreateFileW (sax->manifest_filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file_handle == INVALID_HANDLE_VALUE) + { + g_warning ("Failed to open application manifest `%S' for package #%zu (`%S'): error code 0x%lx", + sax->manifest_filename, sax->package_index, sax->wcs_full_name, GetLastError ()); + return FALSE; + } + + file_stream = g_win32_file_sync_stream_new (file_handle, TRUE, STGM_READ | STGM_SHARE_DENY_WRITE, &hr); + if (file_stream == NULL) + { + g_warning ("Failed to create an IStream for application manifest `%S' for package #%zu (`%S'): HRESULT 0x%lx", + sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr); + CloseHandle (file_handle); + return FALSE; + } + + /* file_stream owns it now */ + file_handle = NULL; + + hr = sax_CreateXmlReader (&IID_IXmlReader, (void **) &xml_reader, NULL); + /* Slightly incorrect - xml reader is not created for any particular file, + * in theory we could re-use the same xml reader instance for all files. + */ + if (FAILED (hr)) + { + g_warning ("CreateXmlReader() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr); + (void) IStream_Release (file_stream); + return FALSE; + } + + hr = IXmlReader_SetInput (xml_reader, (IUnknown *) file_stream); + if (FAILED (hr)) + { + g_warning ("IXmlReader_SetInput() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr); + (void) IXmlReader_Release (xml_reader); + (void) IStream_Release (file_stream); + return FALSE; + } + + sax->supported_extensions = g_ptr_array_new_full (0, (GDestroyNotify) g_free); + sax->supported_protocols = g_ptr_array_new_full (0, (GDestroyNotify) g_free); + sax->supported_verbs = g_ptr_array_new_full (0, (GDestroyNotify) g_free); + sax->supported_extgroups = g_ptr_array_new_full (0, (GDestroyNotify) free_extgroup); + + result = TRUE; + + while (!sax->exit_early && result && !IXmlReader_IsEOF (xml_reader)) + result = xml_parser_iteration (sax, xml_reader); + + g_clear_pointer (&sax->application_usermodelid, g_free); + g_clear_pointer (&sax->supported_extensions, g_ptr_array_unref); + g_clear_pointer (&sax->supported_verbs, g_ptr_array_unref); + g_clear_pointer (&sax->supported_extgroups, g_ptr_array_unref); + g_clear_pointer (&sax->supported_protocols, g_ptr_array_unref); + + (void) IXmlReader_Release (xml_reader); + (void) IStream_Release (file_stream); + + return result; +} + +static gboolean +xml_parser_get_current_state (struct _xml_sax_state *sax, + IXmlReader *xml_reader, + const wchar_t **local_name, + const wchar_t **prefix, + const wchar_t **value) +{ + HRESULT hr; + UINT xml_line_number; + UINT xml_line_position; + + hr = IXmlReader_GetLineNumber (xml_reader, &xml_line_number); + if (FAILED (hr)) + { + g_warning ("IXmlReader_GetLineNumber() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr); + return FALSE; + } + + hr = IXmlReader_GetLinePosition (xml_reader, &xml_line_position); + if (FAILED (hr)) + { + g_warning ("IXmlReader_GetLinePosition() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr); + return FALSE; + } + + hr = IXmlReader_GetLocalName (xml_reader, local_name, NULL); + if (FAILED (hr)) + { + g_warning ("IXmlReader_GetLocalName() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr); + return FALSE; + } + + hr = IXmlReader_GetPrefix (xml_reader, prefix, NULL); + if (FAILED (hr)) + { + g_warning ("IXmlReader_GetPrefix() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr); + return FALSE; + } + + hr = IXmlReader_GetValue (xml_reader, value, NULL); + if (FAILED (hr)) + { + g_warning ("IXmlReader_GetValue() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr); + return FALSE; + } + + return TRUE; +} + +static gboolean +xml_parser_iteration (struct _xml_sax_state *sax, + IXmlReader *xml_reader) +{ + HRESULT hr; + XmlNodeType xml_node_type; + const wchar_t *local_name; + const wchar_t *prefix; + const wchar_t *value; + BOOL is_visual_elements = FALSE; + BOOL is_extension = FALSE; + BOOL is_protocol = FALSE; + BOOL is_empty; + BOOL is_application = FALSE; + BOOL is_verb = FALSE; + + hr = IXmlReader_Read (xml_reader, &xml_node_type); + if (FAILED (hr)) + { + g_warning ("IXmlReader_Read() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx", + sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr); + return FALSE; + } + + if (!xml_parser_get_current_state (sax, xml_reader, &local_name, &prefix, &value)) + return FALSE; + + switch (xml_node_type) + { + case XmlNodeType_Element: + is_empty = IXmlReader_IsEmptyElement (xml_reader); + g_assert (local_name != NULL); + + if (!is_empty && + _wcsicmp (local_name, L"Package") == 0 && + prefix[0] == 0) + sax->in_package += 1; + else if (!is_empty && + sax->in_package == 1 && + _wcsicmp (local_name, L"Applications") == 0 && + prefix[0] == 0) + sax->in_applications += 1; + else if (!is_empty && + sax->in_applications == 1 && + _wcsicmp (local_name, L"Application") == 0 && + prefix[0] == 0) + { + sax->in_application += 1; + is_application = TRUE; + sax->applist = TRUE; + g_clear_pointer (&sax->application_usermodelid, g_free); + } + else if (sax->in_application == 1 && + _wcsicmp (local_name, L"VisualElements") == 0 && + (_wcsicmp (prefix, L"uap") == 0 || _wcsicmp (prefix, L"uap3") == 0)) + is_visual_elements = TRUE; + else if (!is_empty && + sax->in_application == 1 && + _wcsicmp (local_name, L"Extensions") == 0 && + prefix[0] == 0) + sax->in_extensions += 1; + else if (!is_empty && + sax->in_application == 1 && + _wcsicmp (local_name, L"Extension") == 0 && + _wcsicmp (prefix, L"uap") == 0) + is_extension = TRUE; + else if (sax->in_extension_protocol == 1 && + _wcsicmp (local_name, L"Protocol") == 0 && + _wcsicmp (prefix, L"uap") == 0) + is_protocol = TRUE; + else if (!is_empty && + sax->in_extension_fta == 1 && + _wcsicmp (local_name, L"FileTypeAssociation") == 0 && + _wcsicmp (prefix, L"uap") == 0) + sax->in_fta_group += 1; + else if (!is_empty && + sax->in_fta_group == 1 && + _wcsicmp (local_name, L"SupportedFileTypes") == 0 && + _wcsicmp (prefix, L"uap") == 0) + sax->in_sfp += 1; + else if (!is_empty && + sax->in_fta_group == 1 && + _wcsicmp (local_name, L"SupportedVerbs") == 0 && + _wcsicmp (prefix, L"uap2") == 0) + sax->in_sv += 1; + else if (!is_empty && + sax->in_sfp == 1 && + _wcsicmp (local_name, L"FileType") == 0 && + _wcsicmp (prefix, L"uap") == 0) + sax->in_filetype += 1; + else if (!is_empty && + sax->in_sv == 1 && + _wcsicmp (local_name, L"Verb") == 0 && + _wcsicmp (prefix, L"uap3") == 0) + is_verb = TRUE; + + hr = IXmlReader_MoveToFirstAttribute (xml_reader); + while (hr == S_OK) + { + if (!xml_parser_get_current_state (sax, xml_reader, &local_name, &prefix, &value)) + return FALSE; + + g_assert (local_name != NULL); + g_assert (value != NULL); + g_assert (prefix != NULL); + + if (is_application && + sax->application_usermodelid == NULL && + _wcsicmp (local_name, L"Id") == 0) + { + size_t id_len = 0; + size_t value_len = wcslen (value); + const wchar_t *wcs_package_family; + size_t wcs_package_family_len; + + wcs_package_family = sax_WindowsGetStringRawBuffer (sax->package_family, NULL); + wcs_package_family_len = wcslen (wcs_package_family); + id_len += wcs_package_family_len + 1 + value_len; + sax->application_usermodelid = g_new (wchar_t, id_len + 1); + /* AppUserModelId = ! */ + memcpy (&sax->application_usermodelid[0], wcs_package_family, wcs_package_family_len * sizeof (wchar_t)); + memcpy (&sax->application_usermodelid[wcs_package_family_len], L"!", sizeof (wchar_t)); + memcpy (&sax->application_usermodelid[wcs_package_family_len + 1], value, (value_len + 1) * sizeof (wchar_t)); + } + else if (is_visual_elements && + _wcsicmp (local_name, L"AppListEntry") == 0 && + _wcsicmp (value, L"none") == 0) + sax->applist = FALSE; + else if (is_extension && + _wcsicmp (local_name, L"Category") == 0 && + _wcsicmp (value, L"windows.protocol") == 0) + sax->in_extension_protocol += 1; + else if (is_extension && + _wcsicmp (local_name, L"Category") == 0 && + _wcsicmp (value, L"windows.fileTypeAssociation") == 0) + sax->in_extension_fta += 1; + else if (is_protocol && + _wcsicmp (local_name, L"Name") == 0) + g_ptr_array_add (sax->supported_protocols, g_wcsdup (value, -1)); + else if (is_verb && + _wcsicmp (local_name, L"Id") == 0) + g_ptr_array_add (sax->supported_verbs, g_wcsdup (value, -1)); + + hr = IXmlReader_MoveToNextAttribute (xml_reader); + } + break; + case XmlNodeType_Text: + g_assert (value != NULL); + + if (sax->in_filetype && value[0] != 0) + g_ptr_array_add (sax->supported_extensions, g_wcsdup (value, -1)); + break; + case XmlNodeType_EndElement: + g_assert (local_name != NULL); + + if (_wcsicmp (local_name, L"Package") == 0 && + prefix[0] == 0) + sax->in_package -= 1; + else if (sax->in_package == 1 && + _wcsicmp (local_name, L"Applications") == 0 && + prefix[0] == 0) + sax->in_applications -= 1; + else if (sax->in_application == 1 && + _wcsicmp (local_name, L"Extensions") == 0 && + prefix[0] == 0) + sax->in_extensions -= 1; + else if (sax->in_extension_protocol == 1 && + _wcsicmp (local_name, L"Extension") == 0 && + _wcsicmp (prefix, L"uap") == 0) + sax->in_extension_protocol -= 1; + else if (sax->in_extension_fta == 1 && + _wcsicmp (local_name, L"Extension") == 0 && + _wcsicmp (prefix, L"uap") == 0) + sax->in_extension_fta -= 1; + else if (sax->in_fta_group == 1 && + _wcsicmp (local_name, L"SupportedFileTypes") == 0 && + _wcsicmp (prefix, L"uap") == 0) + sax->in_sfp -= 1; + else if (sax->in_sfp == 1 && + _wcsicmp (local_name, L"FileType") == 0 && + _wcsicmp (prefix, L"uap") == 0) + sax->in_filetype -= 1; + else if (sax->in_fta_group == 1 && + _wcsicmp (local_name, L"SupportedVerbs") == 0 && + _wcsicmp (prefix, L"uap2") == 0) + sax->in_sv -= 1; + else if (sax->in_applications == 1 && + _wcsicmp (local_name, L"Application") == 0 && + prefix[0] == 0) + { + if (sax->application_usermodelid != NULL) + sax->exit_early = !sax->callback (sax->user_data, sax->wcs_full_name, sax->wcs_name, + sax->application_usermodelid, sax->applist, + sax->supported_extgroups, sax->supported_protocols); + g_clear_pointer (&sax->supported_extgroups, g_ptr_array_unref); + g_clear_pointer (&sax->supported_protocols, g_ptr_array_unref); + sax->supported_protocols = g_ptr_array_new_full (0, (GDestroyNotify) g_free); + sax->supported_extgroups = g_ptr_array_new_full (0, (GDestroyNotify) free_extgroup); + sax->in_application -= 1; + } + else if (sax->in_extension_fta == 1 && + _wcsicmp (local_name, L"FileTypeAssociation") == 0 && + _wcsicmp (prefix, L"uap") == 0) + { + GWin32PackageExtGroup *new_group = g_new0 (GWin32PackageExtGroup, 1); + new_group->extensions = g_steal_pointer (&sax->supported_extensions); + sax->supported_extensions = g_ptr_array_new_full (0, (GDestroyNotify) g_free); + new_group->verbs = g_steal_pointer (&sax->supported_verbs); + sax->supported_verbs = g_ptr_array_new_full (0, (GDestroyNotify) g_free); + g_ptr_array_add (sax->supported_extgroups, new_group); + sax->in_fta_group -= 1; + } + break; + default: + break; + } + + return TRUE; +} \ No newline at end of file diff --git a/gio/gwin32packageparser.h b/gio/gwin32packageparser.h new file mode 100755 index 000000000..f55e30c3f --- /dev/null +++ b/gio/gwin32packageparser.h @@ -0,0 +1,48 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2020 Руслан Ижбулатов + * + * 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 + * version 2.1 of the License, or (at your option) any later version. + * + * 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 + * Public License along with this library; if not, see . + * + */ +#ifndef __G_WIN32_PACKAGE_PARSER_H__ +#define __G_WIN32_PACKAGE_PARSER_H__ + +#include + +#ifdef G_PLATFORM_WIN32 + +typedef struct _GWin32PackageExtGroup GWin32PackageExtGroup; + +struct _GWin32PackageExtGroup +{ + GPtrArray *verbs; + GPtrArray *extensions; +}; + +typedef gboolean (*GWin32PackageParserCallback)(gpointer user_data, + const gunichar2 *full_package_name, + const gunichar2 *package_name, + const gunichar2 *app_user_model_id, + gboolean show_in_applist, + GPtrArray *supported_extgroups, + GPtrArray *supported_protocols); + +gboolean g_win32_package_parser_enum_packages (GWin32PackageParserCallback callback, + gpointer user_data, + GError **error); + +#endif /* G_PLATFORM_WIN32 */ + +#endif /* __G_WIN32_PACKAGE_PARSER_H__ */ \ No newline at end of file diff --git a/gio/meson.build b/gio/meson.build index 465a7d2d6..8030f60a5 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -430,12 +430,16 @@ else cc.find_library('dnsapi'), iphlpapi_dep, winsock2] + platform_deps += uwp_gio_deps + win32_sources += files( 'gwin32registrykey.c', 'gwin32mount.c', 'gwin32volumemonitor.c', 'gwin32inputstream.c', 'gwin32outputstream.c', + 'gwin32file-sync-stream.c', + 'gwin32packageparser.c', 'gwin32networkmonitor.c', 'gwin32networkmonitor.h', 'gwin32notificationbackend.c', diff --git a/meson.build b/meson.build index 7e9f89be3..05dfff027 100644 --- a/meson.build +++ b/meson.build @@ -466,9 +466,12 @@ if host_system == 'windows' glib_conf.set('G_WINAPI_ONLY_APP', true) # We require Windows 10+ on WinRT glib_conf.set('_WIN32_WINNT', '0x0A00') + uwp_gio_deps = [cc.find_library('shcore'), + cc.find_library('runtimeobject')] else # We require Windows 7+ on Win32 glib_conf.set('_WIN32_WINNT', '0x0601') + uwp_gio_deps = [] endif endif