通過稍微修改Raymond Chen 的文章中的代碼,我得到了一個用于將矢量作為檔案復制到剪貼板的類:
typedef std::wstring String;
class CFileDataObject : public IDataObject
{
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
IUnknown *punk = NULL;
if (riid == IID_IUnknown)
punk = static_cast<IUnknown*>(this);
else if (riid == IID_IDataObject)
{
punk = static_cast<IDataObject*>(this);
}
*ppv = punk;
if (punk)
{
punk->AddRef();
return S_OK;
}
else return E_NOINTERFACE;
};
STDMETHODIMP_(ULONG) AddRef()
{
return m_cRef;
};
STDMETHODIMP_(ULONG) Release()
{
ULONG cRef = --m_cRef;
if (cRef == 0)
{
delete this;
}
return cRef;
}
// IDataObject
STDMETHODIMP GetData(FORMATETC *pfe, STGMEDIUM *pmed)
{
ZeroMemory(pmed, sizeof(*pmed));
switch (GetDataIndex(pfe))
{
case DATA_FILEGROUPDESCRIPTOR:
{
FILEGROUPDESCRIPTOR fgd;
ZeroMemory(&fgd, sizeof(fgd));
fgd.cItems = 1;
fgd.fgd[0].dwFlags = FD_FILESIZE | FD_WRITESTIME;
fgd.fgd[0].nFileSizeLow = DataSize & 0xFFFFFFFFULL;
fgd.fgd[0].nFileSizeHigh = (DataSize & 0xFFFFFFFF00000000ULL) >> 32;
fgd.fgd[0].ftLastWriteTime.dwLowDateTime = 0x256d4000;
fgd.fgd[0].ftLastWriteTime.dwHighDateTime = 0x01bf53eb;
StringCchCopy(fgd.fgd[0].cFileName,ARRAYSIZE(fgd.fgd[0].cFileName),
m_FileName.c_str());
pmed->tymed = TYMED_HGLOBAL;
return CreateHGlobalFromBlob(&fgd, sizeof(fgd),GMEM_MOVEABLE, &pmed->hGlobal);
}
case DATA_FILECONTENTS:
pmed->tymed = TYMED_HGLOBAL;
pmed->hGlobal = hData;
return S_OK;
}
return DV_E_FORMATETC;
};
STDMETHODIMP GetDataHere(FORMATETC *pfe, STGMEDIUM *pmed)
{
return E_NOTIMPL;
};
STDMETHODIMP QueryGetData(FORMATETC *pfe)
{
return GetDataIndex(pfe) == DATA_INVALID ? S_FALSE : S_OK;
};
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pfeIn,FORMATETC *pfeOut)
{
*pfeOut = *pfeIn;
pfeOut->ptd = NULL;
return DATA_S_SAMEFORMATETC;
};
STDMETHODIMP SetData(FORMATETC *pfe, STGMEDIUM *pmed,BOOL fRelease)
{
return E_NOTIMPL;
};
STDMETHODIMP EnumFormatEtc(DWORD dwDirection,LPENUMFORMATETC *ppefe)
{
if (dwDirection == DATADIR_GET)
return SHCreateStdEnumFmtEtc(ARRAYSIZE(m_rgfe), m_rgfe, ppefe);
*ppefe = NULL;
return E_NOTIMPL;
}
STDMETHODIMP DAdvise(FORMATETC *pfe, DWORD grfAdv,IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
};
STDMETHODIMP DUnadvise(DWORD dwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
};
STDMETHODIMP EnumDAdvise(LPENUMSTATDATA *ppefe)
{
return OLE_E_ADVISENOTSUPPORTED;
};
CFileDataObject(const String& FileName, const std::vector<uint8_t>& Data)
: m_cRef(1),m_FileName(FileName)
{
SetFORMATETC(&m_rgfe[DATA_FILEGROUPDESCRIPTOR],
RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
SetFORMATETC(&m_rgfe[DATA_FILECONTENTS],
RegisterClipboardFormat(CFSTR_FILECONTENTS),TYMED_HGLOBAL, 0);
HRESULT hres = CreateHGlobalFromBlob(Data.data(), Data.size(), GMEM_MOVEABLE, &hData);
if (!SUCCEEDED(hres)) { /* Some error handling goes here */ };
DataSize = Data.size();
}
private:
enum {
DATA_FILEGROUPDESCRIPTOR,
DATA_FILECONTENTS,
DATA_NUM,
DATA_INVALID = -1,
};
int GetDataIndex(const FORMATETC *pfe)
{
for (int i = 0; i < ARRAYSIZE(m_rgfe); i )
{
if (pfe->cfFormat == m_rgfe[i].cfFormat && (pfe->tymed & m_rgfe[i].tymed) &&
pfe->dwAspect == m_rgfe[i].dwAspect && pfe->lindex == m_rgfe[i].lindex)
return i;
}
return DATA_INVALID;
}
private:
ULONG m_cRef;
FORMATETC m_rgfe[DATA_NUM];
String m_FileName;
HGLOBAL hData = 0;
size_t DataSize = 0;
};
用法:
std::vector<uint8_t> FileData;
// Filling the vector with some data
IDataObject* fdo = new CFileDataObject(FileName, FileData);
if (fdo)
{
HRESULT hres = OleSetClipboard(fdo);
fdo->Release();
}
該類服務于它的目的,但有一個細節:檔案只能粘貼一次。粘貼檔案的所有進一步嘗試均失敗。這是正常的還是上課有問題?
uj5u.com熱心網友回復:
您GetData()正在回傳原始hData物件DATA_FILECONTENTS而不是副本。由于該pmed->pUnkForRelease欄位被回傳為 NULL,呼叫者GetData()將在使用完成后釋放回傳的欄位HGLOBAL。
https://learn.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-ustgmedium-r1
pUnkForRelease指向一個介面實體的指標,該實體允許發送行程控制在接收行程呼叫
ReleaseStgMedium函式時釋放存盤的方式。如果pUnkForRelease為 NULL,則ReleaseStgMedium使用默認程式釋放存盤空間;否則,ReleaseStgMedium使用指定的IUnknown介面。
https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-releasestgmedium
punkForRelease提供者通過為結構成員指定 NULL 來指示介質的接收者負責釋放介質 。然后接收者呼叫ReleaseStgMedium,根據被釋放的存盤介質的型別,它會按照下表中的描述進行呼叫。
中等的 發布StgMedium Action TYMED_HGLOBAL 呼叫 GlobalFree句柄上的函式。
因此,為了防止這種情況發生,請設定pmed->pUnkForRelease一個 AddRef'ed ,當沒有人再使用它時IUnknown負責釋放它。hData在您的情況下,您可以使用該介面的物件this指標。CFileDataObjectIUnknown
當介質的原始提供者負責釋放介質時,提供者呼叫
ReleaseStgMedium,指定介質和適當的IUnknown指標作為punkForRelease結構成員。根據被釋放的存盤介質的型別,將采取以下操作之一,然后呼叫IUnknown::Release指定IUnknown指標上的方法。
中等的 發布StgMedium Action TYMED_HGLOBAL 沒有任何。
否則,另一種選擇是實作DATA_FILECONTENTS為 anIStream而不是HGLOBAL,其中IStream訪問原始vector資料。這個選項甚至在Shell Clipboard Formats檔案中都有描述:
CFSTR_FILECONTENTS
此格式識別符號與
CFSTR_FILEDESCRIPTOR格式一起使用,以將資料作為檔案傳輸,無論其實際存盤方式如何。資料由STGMEDIUM表示一個檔案內容的結構組成。該檔案通常表示為一個流物件,這避免了將檔案的內容放在記憶體中。在這種情況下,結構的 tymed 成員STGMEDIUM設定為TYMED_ISTREAM,并且檔案由IStream介面表示。該檔案也可以是存盤或全域記憶體物件(TYMED_ISTORAGE或TYMED_HGLOBAL)。關聯CFSTR_FILEDESCRIPTOR格式包含FILEDESCRIPTOR每個檔案的結構,該結構指定檔案的名稱和屬性。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/510142.html
標籤:C 温纳皮剪贴板奥莱
上一篇:從執行緒中一一讀取輸入
下一篇:在除錯幫助庫中取消下載符號
