Документация Microsoft привела меня к считаю, что мне нужно создать два объекта данных типа CFSTR_FILECONTENTS и CFSTR_FILEDESCRIPTOR, чтобы хранить виртуальный файл в памяти. Из исходного кода реализации MSW wxWidget видно, что wxDataObjectSimple выделяет буфер GlobalAlloc и сохраняет этот буфер в структуре STGMEDIUM, которая ожидается для этих двух форматов. В результате я создал классы VirtualFileContents и VirtualFileDescriptor, производные от wxDataObjectSimple, которые предоставляют содержимое файла и данные структуры 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(this->m_contents.GetDataSize() >> 32),
.nFileSizeLow = 0, //static_cast(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();
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... o-explorer