From d57773e8ef39615fac1ecff82c70efc3c75324e8 Mon Sep 17 00:00:00 2001 From: Andrey Nekrasov Date: Fri, 23 Jul 2021 16:59:22 +0300 Subject: [PATCH] [VCM] Fix issues on certain systems (#12481) - Don't depend on System.Windows.Forms in the Settings -> use callback instead - Fix overlay image setting saving - Use dynamic DLL loading in Interop - Load VCM only when mf.dll is available --- .github/actions/spell-check/expect.txt | 3 ++ installer/PowerToysSetup/Product.wxs | 2 +- src/common/interop/PowerToysInterop.vcxproj | 2 +- .../Video Conference.vcxproj | 4 +-- .../ImageLoading.cpp | 1 - .../VideoConferenceProxyFilter.vcxproj | 4 +-- .../DLLProviderHelpers.h | 33 +++++++++++++++++++ .../VideoConferenceShared/Logging.cpp | 1 + .../MediaFoundationAPIProvider.h | 14 ++++++++ .../VideoCaptureDeviceList.cpp | 12 +++++-- .../VideoConferenceShared.vcxproj | 4 ++- src/runner/main.cpp | 12 +++++-- ...osoft.PowerToys.Settings.UI.Library.csproj | 6 ---- .../ViewModels/VideoConferenceViewModel.cs | 24 +++++--------- .../ViewModels/ShellViewModel.cs | 16 ++++++++- .../Views/VideoConference.xaml.cs | 21 ++++++++++-- 16 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 src/modules/videoconference/VideoConferenceShared/DLLProviderHelpers.h create mode 100644 src/modules/videoconference/VideoConferenceShared/MediaFoundationAPIProvider.h diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index f3cc098738..1a29c7eb07 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -399,6 +399,7 @@ ddd ddee ddf Deact +DECLAR declspec decltype Dedup @@ -1434,6 +1435,7 @@ ntdll NTFS NTSTATUS nuget +null nullopt nullptr NUMLOCK @@ -2311,6 +2313,7 @@ WINDOWPOSCHANGED WINDOWPOSCHANGING Windowsapp WINDOWSBUILDNUMBER +Windowscodecs windowsdesktop windowssearch windowsx diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 4b2d143395..419fe6406e 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -791,7 +791,7 @@ - + diff --git a/src/common/interop/PowerToysInterop.vcxproj b/src/common/interop/PowerToysInterop.vcxproj index fb2d207fb9..a73ba06b16 100644 --- a/src/common/interop/PowerToysInterop.vcxproj +++ b/src/common/interop/PowerToysInterop.vcxproj @@ -55,7 +55,7 @@ stdcpp17 - Mf.lib;WindowsApp.lib;%(AdditionalDependencies) + WindowsApp.lib;%(AdditionalDependencies) diff --git a/src/modules/videoconference/VideoConferenceModule/Video Conference.vcxproj b/src/modules/videoconference/VideoConferenceModule/Video Conference.vcxproj index 425313bce1..a162209fd4 100644 --- a/src/modules/videoconference/VideoConferenceModule/Video Conference.vcxproj +++ b/src/modules/videoconference/VideoConferenceModule/Video Conference.vcxproj @@ -78,7 +78,7 @@ true true $(OutDir)$(TargetName)$(TargetExt) - mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;%(AdditionalDependencies) + shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;%(AdditionalDependencies) xcopy /y /I "$(ProjectDir)Icons\*" "$(OutDir)Icons" @@ -101,7 +101,7 @@ xcopy /y /I "$(ProjectDir)black.bmp*" "$(OutDir)" Windows true $(OutDir)$(TargetName)$(TargetExt) - mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;dxguid.lib;%(AdditionalDependencies) + shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;dxguid.lib;%(AdditionalDependencies) xcopy /y /I "$(ProjectDir)Icons\*" "$(OutDir)Icons" diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp b/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp index 656fd5ea7f..9bc9e8e456 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp +++ b/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj b/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj index 4bbb2c2282..57966d6d31 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj +++ b/src/modules/videoconference/VideoConferenceProxyFilter/VideoConferenceProxyFilter.vcxproj @@ -62,7 +62,7 @@ call "$(ProjectDir)build_vcm_x86.cmd" - + $(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\ @@ -87,7 +87,7 @@ MultiThreadedDebug - $(OutDir)VideoConferenceShared.lib;mfplat.lib;Mfsensorgroup.lib;OneCoreUAP.lib;Mf.lib;Shlwapi.lib;Strmiids.lib;%(AdditionalDependencies); + $(OutDir)VideoConferenceShared.lib;Windowscodecs.lib;Wtsapi32.lib;mfplat.lib;WindowsApp.lib;Mfsensorgroup.lib;Mf.lib;Shlwapi.lib;Strmiids.lib;%(AdditionalDependencies); module.def diff --git a/src/modules/videoconference/VideoConferenceShared/DLLProviderHelpers.h b/src/modules/videoconference/VideoConferenceShared/DLLProviderHelpers.h new file mode 100644 index 0000000000..c1c6efaef3 --- /dev/null +++ b/src/modules/videoconference/VideoConferenceShared/DLLProviderHelpers.h @@ -0,0 +1,33 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include +#include + +#define DECLARE_DLL_FUNCTION(NAME) \ + std::function NAME = (std::add_pointer_t)GetProcAddress(_library_handle, #NAME); + +#define DECLARE_DLL_PROVIDER_BEGIN(DLL_NAME) \ + class DLL_NAME##APIProvider final \ + { \ + HMODULE _library_handle; \ + DLL_NAME##APIProvider(HMODULE h) : _library_handle{ h } {} \ + \ + public: \ + ~DLL_NAME##APIProvider() { FreeLibrary(_library_handle); } \ + static std::optional create() \ + { \ + HMODULE h = LoadLibraryA(#DLL_NAME ".dll"); \ + std::optional result; \ + if (!h) \ + return result; \ + result.emplace(DLL_NAME##APIProvider{ h }); \ + return result; \ + } + +#define DECLAR_DLL_PROVIDER_END \ + } \ + ; diff --git a/src/modules/videoconference/VideoConferenceShared/Logging.cpp b/src/modules/videoconference/VideoConferenceShared/Logging.cpp index 382233f974..45f643fa12 100644 --- a/src/modules/videoconference/VideoConferenceShared/Logging.cpp +++ b/src/modules/videoconference/VideoConferenceShared/Logging.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #pragma warning(disable : 4127) diff --git a/src/modules/videoconference/VideoConferenceShared/MediaFoundationAPIProvider.h b/src/modules/videoconference/VideoConferenceShared/MediaFoundationAPIProvider.h new file mode 100644 index 0000000000..470f8b1f0f --- /dev/null +++ b/src/modules/videoconference/VideoConferenceShared/MediaFoundationAPIProvider.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include "DLLProviderHelpers.h" + +DECLARE_DLL_PROVIDER_BEGIN(mfplat) +DECLARE_DLL_FUNCTION(MFCreateAttributes) +DECLAR_DLL_PROVIDER_END + +DECLARE_DLL_PROVIDER_BEGIN(mf) +DECLARE_DLL_FUNCTION(MFEnumDeviceSources) +DECLAR_DLL_PROVIDER_END diff --git a/src/modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.cpp b/src/modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.cpp index 3245524aad..3ec325dbdd 100644 --- a/src/modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.cpp +++ b/src/modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.cpp @@ -1,5 +1,6 @@ #include "VideoCaptureDeviceList.h" #include "Logging.h" +#include "MediaFoundationAPIProvider.h" #include #include @@ -32,11 +33,17 @@ HRESULT VideoCaptureDeviceList::EnumerateDevices() HRESULT hr = S_OK; wil::com_ptr pAttributes; Clear(); + auto mfplatAPI = mfplatAPIProvider::create(); + auto mfAPI = mfAPIProvider::create(); + if (!mfplatAPI || !mfAPI) + { + return ERROR_FILE_NOT_FOUND; + } // Initialize an attribute store. We will use this to // specify the enumeration parameters. - hr = MFCreateAttributes(&pAttributes, 1); + hr = mfplatAPI->MFCreateAttributes(&pAttributes, 1); // Ask for source type = video capture devices if (SUCCEEDED(hr)) @@ -52,14 +59,13 @@ HRESULT VideoCaptureDeviceList::EnumerateDevices() // Enumerate devices. if (SUCCEEDED(hr)) { - hr = MFEnumDeviceSources(pAttributes.get(), &m_ppDevices, &m_numberDevices); + hr = mfAPI->MFEnumDeviceSources(pAttributes.get(), &m_ppDevices, &m_numberDevices); } else { LOG("VideoCaptureDeviceList::EnumerateDevices(): Couldn't SetGUID"); } - if (FAILED(hr)) { LOG("VideoCaptureDeviceList::EnumerateDevices(): MFEnumDeviceSources failed"); diff --git a/src/modules/videoconference/VideoConferenceShared/VideoConferenceShared.vcxproj b/src/modules/videoconference/VideoConferenceShared/VideoConferenceShared.vcxproj index 1b628c7e89..872edeadde 100644 --- a/src/modules/videoconference/VideoConferenceShared/VideoConferenceShared.vcxproj +++ b/src/modules/videoconference/VideoConferenceShared/VideoConferenceShared.vcxproj @@ -21,7 +21,7 @@ - mfplat.lib;Mfsensorgroup.lib;OneCoreUAP.lib;Mf.lib;Shlwapi.lib;Strmiids.lib;%(AdditionalDependencies); + mfplat.lib;Mfsensorgroup.lib;Mf.lib;Shlwapi.lib;Strmiids.lib;%(AdditionalDependencies); @@ -116,7 +116,9 @@ + + diff --git a/src/runner/main.cpp b/src/runner/main.cpp index a1e9a4c5fd..564b489751 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -141,10 +141,16 @@ int runner(bool isProcessElevated, bool openSettings, bool openOobe) L"modules/PowerRename/PowerRenameExt.dll", L"modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.dll", L"modules/ColorPicker/ColorPicker.dll", - L"modules/Awake/AwakeModuleInterface.dll", - // TODO(yuyoyuppe): uncomment when VCM should be enabled - //L"modules/VideoConference/VideoConferenceModule.dll" + L"modules/Awake/AwakeModuleInterface.dll" + }; + // TODO(yuyoyuppe): uncomment when VCM should be enabled + //const auto VCM_PATH = L"modules/VideoConference/VideoConferenceModule.dll"; + //if (const auto mf = LoadLibraryA("mf.dll")) + //{ + // FreeLibrary(mf); + // knownModules.emplace_back(VCM_PATH); + //} for (const auto& moduleSubdir : knownModules) { diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj index 2b4e5447b9..dd61ee54fc 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Microsoft.PowerToys.Settings.UI.Library.csproj @@ -59,10 +59,4 @@ - - - C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll - - - diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/VideoConferenceViewModel.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/VideoConferenceViewModel.cs index 03494cc2f8..115d6e40b9 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/VideoConferenceViewModel.cs +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/VideoConferenceViewModel.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Windows.Forms; +using System.Threading.Tasks; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; @@ -28,10 +28,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private Func SendConfigMSG { get; } + private Func> PickFileDialog { get; } + private string _settingsConfigFileFolder = string.Empty; - public VideoConferenceViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc, string configFileSubfolder = "") + public VideoConferenceViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc, Func> pickFileDialog, string configFileSubfolder = "") { + PickFileDialog = pickFileDialog; + if (settingsRepository == null) { throw new ArgumentNullException(nameof(settingsRepository)); @@ -161,21 +165,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels RaisePropertyChanged(nameof(CameraImageOverlayPath)); } - private void SelectOverlayImageAction() + private async void SelectOverlayImageAction() { try { - string pickedImage = null; - using (OpenFileDialog openFileDialog = new OpenFileDialog()) - { - openFileDialog.Filter = "Image Files (*.jpeg;*.jpg;*.png)|*.jpeg;*.jpg;*.png"; - openFileDialog.RestoreDirectory = true; - if (openFileDialog.ShowDialog() == DialogResult.OK) - { - pickedImage = openFileDialog.FileName; - } - } - + string pickedImage = await PickFileDialog().ConfigureAwait(true); if (pickedImage != null) { CameraImageOverlayPath = pickedImage; @@ -417,8 +411,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels OnPropertyChanged(propertyName); SndVideoConferenceSettings outsettings = new SndVideoConferenceSettings(Settings); SndModuleSettings ipcMessage = new SndModuleSettings(outsettings); - SendConfigMSG(ipcMessage.ToJsonString()); + _settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); } } diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs index 9ded11ab04..61e675d1f1 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/ViewModels/ShellViewModel.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Input; using Microsoft.PowerToys.Settings.UI.Helpers; @@ -37,11 +38,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels set { Set(ref isBackEnabled, value); } } + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern bool FreeLibrary(IntPtr hModule); + public bool IsVideoConferenceBuild { get { - return this != null && File.Exists("modules/VideoConference/VideoConferenceModule.dll"); + var mfHandle = LoadLibrary("mf.dll"); + bool mfAvailable = mfHandle != null; + if (mfAvailable) + { + FreeLibrary(mfHandle); + } + + return this != null && File.Exists("modules/VideoConference/VideoConferenceModule.dll") && mfAvailable; } } diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/VideoConference.xaml.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/VideoConference.xaml.cs index c78f3a305e..c5ea2df908 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/VideoConference.xaml.cs +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/VideoConference.xaml.cs @@ -2,9 +2,12 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Threading.Tasks; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.ViewModels; +using Windows.Storage; +using Windows.Storage.Pickers; using Windows.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views @@ -13,10 +16,24 @@ namespace Microsoft.PowerToys.Settings.UI.Views { private VideoConferenceViewModel ViewModel { get; set; } + private static async Task PickFileDialog() + { + FileOpenPicker openPicker = new FileOpenPicker(); + openPicker.ViewMode = PickerViewMode.Thumbnail; + openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; + openPicker.FileTypeFilter.Add(".jpg"); + openPicker.FileTypeFilter.Add(".jpeg"); + openPicker.FileTypeFilter.Add(".png"); + ((IInitializeWithWindow)(object)openPicker).Initialize(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle); + + StorageFile file = await openPicker.PickSingleFileAsync(); + return file?.Path; + } + public VideoConferencePage() { var settingsUtils = new SettingsUtils(); - ViewModel = new VideoConferenceViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); + ViewModel = new VideoConferenceViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, PickFileDialog); DataContext = ViewModel; InitializeComponent(); }