Handle scenario with only primary desktop and no desktop switch in current session. (#2339)

* Handle scenario with only primary desktop and no desktop swithc in current session.

* Add scoped lock when changing current desktop id. Address PR comments.

* Explain purpose of UpdatePrimaryDesktopData method.

* Fix typo in documentation.
This commit is contained in:
vldmr11080 2020-04-30 11:16:25 +02:00 committed by GitHub
parent 2db98715cc
commit 648f3abcbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 70 deletions

View File

@ -5,7 +5,6 @@
#include "FancyZones.h"
#include "lib/Settings.h"
#include "lib/ZoneWindow.h"
#include "lib/RegistryHelpers.h"
#include "lib/JsonHelpers.h"
#include "lib/ZoneSet.h"
#include "trace.h"
@ -642,15 +641,25 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
// then this value will be empty. This means loading the first virtual desktop's configuration can be
// funky the first time we load up at boot since the user will not have switched virtual desktops yet.
GUID currentVirtualDesktopId{};
if (SUCCEEDED(RegistryHelpers::GetCurrentVirtualDesktop(&currentVirtualDesktopId)))
if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(&currentVirtualDesktopId))
{
std::unique_lock writeLock(m_lock);
m_currentVirtualDesktopId = currentVirtualDesktopId;
}
else
{
// TODO: Use the previous "Desktop 1" fallback
// Need to maintain a map of desktop name to virtual desktop uuid
std::vector<GUID> ids{};
if (VirtualDesktopUtils::GetVirtualDekstopIds(m_virtualDesktopsRegKey, ids) && !ids.empty())
{
std::unique_lock writeLock(m_lock);
m_currentVirtualDesktopId = ids[0];
wil::unique_cotaskmem_string id;
if (changeType == DisplayChangeType::Initialization &&
SUCCEEDED_LOG(StringFromCLSID(m_currentVirtualDesktopId, &id)))
{
JSONHelpers::FancyZonesDataInstance().UpdatePrimaryDesktopData(id.get());
}
}
}
}
@ -1132,28 +1141,12 @@ void FancyZones::HandleVirtualDesktopUpdates(HANDLE fancyZonesDestroyedEvent) no
// if fancyZonesDestroyedEvent is signalized or WaitForMultipleObjects failed, terminate thread execution
return;
}
DWORD bufferCapacity;
const WCHAR* key = L"VirtualDesktopIDs";
// request regkey binary buffer capacity only
if (RegQueryValueExW(m_virtualDesktopsRegKey, key, 0, nullptr, nullptr, &bufferCapacity) != ERROR_SUCCESS)
std::vector<GUID> ids{};
if (VirtualDesktopUtils::GetVirtualDekstopIds(m_virtualDesktopsRegKey, ids))
{
return;
std::unordered_set<GUID> idSet(std::begin(ids), std::end(ids));
RegisterVirtualDesktopUpdates(idSet);
}
std::unique_ptr<BYTE[]> buffer = std::make_unique<BYTE[]>(bufferCapacity);
// request regkey binary content
if (RegQueryValueExW(m_virtualDesktopsRegKey, key, 0, nullptr, buffer.get(), &bufferCapacity) != ERROR_SUCCESS)
{
return;
}
const size_t guidSize = sizeof(GUID);
std::unordered_set<GUID> temp;
temp.reserve(bufferCapacity / guidSize);
for (size_t i = 0; i < bufferCapacity; i += guidSize)
{
GUID* guid = reinterpret_cast<GUID*>(buffer.get() + i);
temp.insert(*guid);
}
RegisterVirtualDesktopUpdates(temp);
}
}

View File

@ -97,7 +97,6 @@
<ClInclude Include="FancyZones.h" />
<ClInclude Include="JsonHelpers.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="RegistryHelpers.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="trace.h" />

View File

@ -30,9 +30,6 @@
<ClInclude Include="FancyZones.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RegistryHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>

View File

@ -1,6 +1,5 @@
#include "pch.h"
#include "JsonHelpers.h"
#include "RegistryHelpers.h"
#include "ZoneSet.h"
#include "trace.h"
@ -37,6 +36,7 @@ namespace
const wchar_t* FANCY_ZONES_DATA_FILE = L"zones-settings.json";
const wchar_t* DEFAULT_GUID = L"{00000000-0000-0000-0000-000000000000}";
const wchar_t* REG_SETTINGS = L"Software\\SuperFancyZones";
std::wstring ExtractVirtualDesktopId(const std::wstring& deviceId)
{
@ -321,6 +321,46 @@ namespace JSONHelpers
}
}
void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
{
// Explorer persists current virtual desktop identifier to registry on a per session basis,
// but only after first virtual desktop switch happens. If the user hasn't switched virtual
// desktops in this session value in registry will be empty and we will use default GUID in
// that case (00000000-0000-0000-0000-000000000000).
// This method will go through all our persisted data with default GUID and update it with
// valid one.
auto replaceDesktopId = [&desktopId](const std::wstring& deviceId) {
return deviceId.substr(0, deviceId.rfind('_') + 1) + desktopId;
};
std::scoped_lock lock{ dataLock };
for (auto& [path, data] : appZoneHistoryMap)
{
if (ExtractVirtualDesktopId(data.deviceId) == DEFAULT_GUID)
{
data.deviceId = replaceDesktopId(data.deviceId);
}
}
std::vector<std::wstring> toReplace{};
for (const auto& [id, data] : deviceInfoMap)
{
if (ExtractVirtualDesktopId(id) == DEFAULT_GUID)
{
toReplace.push_back(id);
}
}
for (const auto& id : toReplace)
{
auto mapEntry = deviceInfoMap.extract(id);
mapEntry.key() = replaceDesktopId(id);
deviceInfoMap.insert(std::move(mapEntry));
}
if (activeDeviceId == DEFAULT_GUID)
{
activeDeviceId = replaceDesktopId(activeDeviceId);
}
SaveFancyZonesData();
}
int FancyZonesData::GetAppLastZoneIndex(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
{
std::scoped_lock lock{ dataLock };
@ -640,7 +680,7 @@ namespace JSONHelpers
{
std::scoped_lock lock{ dataLock };
wchar_t key[256];
StringCchPrintf(key, ARRAYSIZE(key), L"%s\\%s", RegistryHelpers::REG_SETTINGS, L"Layouts");
StringCchPrintf(key, ARRAYSIZE(key), L"%s\\%s", REG_SETTINGS, L"Layouts");
HKEY hkey;
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
{

View File

@ -233,6 +233,7 @@ namespace JSONHelpers
void AddDevice(const std::wstring& deviceId);
bool RemoveDevicesByVirtualDesktopId(const std::wstring& virtualDesktopId);
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
int GetAppLastZoneIndex(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const;
bool RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId);

View File

@ -1,38 +0,0 @@
#pragma once
#include <shlwapi.h>
namespace RegistryHelpers
{
static PCWSTR REG_SETTINGS = L"Software\\SuperFancyZones";
static PCWSTR APP_ZONE_HISTORY_SUBKEY = L"AppZoneHistory";
inline HRESULT GetCurrentVirtualDesktop(_Out_ GUID* id)
{
*id = GUID_NULL;
DWORD sessionId;
ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
wchar_t sessionKeyPath[256]{};
RETURN_IF_FAILED(
StringCchPrintfW(
sessionKeyPath,
ARRAYSIZE(sessionKeyPath),
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d\\VirtualDesktops",
sessionId));
wil::unique_hkey key{};
GUID value{};
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionKeyPath, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
{
DWORD size = sizeof(value);
if (RegQueryValueExW(key.get(), L"CurrentVirtualDesktop", 0, nullptr, reinterpret_cast<BYTE*>(&value), &size) == ERROR_SUCCESS)
{
*id = value;
return S_OK;
}
}
return E_FAIL;
}
}

View File

@ -7,6 +7,9 @@ namespace VirtualDesktopUtils
const CLSID CLSID_ImmersiveShell = { 0xC2F03A33, 0x21F5, 0x47FA, 0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39 };
const wchar_t GUID_EmptyGUID[] = L"{00000000-0000-0000-0000-000000000000}";
const wchar_t RegCurrentVirtualDesktop[] = L"CurrentVirtualDesktop";
const wchar_t RegVirtualDesktopIds[] = L"VirtualDesktopIDs";
IServiceProvider* GetServiceProvider()
{
IServiceProvider* provider{ nullptr };
@ -46,4 +49,57 @@ namespace VirtualDesktopUtils
}
return SUCCEEDED(CLSIDFromString(virtualDesktopId.c_str(), desktopId));
}
bool GetCurrentVirtualDesktopId(GUID* desktopId)
{
DWORD sessionId;
ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
wchar_t sessionKeyPath[256]{};
RETURN_IF_FAILED(
StringCchPrintfW(
sessionKeyPath,
ARRAYSIZE(sessionKeyPath),
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d\\VirtualDesktops",
sessionId));
wil::unique_hkey key{};
GUID value{};
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionKeyPath, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
{
DWORD size = sizeof(GUID);
if (RegQueryValueExW(key.get(), RegCurrentVirtualDesktop, 0, nullptr, reinterpret_cast<BYTE*>(&value), &size) == ERROR_SUCCESS)
{
*desktopId = value;
return true;
}
}
return false;
}
bool GetVirtualDekstopIds(HKEY hKey, std::vector<GUID>& ids)
{
DWORD bufferCapacity;
// request regkey binary buffer capacity only
if (RegQueryValueExW(hKey, RegVirtualDesktopIds, 0, nullptr, nullptr, &bufferCapacity) != ERROR_SUCCESS)
{
return false;
}
std::unique_ptr<BYTE[]> buffer = std::make_unique<BYTE[]>(bufferCapacity);
// request regkey binary content
if (RegQueryValueExW(hKey, RegVirtualDesktopIds, 0, nullptr, buffer.get(), &bufferCapacity) != ERROR_SUCCESS)
{
return false;
}
const size_t guidSize = sizeof(GUID);
std::vector<GUID> temp;
temp.reserve(bufferCapacity / guidSize);
for (size_t i = 0; i < bufferCapacity; i += guidSize)
{
GUID* guid = reinterpret_cast<GUID*>(buffer.get() + i);
temp.push_back(*guid);
}
ids = std::move(temp);
return true;
}
}

View File

@ -6,4 +6,6 @@ namespace VirtualDesktopUtils
{
bool GetWindowDesktopId(HWND topLevelWindow, GUID* desktopId);
bool GetZoneWindowDesktopId(IZoneWindow* zoneWindow, GUID* desktopId);
bool GetCurrentVirtualDesktopId(GUID* desktopId);
bool GetVirtualDekstopIds(HKEY hKey, std::vector<GUID>& ids);
}

View File

@ -2,7 +2,6 @@
#include "util.h"
#include "lib/ZoneSet.h"
#include "lib/RegistryHelpers.h"
#include <common/dpi_aware.h>

View File

@ -5,7 +5,6 @@
#include "ZoneWindow.h"
#include "trace.h"
#include "util.h"
#include "RegistryHelpers.h"
#include <ShellScalingApi.h>
#include <mutex>