自定义XML数据类型剪贴板复制粘贴随机失效问题求助
剪贴板XML数据读取随机失败问题排查
问题现象
向剪贴板添加XML、文本、HTML多种格式数据后,调用GetXMLData()随机返回空字符串;同一复制粘贴操作,重启应用后可能正常执行,测试数据固定。
相关代码
复制到剪贴板的实现
std::pair<wxGridCellCoords, wxGridCellCoords> AddSelToClipbrd(const WorksheetBase* ws) { //some code wxString HTMLStr = GenerateHTMLTable(ws, TL, BR); wxString XMLStr = xml::GenerateXMLString(ws, TL, BR); wxString TabStr = GenerateTabString(ws, TL, BR); wxDataObjectComposite* dataobj = new wxDataObjectComposite(); dataobj->Add(new xml::XMLDataObject(XMLStr), true); dataobj->Add(new wxTextDataObject(TabStr)); dataobj->Add(new wxHTMLDataObject(HTMLStr)); if (wxTheClipboard->Open()) { wxTheClipboard->SetData(dataobj); wxTheClipboard->Flush(); wxTheClipboard->Close(); } }
自定义XML数据格式与数据对象
export struct XMLDataFormat : public wxDataFormat { XMLDataFormat() : wxDataFormat("XMLDataFormat") {} }; export class XMLDataObject : public wxDataObjectSimple { public: XMLDataObject(const wxString& xmlstring = wxEmptyString) : wxDataObjectSimple(), m_XMLString{ xmlstring } { SetFormat(XMLDataFormat()); } size_t GetLength() const { std::string s = (const char*)m_XMLString.mb_str(wxConvUTF8); return s.length() + 1; } wxString GetXML() const { return m_XMLString; } void SetXML(const wxString& xml) { m_XMLString = xml; } // Must provide overloads to avoid hiding them (and warnings about it) size_t GetDataSize() const { return GetLength(); } bool GetDataHere(void* buf) const { std::string s = (const char*)m_XMLString.mb_str(wxConvUTF8); memcpy(buf, s.c_str(), s.length() + 1); return true; } bool SetData(size_t len, const void* buf) { m_XMLString = wxString::FromUTF8((const char*)buf, len + 1); return true; } size_t GetDataSize(const wxDataFormat&) const { return GetDataSize(); } bool GetDataHere(const wxDataFormat&, void* buf) const { return GetDataHere(buf); } bool SetData(const wxDataFormat&, size_t len, const void* buf) { return SetData(len, buf); } private: wxString m_XMLString; };
从剪贴板读取XML数据的实现
wxString GetXMLData() { if (!wxTheClipboard->Open()) return wxEmptyString; if (!wxTheClipboard->IsSupported(XMLDataFormat())) return wxEmptyString; XMLDataObject xmlObj; wxTheClipboard->GetData(xmlObj); wxTheClipboard->Close(); return xmlObj.GetXML(); }
排查思路与解决方案
1. 规范自定义数据格式注册
直接用字符串构造wxDataFormat可能导致格式ID不稳定(不同进程或重启后系统分配的ID可能变化),引发IsSupported随机失效。
修改方案:通过wxDataFormat::Register()注册格式,确保ID全局稳定:
export struct XMLDataFormat : public wxDataFormat { XMLDataFormat() { static wxDataFormatId formatId = wxDataFormat::Register("XMLDataFormat"); SetId(formatId); } };
2. 完善剪贴板操作的错误校验
原代码未检查SetData、Flush、GetData的返回值,无法定位操作失败环节。
修改方案:添加错误检查与日志输出:
// 复制到剪贴板的代码修改 std::pair<wxGridCellCoords, wxGridCellCoords> AddSelToClipbrd(const WorksheetBase* ws) { //some code wxString HTMLStr = GenerateHTMLTable(ws, TL, BR); wxString XMLStr = xml::GenerateXMLString(ws, TL, BR); wxString TabStr = GenerateTabString(ws, TL, BR); wxDataObjectComposite* dataobj = new wxDataObjectComposite(); dataobj->Add(new xml::XMLDataObject(XMLStr), true); dataobj->Add(new wxTextDataObject(TabStr)); dataobj->Add(new wxHTMLDataObject(HTMLStr)); if (wxTheClipboard->Open()) { bool setOk = wxTheClipboard->SetData(dataobj); if (setOk) { bool flushOk = wxTheClipboard->Flush(); if (!flushOk) { // 记录Flush失败日志 } } else { // 记录SetData失败日志 } wxTheClipboard->Close(); } else { // 记录剪贴板打开失败日志 } } // 读取XML数据的代码修改 wxString GetXMLData() { if (!wxTheClipboard->Open()) return wxEmptyString; XMLDataFormat xmlFormat; if (!wxTheClipboard->IsSupported(xmlFormat)) { wxTheClipboard->Close(); return wxEmptyString; } XMLDataObject xmlObj; bool getOk = wxTheClipboard->GetData(xmlObj); wxTheClipboard->Close(); if (!getOk) return wxEmptyString; return xmlObj.GetXML(); }
3. 修复编码转换与数据长度问题
XMLDataObject中SetData方法传入的len是数据实际长度,调用FromUTF8时无需额外加1,否则可能读取越界内存;同时要校验mb_str(wxConvUTF8)的返回值,避免空指针操作。
修改方案:
// XMLDataObject中的GetLength与GetDataHere修改 size_t GetLength() const { const wxCharBuffer buf = m_XMLString.mb_str(wxConvUTF8); if (!buf) return 0; std::string s(buf.data()); return s.length() + 1; } bool GetDataHere(void* buf) const { const wxCharBuffer buffer = m_XMLString.mb_str(wxConvUTF8); if (!buffer) return false; std::string s(buffer.data()); memcpy(buf, s.c_str(), s.length() + 1); return true; } // SetData方法修改 bool SetData(size_t len, const void* buf) { if (len == 0 || !buf) { m_XMLString.Clear(); return false; } m_XMLString = wxString::FromUTF8(static_cast<const char*>(buf), len); return true; }
4. 确认剪贴板数据所有权
wx剪贴板在调用SetData后会接管数据对象的所有权,无需手动delete;但如果SetData失败,需手动释放对象避免内存泄漏。
5. 排除外部干扰
测试时关闭其他可能访问剪贴板的程序,确认是否是外部程序覆盖了剪贴板内容;在复制完成后立即调用GetXMLData验证,判断问题出在复制还是读取环节。
内容的提问来源于stack exchange,提问作者macroland
相关产品推荐
相关产品推荐

