From 48f3173f2d4201a723645eac07504880b0e0f1ea Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:16:19 +0900 Subject: [PATCH 1/5] Init --- .../Content/Background/BaseSetAsAction.cs | 18 +++++ .../SetAsLockscreenBackgroundAction.cs | 17 +++- .../SetAsSlideshowBackgroundAction.cs | 12 ++- .../SetAsWallpaperBackgroundAction.cs | 19 ++++- .../Contracts/IWindowsWallpaperService.cs | 29 +++++++ .../Helpers/Application/AppLifecycleHelper.cs | 1 + src/Files.App/NativeMethods.txt | 4 + .../Windows/WindowsWallpaperService.cs | 77 ++++++++++++++++++ .../Utils/Global/WallpaperHelpers.cs | 81 ------------------- 9 files changed, 170 insertions(+), 88 deletions(-) create mode 100644 src/Files.App/Data/Contracts/IWindowsWallpaperService.cs create mode 100644 src/Files.App/Services/Windows/WindowsWallpaperService.cs delete mode 100644 src/Files.App/Utils/Global/WallpaperHelpers.cs diff --git a/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs b/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs index d4239457c320..67065384267a 100644 --- a/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs +++ b/src/Files.App/Actions/Content/Background/BaseSetAsAction.cs @@ -1,6 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; + namespace Files.App.Actions { internal abstract class BaseSetAsAction : ObservableObject, IAction @@ -28,6 +31,21 @@ public BaseSetAsAction() public abstract Task ExecuteAsync(object? parameter = null); + protected async void ShowErrorDialog(string message) + { + var errorDialog = new ContentDialog() + { + Title = "FailedToSetBackground".GetLocalizedResource(), + Content = message, + PrimaryButtonText = "OK".GetLocalizedResource(), + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + await errorDialog.TryShowAsync(); + } + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) diff --git a/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs index fbbff1712b68..e98402a46b95 100644 --- a/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsLockscreenBackgroundAction.cs @@ -5,6 +5,8 @@ namespace Files.App.Actions { internal sealed class SetAsLockscreenBackgroundAction : BaseSetAsAction { + private readonly IWindowsWallpaperService WindowsWallpaperService = Ioc.Default.GetRequiredService(); + public override string Label => "SetAsLockscreen".GetLocalizedResource(); @@ -20,10 +22,19 @@ public override RichGlyph Glyph public override Task ExecuteAsync(object? parameter = null) { - if (context.SelectedItem is not null) - return WallpaperHelpers.SetAsBackgroundAsync(WallpaperType.LockScreen, context.SelectedItem.ItemPath); + if (context.SelectedItem is null) + return Task.CompletedTask; + + try + { + return WindowsWallpaperService.SetLockScreenWallpaper(context.SelectedItem.ItemPath); + } + catch (Exception ex) + { + ShowErrorDialog(ex.Message); - return Task.CompletedTask; + return Task.CompletedTask; + } } } } diff --git a/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs index c66e7d8cbda4..d494eeb9b964 100644 --- a/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsSlideshowBackgroundAction.cs @@ -5,6 +5,8 @@ namespace Files.App.Actions { internal sealed class SetAsSlideshowBackgroundAction : BaseSetAsAction { + private readonly IWindowsWallpaperService WindowsWallpaperService = Ioc.Default.GetRequiredService(); + public override string Label => "SetAsSlideshow".GetLocalizedResource(); @@ -21,7 +23,15 @@ public override RichGlyph Glyph public override Task ExecuteAsync(object? parameter = null) { var paths = context.SelectedItems.Select(item => item.ItemPath).ToArray(); - WallpaperHelpers.SetSlideshow(paths); + + try + { + WindowsWallpaperService.SetDesktopSlideshow(paths); + } + catch (Exception ex) + { + ShowErrorDialog(ex.Message); + } return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs index a87d7bc4cec9..2b666c1e2c96 100644 --- a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs @@ -5,6 +5,8 @@ namespace Files.App.Actions { internal sealed class SetAsWallpaperBackgroundAction : BaseSetAsAction { + private readonly IWindowsWallpaperService WindowsWallpaperService = Ioc.Default.GetRequiredService(); + public override string Label => "SetAsBackground".GetLocalizedResource(); @@ -20,10 +22,21 @@ public override RichGlyph Glyph public override Task ExecuteAsync(object? parameter = null) { - if (context.SelectedItem is not null) - return WallpaperHelpers.SetAsBackgroundAsync(WallpaperType.Desktop, context.SelectedItem.ItemPath); + if (context.SelectedItem is null) + return Task.CompletedTask; + + try + { + WindowsWallpaperService.SetDesktopWallpaper(context.SelectedItem.ItemPath); + + return Task.CompletedTask; + } + catch (Exception ex) + { + ShowErrorDialog(ex.Message); - return Task.CompletedTask; + return Task.CompletedTask; + } } } } diff --git a/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs b/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs new file mode 100644 index 000000000000..ab1263bbe527 --- /dev/null +++ b/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Data.Contracts +{ + /// + /// Provides service for manipulating shell wallpapers on Windows. + /// + public interface IWindowsWallpaperService + { + /// + /// Sets desktop wallpaper using the specified image path. + /// + /// The image path to use to set as wallpaper. + void SetDesktopWallpaper(string szPath); + + /// + /// Sets desktop slideshow using the specified image paths. + /// + /// The image paths to use to set as slideshow. + void SetDesktopSlideshow(string[] aszPaths); + + /// + /// Gets lock screen wallpaper using the specified image path. + /// + /// The image path to use to set as lock screen wallpaper. + Task SetLockScreenWallpaper(string szPath); + } +} diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs index 4cfced5b98df..d05d49735bd4 100644 --- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs +++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs @@ -165,6 +165,7 @@ public static IHost ConfigureHost() .AddSingleton() // Services .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/src/Files.App/NativeMethods.txt b/src/Files.App/NativeMethods.txt index 92b8c114269d..8979795fe821 100644 --- a/src/Files.App/NativeMethods.txt +++ b/src/Files.App/NativeMethods.txt @@ -89,3 +89,7 @@ LocalAlloc InitializeAcl AddAce LocalFree +IDesktopWallpaper +DesktopWallpaper +SHCreateShellItemArrayFromIDLists +ILCreateFromPath diff --git a/src/Files.App/Services/Windows/WindowsWallpaperService.cs b/src/Files.App/Services/Windows/WindowsWallpaperService.cs new file mode 100644 index 000000000000..5a05287327e7 --- /dev/null +++ b/src/Files.App/Services/Windows/WindowsWallpaperService.cs @@ -0,0 +1,77 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Windows.Storage; +using Windows.System.UserProfile; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.Shell.Common; + +namespace Files.App.Services +{ + /// + public sealed class WindowsWallpaperService : IWindowsWallpaperService + { + /// + public unsafe void SetDesktopWallpaper(string szPath) + { + PInvoke.CoCreateInstance( + typeof(DesktopWallpaper).GUID, + null, + CLSCTX.CLSCTX_INPROC_SERVER, + out IDesktopWallpaper desktopWallpaper); + + desktopWallpaper.GetMonitorDevicePathCount(out var dwMonitorCount); + + fixed (char* pszPath = szPath) + { + var pwszPath = new PWSTR(pszPath); + + for (uint dwIndex = 0; dwIndex < dwMonitorCount; dwIndex++) + { + desktopWallpaper.GetMonitorDevicePathAt(dwIndex, out var pMonitorID); + desktopWallpaper.SetWallpaper(pMonitorID, pwszPath); + } + } + } + + /// + public unsafe void SetDesktopSlideshow(string[] aszPaths) + { + PInvoke.CoCreateInstance( + typeof(DesktopWallpaper).GUID, + null, + CLSCTX.CLSCTX_INPROC_SERVER, + out IDesktopWallpaper desktopWallpaper); + + var dwCount = (uint)aszPaths.Length; + + fixed (ITEMIDLIST** idList = new ITEMIDLIST*[dwCount]) + { + for (uint dwIndex = 0u; dwIndex < dwCount; dwIndex++) + { + var id = PInvoke.ILCreateFromPath(aszPaths[dwIndex]); + idList[dwIndex] = id; + } + + // Get shell item array from images to use for slideshow + PInvoke.SHCreateShellItemArrayFromIDLists(dwCount, idList, out var shellItemArray); + + // Set slideshow + desktopWallpaper.SetSlideshow(shellItemArray); + } + + // Set wallpaper to fill desktop. + desktopWallpaper.SetPosition(DESKTOP_WALLPAPER_POSITION.DWPOS_FILL); + } + + /// + public async Task SetLockScreenWallpaper(string szPath) + { + IStorageFile sourceFile = await StorageFile.GetFileFromPathAsync(szPath); + await LockScreen.SetImageFileAsync(sourceFile); + } + } +} diff --git a/src/Files.App/Utils/Global/WallpaperHelpers.cs b/src/Files.App/Utils/Global/WallpaperHelpers.cs deleted file mode 100644 index 73054947983f..000000000000 --- a/src/Files.App/Utils/Global/WallpaperHelpers.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Xaml.Controls; -using Vanara.PInvoke; -using Windows.Foundation.Metadata; -using Windows.Storage; -using Windows.System.UserProfile; - -namespace Files.App.Utils -{ - public static class WallpaperHelpers - { - public static async Task SetAsBackgroundAsync(WallpaperType type, string filePath) - { - try - { - if (type == WallpaperType.Desktop) - { - // Set the desktop background - var wallpaper = (Shell32.IDesktopWallpaper)new Shell32.DesktopWallpaper(); - var monitorCount = wallpaper.GetMonitorDevicePathCount(); - - for (uint i = 0; i < monitorCount; i++) - { - wallpaper.GetMonitorDevicePathAt(i, out var monitorId); - wallpaper.SetWallpaper(monitorId, filePath); - } - } - else if (type == WallpaperType.LockScreen) - { - // Set the lockscreen background - IStorageFile sourceFile = await StorageFile.GetFileFromPathAsync(filePath); - await LockScreen.SetImageFileAsync(sourceFile); - } - } - catch (Exception ex) - { - ShowErrorPrompt(ex.Message); - } - } - - public static void SetSlideshow(string[] filePaths) - { - if (filePaths is null || !filePaths.Any()) - return; - - try - { - var idList = filePaths.Select(Shell32.IntILCreateFromPath).ToArray(); - Shell32.SHCreateShellItemArrayFromIDLists((uint)idList.Length, [.. idList], out var shellItemArray); - - // Set SlideShow - var wallpaper = (Shell32.IDesktopWallpaper)new Shell32.DesktopWallpaper(); - wallpaper.SetSlideshow(shellItemArray); - - // Set wallpaper to fill desktop. - wallpaper.SetPosition(Shell32.DESKTOP_WALLPAPER_POSITION.DWPOS_FILL); - } - catch (Exception ex) - { - ShowErrorPrompt(ex.Message); - } - } - - private static async void ShowErrorPrompt(string exception) - { - var errorDialog = new ContentDialog() - { - Title = "FailedToSetBackground".GetLocalizedResource(), - Content = exception, - PrimaryButtonText = "OK".GetLocalizedResource(), - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - errorDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - await errorDialog.TryShowAsync(); - } - } -} From efbf042d34b5b3441cc51abc091c1f84e09eaebc Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:31:25 +0900 Subject: [PATCH 2/5] fix --- src/Files.App/NativeMethods.txt | 1 + .../Services/Windows/WindowsWallpaperService.cs | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Files.App/NativeMethods.txt b/src/Files.App/NativeMethods.txt index 8979795fe821..38676b61de48 100644 --- a/src/Files.App/NativeMethods.txt +++ b/src/Files.App/NativeMethods.txt @@ -93,3 +93,4 @@ IDesktopWallpaper DesktopWallpaper SHCreateShellItemArrayFromIDLists ILCreateFromPath +CLSIDFromString diff --git a/src/Files.App/Services/Windows/WindowsWallpaperService.cs b/src/Files.App/Services/Windows/WindowsWallpaperService.cs index 5a05287327e7..fc9ebfadd28b 100644 --- a/src/Files.App/Services/Windows/WindowsWallpaperService.cs +++ b/src/Files.App/Services/Windows/WindowsWallpaperService.cs @@ -18,10 +18,11 @@ public sealed class WindowsWallpaperService : IWindowsWallpaperService public unsafe void SetDesktopWallpaper(string szPath) { PInvoke.CoCreateInstance( - typeof(DesktopWallpaper).GUID, + new Guid("{C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD}"), null, - CLSCTX.CLSCTX_INPROC_SERVER, - out IDesktopWallpaper desktopWallpaper); + CLSCTX.CLSCTX_LOCAL_SERVER, + out IDesktopWallpaper desktopWallpaper) + .ThrowOnFailure(); desktopWallpaper.GetMonitorDevicePathCount(out var dwMonitorCount); @@ -41,10 +42,11 @@ public unsafe void SetDesktopWallpaper(string szPath) public unsafe void SetDesktopSlideshow(string[] aszPaths) { PInvoke.CoCreateInstance( - typeof(DesktopWallpaper).GUID, + new Guid("{C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD}"), null, - CLSCTX.CLSCTX_INPROC_SERVER, - out IDesktopWallpaper desktopWallpaper); + CLSCTX.CLSCTX_LOCAL_SERVER, + out IDesktopWallpaper desktopWallpaper) + .ThrowOnFailure(); var dwCount = (uint)aszPaths.Length; From 406a43e270e41a9d2ee2825926ce3cb82da26363 Mon Sep 17 00:00:00 2001 From: Yair <39923744+yaira2@users.noreply.github.com> Date: Sun, 21 Jul 2024 11:34:02 -0400 Subject: [PATCH 3/5] Update src/Files.App/Data/Contracts/IWindowsWallpaperService.cs --- src/Files.App/Data/Contracts/IWindowsWallpaperService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs b/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs index ab1263bbe527..64d2898827e4 100644 --- a/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs +++ b/src/Files.App/Data/Contracts/IWindowsWallpaperService.cs @@ -11,7 +11,7 @@ public interface IWindowsWallpaperService /// /// Sets desktop wallpaper using the specified image path. /// - /// The image path to use to set as wallpaper. + /// The image path to assign as the desktop wallpaper. void SetDesktopWallpaper(string szPath); /// From 71c4191a80b8c31282286c52504f9873019983cc Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:37:53 +0900 Subject: [PATCH 4/5] Update src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs Co-authored-by: Yair <39923744+yaira2@users.noreply.github.com> --- .../Content/Background/SetAsWallpaperBackgroundAction.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs index 2b666c1e2c96..5e029d8d62d6 100644 --- a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs @@ -28,12 +28,11 @@ public override Task ExecuteAsync(object? parameter = null) try { WindowsWallpaperService.SetDesktopWallpaper(context.SelectedItem.ItemPath); - - return Task.CompletedTask; } catch (Exception ex) { ShowErrorDialog(ex.Message); + } return Task.CompletedTask; } From 8627d24657aa868054ff2d1a498d9e7e6f799526 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:38:10 +0900 Subject: [PATCH 5/5] Update src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs Co-authored-by: Yair <39923744+yaira2@users.noreply.github.com> --- .../Content/Background/SetAsWallpaperBackgroundAction.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs index 5e029d8d62d6..d0a8a5a5cb3e 100644 --- a/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs +++ b/src/Files.App/Actions/Content/Background/SetAsWallpaperBackgroundAction.cs @@ -34,8 +34,7 @@ public override Task ExecuteAsync(object? parameter = null) ShowErrorDialog(ex.Message); } - return Task.CompletedTask; - } + return Task.CompletedTask; } } }