如何在剪贴板中创建虚拟文件以粘贴到资源管理器中?

问题描述 投票:0回答:1

我目前正在为 Windows 10/11 开发一个 wxWidgets 应用程序,该应用程序应该生成一个文件。该文件是安全敏感文件,因此尽量减少磁盘上的副本是理想的选择。因此,我想在内存中虚拟生成这个文件,并将其复制到剪贴板,以便用户通过 Microsoft 远程桌面连接粘贴它。我已阅读 Microsoft 关于剪贴板格式的文档,并尝试使用 wxWidget 的

wxDataFormat
wxDataObjectSimple
wxDataObjectComposite
来实现此功能。

微软的文档让我相信我需要创建两个类型为

CFSTR_FILECONTENTS
CFSTR_FILEDESCRIPTOR
的数据对象来将虚拟文件保存在内存中。从 wxWidget 的 MSW 实现的源代码来看,wxDataObjectSimple 分配了一个
GlobalAlloc
缓冲区,并将该缓冲区存储在
STGMEDIUM
结构中,这对于这两种格式来说是预期的。因此,我创建了一个从 wxDataObjectSimple 派生的
VirtualFileContents
VirtualFileDescriptor
类,它们分别提供文件内容和
FILEGROUPDESCRIPTOR
结构数据。
VirtualFile
类派生自
wxDataObjectComposite
,将这两种类型附加在一起。

以这种方式实现时,我能够在多个 Win32 剪贴板查看器中观察到我拥有 Microsoft 所描述的正确剪贴板数据,其中“FileContents”和“FileGroupDescriptor”都是“HGlobal”句柄类型,具有预期的数据和内的正确格式。在资源管理器中打开上下文菜单时,可以使用粘贴选项,但会出现旋转的光标几帧,然后系统铃声响起以指示错误。不显示错误对话框,并且不粘贴任何文件。

为了进一步调试这种情况,我打开了 Microsoft Outlook 2016 的副本并从电子邮件中复制了附件。这也以类似的方式与

CFSTR_FILECONTENTS
CFSTR_FILEDESCRIPTOR
实现,但 Outlook 的文件包含“IStream”类型而不是“HGlobal”类型。这很好地粘贴到资源管理器中,我比较了
FileGroupDescriptor
结构的布局,没有发现内存布局错误。我看到 Outlook 在复制文件时同时包含
FileGroupDescriptorW
FileGroupDescriptor
,但我自己实现也没有解决问题。以下是我当前实现的片段:

标题

namespace Clipboard {


class VirtualFileContents : public wxDataObjectSimple {
  private:
    std::string m_str;
 
  public:
    VirtualFileContents(const std::string &str);
 
    std::size_t GetDataSize() const;
    bool GetDataHere(void *buf) const;
};
 
class VirtualFileDescriptorW : public wxDataObjectSimple {
  private:
    std::wstring m_fileName;
    VirtualFileContents &m_contents;
 
  public:
    VirtualFileDescriptorW(const std::wstring &file_name, VirtualFileContents &contents);
 
    std::size_t GetDataSize() const;
    bool GetDataHere(void *buf) const;
};
 
class VirtualFileDescriptorA : public wxDataObjectSimple {
  private:
    std::string m_fileName;
    VirtualFileContents &m_contents;
 
  public:
    VirtualFileDescriptorA(const std::string &file_name, VirtualFileContents &contents);
 
    std::size_t GetDataSize() const;
    bool GetDataHere(void *buf) const;
};
 
class VirtualFile : public wxDataObjectComposite {
  private:
    VirtualFileContents *m_contents;
    VirtualFileDescriptorW *m_wDescriptor;
    VirtualFileDescriptorA *m_aDescriptor;
 
  public:
    VirtualFile(const std::string &str, const std::filesystem::path &file_name);
};
 
};  // namespace Clipboard

来源

Clipboard::VirtualFileContents::VirtualFileContents(const std::string &str)
    : wxDataObjectSimple{CFSTR_FILECONTENTS}, m_str(str) {
}
 
std::size_t Clipboard::VirtualFileContents::GetDataSize() const {
    return this->m_str.size();
}
 
bool Clipboard::VirtualFileContents::GetDataHere(void *buf) const {
    std::memcpy(buf, this->m_str.data(), this->m_str.size());
    return true;
}
 
Clipboard::VirtualFileDescriptorW::VirtualFileDescriptorW(const std::wstring &file_name,
                                                          VirtualFileContents &contents)
    : wxDataObjectSimple{CFSTR_FILEDESCRIPTORW}, m_fileName(file_name), m_contents(contents) {
}
 
std::size_t Clipboard::VirtualFileDescriptorW::GetDataSize() const {
    return sizeof(FILEGROUPDESCRIPTORW);
}
 
bool Clipboard::VirtualFileDescriptorW::GetDataHere(void *buf) const {
    if (this->m_fileName.size() >= MAX_PATH) {
        std::runtime_error("File name too long.");
    }
 
    FILEGROUPDESCRIPTORW files = {
        .cItems = 1,
        .fgd = {{.dwFlags = 0,
                 .clsid = {},
                 .sizel = {},
                 .pointl = {},
                 .dwFileAttributes = 0x0,
                 .ftCreationTime = {},
                 .ftLastAccessTime = {},
                 .ftLastWriteTime = {},
                 .nFileSizeHigh = 0, //static_cast<DWORD>(this->m_contents.GetDataSize() >> 32),
                 .nFileSizeLow = 0, //static_cast<DWORD>(this->m_contents.GetDataSize() & 0xFFFFFFFF),
                 .cFileName = L""}}};
    std::memcpy(files.fgd[0].cFileName, this->m_fileName.data(),
                this->m_fileName.size() * sizeof(decltype(this->m_fileName)::value_type));
 
    std::memcpy(buf, &files, sizeof(files));
    return true;
}
 
Clipboard::VirtualFileDescriptorA::VirtualFileDescriptorA(const std::string &file_name,
                                                          VirtualFileContents &contents)
    : wxDataObjectSimple{CFSTR_FILEDESCRIPTORA}, m_fileName(file_name), m_contents(contents) {
}
 
std::size_t Clipboard::VirtualFileDescriptorA::GetDataSize() const {
    return sizeof(FILEGROUPDESCRIPTORA);
}
 
bool Clipboard::VirtualFileDescriptorA::GetDataHere(void *buf) const {
    if (this->m_fileName.size() >= MAX_PATH) {
        std::runtime_error("File name too long.");
    }
 
    FILEGROUPDESCRIPTORA files = {
        .cItems = 1,
        .fgd = {{.dwFlags = 0,
                 .clsid = {},
                 .sizel = {},
                 .pointl = {},
                 .dwFileAttributes = 0x0,
                 .ftCreationTime = {},
                 .ftLastAccessTime = {},
                 .ftLastWriteTime = {},
                 .nFileSizeHigh = 0,
                 .nFileSizeLow = 0,
                 .cFileName = ""}}};
    std::memcpy(files.fgd[0].cFileName, this->m_fileName.data(),
                this->m_fileName.size() * sizeof(decltype(this->m_fileName)::value_type));
 
    std::memcpy(buf, &files, sizeof(files));
    return true;
}
 
Clipboard::VirtualFile::VirtualFile(const std::string &str, const std::filesystem::path &file_name)
    : m_contents(new VirtualFileContents{str}),
      m_wDescriptor(new VirtualFileDescriptorW{file_name.wstring(), *this->m_contents}),
      m_aDescriptor(new VirtualFileDescriptorA{file_name.string(), *this->m_contents}) {
    this->Add(this->m_wDescriptor, true);
    this->Add(this->m_aDescriptor);
    this->Add(this->m_contents);
}

复制代码

if (wxTheClipboard->Open()) {
    wxTheClipboard->SetData(new Clipboard::VirtualFile("hello there", "test.txt"));
    wxTheClipboard->Close();
}

有人知道我的方法有任何缺陷,或者有任何工具可以帮助我更多地调试 Windows 剪贴板/资源管理器兼容性吗?这是像我这样一个更简单的问题,只是排除结构中的创建时间吗?

windows wxwidgets clipboard ole virtual-file
1个回答
0
投票

我相信这是不可能通过 wxWidgets 实现的,因为 MSW 端口的限制阻止了设置

lindex
结构的
FORMATETC
值,这是识别虚拟文件所必需的。

我已在此处创建了功能请求,以尝试并讨论如何解决此问题。

© www.soinside.com 2019 - 2024. All rights reserved.