mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 16:36:40 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a27c013196 | ||
|
|
e9a7b5d08e | ||
|
|
2b0896dded | ||
|
|
d246bdd32f | ||
|
|
7116d69002 | ||
|
|
d8f0ebe105 | ||
|
|
2946b08823 | ||
|
|
7bcbda39ef | ||
|
|
b90363a041 |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Version>0.21.2</Version>
|
||||
<Version>0.23.2</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -171,7 +171,7 @@ std::optional<FancyZonesDataTypes::CustomZoneSetData> FancyZonesData::FindCustom
|
||||
return it != end(customZoneSetsMap) ? std::optional{ it->second } : std::nullopt;
|
||||
}
|
||||
|
||||
void FancyZonesData::AddDevice(const std::wstring& deviceId)
|
||||
bool FancyZonesData::AddDevice(const std::wstring& deviceId)
|
||||
{
|
||||
using namespace FancyZonesDataTypes;
|
||||
|
||||
@@ -192,7 +192,11 @@ void FancyZonesData::AddDevice(const std::wstring& deviceId)
|
||||
{
|
||||
deviceInfoMap[deviceId] = DeviceInfoData{ ZoneSetData{ NonLocalizable::NullStr, ZoneSetLayoutType::Blank } };
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstring& destination)
|
||||
@@ -209,7 +213,6 @@ void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstr
|
||||
return;
|
||||
}
|
||||
|
||||
// Clone information from source device if destination device is uninitialized (Blank).
|
||||
deviceInfoMap[destination] = deviceInfoMap[source];
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
return appZoneHistoryMap;
|
||||
}
|
||||
|
||||
void AddDevice(const std::wstring& deviceId);
|
||||
bool AddDevice(const std::wstring& deviceId);
|
||||
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
|
||||
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
|
||||
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);
|
||||
|
||||
@@ -86,14 +86,7 @@ namespace VirtualDesktopUtils
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// First fallback scenario is to try obtaining virtual desktop id through IVirtualDesktopManager
|
||||
// interface. Use foreground window (the window with which the user is currently working) to determine
|
||||
// current virtual desktop.
|
||||
else if (GetWindowDesktopId(GetForegroundWindow(), desktopId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Second fallback scenario is to get array of virtual desktops stored in registry, but not kept per
|
||||
// First fallback scenario is to get array of virtual desktops stored in registry, but not kept per
|
||||
// session. Note that we are taking first element from virtual desktop array, which is primary desktop.
|
||||
// If user has more than one virtual desktop, one of previous functions should return correct value,
|
||||
// as desktop switch occured in current session.
|
||||
|
||||
@@ -335,10 +335,10 @@ void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, c
|
||||
auto zoneWindow = std::move(m_zoneWindowMoveSize);
|
||||
ResetWindowTransparency();
|
||||
|
||||
bool hasNoVisibleOwnoer = FancyZonesUtils::HasNoVisibleOwner(window);
|
||||
bool hasNoVisibleOwner = FancyZonesUtils::HasNoVisibleOwner(window);
|
||||
bool isStandardWindow = FancyZonesUtils::IsStandardWindow(window);
|
||||
|
||||
if ((isStandardWindow == false && hasNoVisibleOwnoer == false &&
|
||||
if ((isStandardWindow == false && hasNoVisibleOwner == true &&
|
||||
m_moveSizeWindowInfo.standardWindow == true && m_moveSizeWindowInfo.noVisibleOwner == true) ||
|
||||
FancyZonesUtils::IsWindowMaximized(window))
|
||||
{
|
||||
|
||||
@@ -443,9 +443,9 @@ ZoneWindow::ClearSelectedZones() noexcept
|
||||
|
||||
void ZoneWindow::InitializeZoneSets(const std::wstring& parentUniqueId) noexcept
|
||||
{
|
||||
// If there is not defined zone layout for this work area, created default entry.
|
||||
FancyZonesDataInstance().AddDevice(m_uniqueId);
|
||||
if (!parentUniqueId.empty())
|
||||
bool deviceAdded = FancyZonesDataInstance().AddDevice(m_uniqueId);
|
||||
// If the device has been added, check if it should inherit the parent's layout
|
||||
if (deviceAdded && !parentUniqueId.empty())
|
||||
{
|
||||
FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pch.h"
|
||||
#include "KeyDelay.h"
|
||||
|
||||
// NOTE: The destructor should never be called on the DelayThread, i.e. from any of shortPress, longPress or longPressReleased, as it will re-enter the mutex. Even if the mutex is removed it will deadlock because of the join statement
|
||||
KeyDelay::~KeyDelay()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(_queueMutex);
|
||||
|
||||
@@ -613,25 +613,53 @@ bool Shortcut::CheckModifiersKeyboardState(InputInterface& ii) const
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper method for checking if a key is in a range for cleaner code
|
||||
bool in_range(DWORD key, DWORD a, DWORD b)
|
||||
{
|
||||
return (key >= a && key <= b);
|
||||
}
|
||||
|
||||
// Helper method for checking if a key is equal to a value for cleaner code
|
||||
bool equals(DWORD key, DWORD a)
|
||||
{
|
||||
return (key == a);
|
||||
}
|
||||
|
||||
// Function to check if the key code is to be ignored
|
||||
bool IgnoreKeyCode(DWORD key)
|
||||
{
|
||||
// Ignore mouse buttons. Keeping this could cause a remapping to fail if a mouse button is also pressed at the same time
|
||||
switch (key)
|
||||
{
|
||||
// Ignore mouse buttons. Keeping this could cause a remapping to fail if a mouse button is also pressed at the same time
|
||||
case VK_LBUTTON:
|
||||
case VK_RBUTTON:
|
||||
case VK_MBUTTON:
|
||||
case VK_XBUTTON1:
|
||||
case VK_XBUTTON2:
|
||||
// Ignore these key codes as they are reserved. Used by IME keyboards. More information at https://github.com/microsoft/PowerToys/issues/5225
|
||||
case 0xF0:
|
||||
case 0xF1:
|
||||
case 0xF2:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// As per docs: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||
// Undefined keys
|
||||
bool isUndefined = equals(key, 0x07) || in_range(key, 0x0E, 0x0F) || in_range(key, 0x3A, 0x40);
|
||||
|
||||
// Reserved keys
|
||||
bool isReserved = in_range(key, 0x0A, 0x0B) || equals(key, 0x5E) || in_range(key, 0xB8, 0xB9) || in_range(key, 0xC1, 0xD7) || equals(key, 0xE0) || equals(key, VK_NONAME);
|
||||
|
||||
// Unassigned keys
|
||||
bool isUnassigned = in_range(key, 0x88, 0x8F) || in_range(key, 0x97, 0x9F) || in_range(key, 0xD8, 0xDA) || equals(key, 0xE8);
|
||||
|
||||
//OEM Specific keys. Ignore these key codes as some of them are used by IME keyboards. More information at https://github.com/microsoft/PowerToys/issues/5225
|
||||
bool isOEMSpecific = in_range(key, 0x92, 0x96) || equals(key, 0xE1) || in_range(key, 0xE3, 0xE4) || equals(key, 0xE6) || in_range(key, 0xE9, 0xF5);
|
||||
|
||||
if (isUndefined || isReserved || isUnassigned || isOEMSpecific)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check if any keys are pressed down except those in the shortcut
|
||||
|
||||
@@ -355,6 +355,7 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
onAccept();
|
||||
});
|
||||
|
||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_RETURN,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
@@ -367,19 +368,24 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
onPressEnter();
|
||||
});
|
||||
},
|
||||
[onReleaseEnter](DWORD) {
|
||||
onReleaseEnter();
|
||||
[onReleaseEnter, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onReleaseEnter]() {
|
||||
onReleaseEnter();
|
||||
});
|
||||
});
|
||||
|
||||
TextBlock cancelButtonText;
|
||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||
|
||||
Button cancelButton;
|
||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
// Cancel button
|
||||
cancelButton.Click([detectShortcutBox, unregisterKeys, &keyboardManagerState, isSingleKeyWindow, parentWindow](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
auto onCancel = [&keyboardManagerState,
|
||||
detectShortcutBox,
|
||||
unregisterKeys,
|
||||
isSingleKeyWindow,
|
||||
parentWindow] {
|
||||
detectShortcutBox.Hide();
|
||||
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
@@ -393,31 +399,27 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
unregisterKeys();
|
||||
detectShortcutBox.Hide();
|
||||
};
|
||||
|
||||
Button cancelButton;
|
||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
// Cancel button
|
||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_ESCAPE,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
[&keyboardManagerState, detectShortcutBox, unregisterKeys, isSingleKeyWindow, parentWindow](DWORD) {
|
||||
[onCancel, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[detectShortcutBox] {
|
||||
detectShortcutBox.Hide();
|
||||
[onCancel] {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
unregisterKeys();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
|
||||
@@ -269,6 +269,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
|
||||
onAccept();
|
||||
});
|
||||
|
||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_RETURN,
|
||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||
@@ -281,41 +282,48 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
|
||||
onPressEnter();
|
||||
});
|
||||
},
|
||||
[onReleaseEnter](DWORD) {
|
||||
onReleaseEnter();
|
||||
[onReleaseEnter, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onReleaseEnter]() {
|
||||
onReleaseEnter();
|
||||
});
|
||||
});
|
||||
|
||||
TextBlock cancelButtonText;
|
||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||
|
||||
auto onCancel = [&keyboardManagerState,
|
||||
detectRemapKeyBox,
|
||||
unregisterKeys] {
|
||||
detectRemapKeyBox.Hide();
|
||||
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
Button cancelButton;
|
||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
// Cancel button
|
||||
cancelButton.Click([detectRemapKeyBox, unregisterKeys, &keyboardManagerState](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
unregisterKeys();
|
||||
detectRemapKeyBox.Hide();
|
||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_ESCAPE,
|
||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||
[&keyboardManagerState, detectRemapKeyBox, unregisterKeys](DWORD) {
|
||||
[onCancel, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[detectRemapKeyBox] {
|
||||
detectRemapKeyBox.Hide();
|
||||
[onCancel] {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
keyboardManagerState.ResetUIState();
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
unregisterKeys();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
|
||||
@@ -330,6 +330,7 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
return ExecutableName;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Any error in CreateWin32Program should not prevent other programs from loading.")]
|
||||
private static Win32Program CreateWin32Program(string path)
|
||||
{
|
||||
try
|
||||
@@ -353,78 +354,94 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
ProgramLogger.Exception($"|Permission denied when trying to load the program from {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.Exception($"|An unexpected error occurred in the calling method CreateWin32Program at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
}
|
||||
|
||||
// This function filters Internet Shortcut programs
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Any error in InternetShortcutProgram should not prevent other programs from loading.")]
|
||||
private static Win32Program InternetShortcutProgram(string path)
|
||||
{
|
||||
string[] lines = FileWrapper.ReadAllLines(path);
|
||||
string iconPath = string.Empty;
|
||||
string urlPath = string.Empty;
|
||||
bool validApp = false;
|
||||
|
||||
Regex internetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run)\/|^com\.epicgames\.launcher:\/\/apps\/");
|
||||
|
||||
const string urlPrefix = "URL=";
|
||||
const string iconFilePrefix = "IconFile=";
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (line.StartsWith(urlPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
urlPath = line.Substring(urlPrefix.Length);
|
||||
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(urlPath);
|
||||
}
|
||||
catch (UriFormatException e)
|
||||
{
|
||||
// To catch the exception if the uri cannot be parsed.
|
||||
// Link to watson crash: https://watsonportal.microsoft.com/Failure?FailureSearchText=5f871ea7-e886-911f-1b31-131f63f6655b
|
||||
ProgramLogger.Exception($"url could not be parsed", e, MethodBase.GetCurrentMethod().DeclaringType, urlPath);
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
|
||||
// To filter out only those steam shortcuts which have 'run' or 'rungameid' as the hostname
|
||||
if (internetShortcutURLPrefixes.Match(urlPath).Success)
|
||||
{
|
||||
validApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (line.StartsWith(iconFilePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
iconPath = line.Substring(iconFilePrefix.Length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!validApp)
|
||||
{
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var p = new Win32Program
|
||||
string[] lines = FileWrapper.ReadAllLines(path);
|
||||
string iconPath = string.Empty;
|
||||
string urlPath = string.Empty;
|
||||
bool validApp = false;
|
||||
|
||||
Regex internetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run)\/|^com\.epicgames\.launcher:\/\/apps\/");
|
||||
|
||||
const string urlPrefix = "URL=";
|
||||
const string iconFilePrefix = "IconFile=";
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(path),
|
||||
ExecutableName = Path.GetFileName(path),
|
||||
IcoPath = iconPath,
|
||||
FullPath = urlPath,
|
||||
UniqueIdentifier = path,
|
||||
ParentDirectory = Directory.GetParent(path).FullName,
|
||||
Valid = true,
|
||||
Enabled = true,
|
||||
AppType = ApplicationType.InternetShortcutApplication,
|
||||
};
|
||||
return p;
|
||||
if (line.StartsWith(urlPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
urlPath = line.Substring(urlPrefix.Length);
|
||||
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(urlPath);
|
||||
}
|
||||
catch (UriFormatException e)
|
||||
{
|
||||
// To catch the exception if the uri cannot be parsed.
|
||||
// Link to watson crash: https://watsonportal.microsoft.com/Failure?FailureSearchText=5f871ea7-e886-911f-1b31-131f63f6655b
|
||||
ProgramLogger.Exception($"url could not be parsed", e, MethodBase.GetCurrentMethod().DeclaringType, urlPath);
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
|
||||
// To filter out only those steam shortcuts which have 'run' or 'rungameid' as the hostname
|
||||
if (internetShortcutURLPrefixes.Match(urlPath).Success)
|
||||
{
|
||||
validApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (line.StartsWith(iconFilePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
iconPath = line.Substring(iconFilePrefix.Length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!validApp)
|
||||
{
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var p = new Win32Program
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(path),
|
||||
ExecutableName = Path.GetFileName(path),
|
||||
IcoPath = iconPath,
|
||||
FullPath = urlPath,
|
||||
UniqueIdentifier = path,
|
||||
ParentDirectory = Directory.GetParent(path).FullName,
|
||||
Valid = true,
|
||||
Enabled = true,
|
||||
AppType = ApplicationType.InternetShortcutApplication,
|
||||
};
|
||||
return p;
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.Exception($"|Permission denied when trying to load the program from {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.Exception($"|Permission denied when trying to load the program from {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
ProgramLogger.Exception($"|An unexpected error occurred in the calling method InternetShortcutProgram at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
@@ -433,9 +450,9 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Unsure of what exceptions are caught here while enabling static analysis")]
|
||||
private static Win32Program LnkProgram(string path)
|
||||
{
|
||||
var program = CreateWin32Program(path);
|
||||
try
|
||||
{
|
||||
var program = CreateWin32Program(path);
|
||||
const int MAX_PATH = 260;
|
||||
StringBuilder buffer = new StringBuilder(MAX_PATH);
|
||||
|
||||
@@ -472,13 +489,13 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
// Error caused likely due to trying to get the description of the program
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.Exception("An unexpected error occurred in the calling method LnkProgram", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
ProgramLogger.Exception($"|An unexpected error occurred in the calling method LnkProgram at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
|
||||
program.Valid = false;
|
||||
return program;
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Any error in ExeProgram should not prevent other programs from loading.")]
|
||||
private static Win32Program ExeProgram(string path)
|
||||
{
|
||||
try
|
||||
@@ -503,6 +520,12 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
ProgramLogger.Exception($"|Unable to locate exe file at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.Exception($"|An unexpected error occurred in the calling method ExeProgram at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
|
||||
|
||||
return new Win32Program() { Valid = false, Enabled = false };
|
||||
}
|
||||
}
|
||||
@@ -585,6 +608,7 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Minimise the effect of error on other programs")]
|
||||
private static IEnumerable<string> ProgramPaths(string directory, IList<string> suffixes, bool recursiveSearch = true)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
@@ -617,6 +641,10 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
ProgramLogger.Exception($"|Permission denied when trying to load programs from {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.Exception($"|An unexpected error occurred in the calling method ProgramPaths at {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -635,6 +663,10 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
{
|
||||
ProgramLogger.Exception($"|Permission denied when trying to load programs from {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.Exception($"|An unexpected error occurred in the calling method ProgramPaths at {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
|
||||
}
|
||||
}
|
||||
while (folderQueue.Any());
|
||||
|
||||
|
||||
@@ -194,14 +194,15 @@ namespace Wox.Core.Plugin
|
||||
{
|
||||
foreach (Result result in results)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(result.QueryTextDisplay))
|
||||
if (string.IsNullOrEmpty(result.QueryTextDisplay))
|
||||
{
|
||||
result.QueryTextDisplay = result.Title;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.ActionKeyword))
|
||||
{
|
||||
result.QueryTextDisplay = string.Format("{0} {1}", query.ActionKeyword, result.QueryTextDisplay);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.QueryTextDisplay = string.Format("{0} {1}", query.ActionKeyword, result.Title);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
@@ -15,13 +15,18 @@ namespace Wox.Test
|
||||
[TestFixture]
|
||||
public class PluginManagerTest
|
||||
{
|
||||
[Test]
|
||||
public void QueryForPlugin_SetsActionKeyword_WhenQueryTextDisplayIsSet()
|
||||
[TestCase(">", "dummyQueryText", "dummyTitle", "> dummyQueryText")]
|
||||
[TestCase(">", null, "dummyTitle", "> dummyTitle")]
|
||||
[TestCase(">", "", "dummyTitle", "> dummyTitle")]
|
||||
[TestCase("", "dummyQueryText", "dummyTitle", "dummyQueryText")]
|
||||
[TestCase("", null, "dummyTitle", "dummyTitle")]
|
||||
[TestCase("", "", "dummyTitle", "dummyTitle")]
|
||||
[TestCase(null, "dummyQueryText", "dummyTitle", "dummyQueryText")]
|
||||
[TestCase(null, null, "dummyTitle", "dummyTitle")]
|
||||
[TestCase(null, "", "dummyTitle", "dummyTitle")]
|
||||
public void QueryForPlugin_SetsActionKeyword_WhenQueryTextDisplayIsEmpty(string actionKeyword, string queryTextDisplay, string title, string expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var actionKeyword = ">";
|
||||
var title = "dummyTitle";
|
||||
var queryTextDisplay = "dummyQueryTextDisplay";
|
||||
var query = new Query
|
||||
{
|
||||
ActionKeyword = actionKeyword,
|
||||
@@ -51,46 +56,7 @@ namespace Wox.Test
|
||||
var queryOutput = PluginManager.QueryForPlugin(pluginPair, query);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(string.Format("{0} {1}", ">", queryTextDisplay), queryOutput[0].QueryTextDisplay);
|
||||
}
|
||||
|
||||
[TestCase("")]
|
||||
[TestCase(null)]
|
||||
public void QueryForPlugin_SetsActionKeyword_WhenQueryTextDisplayIsEmpty(string queryTextDisplay)
|
||||
{
|
||||
// Arrange
|
||||
var actionKeyword = ">";
|
||||
var title = "dummyTitle";
|
||||
var query = new Query
|
||||
{
|
||||
ActionKeyword = actionKeyword,
|
||||
};
|
||||
var metadata = new PluginMetadata
|
||||
{
|
||||
ID = "dummyName",
|
||||
IcoPath = "dummyIcoPath",
|
||||
ExecuteFileName = "dummyExecuteFileName",
|
||||
PluginDirectory = "dummyPluginDirectory",
|
||||
};
|
||||
var result = new Result()
|
||||
{
|
||||
QueryTextDisplay = queryTextDisplay,
|
||||
Title = title,
|
||||
};
|
||||
var results = new List<Result>() { result };
|
||||
var pluginMock = new Mock<IPlugin>();
|
||||
pluginMock.Setup(r => r.Query(query)).Returns(results);
|
||||
var pluginPair = new PluginPair
|
||||
{
|
||||
Plugin = pluginMock.Object,
|
||||
Metadata = metadata,
|
||||
};
|
||||
|
||||
// Act
|
||||
var queryOutput = PluginManager.QueryForPlugin(pluginPair, query);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(string.Format("{0} {1}", ">", title), queryOutput[0].QueryTextDisplay);
|
||||
Assert.AreEqual(expectedResult, queryOutput[0].QueryTextDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user