399 lines
12 KiB
C++
399 lines
12 KiB
C++
#include "shellprv.h"
|
|
#include "datautil.h"
|
|
|
|
#include "idlcomm.h"
|
|
|
|
STDAPI DataObj_SetBlob(IDataObject* pdtobj, UINT cf, LPCVOID pvBlob, UINT cbBlob)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
LPVOID pv = GlobalAlloc(GPTR, cbBlob);
|
|
if (pv) {
|
|
CopyMemory(pv, pvBlob, cbBlob);
|
|
hres = DataObj_SetGlobal(pdtobj, cf, pv);
|
|
|
|
if (FAILED(hres))
|
|
GlobalFree((HGLOBAL)pv);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
STDAPI DataObj_GetBlob(IDataObject* pdtobj, UINT cf, LPVOID pvBlob, UINT cbBlob)
|
|
{
|
|
STGMEDIUM medium = {0};
|
|
FORMATETC fmte = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
HRESULT hres = pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hres)) {
|
|
LPVOID pv = GlobalLock(medium.hGlobal);
|
|
if (pv) {
|
|
CopyMemory(pvBlob, pv, cbBlob);
|
|
GlobalUnlock(medium.hGlobal);
|
|
} else {
|
|
hres = E_UNEXPECTED;
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
STDAPI DataObj_SetDWORD(IDataObject* pdtobj, UINT cf, DWORD dw)
|
|
{
|
|
return DataObj_SetBlob(pdtobj, cf, &dw, sizeof(DWORD));
|
|
}
|
|
|
|
STDAPI_(DWORD) DataObj_GetDWORD(IDataObject* pdtobj, UINT cf, DWORD dwDefault)
|
|
{
|
|
DWORD dwRet;
|
|
if (FAILED(DataObj_GetBlob(pdtobj, cf, &dwRet, sizeof(DWORD))))
|
|
dwRet = dwDefault;
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
STDAPI DataObj_SetGlobal(IDataObject* pdtobj, UINT cf, HGLOBAL hGlobal)
|
|
{
|
|
FORMATETC fmte = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
|
|
medium.tymed = TYMED_HGLOBAL;
|
|
medium.hGlobal = hGlobal;
|
|
medium.pUnkForRelease = NULL;
|
|
|
|
// give the data object ownership of ths
|
|
return pdtobj->SetData(&fmte, &medium, TRUE);
|
|
}
|
|
|
|
STDAPI DataObj_SetDropTarget(IDataObject* pdtobj, const CLSID* pclsid)
|
|
{
|
|
return DataObj_SetBlob(pdtobj, g_cfTargetCLSID, pclsid, sizeof(CLSID));
|
|
}
|
|
|
|
STDAPI DataObj_GetDropTarget(IDataObject* pdtobj, CLSID* pclsid)
|
|
{
|
|
return DataObj_GetBlob(pdtobj, g_cfTargetCLSID, pclsid, sizeof(CLSID));
|
|
}
|
|
|
|
STDAPI DataObj_SetPoints(IDataObject* pdtobj, LPFNCIDLPOINTS pfnPts, LPARAM lParam, SCALEINFO* psi)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
STGMEDIUM medium = {0};
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida) {
|
|
POINT* ppt = (POINT*)GlobalAlloc(GPTR, SIZEOF(POINT) * (1 + pida->cidl));
|
|
if (ppt) {
|
|
UINT i;
|
|
POINT ptOrigin;
|
|
// Grab the anchor point
|
|
pfnPts(NULL, &ptOrigin, lParam);
|
|
ppt[0] = ptOrigin;
|
|
|
|
for (i = 1; i <= pida->cidl; i++) {
|
|
LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, i - 1);
|
|
|
|
pfnPts(pidl, &ppt[i], lParam);
|
|
ppt[i].x -= ptOrigin.x;
|
|
ppt[i].y -= ptOrigin.y;
|
|
ppt[i].x = (ppt[i].x * psi->xMul) / psi->xDiv;
|
|
ppt[i].y = (ppt[i].y * psi->yMul) / psi->yDiv;
|
|
}
|
|
|
|
hres = DataObj_SetGlobal(pdtobj, g_cfOFFSETS, ppt);
|
|
if (FAILED(hres))
|
|
GlobalFree((HGLOBAL)ppt);
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDAPI_(LPIDA) DataObj_GetHIDA(IDataObject* pdtobj, STGMEDIUM* pmedium)
|
|
{
|
|
FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
|
|
if (pmedium) {
|
|
pmedium->pUnkForRelease = NULL;
|
|
pmedium->hGlobal = NULL;
|
|
}
|
|
|
|
if (!pmedium) {
|
|
if (SUCCEEDED(pdtobj->QueryGetData(&fmte)))
|
|
return (LPIDA)TRUE;
|
|
else
|
|
return (LPIDA)FALSE;
|
|
} else if (SUCCEEDED(pdtobj->GetData(&fmte, pmedium))) {
|
|
return (LPIDA)GlobalLock(pmedium->hGlobal);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
STDAPI_(void) ReleaseStgMediumHGLOBAL(void* pv, STGMEDIUM* pmedium)
|
|
{
|
|
if (pmedium->hGlobal && (pmedium->tymed == TYMED_HGLOBAL)) {
|
|
#ifdef DEBUG
|
|
if (pv) {
|
|
void* pvT = (void*)GlobalLock(pmedium->hGlobal);
|
|
ASSERT(pvT == pv);
|
|
GlobalUnlock(pmedium->hGlobal);
|
|
}
|
|
#endif
|
|
GlobalUnlock(pmedium->hGlobal);
|
|
} else {
|
|
ASSERT(0);
|
|
}
|
|
|
|
ReleaseStgMedium(pmedium);
|
|
}
|
|
|
|
STDAPI_(void) HIDA_ReleaseStgMedium(LPIDA pida, STGMEDIUM* pmedium)
|
|
{
|
|
ReleaseStgMediumHGLOBAL((void*)pida, pmedium);
|
|
}
|
|
|
|
STDAPI_(UINT) DataObj_GetHIDACount(IDataObject* pdtobj)
|
|
{
|
|
STGMEDIUM medium = {0};
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida) {
|
|
UINT count = pida->cidl;
|
|
|
|
ASSERT(pida->cidl == HIDA_GetCount(medium.hGlobal));
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
return count;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// PERFPERF
|
|
// This routine used to copy 512 bytes at a time, but that had a major negative perf impact.
|
|
// I have measured a 2-3x speedup in copy times by increasing this buffer size to 16k.
|
|
// Yes, its a lot of stack, but it is memory well spent. -saml
|
|
#define STREAM_COPY_BUF_SIZE 16384
|
|
#define STREAM_PROGRESS_INTERVAL (100*1024/STREAM_COPY_BUF_SIZE) // display progress after this many blocks
|
|
|
|
HRESULT StreamCopyWithProgress(IStream* pstmFrom, IStream* pstmTo, ULARGE_INTEGER cb, PROGRESSINFO* ppi)
|
|
{
|
|
BYTE buf[STREAM_COPY_BUF_SIZE];
|
|
ULONG cbRead;
|
|
HRESULT hres = NOERROR;
|
|
int nSection = 0; // How many buffer sizes have we copied?
|
|
ULARGE_INTEGER uliNewCompleted;
|
|
|
|
if (ppi) {
|
|
uliNewCompleted.QuadPart = ppi->uliBytesCompleted.QuadPart;
|
|
}
|
|
|
|
while (cb.QuadPart) {
|
|
if (ppi && ppi->ppd) {
|
|
if (0 == (nSection % STREAM_PROGRESS_INTERVAL)) {
|
|
EVAL(SUCCEEDED(ppi->ppd->SetProgress64(uliNewCompleted.QuadPart, ppi->uliBytesTotal.QuadPart)));
|
|
|
|
if (ppi->ppd->HasUserCancelled()) {
|
|
hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
hres = pstmFrom->Read(buf, min(cb.LowPart, SIZEOF(buf)), &cbRead);
|
|
if (FAILED(hres) || (cbRead == 0)) {
|
|
// sometimes we are just done.
|
|
if (SUCCEEDED(hres))
|
|
hres = S_OK;
|
|
break;
|
|
}
|
|
|
|
if (ppi) {
|
|
uliNewCompleted.QuadPart += (ULONGLONG)cbRead;
|
|
}
|
|
|
|
cb.QuadPart -= cbRead;
|
|
|
|
hres = pstmTo->Write(buf, cbRead, &cbRead);
|
|
if (FAILED(hres) || (cbRead == 0))
|
|
break;
|
|
|
|
nSection++;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDAPI DataObj_SaveToFile(IDataObject* pdtobj, UINT cf, LONG lindex, LPCTSTR pszFile, DWORD dwFileSize, PROGRESSINFO* ppi)
|
|
{
|
|
STGMEDIUM medium = {0};
|
|
FORMATETC fmte;
|
|
HRESULT hres;
|
|
|
|
fmte.cfFormat = (CLIPFORMAT)cf;
|
|
fmte.ptd = NULL;
|
|
fmte.dwAspect = DVASPECT_CONTENT;
|
|
fmte.lindex = lindex;
|
|
fmte.tymed = TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE;
|
|
|
|
hres = pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hres)) {
|
|
switch (medium.tymed) {
|
|
case TYMED_HGLOBAL:
|
|
{
|
|
HANDLE hfile = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
|
|
if (hfile != INVALID_HANDLE_VALUE) {
|
|
DWORD dwWrite;
|
|
// BUGBUG raymondc: what about writes greater than 4 GB?
|
|
if (!WriteFile(hfile, GlobalLock(medium.hGlobal), dwFileSize ? dwFileSize : (DWORD)GlobalSize(medium.hGlobal), &dwWrite, NULL))
|
|
hres = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
GlobalUnlock(medium.hGlobal);
|
|
CloseHandle(hfile);
|
|
|
|
if (FAILED(hres))
|
|
EVAL(DeleteFile(pszFile));
|
|
} else {
|
|
hres = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
break;
|
|
}
|
|
case TYMED_ISTREAM:
|
|
{
|
|
IStream* pstm;
|
|
hres = SHCreateStreamOnFile(pszFile, OF_CREATE | OF_WRITE | OF_SHARE_DENY_WRITE, &pstm);
|
|
if (SUCCEEDED(hres)) {
|
|
const ULARGE_INTEGER ul = {(UINT)-1, (UINT)-1}; // the whole thing
|
|
|
|
hres = StreamCopyWithProgress(medium.pstm, pstm, ul, ppi);
|
|
pstm->Release();
|
|
|
|
if (FAILED(hres))
|
|
EVAL(DeleteFile(pszFile));
|
|
|
|
DebugMsg(TF_FSTREE, TEXT("IStream::CopyTo() -> %x"), hres);
|
|
}
|
|
break;
|
|
}
|
|
case TYMED_ISTORAGE:
|
|
{
|
|
WCHAR wszNewFile[MAX_PATH];
|
|
IStorage* pstg;
|
|
|
|
DebugMsg(TF_FSTREE, TEXT("got IStorage"));
|
|
|
|
SHTCharToUnicode(pszFile, wszNewFile, ARRAYSIZE(wszNewFile));
|
|
hres = StgCreateDocfile(wszNewFile,
|
|
STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
|
|
0, &pstg);
|
|
if (SUCCEEDED(hres)) {
|
|
hres = medium.pstg->CopyTo(0, NULL, NULL, pstg);
|
|
|
|
DebugMsg(TF_FSTREE, TEXT("IStorage::CopyTo() -> %x"), hres);
|
|
|
|
pstg->Commit(STGC_OVERWRITE);
|
|
pstg->Release();
|
|
|
|
if (FAILED(hres))
|
|
EVAL(DeleteFile(pszFile));
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
AssertMsg(FALSE, TEXT("got typed that I didn't ask for %d"), medium.tymed);
|
|
}
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, pszFile, NULL);
|
|
SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, pszFile, NULL);
|
|
}
|
|
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
STDAPI DataObj_GetShellURL(IDataObject* pdtobj, STGMEDIUM* pmedium, LPCSTR* ppszURL)
|
|
{
|
|
FORMATETC fmte = {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
HRESULT hres;
|
|
|
|
if (pmedium) {
|
|
hres = pdtobj->GetData(&fmte, pmedium);
|
|
if (SUCCEEDED(hres))
|
|
*ppszURL = (LPCSTR)GlobalLock(pmedium->hGlobal);
|
|
} else
|
|
hres = pdtobj->QueryGetData(&fmte); // query only
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDAPI DataObj_GetOFFSETs(IDataObject* pdtobj, POINT* ppt)
|
|
{
|
|
STGMEDIUM medium = {0};
|
|
FORMATETC fmt = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
ASSERT(g_cfOFFSETS);
|
|
ASSERT(ppt);
|
|
ppt->x = ppt->y = 0;
|
|
HRESULT hres = pdtobj->GetData(&fmt, &medium);
|
|
if (SUCCEEDED(hres)) {
|
|
POINT* pptTemp = (POINT*)GlobalLock(medium.hGlobal);
|
|
if (pptTemp) {
|
|
*ppt = *pptTemp;
|
|
GlobalUnlock(medium.hGlobal);
|
|
} else
|
|
hres = E_UNEXPECTED;
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDAPI_(BOOL) DataObj_CanGoAsync(IDataObject* pdtobj)
|
|
{
|
|
BOOL fDoOpAsynch = FALSE;
|
|
IAsyncOperation* pao;
|
|
|
|
if (SUCCEEDED(pdtobj->QueryInterface(IID_IAsyncOperation, (void**)&pao))) {
|
|
BOOL fIsOpAsync;
|
|
if (SUCCEEDED(pao->GetAsyncMode(&fIsOpAsync)) && (TRUE == fIsOpAsync)) {
|
|
fDoOpAsynch = SUCCEEDED(pao->StartOperation(NULL));
|
|
}
|
|
pao->Release();
|
|
}
|
|
return fDoOpAsynch;
|
|
}
|
|
|
|
|
|
// HACKHACK: (reinerf) - We used to always do async drag/drop operations on NT4 by cloning the
|
|
// dataobject. Some apps (WS_FTP 6.0) rely on the async nature in order for drag/drop to work since
|
|
// they stash the return value from DoDragDrop and look at it later when their copy hook is invoked
|
|
// by SHFileOperation(). So, we sniff the HDROP and if it has one path that contains "WS_FTPE\Notify"
|
|
// in it, then we do the operation async.
|
|
STDAPI_(BOOL) DataObj_GoAsyncForCompat(IDataObject* pdtobj)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
|
|
if (SUCCEEDED(pdtobj->GetData(&fmte, &medium))) {
|
|
// is there only one path in the hdrop?
|
|
if (DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0) == 1) {
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// is it the magical WS_FTP path ("%temp%\WS_FTPE\Notify") that WS_FTP sniffs
|
|
// for in their copy hook?
|
|
if (DragQueryFile((HDROP)medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)) &&
|
|
StrStrI(szPath, TEXT("WS_FTPE\\Notify"))) {
|
|
// yes, we have to do an async operation for app compat
|
|
TraceMsg(TF_WARNING, "DataObj_GoAsyncForCompat: found WS_FTP HDROP, doing async drag-drop");
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
|
|
return bRet;
|
|
}
|