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

OLE剪贴板刷新后渲染数据丢失问题排查求助

OLE剪贴板延迟渲染刷新无效问题

我通过OLE剪贴板实现了延迟渲染(当前仅支持全局内存)。在应用关闭时,会检查之前放入OLE剪贴板的对象是否仍存在,若存在则调用OleFlushClipboard刷新剪贴板:

CComPtr<IDataObject> dataInClipboard;

void CImage::OnCloseDocument(){
    // document is being closed
    // - render data to clipboard
    if (::OleIsCurrentClipboard(dataInClipboard)==S_OK)
        ::OleFlushClipboard();
    // - base
    ...
}

问题在于这个操作无效:尽管OleFlushClipboard触发了我实现的IDataObject::GetData方法(已完成全局内存分配、数据填充并通过STGMEDIUM返回),但剪贴板中无数据(可通过Nirsoft的InsideClipboard工具验证)。

以下是我实现的IDataObject接口代码:

struct TPosInterval{
    long a; // start (incl.)
    long z; // end (excl.)

    inline long GetLength() const{ return z-a; }
};

class COleDataObject:public IDataObject{
    volatile ULONG nReferences;
    IStream &s; // the source document that is about to be closed
    const TPosInterval range; // the range of Bytes to copy from the source document
    struct{
        ULONG n;
        FORMATETC list[2];
        UINT tymedMask;

        void Add(UINT tymed){
            assert( n<ARRAYSIZE(list) );
            assert( (tymedMask&tymed)==0 );
            FORMATETC &r=list[n++];
                r.cfFormat=RegisterMyClipboardFormat();
                r.lindex=-1;
                r.tymed=tymed;
            tymedMask|=tymed;
        }
    } supportedFormats;
public:
    COleDataObject(IStream &s,const TPosInterval &range)
        : nReferences(1)
        , s(s) , range(range) {
        s.AddRef();
        ::ZeroMemory( &supportedFormats, sizeof(supportedFormats) );
        supportedFormats.Add(TYMED_HGLOBAL); // global data to be rendered upon later request, e.g. OleFlushClipboard
    }
    virtual ~COleDataObject(){
        s.Release();
    }

    // IUnknown methods
    ULONG STDMETHODCALLTYPE AddRef() override{
        return ::InterlockedIncrement(&nReferences);
    }

    ULONG STDMETHODCALLTYPE Release() override{
        if (const auto n=::InterlockedDecrement(&nReferences))
            return n;
        delete this;
        return 0;
    }

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,PVOID *ppvObject) override{
        static const QITAB qit[]={
            QITABENT( COleDataObject, IDataObject ),
            { 0 },
        };
        return ::QISearch( this, qit, riid, ppvObject );
    }

    // IDataObject methods
    HRESULT STDMETHODCALLTYPE GetData(LPFORMATETC pFormatEtc,LPSTGMEDIUM outMedium) override{
        const HRESULT hResult=QueryGetData(pFormatEtc);
        if (FAILED(hResult))
            return hResult;
        if (!outMedium)
            return E_INVALIDARG;
        ::ZeroMemory( outMedium, sizeof(*outMedium) );
        auto nBytesToRead=range.GetLength();
        switch ( outMedium->tymed=pFormatEtc->tymed ){
            case TYMED_HGLOBAL:
                if (HGLOBAL hMem=::GlobalAlloc( GMEM_SHARE|GMEM_MOVEABLE, nBytesToRead )){
                    if (const PBYTE pData=(PBYTE)::GlobalLock(hMem)){
                        const CInstance::TContent f=&s; // a helper class that facilitates access to IStream
                        PBYTE p=pData; f.Seek(range.a);
                        while (nBytesToRead>0)
                            if (const auto nBytesRead=f.Read( p, nBytesToRead ))
                                p+=nBytesRead, nBytesToRead-=nBytesRead;
                            else
                                break;
                        ::GlobalUnlock(hMem);
                        if (nBytesToRead>0) // failure during reading?
                            if (const HGLOBAL hRealloc=::GlobalReAlloc( hMem, p-pData, 0 ))
                                hMem=hRealloc;
                            else{
                                ::GlobalFree( hMem );
                                return E_OUTOFMEMORY;
                            }
                        outMedium->hGlobal=hMem;
                        return S_OK;
                    }
                    break;
                }else
                    return E_OUTOFMEMORY;
        }
        return E_UNEXPECTED; // we shouldn't end up here!
    }

    HRESULT STDMETHODCALLTYPE GetDataHere(LPFORMATETC pformatetc,LPSTGMEDIUM pmedium) override{
        return E_NOTIMPL;
    }

    HRESULT STDMETHODCALLTYPE QueryGetData(LPFORMATETC pFormatEtc) override{
        if (!pFormatEtc)
            return DV_E_FORMATETC;
        if (pFormatEtc->lindex!=-1)
            return DV_E_LINDEX;
        if (pFormatEtc->cfFormat!=RegisterMyClipboardFormat())
            return DV_E_FORMATETC;
        if ((pFormatEtc->tymed&supportedFormats.tymedMask)==0)
            return DV_E_TYMED;
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(LPFORMATETC pFormatEtcIn,LPFORMATETC pFormatEtcOut) override{
        const HRESULT hResult=QueryGetData(pFormatEtcIn);
        if (FAILED(hResult))
            return hResult;
        if (!pFormatEtcOut)
            return E_INVALIDARG;
        ( *pFormatEtcOut=*pFormatEtcIn ).ptd=nullptr; // target-device (ptd) for server metafile format is not supported
        return DATA_S_SAMEFORMATETC;
    }

    HRESULT STDMETHODCALLTYPE SetData(LPFORMATETC pFormatEtc,LPSTGMEDIUM pMedium,BOOL fRelease) override{
        return E_UNEXPECTED; // construct another IDataObject to change the source Stream!
    }

    HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC **ppEnumFormatEtc) override{
        if (dwDirection!=DATADIR_GET)
            return E_NOTIMPL;
        if (!ppEnumFormatEtc)
            return E_INVALIDARG;
        return ::SHCreateStdEnumFmtEtc( supportedFormats.n, supportedFormats.list, ppEnumFormatEtc );
    }

    HRESULT STDMETHODCALLTYPE DAdvise(LPFORMATETC pFormatEtc,DWORD advf,LPADVISESINK pAdvSink,DWORD *pdwConnection) override{
        return OLE_E_ADVISENOTSUPPORTED;
    }

    HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) override{
        return OLE_E_ADVISENOTSUPPORTED;
    }

    HRESULT STDMETHODCALLTYPE EnumDAdvise(IEnumSTATDATA **ppenumAdvise) override{
        return OLE_E_ADVISENOTSUPPORTED;
    }
};

恳请各位提供解决建议!


内容的提问来源于stack exchange,提问作者tomascz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:07:05