You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 20:00:02