mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-18 09:53:05 +08:00
101 lines
2.9 KiB
C++
101 lines
2.9 KiB
C++
/*
|
|
* @Author: DI JUNKUN
|
|
* @Date: 2026-09-07
|
|
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
|
*/
|
|
|
|
#ifndef _INTERACTIVE_DESKTOP_H_
|
|
#define _INTERACTIVE_DESKTOP_H_
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <string>
|
|
|
|
namespace crossdesk {
|
|
|
|
inline std::wstring GetDesktopNameW(HDESK desktop) {
|
|
DWORD bytes_needed = 0;
|
|
if (desktop == nullptr) {
|
|
return {};
|
|
}
|
|
GetUserObjectInformationW(desktop, UOI_NAME, nullptr, 0, &bytes_needed);
|
|
if (bytes_needed == 0) {
|
|
return {};
|
|
}
|
|
std::wstring name(bytes_needed / sizeof(wchar_t), L'\0');
|
|
if (!GetUserObjectInformationW(desktop, UOI_NAME, name.data(), bytes_needed,
|
|
&bytes_needed)) {
|
|
return {};
|
|
}
|
|
while (!name.empty() && name.back() == L'\0') {
|
|
name.pop_back();
|
|
}
|
|
return name;
|
|
}
|
|
|
|
// Use only on worker threads without windows or hooks. Restore the original
|
|
// desktop before closing the handle: Windows cannot close a desktop in use.
|
|
class ScopedInteractiveDesktop {
|
|
public:
|
|
ScopedInteractiveDesktop()
|
|
: original_(GetThreadDesktop(GetCurrentThreadId())) {}
|
|
ScopedInteractiveDesktop(const ScopedInteractiveDesktop&) = delete;
|
|
ScopedInteractiveDesktop& operator=(const ScopedInteractiveDesktop&) = delete;
|
|
|
|
~ScopedInteractiveDesktop() {
|
|
if (desktop_ != nullptr) {
|
|
SetThreadDesktop(original_);
|
|
CloseDesktop(desktop_);
|
|
}
|
|
}
|
|
|
|
bool Bind(const std::wstring& fallback_name) {
|
|
constexpr ACCESS_MASK access =
|
|
DESKTOP_CREATEWINDOW | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
|
|
DESKTOP_SWITCHDESKTOP | DESKTOP_JOURNALPLAYBACK;
|
|
// SendInput requires playback access on the attached desktop; otherwise
|
|
// it returns ERROR_ACCESS_DENIED even for a correctly attached SYSTEM helper.
|
|
// The service's stage/name can lag a lock, unlock, or UAC transition.
|
|
// Opening a cached desktop can succeed even when it is no longer visible.
|
|
HDESK desktop = OpenInputDesktop(0, FALSE, access);
|
|
if (desktop == nullptr && !fallback_name.empty()) {
|
|
desktop = OpenDesktopW(fallback_name.c_str(), 0, FALSE, access);
|
|
}
|
|
if (desktop == nullptr) {
|
|
return false;
|
|
}
|
|
if (!SetThreadDesktop(desktop)) {
|
|
const DWORD error = GetLastError();
|
|
CloseDesktop(desktop);
|
|
SetLastError(error);
|
|
return false;
|
|
}
|
|
if (desktop_ != nullptr) {
|
|
CloseDesktop(desktop_);
|
|
}
|
|
desktop_ = desktop;
|
|
name_ = GetDesktopNameW(desktop_);
|
|
return true;
|
|
}
|
|
|
|
bool IsInputDesktop() const {
|
|
HDESK input = OpenInputDesktop(0, FALSE, DESKTOP_READOBJECTS);
|
|
if (input == nullptr) {
|
|
return false;
|
|
}
|
|
const std::wstring input_name = GetDesktopNameW(input);
|
|
CloseDesktop(input);
|
|
return !name_.empty() && _wcsicmp(name_.c_str(), input_name.c_str()) == 0;
|
|
}
|
|
|
|
const std::wstring& name() const { return name_; }
|
|
|
|
private:
|
|
HDESK original_ = nullptr;
|
|
HDESK desktop_ = nullptr;
|
|
std::wstring name_;
|
|
};
|
|
|
|
} // namespace crossdesk
|
|
|
|
#endif |