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
相关产品推荐
相关产品推荐

