#include "shellprv.h" #pragma hdrstop #include "dsdata.h" UINT g_acfDS_IDLData[ICF_DSMAX] = { CF_DSHDROP, 0 }; #define CFSTR_DS_OURHDROP TEXT("DS Disguised HDROP") #define CFSTR_DS_SHELLIDLIST TEXT("DS Shell IDList Array") const TCHAR c_szDS_HDrop[] = CFSTR_DS_OURHDROP; const TCHAR c_szDS_ShellIDList[] = CFSTR_DS_SHELLIDLIST; const TCHAR c_szDS_ShellIDListOffset[] = CFSTR_SHELLIDLISTOFFSET; void WINAPI DS_IDLData_InitializeClipboardFormats(void) { if (g_cfDS_HIDA == 0) { g_cfDS_HDROP = RegisterClipboardFormat(c_szDS_HDrop); g_cfDS_HIDA = RegisterClipboardFormat(c_szDS_ShellIDList); g_cfDS_OFFSETS = RegisterClipboardFormat(c_szDS_ShellIDListOffset); } } //=========================================================================== // CDS_IDLData : Class definition (for subclass) //=========================================================================== #define MAX_FORMATS ICF_DSMAX typedef struct _DS_IDLData // idt { IDataObject dtobj; UINT cRef; LPDATAOBJECT _pdtInner; BOOL _fEnumFormatCalled; // TRUE once called. FORMATETC fmte[MAX_FORMATS]; STGMEDIUM medium[MAX_FORMATS]; BOOL _fhDrop_Enabled; } CDS_IDLData; //=========================================================================== // CDS_IDLData : Vtable //=========================================================================== #pragma data_seg(".text", "CODE") IDataObjectVtbl c_CDS_IDLDataVtbl = { CDS_IDLData_QueryInterface, CDS_IDLData_AddRef, CDS_IDLData_Release, CDS_IDLData_GetData, CDS_IDLData_GetDataHere, CDS_IDLData_QueryGetData, CDS_IDLData_GetCanonicalFormatEtc, CDS_IDLData_SetData, CDS_IDLData_EnumFormatEtc, CDS_IDLData_Advise, CDS_IDLData_Unadvise, CDS_IDLData_EnumAdvise }; #pragma data_seg() // // We can't just compare the Vtable pointer, because we have subclasses. // #define ISIDLDATA(pdtobj) (pdtobj->lpVtbl->Release == CDS_IDLData_Release) // // Create an instance of CDS_IDLData with specified Vtable pointer. // HRESULT CDS_IDLData_CreateInstance(IDataObjectVtbl *lpVtbl, IDataObject **ppdtobj, IDataObject *pdtInner) { CDS_IDLData *pidt = (void*)LocalAlloc(LPTR, SIZEOF(CDS_IDLData)); if (pidt) { pidt->dtobj.lpVtbl = lpVtbl ? lpVtbl : &c_CDS_IDLDataVtbl; pidt->cRef = 1; pidt->_pdtInner = pdtInner; if (pdtInner) { pdtInner->lpVtbl->AddRef(pdtInner); } *ppdtobj = &pidt->dtobj; pidt->_fhDrop_Enabled = FALSE; // used to support std prop sheet itf return S_OK; } else { *ppdtobj = NULL; return E_OUTOFMEMORY; } } //=========================================================================== // CDS_IDLData : Members //=========================================================================== // // Member: CDS_IDLData::QueryInterface // STDMETHODIMP CDS_IDLData_QueryInterface(IDataObject * pdtobj, REFIID riid, LPVOID *ppvObj) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); if (IsEqualIID(riid, &IID_IDataObject) || IsEqualIID(riid, &IID_IUnknown)) { *ppvObj = this; this->cRef++; return S_OK; } *ppvObj = NULL; return E_NOINTERFACE; } // // Member: CDS_IDLData::AddRef // STDMETHODIMP_(ULONG) CDS_IDLData_AddRef(IDataObject *pdtobj) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); this->cRef++; return this->cRef; } // // Member: CDS_IDLData::Release // STDMETHODIMP_(ULONG) CDS_IDLData_Release(IDataObject *pdtobj) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); int i; if (InterlockedDecrement(&this->cRef)) return this->cRef; for (i = 0; i < MAX_FORMATS; i++) { if (this->medium[i].hGlobal) SHReleaseStgMedium(&this->medium[i]); } if (this->_pdtInner) this->_pdtInner->lpVtbl->Release(this->_pdtInner); LocalFree((HLOCAL)this); return 0; } // // Member: CDS_IDLData::GetData // STDMETHODIMP CDS_IDLData_GetData(IDataObject * pdtobj, LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); HRESULT hres = E_INVALIDARG; if ((pformatetcIn->cfFormat == g_cfDS_HDROP && (pformatetcIn->tymed & TYMED_HGLOBAL)) || (pformatetcIn->cfFormat == CF_HDROP && (pformatetcIn->tymed & TYMED_HGLOBAL) && (this->_fhDrop_Enabled))) { hres = CDS_IDLData_GetHDrop(pdtobj, pmedium, pformatetcIn->dwAspect == DVASPECT_SHORTNAME); } else { int i; pmedium->hGlobal = NULL; pmedium->pUnkForRelease = NULL; // BUGBUG (Davepl) Does this need to keep looping after it finds the right one? for (i = 0; i < MAX_FORMATS; i++) { if ((this->fmte[i].cfFormat == pformatetcIn->cfFormat) && (this->fmte[i].tymed & pformatetcIn->tymed)) { *pmedium = this->medium[i]; if ((pmedium->tymed == TYMED_HGLOBAL) && (pmedium->hGlobal == NULL)) { // might be render on demand clipboard format } if (pmedium->hGlobal) { // Indicate that the caller should not release hmem. this->cRef++; pmedium->pUnkForRelease = (IUnknown*)&this->dtobj; hres = S_OK; } } } if (hres==E_INVALIDARG && this->_pdtInner) { hres = this->_pdtInner->lpVtbl->GetData(this->_pdtInner, pformatetcIn, pmedium); } } return hres; } // // Member: CDS_IDLData::GetDataHere // STDMETHODIMP CDS_IDLData_GetDataHere(IDataObject * pdtobj, LPFORMATETC pformatetc, LPSTGMEDIUM pmedium ) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); HRESULT hres = E_NOTIMPL; if (this->_pdtInner) { hres = this->_pdtInner->lpVtbl->GetDataHere(this->_pdtInner, pformatetc, pmedium); } return hres; } // // Member: CDS_IDLData::QueryGetData // STDMETHODIMP CDS_IDLData_QueryGetData(IDataObject * pdtobj, LPFORMATETC pformatetcIn) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); HRESULT hres; int i; if ((pformatetcIn->cfFormat == g_cfDS_HDROP) && (pformatetcIn->tymed & TYMED_HGLOBAL)) { return S_OK; } else { for (i = 0; i < MAX_FORMATS; i++) { if ((this->fmte[i].cfFormat == pformatetcIn->cfFormat) && (this->fmte[i].tymed & pformatetcIn->tymed)) return S_OK; } hres = S_FALSE; if (this->_pdtInner) { hres = this->_pdtInner->lpVtbl->QueryGetData(this->_pdtInner, pformatetcIn); } return hres; } } // // Member: CDS_IDLData::GetCanonicalFormatEtc // STDMETHODIMP CDS_IDLData_GetCanonicalFormatEtc(IDataObject *pdtobj, LPFORMATETC pformatetc, LPFORMATETC pformatetcOut) { // // This is the simplest implemtation. It means we always return // the data in the format requested. // return DATA_S_SAMEFORMATETC; } // // Member: CDS_IDLData::SetData // STDMETHODIMP CDS_IDLData_SetData(IDataObject *pdtobj, FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); HRESULT hres; Assert(pformatetc->tymed == pmedium->tymed); if (fRelease) { int i; // first add it if that format is already present // on a NULL medium (render on demand) for (i = 0; i < MAX_FORMATS; i++) { if ((this->fmte[i].cfFormat == pformatetc->cfFormat) && (this->fmte[i].tymed == pformatetc->tymed)) { // // We are simply adding a format, ignore. // if (pmedium->hGlobal==NULL) { return S_OK; } // if we are set twice on the same object if (this->medium[i].hGlobal) SHReleaseStgMedium(&this->medium[i]); this->medium[i] = *pmedium; return S_OK; } } // now look for a free slot for (i = 0; i < MAX_FORMATS; i++) { if (this->fmte[i].cfFormat == 0) { // found a free slot this->medium[i] = *pmedium; this->fmte[i] = *pformatetc; return S_OK; } } // fixed size table hres = E_OUTOFMEMORY; } else hres = E_INVALIDARG; return hres; } // // Member: CDS_IDLData::EnumFormatEtc // STDMETHODIMP CDS_IDLData_EnumFormatEtc(IDataObject *pdtobj, DWORD dwDirection, LPENUMFORMATETC *ppenumFormatEtc) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); UINT cfmt; // // If this is the first time, build the format list by calling // QueryGetData with each clipboard format. // if (!this->_fEnumFormatCalled) { UINT ifmt; FORMATETC fmte = { 0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM medium = { TYMED_HGLOBAL, (HGLOBAL)NULL, (LPUNKNOWN)NULL }; for (ifmt = 0; ifmt < ICF_DSMAX; ifmt++) { fmte.cfFormat = g_acfDS_IDLData[ifmt]; if (pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte) == S_OK) { pdtobj->lpVtbl->SetData(pdtobj, &fmte, &medium, TRUE); } } this->_fEnumFormatCalled = TRUE; } // Get the number of formatetc for (cfmt = 0; cfmt < MAX_FORMATS; cfmt++) { if (this->fmte[cfmt].cfFormat == 0) break; } return SHCreateStdEnumFmtEtcEx(cfmt, this->fmte, this->_pdtInner, ppenumFormatEtc); } // // Member: CDS_IDLData::Advise // STDMETHODIMP CDS_IDLData_Advise(IDataObject *pdtobj, FORMATETC * pFormatetc, DWORD advf, LPADVISESINK pAdvSink, DWORD *pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; } // // Member: CDS_IDLData::Unadvise // STDMETHODIMP CDS_IDLData_Unadvise(IDataObject *pdtobj, DWORD dwConnection) { return OLE_E_ADVISENOTSUPPORTED; } // // Member: CDS_IDLData::EnumAdvise // STDMETHODIMP CDS_IDLData_EnumAdvise(IDataObject *pdtobj, LPENUMSTATDATA *ppenumAdvise) { return OLE_E_ADVISENOTSUPPORTED; } LPVOID DSDataObj_SaveShellData(IDataObject *pdtobj, BOOL fShared) { UINT fmts[2]; // UINT fmts[1]; DS_IDLData_InitializeClipboardFormats(); // init our registerd data formats fmts[0] = g_cfDS_HIDA; fmts[1] = g_cfOFFSETS; return DataObj_SaveToMemory(pdtobj, ARRAYSIZE(fmts), fmts, fShared); } // // marshal a set of clipboard formats into a memory block in the form of // typedef struct { ULONG offVtbl; UINT iNumFormats; // UINT cfFormat // UINT cbFormat // BYTE data[] // ... } MEM_CRAP; LPVOID DSDataObj_SaveToMemory(IDataObject *pdtobj, UINT cntFmt, UINT fmts[], BOOL fShared) { MEM_CRAP *pmem = NULL; // assume error UINT cbDataSize = 0; UINT iNumFormats = 0; UINT i; if (!ISIDLDATA(pdtobj)) return NULL; for (i = 0; i < cntFmt; i++) { STGMEDIUM medium; FORMATETC fmte = {fmts[i], NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium))) { cbDataSize += GlobalSize(medium.hGlobal); iNumFormats++; SHReleaseStgMedium(&medium); } } if (cbDataSize) { UINT cbTotal = SIZEOF(MEM_CRAP) + (iNumFormats * SIZEOF(UINT) * 2) + // cfFormat, cbFormat cbDataSize; pmem = fShared ? Alloc(cbTotal) : GlobalAlloc(GPTR, cbTotal); if (pmem) { UNALIGNED UINT *pdata = (UNALIGNED UINT *)((LPBYTE)pmem + SIZEOF(MEM_CRAP)); pmem->iNumFormats = iNumFormats; // ultra cool HACK.... pmem->offVtbl = (ULONG)pdtobj->lpVtbl - (ULONG)&c_CDS_IDLDataVtbl; for (i = 0; i < cntFmt; i++) { STGMEDIUM medium; FORMATETC fmte = {fmts[i], NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium))) { UINT cbData = GlobalSize(medium.hGlobal); *pdata++ = fmts[i]; *pdata++ = cbData; hmemcpy(pdata, (LPVOID)medium.hGlobal, cbData); pdata = (UNALIGNED UINT *)((LPBYTE)pdata + cbData); SHReleaseStgMedium(&medium); Assert(((UINT)pdata - (UINT)pmem) <= cbTotal); } } } } return pmem; } // This function creates an instance of CDS_IDLData from a block of memory // which is created by CDS_IDLData_SaveToMemory. // HRESULT DSDataObj_CreateFromMemory(LPVOID pv, IDataObject **ppdtobj) { MEM_CRAP *pmem = pv; HRESULT hres = CDS_IDLData_CreateInstance( (IDataObjectVtbl *)((ULONG)&c_CDS_IDLDataVtbl + pmem->offVtbl), ppdtobj, NULL); if (SUCCEEDED(hres)) { UINT i; BOOL bSomethingWasAdded = FALSE; UNALIGNED UINT *pdata = (UNALIGNED UINT *)((LPBYTE)pmem + SIZEOF(MEM_CRAP)); for (i = 0; i < pmem->iNumFormats; i++) { UINT cfFormat = *pdata++; UINT cbData = *pdata++; HGLOBAL hglobal = GlobalAlloc(GPTR, cbData); if (hglobal) { CopyMemory(hglobal, pdata, cbData); if (SUCCEEDED(DataObj_SetGlobal(*ppdtobj, cfFormat, hglobal))) bSomethingWasAdded = TRUE; else { DebugMsg(DM_TRACE, TEXT("set data fiailed creating from global")); GlobalFree(hglobal); } } pdata = (UNALIGNED UINT *)((LPBYTE)pdata + cbData); } if (bSomethingWasAdded) { hres = S_OK; } else { (*ppdtobj)->lpVtbl->Release(*ppdtobj); *ppdtobj = NULL; hres = E_OUTOFMEMORY; } } return hres; } // // Create an instance of CDS_IDLData with specified Vtable pointer. // HRESULT CDS_IDLData_CreateFromIDArray3(IDataObjectVtbl *lpVtbl, LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[], LPDATAOBJECT pdtInner, IDataObject **ppdtobj) { HRESULT hres = CDS_IDLData_CreateInstance(lpVtbl, ppdtobj, pdtInner); if (SUCCEEDED(hres)) { // allow empty array to be passed in if (apidl) { HIDA hida = HIDA_Create(pidlFolder, cidl, apidl); if (hida) { // init our registerd data formats DS_IDLData_InitializeClipboardFormats(); hres = DataObj_SetGlobal(*ppdtobj, g_cfDS_HIDA, hida); if (FAILED(hres)) goto SetFailed; } else { hres = E_OUTOFMEMORY; SetFailed: (*ppdtobj)->lpVtbl->Release(*ppdtobj); *ppdtobj = NULL; } } } return hres; } HRESULT CDS_IDLData_CreateFromIDArray2(IDataObjectVtbl *lpVtbl, LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[], IDataObject **ppdtobj) { return CDS_IDLData_CreateFromIDArray3(lpVtbl, pidlFolder, cidl, apidl, NULL, ppdtobj); } // // Create an instance of CDS_IDLData with default Vtable pointer. // HRESULT WINAPI CDS_IDLData_CreateFromIDArray(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[], IDataObject **ppdtobj) { return CDS_IDLData_CreateFromIDArray3(NULL, pidlFolder, cidl, apidl, NULL, ppdtobj); } // // Returns: TRUE, if this dataobject is one of ours. // BOOL CDS_IDLData_IsOurs(LPDATAOBJECT pdtobj) { if (pdtobj==NULL) return FALSE; return (pdtobj->lpVtbl->QueryInterface == CDS_IDLData_QueryInterface); } // // Returns: TRUE, if this dataobject is one of ours that does not contain // any innner dataobject. // BOOL CDS_IDLData_IsSimple(LPDATAOBJECT pdtobj) { if (CDS_IDLData_IsOurs(pdtobj)) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pdtobj); if (this->_pdtInner) { return FALSE; // aggregated } return TRUE; // pure and simple. } return FALSE; // not ours } // // Clone DataObject only for MOVE/COPY operation // HRESULT CDS_IDLData_Clone(LPDATAOBJECT pdtobjIn, UINT acf[], UINT ccf, LPDATAOBJECT *ppdtobjOut) { HRESULT hres = CDS_IDLData_CreateInstance(NULL, ppdtobjOut, NULL); if (SUCCEEDED(hres)) { UINT i; FORMATETC fmte = { 0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; for (i=0 ; ilpVtbl->GetData(pdtobjIn, &fmte, &medium); if (SUCCEEDED(hresT)) { HGLOBAL hmem; if (medium.pUnkForRelease) { // We need to clone the hGlobal. UINT cbMem = GlobalSize(medium.hGlobal); hmem = GlobalAlloc(GPTR, cbMem); if (hmem) { hmemcpy((LPVOID)hmem, GlobalLock(medium.hGlobal), cbMem); GlobalUnlock(medium.hGlobal); } SHReleaseStgMedium(&medium); } else { // We don't need to clone the hGlobal. hmem = medium.hGlobal; } if (hmem) DataObj_SetGlobal(*ppdtobjOut, acf[i], hmem); } } } return hres; } void DSDataObj_EnableHDROP(LPDATAOBJECT pDataObj) { CDS_IDLData * this = IToClass(CDS_IDLData, dtobj, pDataObj); this->_fhDrop_Enabled = TRUE; } HRESULT CDS_IDLData_CloneForMoveCopy(LPDATAOBJECT pdtobjIn, LPDATAOBJECT *ppdtobjOut) { UINT acf[] = { g_cfDS_HIDA, g_cfDS_OFFSETS, g_cfDS_HDROP }; return CDS_IDLData_Clone(pdtobjIn, acf, ARRAYSIZE(acf), ppdtobjOut); } LPIDA DataObj_GetDS_HIDA(LPDATAOBJECT pdtobj, STGMEDIUM *pmedium); // // Creates a HDROP (Win 3.1 compatible file list) from DS_HIDA. // // HRESULT CDS_IDLData_GetHDrop(IDataObject *pdtobj, STGMEDIUM *pmedium, BOOL fAltName) { HRESULT hres = E_OUTOFMEMORY; LPITEMIDLIST pidl = NULL; // realloced in HIDA_FillIDList STGMEDIUM medium; TCHAR szPath[MAX_PATH]; UINT i, cbAlloc = SIZEOF(DROPFILES) + SIZEOF(TCHAR); // header + null terminator LPIDA pida = DataObj_GetDS_HIDA(pdtobj, &medium); Assert(pida && pida->cidl); // we created this for (i = 0; i < pida->cidl; i++) { // HIDA_FillIDList may realloc pidl LPITEMIDLIST pidlTemp = HIDA_FillIDList(medium.hGlobal, i, pidl); if (pidlTemp == NULL) { // hres = E_OUTOFMEMORY; // already set break; } pidl = pidlTemp; // We may ask for the ALT name even if they did not ask for it in the // case where we failed to get the long name... if (!SHGetPathFromIDListEx(pidl, szPath, fAltName) && !(!fAltName && (SHGetPathFromIDListEx(pidl, szPath, TRUE)))) { // The path probably exceeds the max lenght, lets Bail... DebugMsg(DM_TRACE, TEXT("s.CFSIDLData_GetHDrop: SHGetPathFromIDList failed.")); hres = E_FAIL; goto Abort; } cbAlloc += lstrlen(szPath) * SIZEOF(TCHAR) + SIZEOF(TCHAR); } pmedium->hGlobal = GlobalAlloc(GPTR, cbAlloc); if (pmedium->hGlobal) { LPDROPFILES pdf = (LPDROPFILES)pmedium->hGlobal; LPTSTR pszFiles = (LPTSTR)(pdf + 1); pdf->pFiles = SIZEOF(DROPFILES); Assert(pdf->pt.x==0); Assert(pdf->pt.y==0); Assert(pdf->fNC==FALSE); Assert(pdf->fWide==FALSE); #ifdef UNICODE pdf->fWide = TRUE; #endif for (i = 0; i < pida->cidl; i++) { LPITEMIDLIST pidlTemp = HIDA_FillIDList(medium.hGlobal, i, pidl); Assert(pidl == pidlTemp); // Don't read directly into buffer as we my have been forced to use alternate name and the // total path we allocated may be smaller than we would tromp on which will screw up the heap. if (!SHGetPathFromIDListEx(pidl, szPath, fAltName)) SHGetPathFromIDListEx(pidl, szPath, TRUE); lstrcpy(pszFiles, szPath); pszFiles += lstrlen(pszFiles) + 1; Assert((UINT)((LPBYTE)pszFiles - (LPBYTE)pdf) < cbAlloc); } Assert((LPTSTR)((LPBYTE)pdf + cbAlloc - SIZEOF(TCHAR)) == pszFiles); Assert(*pszFiles == 0); // zero init alloc pmedium->tymed = TYMED_HGLOBAL; pmedium->pUnkForRelease = NULL; hres = S_OK; } Abort: HIDA_ReleaseStgMedium(pida, &medium); ILFree(pidl); return hres; } //---------------------------------------------------------------------------- // HRESULT FSDS_CreateFSIDArray(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST * apidl, LPDATAOBJECT pdtInner, LPDATAOBJECT * pdtobjOut) { LPCITEMIDLIST pidlAbs; TCHAR szPath[MAX_PATH]; UINT index; DWORD dwFlags; BOOL fAnyDSObjects = FALSE; for (index = 0; index < cidl; index++) { pidlAbs = ILCombine (pidlFolder, apidl[index]); SHGetPathFromIDList (pidlAbs, szPath); dwFlags = SHGetClassFlags ((LPIDFOLDER)pidlAbs, TRUE); if (dwFlags & SHCF_SUPPORTS_IOBJLIFE) { fAnyDSObjects = TRUE; break; } } if (fAnyDSObjects) { return CDS_IDLData_CreateFromIDArray3(&c_CDS_IDLDataVtbl, pidlFolder, cidl, apidl, pdtInner, pdtobjOut); } else { return CIDLData_CreateFromIDArray3(&c_CFSIDLDataVtbl, pidlFolder, cidl, apidl, pdtInner, pdtobjOut); } } LPIDA DataObj_GetDS_HIDA(LPDATAOBJECT pdtobj, STGMEDIUM *pmedium) { FORMATETC fmte = {g_cfDS_HIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; if (pmedium) { pmedium->pUnkForRelease = NULL; pmedium->hGlobal = NULL; } if (!pmedium) { if (SUCCEEDED(pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte))) return (LPIDA)TRUE; else return (LPIDA)FALSE; } else if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, pmedium))) { return (LPIDA)GlobalLock(pmedium->hGlobal); } return NULL; }