mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] helper: capture file clipboard under lock
This commit is contained in:
@@ -48,6 +48,7 @@ namespace
|
||||
private:
|
||||
HANDLE m_previousToken = nullptr;
|
||||
bool m_active = false;
|
||||
DWORD m_error = ERROR_SUCCESS;
|
||||
|
||||
void ClosePreviousToken()
|
||||
{
|
||||
@@ -76,6 +77,7 @@ namespace
|
||||
if (!m_previousToken && SetThreadToken(nullptr, nullptr))
|
||||
{
|
||||
m_active = false;
|
||||
m_error = error;
|
||||
SetLastError(error);
|
||||
return false;
|
||||
}
|
||||
@@ -97,7 +99,10 @@ namespace
|
||||
{
|
||||
const DWORD error = GetLastError();
|
||||
if (error != ERROR_NO_TOKEN)
|
||||
{
|
||||
m_error = error;
|
||||
return;
|
||||
}
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -107,6 +112,7 @@ namespace
|
||||
{
|
||||
const DWORD error = GetLastError();
|
||||
ClosePreviousToken();
|
||||
m_error = error;
|
||||
SetLastError(error);
|
||||
}
|
||||
}
|
||||
@@ -127,6 +133,11 @@ namespace
|
||||
return m_active;
|
||||
}
|
||||
|
||||
DWORD Error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
bool Finish()
|
||||
{
|
||||
const DWORD error = GetLastError();
|
||||
@@ -150,13 +161,15 @@ namespace
|
||||
}
|
||||
|
||||
bool CaptureUserToken(HANDLE& token,
|
||||
KVMFRClipboardFileError& error)
|
||||
KVMFRClipboardFileError& error, DWORD& winError)
|
||||
{
|
||||
token = nullptr;
|
||||
winError = ERROR_SUCCESS;
|
||||
DWORD sessionId = 0;
|
||||
if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId))
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
winError = GetLastError();
|
||||
error = TokenError(winError);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -164,7 +177,8 @@ namespace
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE,
|
||||
&processToken))
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
winError = GetLastError();
|
||||
error = TokenError(winError);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -177,7 +191,7 @@ namespace
|
||||
CloseHandle(processToken);
|
||||
if (!brokerDuplicated)
|
||||
{
|
||||
SetLastError(brokerError);
|
||||
winError = brokerError;
|
||||
error = TokenError(brokerError);
|
||||
return false;
|
||||
}
|
||||
@@ -185,9 +199,8 @@ namespace
|
||||
LUID privilege = {};
|
||||
if (!LookupPrivilegeValueW(nullptr, SE_TCB_NAME, &privilege))
|
||||
{
|
||||
const DWORD winError = GetLastError();
|
||||
winError = GetLastError();
|
||||
CloseHandle(brokerToken);
|
||||
SetLastError(winError);
|
||||
error = TokenError(winError);
|
||||
return false;
|
||||
}
|
||||
@@ -197,22 +210,23 @@ namespace
|
||||
privileges.Privileges[0].Luid = privilege;
|
||||
privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
if (!AdjustTokenPrivileges(brokerToken, FALSE, &privileges, 0, nullptr,
|
||||
nullptr) || GetLastError() != ERROR_SUCCESS)
|
||||
const bool adjusted = AdjustTokenPrivileges(brokerToken, FALSE,
|
||||
&privileges, 0, nullptr, nullptr) != FALSE;
|
||||
const DWORD adjustError = GetLastError();
|
||||
if (!adjusted || adjustError != ERROR_SUCCESS)
|
||||
{
|
||||
const DWORD winError = GetLastError();
|
||||
CloseHandle(brokerToken);
|
||||
SetLastError(winError ? winError : ERROR_ACCESS_DENIED);
|
||||
error = TokenError(winError ? winError : ERROR_ACCESS_DENIED);
|
||||
winError = adjustError ? adjustError : ERROR_ACCESS_DENIED;
|
||||
error = TokenError(winError);
|
||||
return false;
|
||||
}
|
||||
|
||||
CThreadImpersonation brokerImpersonation(brokerToken);
|
||||
if (!brokerImpersonation.Active())
|
||||
{
|
||||
const DWORD winError = GetLastError();
|
||||
const DWORD brokerImpersonationError = brokerImpersonation.Error();
|
||||
CloseHandle(brokerToken);
|
||||
SetLastError(winError);
|
||||
winError = brokerImpersonationError;
|
||||
error = TokenError(winError);
|
||||
return false;
|
||||
}
|
||||
@@ -222,11 +236,11 @@ namespace
|
||||
const DWORD queryError = queried ? ERROR_SUCCESS : GetLastError();
|
||||
if (!brokerImpersonation.Finish())
|
||||
{
|
||||
const DWORD winError = GetLastError();
|
||||
const DWORD restoreError = brokerImpersonation.Error();
|
||||
if (sourceToken)
|
||||
CloseHandle(sourceToken);
|
||||
CloseHandle(brokerToken);
|
||||
SetLastError(winError);
|
||||
winError = restoreError;
|
||||
error = TokenError(winError);
|
||||
return false;
|
||||
}
|
||||
@@ -235,8 +249,8 @@ namespace
|
||||
{
|
||||
if (sourceToken)
|
||||
CloseHandle(sourceToken);
|
||||
SetLastError(queryError ? queryError : ERROR_ACCESS_DENIED);
|
||||
error = TokenError(queryError ? queryError : ERROR_ACCESS_DENIED);
|
||||
winError = queryError ? queryError : ERROR_ACCESS_DENIED;
|
||||
error = TokenError(winError);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -247,7 +261,7 @@ namespace
|
||||
CloseHandle(sourceToken);
|
||||
if (!duplicated)
|
||||
{
|
||||
SetLastError(duplicateError);
|
||||
winError = duplicateError;
|
||||
error = TokenError(duplicateError);
|
||||
return false;
|
||||
}
|
||||
@@ -1148,6 +1162,61 @@ namespace
|
||||
};
|
||||
}
|
||||
|
||||
class CClipboardUserImpersonation::Impl
|
||||
{
|
||||
public:
|
||||
HANDLE token;
|
||||
CThreadImpersonation impersonation;
|
||||
|
||||
explicit Impl(HANDLE token) :
|
||||
token(token), impersonation(token)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
impersonation.Finish();
|
||||
CloseHandle(token);
|
||||
}
|
||||
};
|
||||
|
||||
CClipboardUserImpersonation::CClipboardUserImpersonation(
|
||||
KVMFRClipboardFileError& error)
|
||||
{
|
||||
HANDLE token = nullptr;
|
||||
if (!CaptureUserToken(token, error, m_error))
|
||||
return;
|
||||
try
|
||||
{
|
||||
m_impl = std::make_unique<Impl>(token);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
CloseHandle(token);
|
||||
m_error = ERROR_OUTOFMEMORY;
|
||||
error = KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
if (!m_impl->impersonation.Active())
|
||||
{
|
||||
m_error = m_impl->impersonation.Error();
|
||||
m_impl.reset();
|
||||
error = TokenError(m_error);
|
||||
}
|
||||
}
|
||||
|
||||
CClipboardUserImpersonation::~CClipboardUserImpersonation() = default;
|
||||
|
||||
bool CClipboardUserImpersonation::Active() const
|
||||
{
|
||||
return m_impl && m_impl->impersonation.Active();
|
||||
}
|
||||
|
||||
DWORD CClipboardUserImpersonation::Error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
CLocalClipboardFiles::CLocalClipboardFiles(HANDLE userToken) :
|
||||
m_userToken(userToken)
|
||||
{
|
||||
@@ -1173,7 +1242,8 @@ std::shared_ptr<CLocalClipboardFiles> CLocalClipboardFiles::Capture(
|
||||
std::shared_ptr<CLocalClipboardFiles> dataset;
|
||||
try
|
||||
{
|
||||
if (!CaptureUserToken(userToken, error))
|
||||
DWORD tokenError = ERROR_SUCCESS;
|
||||
if (!CaptureUserToken(userToken, error, tokenError))
|
||||
return nullptr;
|
||||
CLocalClipboardFiles * raw = new CLocalClipboardFiles(userToken);
|
||||
userToken = nullptr;
|
||||
@@ -1213,7 +1283,7 @@ std::shared_ptr<CLocalClipboardFiles> CLocalClipboardFiles::Capture(
|
||||
CThreadImpersonation impersonation(dataset->m_userToken);
|
||||
if (!impersonation.Active())
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return nullptr;
|
||||
}
|
||||
for (const std::wstring& root : roots)
|
||||
@@ -1221,7 +1291,7 @@ std::shared_ptr<CLocalClipboardFiles> CLocalClipboardFiles::Capture(
|
||||
return nullptr;
|
||||
if (!impersonation.Finish())
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1512,14 +1582,14 @@ bool CLocalClipboardFiles::List(uint64_t node, std::vector<uint8_t>& data,
|
||||
CThreadImpersonation impersonation(m_userToken);
|
||||
if (!impersonation.Active())
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return false;
|
||||
}
|
||||
if (!RefreshRoots(error))
|
||||
return false;
|
||||
if (!impersonation.Finish())
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return false;
|
||||
}
|
||||
m_published = true;
|
||||
@@ -1542,14 +1612,14 @@ bool CLocalClipboardFiles::List(uint64_t node, std::vector<uint8_t>& data,
|
||||
CThreadImpersonation impersonation(m_userToken);
|
||||
if (!impersonation.Active())
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return false;
|
||||
}
|
||||
if (!LoadChildren(index, error))
|
||||
return false;
|
||||
if (!impersonation.Finish())
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return false;
|
||||
}
|
||||
children = &m_nodes[index].children;
|
||||
@@ -1653,7 +1723,7 @@ bool CLocalClipboardFiles::Read(uint64_t node, uint64_t offset,
|
||||
CThreadImpersonation impersonation(m_userToken);
|
||||
if (!impersonation.Active())
|
||||
{
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1758,7 +1828,7 @@ bool CLocalClipboardFiles::Read(uint64_t node, uint64_t offset,
|
||||
if (!impersonation.Finish())
|
||||
{
|
||||
data.clear();
|
||||
error = TokenError(GetLastError());
|
||||
error = TokenError(impersonation.Error());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -45,6 +45,25 @@ struct ClipboardRemoteFileEntry
|
||||
std::wstring name;
|
||||
};
|
||||
|
||||
class CClipboardUserImpersonation final
|
||||
{
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
DWORD m_error = ERROR_SUCCESS;
|
||||
|
||||
public:
|
||||
explicit CClipboardUserImpersonation(KVMFRClipboardFileError& error);
|
||||
~CClipboardUserImpersonation();
|
||||
|
||||
CClipboardUserImpersonation(const CClipboardUserImpersonation&) = delete;
|
||||
CClipboardUserImpersonation& operator=(
|
||||
const CClipboardUserImpersonation&) = delete;
|
||||
|
||||
bool Active() const;
|
||||
DWORD Error() const;
|
||||
};
|
||||
|
||||
class CLocalClipboardFiles final
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <CDebug.h>
|
||||
#include <bcrypt.h>
|
||||
#include <Ole2.h>
|
||||
#include <ShlObj.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
@@ -92,6 +93,140 @@ namespace
|
||||
{ if (acquired) ReleaseStgMedium(&medium); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ClipboardComScope
|
||||
{
|
||||
T * object = nullptr;
|
||||
~ClipboardComScope() { if (object) object->Release(); }
|
||||
};
|
||||
|
||||
struct ClipboardTaskStringScope
|
||||
{
|
||||
PWSTR value = nullptr;
|
||||
~ClipboardTaskStringScope() { CoTaskMemFree(value); }
|
||||
};
|
||||
|
||||
bool ClipboardDataObjectHasFileFormats(IDataObject * object,
|
||||
DWORD sequence, HRESULT& enumError)
|
||||
{
|
||||
enumError = E_INVALIDARG;
|
||||
if (!object)
|
||||
{
|
||||
DEBUG_ERROR_HR(E_POINTER,
|
||||
"Failed to inspect local clipboard file formats: "
|
||||
"stage=IDataObject sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
return false;
|
||||
}
|
||||
|
||||
ClipboardComScope<IEnumFORMATETC> formats;
|
||||
enumError = object->EnumFormatEtc(DATADIR_GET, &formats.object);
|
||||
if (FAILED(enumError) || !formats.object)
|
||||
{
|
||||
if (SUCCEEDED(enumError))
|
||||
enumError = E_UNEXPECTED;
|
||||
DEBUG_ERROR_HR(enumError,
|
||||
"Failed to inspect local clipboard file formats: "
|
||||
"stage=IDataObject::EnumFormatEtc sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
return false;
|
||||
}
|
||||
|
||||
const UINT shellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
|
||||
if (!shellIDList)
|
||||
{
|
||||
const DWORD formatError = GetLastError();
|
||||
DEBUG_ERROR_HR(formatError ? HRESULT_FROM_WIN32(formatError) :
|
||||
E_UNEXPECTED,
|
||||
"Failed to inspect local clipboard file formats: "
|
||||
"stage=RegisterClipboardFormatW(CFSTR_SHELLIDLIST) sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
}
|
||||
const UINT fileDescriptor =
|
||||
RegisterClipboardFormatW(CFSTR_FILEDESCRIPTORW);
|
||||
if (!fileDescriptor)
|
||||
{
|
||||
const DWORD formatError = GetLastError();
|
||||
DEBUG_ERROR_HR(formatError ? HRESULT_FROM_WIN32(formatError) :
|
||||
E_UNEXPECTED,
|
||||
"Failed to inspect local clipboard file formats: "
|
||||
"stage=RegisterClipboardFormatW(CFSTR_FILEDESCRIPTORW) sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
FORMATETC format = {};
|
||||
ULONG fetched = 0;
|
||||
enumError = formats.object->Next(1, &format, &fetched);
|
||||
const bool files = fetched == 1U &&
|
||||
(format.cfFormat == CF_HDROP ||
|
||||
(shellIDList && format.cfFormat == shellIDList) ||
|
||||
(fileDescriptor && format.cfFormat == fileDescriptor));
|
||||
CoTaskMemFree(format.ptd);
|
||||
if (files)
|
||||
{
|
||||
enumError = S_OK;
|
||||
return true;
|
||||
}
|
||||
if (enumError == S_FALSE)
|
||||
{
|
||||
enumError = S_OK;
|
||||
return false;
|
||||
}
|
||||
if (FAILED(enumError))
|
||||
{
|
||||
DEBUG_ERROR_HR(enumError,
|
||||
"Failed to inspect local clipboard file formats: "
|
||||
"stage=IEnumFORMATETC::Next sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
return false;
|
||||
}
|
||||
if (!fetched)
|
||||
{
|
||||
enumError = E_UNEXPECTED;
|
||||
DEBUG_ERROR_HR(enumError,
|
||||
"Failed to inspect local clipboard file formats: "
|
||||
"stage=IEnumFORMATETC::Next sequence=%lu fetched=0",
|
||||
static_cast<unsigned long>(sequence));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT ClipboardFileHRESULT(KVMFRClipboardFileError error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_NONE:
|
||||
return S_OK;
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_NOT_FOUND:
|
||||
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_ACCESS:
|
||||
return E_ACCESSDENIED;
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_NOT_DIRECTORY:
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_IS_DIRECTORY:
|
||||
return HRESULT_FROM_WIN32(ERROR_DIRECTORY);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_IO:
|
||||
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_INVALID:
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY:
|
||||
return E_OUTOFMEMORY;
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_NO_SPACE:
|
||||
return HRESULT_FROM_WIN32(ERROR_DISK_FULL);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_DISCONNECTED:
|
||||
return HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_CONNECTED);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_CANCELLED:
|
||||
return HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_NOT_SUPPORTED:
|
||||
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
||||
case KVMFR_CLIPBOARD_FILE_ERROR_STALE:
|
||||
return HRESULT_FROM_WIN32(ERROR_RETRY);
|
||||
default:
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
struct ClipboardOrigin
|
||||
{
|
||||
uint32_t magic;
|
||||
@@ -2036,11 +2171,10 @@ void CClipboardManager::HandleFileAcquire(
|
||||
|
||||
if (error)
|
||||
DEBUG_WARN("Local clipboard file acquire failed: dataset=%llu "
|
||||
"acquisition=%llu fileError=%u winError=%lu",
|
||||
"acquisition=%llu fileError=%u",
|
||||
static_cast<unsigned long long>(record.clipboardGeneration),
|
||||
static_cast<unsigned long long>(record.transfer),
|
||||
static_cast<unsigned int>(error),
|
||||
static_cast<unsigned long>(ERROR_SUCCESS));
|
||||
static_cast<unsigned int>(error));
|
||||
else
|
||||
DEBUG_INFO("Local clipboard file acquire accepted: dataset=%llu "
|
||||
"acquisition=%llu",
|
||||
@@ -2141,11 +2275,10 @@ void CClipboardManager::HandleFileRequest(
|
||||
KVMFR_CLIPBOARD_FILE_ERROR_STALE;
|
||||
if (initialList)
|
||||
DEBUG_WARN("Initial local clipboard file LIST rejected: "
|
||||
"dataset=%llu request=%llu fileError=%u winError=%lu",
|
||||
"dataset=%llu request=%llu fileError=%u",
|
||||
static_cast<unsigned long long>(record.clipboardGeneration),
|
||||
static_cast<unsigned long long>(record.transfer),
|
||||
static_cast<unsigned int>(failure),
|
||||
static_cast<unsigned long>(ERROR_SUCCESS));
|
||||
static_cast<unsigned int>(failure));
|
||||
QueueFileCancel(record, failure);
|
||||
return;
|
||||
}
|
||||
@@ -2158,17 +2291,16 @@ void CClipboardManager::HandleFileRequest(
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
if (initialList)
|
||||
DEBUG_WARN("Initial local clipboard file LIST allocation failed: "
|
||||
"dataset=%llu request=%llu fileError=%u winError=%lu",
|
||||
DEBUG_ERROR_HR(E_OUTOFMEMORY,
|
||||
"Initial local clipboard file LIST allocation failed: "
|
||||
"dataset=%llu request=%llu fileError=%u",
|
||||
static_cast<unsigned long long>(record.clipboardGeneration),
|
||||
static_cast<unsigned long long>(record.transfer),
|
||||
static_cast<unsigned int>(KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY),
|
||||
static_cast<unsigned long>(ERROR_OUTOFMEMORY));
|
||||
static_cast<unsigned int>(KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY));
|
||||
QueueFileCancel(record, KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY);
|
||||
return;
|
||||
}
|
||||
bool success = false;
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
try
|
||||
{
|
||||
if (record.token == KVMFR_CLIPBOARD_FILE_OP_LIST)
|
||||
@@ -2180,18 +2312,16 @@ void CClipboardManager::HandleFileRequest(
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
error = KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY;
|
||||
SetLastError(ERROR_OUTOFMEMORY);
|
||||
}
|
||||
const DWORD winError = success ? ERROR_SUCCESS : GetLastError();
|
||||
if (!success)
|
||||
{
|
||||
if (initialList)
|
||||
DEBUG_WARN("Initial local clipboard file LIST failed: dataset=%llu "
|
||||
"request=%llu fileError=%u winError=%lu",
|
||||
DEBUG_ERROR_HR(ClipboardFileHRESULT(error),
|
||||
"Initial local clipboard file LIST failed: dataset=%llu "
|
||||
"request=%llu fileError=%u",
|
||||
static_cast<unsigned long long>(record.clipboardGeneration),
|
||||
static_cast<unsigned long long>(record.transfer),
|
||||
static_cast<unsigned int>(error),
|
||||
static_cast<unsigned long>(winError));
|
||||
static_cast<unsigned int>(error));
|
||||
QueueFileCancel(record, error);
|
||||
return;
|
||||
}
|
||||
@@ -3151,12 +3281,29 @@ void CClipboardManager::HandleFileDataObject(UIWork& work)
|
||||
m_ownedSequence = GetClipboardSequenceNumber();
|
||||
}
|
||||
|
||||
bool CClipboardManager::OpenClipboardRetry() const
|
||||
bool CClipboardManager::OpenClipboardRetry(DWORD * error,
|
||||
const char * stage, bool useWindow) const
|
||||
{
|
||||
for (unsigned int attempt = 0; attempt != 8; ++attempt)
|
||||
{
|
||||
if (OpenClipboard(m_hwnd))
|
||||
if (OpenClipboard(useWindow ? m_hwnd : nullptr))
|
||||
{
|
||||
if (error)
|
||||
*error = ERROR_SUCCESS;
|
||||
return true;
|
||||
}
|
||||
const DWORD openError = GetLastError();
|
||||
DEBUG_ERROR_HR(openError ? HRESULT_FROM_WIN32(openError) :
|
||||
CLIPBRD_E_CANT_OPEN,
|
||||
"OpenClipboard failed: stage=%s attempt=%u",
|
||||
stage ? stage : "unspecified", attempt + 1U);
|
||||
if (attempt + 1U == 8U)
|
||||
{
|
||||
if (error)
|
||||
*error = openError;
|
||||
SetLastError(openError);
|
||||
return false;
|
||||
}
|
||||
Sleep(5U << (std::min)(attempt, 5U));
|
||||
}
|
||||
return false;
|
||||
@@ -3216,64 +3363,128 @@ uint32_t CClipboardManager::EnumerateFormats() const
|
||||
}
|
||||
|
||||
std::shared_ptr<CLocalClipboardFiles>
|
||||
CClipboardManager::CaptureClipboardFiles(DWORD sequence, bool& viaOLE,
|
||||
bool& openedOLE, KVMFRClipboardFileError& error, DWORD& winError,
|
||||
HRESULT& oleError)
|
||||
CClipboardManager::CaptureClipboardFiles(DWORD sequence, int rawFormatCount,
|
||||
bool& viaOLE, const char *& retryStage,
|
||||
KVMFRClipboardFileError& error, HRESULT& oleError)
|
||||
{
|
||||
viaOLE = false;
|
||||
openedOLE = false;
|
||||
retryStage = nullptr;
|
||||
error = KVMFR_CLIPBOARD_FILE_ERROR_INVALID;
|
||||
winError = ERROR_SUCCESS;
|
||||
oleError = S_FALSE;
|
||||
|
||||
const bool win32Files = IsClipboardFormatAvailable(CF_HDROP) != FALSE;
|
||||
if (win32Files)
|
||||
const DWORD initialSequence = GetClipboardSequenceNumber();
|
||||
if (initialSequence != sequence)
|
||||
{
|
||||
if (GetClipboardSequenceNumber() != sequence)
|
||||
{
|
||||
winError = ERROR_RETRY;
|
||||
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(ERROR_RETRY),
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=sequence-before-capture expected=%lu actual=%lu",
|
||||
static_cast<unsigned long>(sequence),
|
||||
static_cast<unsigned long>(initialSequence));
|
||||
return nullptr;
|
||||
}
|
||||
if (OpenClipboardRetry())
|
||||
|
||||
CClipboardUserImpersonation user(error);
|
||||
if (!user.Active())
|
||||
{
|
||||
const DWORD userError = user.Error();
|
||||
oleError = userError ? HRESULT_FROM_WIN32(userError) :
|
||||
ClipboardFileHRESULT(error);
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=impersonate-interactive-user sequence=%lu rawFormats=%d "
|
||||
"fileError=%u", static_cast<unsigned long>(sequence),
|
||||
rawFormatCount, static_cast<unsigned int>(error));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Acquire the clipboard before probing CF_HDROP. Explorer can still hold
|
||||
// the clipboard when WM_CLIPBOARDUPDATE is delivered, in which case an
|
||||
// unlocked IsClipboardFormatAvailable probe can observe no formats.
|
||||
DWORD openError = ERROR_SUCCESS;
|
||||
if (!OpenClipboardRetry(&openError, "CaptureClipboardFiles(CF_HDROP)",
|
||||
false))
|
||||
{
|
||||
oleError = CLIPBRD_E_CANT_OPEN;
|
||||
retryStage = "OpenClipboard";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<CLocalClipboardFiles> files;
|
||||
bool win32Candidate = false;
|
||||
bool sequenceChanged = false;
|
||||
int openedRawFormatCount = 0;
|
||||
if (GetClipboardSequenceNumber() == sequence)
|
||||
{
|
||||
openedRawFormatCount = CountClipboardFormats();
|
||||
win32Candidate = IsClipboardFormatAvailable(CF_HDROP) != FALSE;
|
||||
if (win32Candidate)
|
||||
{
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
HDROP drop = static_cast<HDROP>(GetClipboardData(CF_HDROP));
|
||||
if (drop)
|
||||
{
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
files = CLocalClipboardFiles::Capture(drop, error);
|
||||
if (!files)
|
||||
winError = GetLastError();
|
||||
{
|
||||
DEBUG_ERROR_HR(ClipboardFileHRESULT(error),
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=CLocalClipboardFiles::Capture(CF_HDROP/win32) "
|
||||
"sequence=%lu rawFormats=%d openedFormats=%d fileError=%u",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount,
|
||||
openedRawFormatCount, static_cast<unsigned int>(error));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
winError = GetLastError();
|
||||
if (!winError)
|
||||
winError = ERROR_INVALID_DATA;
|
||||
DWORD dataError = GetLastError();
|
||||
if (!dataError)
|
||||
dataError = ERROR_INVALID_DATA;
|
||||
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(dataError),
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=GetClipboardData(CF_HDROP) sequence=%lu rawFormats=%d "
|
||||
"openedFormats=%d",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount,
|
||||
openedRawFormatCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
winError = ERROR_RETRY;
|
||||
CloseClipboard();
|
||||
if (files)
|
||||
return files;
|
||||
DEBUG_TRACE("Local clipboard does not expose Win32 CF_HDROP: "
|
||||
"sequence=%lu rawFormats=%d openedFormats=%d",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount,
|
||||
openedRawFormatCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
winError = GetLastError();
|
||||
error = winError == ERROR_ACCESS_DENIED ?
|
||||
KVMFR_CLIPBOARD_FILE_ERROR_ACCESS :
|
||||
KVMFR_CLIPBOARD_FILE_ERROR_IO;
|
||||
}
|
||||
sequenceChanged = true;
|
||||
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(ERROR_RETRY),
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=sequence-after-OpenClipboard expected=%lu actual=%lu",
|
||||
static_cast<unsigned long>(sequence),
|
||||
static_cast<unsigned long>(GetClipboardSequenceNumber()));
|
||||
}
|
||||
|
||||
if (GetClipboardSequenceNumber() != sequence)
|
||||
if (!CloseClipboard())
|
||||
{
|
||||
if (!winError)
|
||||
winError = ERROR_RETRY;
|
||||
const DWORD closeError = GetLastError();
|
||||
DEBUG_ERROR_HR(closeError ? HRESULT_FROM_WIN32(closeError) :
|
||||
CLIPBRD_E_CANT_CLOSE,
|
||||
"Failed to capture local clipboard files: stage=CloseClipboard "
|
||||
"sequence=%lu rawFormats=%d",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount);
|
||||
}
|
||||
if (files)
|
||||
return files;
|
||||
if (sequenceChanged)
|
||||
return nullptr;
|
||||
|
||||
const DWORD oleSequence = GetClipboardSequenceNumber();
|
||||
if (oleSequence != sequence)
|
||||
{
|
||||
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(ERROR_RETRY),
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=sequence-before-OleGetClipboard expected=%lu actual=%lu",
|
||||
static_cast<unsigned long>(sequence),
|
||||
static_cast<unsigned long>(oleSequence));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -3283,9 +3494,14 @@ CClipboardManager::CaptureClipboardFiles(DWORD sequence, bool& viaOLE,
|
||||
{
|
||||
if (SUCCEEDED(oleError))
|
||||
oleError = E_UNEXPECTED;
|
||||
if (oleError == CLIPBRD_E_CANT_OPEN)
|
||||
retryStage = "OleGetClipboard";
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=OleGetClipboard sequence=%lu rawFormats=%d",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount);
|
||||
return nullptr;
|
||||
}
|
||||
openedOLE = true;
|
||||
|
||||
FORMATETC format = {
|
||||
static_cast<CLIPFORMAT>(CF_HDROP),
|
||||
@@ -3295,40 +3511,258 @@ CClipboardManager::CaptureClipboardFiles(DWORD sequence, bool& viaOLE,
|
||||
TYMED_HGLOBAL,
|
||||
};
|
||||
ClipboardStorageScope storage;
|
||||
oleError = object.object->GetData(&format, &storage.medium);
|
||||
if (FAILED(oleError))
|
||||
const HRESULT hdropError = object.object->GetData(
|
||||
&format, &storage.medium);
|
||||
if (FAILED(hdropError))
|
||||
{
|
||||
DEBUG_ERROR_HR(hdropError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=IDataObject::GetData(CF_HDROP) sequence=%lu rawFormats=%d",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount);
|
||||
}
|
||||
if (hdropError == CLIPBRD_E_CANT_OPEN)
|
||||
{
|
||||
oleError = hdropError;
|
||||
retryStage = "IDataObject::GetData(CF_HDROP)";
|
||||
return nullptr;
|
||||
storage.acquired = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<CLocalClipboardFiles> files;
|
||||
if (SUCCEEDED(hdropError))
|
||||
{
|
||||
storage.acquired = true;
|
||||
if (storage.medium.tymed == TYMED_HGLOBAL && storage.medium.hGlobal)
|
||||
{
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
try
|
||||
{
|
||||
files = CLocalClipboardFiles::Capture(
|
||||
std::shared_ptr<CLocalClipboardFiles> files =
|
||||
CLocalClipboardFiles::Capture(
|
||||
static_cast<HDROP>(storage.medium.hGlobal), error);
|
||||
if (!files)
|
||||
winError = GetLastError();
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
if (files)
|
||||
{
|
||||
winError = ERROR_OUTOFMEMORY;
|
||||
error = KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY;
|
||||
viaOLE = true;
|
||||
oleError = S_OK;
|
||||
return files;
|
||||
}
|
||||
catch (const std::length_error&)
|
||||
{
|
||||
winError = ERROR_OUTOFMEMORY;
|
||||
error = KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY;
|
||||
oleError = ClipboardFileHRESULT(error);
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=CLocalClipboardFiles::Capture(CF_HDROP/ole) sequence=%lu "
|
||||
"rawFormats=%d fileError=%u",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount,
|
||||
static_cast<unsigned int>(error));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (SUCCEEDED(hdropError))
|
||||
{
|
||||
oleError = DV_E_TYMED;
|
||||
error = KVMFR_CLIPBOARD_FILE_ERROR_INVALID;
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=IDataObject::GetData(CF_HDROP)/STGMEDIUM sequence=%lu "
|
||||
"rawFormats=%d tymed=0x%08lx hasHGlobal=%u",
|
||||
static_cast<unsigned long>(sequence), rawFormatCount,
|
||||
static_cast<unsigned long>(storage.medium.tymed),
|
||||
storage.medium.hGlobal ? 1U : 0U);
|
||||
return nullptr;
|
||||
}
|
||||
if (files)
|
||||
|
||||
ClipboardComScope<IShellItemArray> items;
|
||||
const HRESULT shellError = SHCreateShellItemArrayFromDataObject(object.object,
|
||||
IID_IShellItemArray, reinterpret_cast<void **>(&items.object));
|
||||
if (FAILED(shellError))
|
||||
{
|
||||
DEBUG_ERROR_HR(shellError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=SHCreateShellItemArrayFromDataObject sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
}
|
||||
else if (!items.object)
|
||||
{
|
||||
DEBUG_ERROR_HR(E_UNEXPECTED,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=SHCreateShellItemArrayFromDataObject sequence=%lu "
|
||||
"result=null",
|
||||
static_cast<unsigned long>(sequence));
|
||||
}
|
||||
if (FAILED(shellError) || !items.object)
|
||||
{
|
||||
oleError = FAILED(shellError) ? shellError : E_UNEXPECTED;
|
||||
if (oleError == CLIPBRD_E_CANT_OPEN)
|
||||
retryStage = "SHCreateShellItemArrayFromDataObject";
|
||||
HRESULT enumError = S_OK;
|
||||
const bool fileCandidate = win32Candidate ||
|
||||
hdropError != DV_E_FORMATETC ||
|
||||
ClipboardDataObjectHasFileFormats(object.object, sequence, enumError);
|
||||
if (!fileCandidate && SUCCEEDED(enumError) &&
|
||||
oleError != CLIPBRD_E_CANT_OPEN)
|
||||
{
|
||||
error = KVMFR_CLIPBOARD_FILE_ERROR_NONE;
|
||||
oleError = S_FALSE;
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DWORD count = 0;
|
||||
oleError = items.object->GetCount(&count);
|
||||
if (FAILED(oleError) || !count)
|
||||
{
|
||||
if (SUCCEEDED(oleError))
|
||||
oleError = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=IShellItemArray::GetCount sequence=%lu count=%lu",
|
||||
static_cast<unsigned long>(sequence),
|
||||
static_cast<unsigned long>(count));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> paths;
|
||||
size_t characters = 1U;
|
||||
try
|
||||
{
|
||||
paths.reserve(count);
|
||||
for (DWORD index = 0; index < count; ++index)
|
||||
{
|
||||
ClipboardComScope<IShellItem> item;
|
||||
oleError = items.object->GetItemAt(index, &item.object);
|
||||
if (FAILED(oleError) || !item.object)
|
||||
{
|
||||
if (SUCCEEDED(oleError))
|
||||
oleError = E_UNEXPECTED;
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=IShellItemArray::GetItemAt sequence=%lu item=%lu",
|
||||
static_cast<unsigned long>(sequence),
|
||||
static_cast<unsigned long>(index));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClipboardTaskStringScope path;
|
||||
oleError = item.object->GetDisplayName(
|
||||
SIGDN_FILESYSPATH, &path.value);
|
||||
if (FAILED(oleError) || !path.value || !path.value[0])
|
||||
{
|
||||
if (SUCCEEDED(oleError))
|
||||
oleError = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=IShellItem::GetDisplayName sequence=%lu item=%lu",
|
||||
static_cast<unsigned long>(sequence),
|
||||
static_cast<unsigned long>(index));
|
||||
return nullptr;
|
||||
}
|
||||
const size_t length = wcslen(path.value);
|
||||
if (length > (std::numeric_limits<size_t>::max)() - characters - 1U)
|
||||
throw std::length_error("clipboard path list");
|
||||
characters += length + 1U;
|
||||
paths.emplace_back(path.value, length);
|
||||
}
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
oleError = E_OUTOFMEMORY;
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: stage=Shell-path-list "
|
||||
"sequence=%lu", static_cast<unsigned long>(sequence));
|
||||
return nullptr;
|
||||
}
|
||||
catch (const std::length_error&)
|
||||
{
|
||||
oleError = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: stage=Shell-path-list "
|
||||
"sequence=%lu", static_cast<unsigned long>(sequence));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (characters > ((std::numeric_limits<SIZE_T>::max)() -
|
||||
sizeof(DROPFILES)) / sizeof(wchar_t))
|
||||
{
|
||||
oleError = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: stage=DROPFILES-size "
|
||||
"sequence=%lu", static_cast<unsigned long>(sequence));
|
||||
return nullptr;
|
||||
}
|
||||
const SIZE_T dropBytes = sizeof(DROPFILES) +
|
||||
characters * sizeof(wchar_t);
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
HGLOBAL drop = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dropBytes);
|
||||
if (!drop)
|
||||
{
|
||||
const DWORD allocError = GetLastError();
|
||||
oleError = allocError ? HRESULT_FROM_WIN32(allocError) : E_OUTOFMEMORY;
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: stage=GlobalAlloc(DROPFILES) "
|
||||
"sequence=%lu", static_cast<unsigned long>(sequence));
|
||||
return nullptr;
|
||||
}
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
DROPFILES * header = static_cast<DROPFILES *>(GlobalLock(drop));
|
||||
if (!header)
|
||||
{
|
||||
const DWORD lockError = GetLastError();
|
||||
oleError = lockError ? HRESULT_FROM_WIN32(lockError) : E_UNEXPECTED;
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: stage=GlobalLock(DROPFILES) "
|
||||
"sequence=%lu", static_cast<unsigned long>(sequence));
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
HGLOBAL freeResult = GlobalFree(drop);
|
||||
if (freeResult)
|
||||
{
|
||||
const DWORD freeError = GetLastError();
|
||||
DEBUG_ERROR_HR(freeError ? HRESULT_FROM_WIN32(freeError) : E_UNEXPECTED,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=GlobalFree(DROPFILES) sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
header->pFiles = sizeof(*header);
|
||||
header->fWide = TRUE;
|
||||
wchar_t * output = reinterpret_cast<wchar_t *>(
|
||||
reinterpret_cast<uint8_t *>(header) + sizeof(*header));
|
||||
for (const std::wstring& path : paths)
|
||||
{
|
||||
memcpy(output, path.c_str(), (path.size() + 1U) * sizeof(wchar_t));
|
||||
output += path.size() + 1U;
|
||||
}
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
const BOOL unlocked = GlobalUnlock(drop);
|
||||
const DWORD unlockError = unlocked ? ERROR_SUCCESS : GetLastError();
|
||||
if (!unlocked && unlockError != ERROR_SUCCESS)
|
||||
{
|
||||
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(unlockError),
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=GlobalUnlock(DROPFILES) sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
}
|
||||
|
||||
files = CLocalClipboardFiles::Capture(static_cast<HDROP>(drop), error);
|
||||
if (!files)
|
||||
{
|
||||
oleError = ClipboardFileHRESULT(error);
|
||||
DEBUG_ERROR_HR(oleError,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=CLocalClipboardFiles::Capture(Shell items) sequence=%lu "
|
||||
"fileError=%u", static_cast<unsigned long>(sequence),
|
||||
static_cast<unsigned int>(error));
|
||||
}
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
HGLOBAL freeResult = GlobalFree(drop);
|
||||
if (freeResult)
|
||||
{
|
||||
const DWORD freeError = GetLastError();
|
||||
DEBUG_ERROR_HR(freeError ? HRESULT_FROM_WIN32(freeError) : E_UNEXPECTED,
|
||||
"Failed to capture local clipboard files: "
|
||||
"stage=GlobalFree(DROPFILES) sequence=%lu",
|
||||
static_cast<unsigned long>(sequence));
|
||||
}
|
||||
if (!files)
|
||||
return nullptr;
|
||||
viaOLE = true;
|
||||
oleError = S_OK;
|
||||
return files;
|
||||
}
|
||||
|
||||
@@ -3406,22 +3840,27 @@ void CClipboardManager::PublishLocalClipboard()
|
||||
|
||||
KVMFRClipboardFileError fileError =
|
||||
KVMFR_CLIPBOARD_FILE_ERROR_INVALID;
|
||||
DWORD fileWinError = ERROR_SUCCESS;
|
||||
HRESULT fileOleError = S_FALSE;
|
||||
bool fileViaOLE = false;
|
||||
bool fileOpenedOLE = false;
|
||||
const char * fileRetryStage = nullptr;
|
||||
std::shared_ptr<CLocalClipboardFiles> files = CaptureClipboardFiles(
|
||||
after, fileViaOLE, fileOpenedOLE, fileError, fileWinError,
|
||||
after, rawFormatCount, fileViaOLE, fileRetryStage, fileError,
|
||||
fileOleError);
|
||||
if (GetClipboardSequenceNumber() != after)
|
||||
const DWORD capturedSequence = GetClipboardSequenceNumber();
|
||||
if (capturedSequence != after)
|
||||
{
|
||||
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(ERROR_RETRY),
|
||||
"Failed to publish local clipboard: "
|
||||
"stage=sequence-after-file-capture expected=%lu actual=%lu",
|
||||
static_cast<unsigned long>(after),
|
||||
static_cast<unsigned long>(capturedSequence));
|
||||
ClearLocalRetry();
|
||||
PostMessageW(m_hwnd, WM_CLIPBOARDUPDATE, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (fileOleError == CLIPBRD_E_CANT_OPEN)
|
||||
{
|
||||
DeferLocalClipboard(after, fileOpenedOLE);
|
||||
DeferLocalClipboard(after, fileRetryStage);
|
||||
return;
|
||||
}
|
||||
ClearLocalRetry();
|
||||
@@ -3457,36 +3896,16 @@ void CClipboardManager::PublishLocalClipboard()
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_WARN("Failed to retain local clipboard file selection: "
|
||||
"sequence=%lu fileError=%u winError=%lu",
|
||||
static_cast<unsigned long>(after),
|
||||
static_cast<unsigned int>(KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY),
|
||||
static_cast<unsigned long>(ERROR_OUTOFMEMORY));
|
||||
DEBUG_ERROR_HR(E_OUTOFMEMORY,
|
||||
"Failed to capture local clipboard files: stage=retain-dataset "
|
||||
"sequence=%lu fileError=%u",
|
||||
static_cast<unsigned long>(after), static_cast<unsigned int>(
|
||||
KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY));
|
||||
formats &= ~KVMFR_CLIPBOARD_FORMAT_MASK_FILES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
formats &= ~KVMFR_CLIPBOARD_FORMAT_MASK_FILES;
|
||||
if ((recognizedFormats & KVMFR_CLIPBOARD_FORMAT_MASK_FILES) ||
|
||||
SUCCEEDED(fileOleError))
|
||||
{
|
||||
DEBUG_WARN("Failed to capture local clipboard file selection: "
|
||||
"sequence=%lu fileError=%u winError=%lu oleError=0x%08lx",
|
||||
static_cast<unsigned long>(after),
|
||||
static_cast<unsigned int>(fileError),
|
||||
static_cast<unsigned long>(fileWinError),
|
||||
static_cast<unsigned long>(fileOleError));
|
||||
}
|
||||
else if (FAILED(fileOleError) && fileOleError != DV_E_FORMATETC &&
|
||||
fileOleError != DATA_E_FORMATETC)
|
||||
{
|
||||
DEBUG_TRACE_HR(fileOleError,
|
||||
"OLE clipboard does not expose local files: sequence=%lu "
|
||||
"rawFormats=%d", static_cast<unsigned long>(after),
|
||||
rawFormatCount);
|
||||
}
|
||||
}
|
||||
DEBUG_INFO("Local clipboard update: sequence=%lu rawFormats=%d "
|
||||
"recognized=0x%08x effective=0x%08x",
|
||||
static_cast<unsigned long>(after), rawFormatCount,
|
||||
@@ -3573,7 +3992,8 @@ void CClipboardManager::ClearLocalRetry()
|
||||
m_localRetryDeadline = 0;
|
||||
}
|
||||
|
||||
void CClipboardManager::DeferLocalClipboard(DWORD sequence, bool openedOLE)
|
||||
void CClipboardManager::DeferLocalClipboard(DWORD sequence,
|
||||
const char * stage)
|
||||
{
|
||||
const uint64_t now = GetTickCount64();
|
||||
if (!m_localRetryDeadline || m_localRetrySequence != sequence)
|
||||
@@ -3583,7 +4003,7 @@ void CClipboardManager::DeferLocalClipboard(DWORD sequence, bool openedOLE)
|
||||
m_localRetryDeadline = now + LOCAL_RETRY_TIMEOUT_MS;
|
||||
DEBUG_TRACE("Clipboard is busy; deferring local publication: "
|
||||
"sequence=%lu stage=%s", static_cast<unsigned long>(sequence),
|
||||
openedOLE ? "IDataObject::GetData" : "OleGetClipboard");
|
||||
stage ? stage : "unknown");
|
||||
}
|
||||
|
||||
if (now < m_localRetryDeadline &&
|
||||
|
||||
@@ -295,12 +295,14 @@ private:
|
||||
void RetryLocalClipboard();
|
||||
void RetryRemoteOffer();
|
||||
|
||||
bool OpenClipboardRetry() const;
|
||||
bool OpenClipboardRetry(DWORD * error = nullptr,
|
||||
const char * stage = nullptr, bool useWindow = true) const;
|
||||
bool IsOurClipboard();
|
||||
uint32_t EnumerateFormats() const;
|
||||
std::shared_ptr<CLocalClipboardFiles> CaptureClipboardFiles(
|
||||
DWORD sequence, bool& viaOLE, bool& openedOLE,
|
||||
KVMFRClipboardFileError& error, DWORD& winError, HRESULT& oleError);
|
||||
DWORD sequence, int rawFormatCount, bool& viaOLE,
|
||||
const char *& retryStage, KVMFRClipboardFileError& error,
|
||||
HRESULT& oleError);
|
||||
void PublishLocalClipboard();
|
||||
void PublishClear(uint64_t generation);
|
||||
bool ApplyRemoteOffer(uint32_t formats, uint64_t generation);
|
||||
@@ -308,7 +310,7 @@ private:
|
||||
void InvalidateOutgoing(uint32_t reason);
|
||||
void InvalidateLocalClipboard(uint32_t reason);
|
||||
void ClearLocalRetry();
|
||||
void DeferLocalClipboard(DWORD sequence, bool openedOLE);
|
||||
void DeferLocalClipboard(DWORD sequence, const char * stage);
|
||||
void ExpireLocalRetry(DWORD sequence);
|
||||
void ClearRemoteRetry();
|
||||
bool SetOriginMarker(uint64_t generation) const;
|
||||
|
||||
@@ -137,8 +137,8 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _
|
||||
const OleScope oleScope;
|
||||
|
||||
const HRESULT security = CoInitializeSecurity(nullptr, 0, nullptr, nullptr,
|
||||
RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE,
|
||||
nullptr);
|
||||
RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IDENTIFY, nullptr,
|
||||
EOAC_DYNAMIC_CLOAKING, nullptr);
|
||||
if (FAILED(security))
|
||||
{
|
||||
DEBUG_ERROR_HR(security, "Failed to initialize COM security");
|
||||
|
||||
Reference in New Issue
Block a user