与拖放实现混淆的东西

时间:2015-02-14 07:50:41

标签: drag-and-drop c++builder ole

我希望完成一个小的演示来实现OLE拖放(将文件从我的应用程序拖到Windows资源管理器中)。

但是出现了一个问题:DoDragDrop返回DRAGDROP_S_DROP,这意味着ole拖放操作已成功完成,但也获得了DROPEFFECT_NONE,这意味着drop target无法接受数据。

我调试了它,但我弄得一团糟,请帮帮我,(

这是gui: enter image description here

关键代码来了: 1.MainWindow.h

#ifndef MainWindowH
#define MainWindowH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Ole2.h>
#include "MyDataObject.h"
#include "MyDropSource.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TLabel *Label1;
    void __fastcall Label1StartDrag(TObject *Sender, TDragObject *&DragObject);
    void __fastcall Label1EndDrag(TObject *Sender, TObject *Target, int X, int Y);
    void __fastcall FormCreate(TObject *Sender);
    void __fastcall FormDestroy(TObject *Sender);
private:    // User declarations
    //准备两个接口实例
    IDataObject *pDataObject;
    IDropSource *pDropSource;

    //
    STGMEDIUM stgmed;
    FORMATETC fmtetc;

public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

2.DoDragDrop在MainWindow.cpp中调用

   void __fastcall TForm1::Label1StartDrag(TObject *Sender, TDragObject *&DragObject)
{
    Label1->Caption = "Start drag";

    //Source file
    char tFileName[256] = "D:\\119.dat";

    //Prepare FOTMATETC
    fmtetc.cfFormat = CF_HDROP;
    fmtetc.dwAspect = DVASPECT_CONTENT;
    fmtetc.lindex = -1;
    fmtetc.ptd = (void*)0;
    fmtetc.tymed = TYMED_HGLOBAL;

    //Prepare DROPFILES
    DROPFILES* tDropFiles;
    //Fill the filename
    HGLOBAL hGblFiles;
    LPSTR lpData;
    stgmed.hGlobal = GlobalAlloc(GHND, sizeof(DROPFILES)+strlen(tFileName)+ 2);
    if(0 == stgmed.hGlobal)
        MessageBoxA(NULL, "OUT_OF_MEMORY!!!", "OUT_OF_MEMORY", 0);

    tDropFiles = (DROPFILES*)GlobalLock(stgmed.hGlobal);
    ZeroMemory(tDropFiles, sizeof(DROPFILES)+strlen(tFileName)+2);
    strcpy(((char*)tDropFiles)+sizeof(DROPFILES), tFileName);
    GlobalUnlock(stgmed.hGlobal);

    tDropFiles->fNC     = true;
    tDropFiles->fWide   = false;
    tDropFiles->pFiles  = sizeof(DROPFILES);
    tDropFiles->pt.x    = 0;
    tDropFiles->pt.y    = 0;

    //set hGlobal
    stgmed.tymed = TYMED_HGLOBAL;
    stgmed.hGlobal = tDropFiles;
    stgmed.pUnkForRelease = 0;

    //Create Instance of IDropSource and IDataObject
    pDropSource  = new MyDropSource();
    pDropSource->AddRef();
    pDataObject  = new MyDataObject();
    pDataObject->AddRef();

    //SetData
    pDataObject->SetData(&fmtetc, &stgmed, true);

    OleInitialize(0);

    //Invoke DoDragDrop
    DWORD dwEffect;
    HRESULT tResult = DoDragDrop((IDataObject*)pDataObject, (IDropSource*)pDropSource, DROPEFFECT_MOVE, &dwEffect);

    //Ckeck drag&drop result
    if(tResult != DRAGDROP_S_DROP)
        {
        if(tResult == DRAGDROP_S_CANCEL)
            MessageBoxA(NULL, "DRAGDROP_S_CANCEL!", "DRAGDROP_S_DROP", 0);
        else
            MessageBoxA(NULL, "E_UNSPEC!", "DRAGDROP_S_DROP", 0);
        return;
        }

    if((dwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
        MessageBoxA(NULL, "Ole drag&drop OK!!", "DRAGDROP_S_DROP", 0);
    else
        {
        if((dwEffect & DROPEFFECT_NONE) == DROPEFFECT_NONE)
            MessageBoxA(NULL, "DROPEFFECT_NONE!!", "DRAGDROP_S_DROP", 0);
        }

    //Clean
    pDropSource->Release();
    pDataObject->Release();

    OleUninitialize();

    return;
}

3.MyDataObject.h

#ifndef _MYDATAOBJECT_H_
#define _MYDATAOBJECT_H_

#include <stdio.h>
#include "IDragDemo.h"
#include "MyDropSource.h"

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p);  (p)=NULL;} }
#endif

HRESULT CreateEnumFormatEtc(UINT cfmt, FORMATETC *afmt, IEnumFORMATETC **ppEnumFormatEtc);

class MyDataObject : public IDataObject
{
public:
    //IUnknown implementation
    ULONG __stdcall AddRef();
    ULONG __stdcall Release();
    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);

    //IDataObject members
    STDMETHODIMP GetData               (FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
    STDMETHODIMP GetDataHere           (FORMATETC *pformatetc,   STGMEDIUM *pmedium);
    STDMETHODIMP QueryGetData          (FORMATETC *pformatetc);
    STDMETHODIMP GetCanonicalFormatEtc (FORMATETC *pformatectIn, FORMATETC *pformatetcOut);
    STDMETHODIMP SetData               (FORMATETC *pformatetc,   STGMEDIUM *pmedium,  BOOL fRelease);
    STDMETHODIMP EnumFormatEtc         (DWORD     dwDirection,   IEnumFORMATETC **ppenumFormatEtc);
    STDMETHODIMP DAdvise               (FORMATETC *pformatetc,   DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
    STDMETHODIMP DUnadvise             (DWORD     dwConnection);
    STDMETHODIMP EnumDAdvise           (IEnumSTATDATA **ppenumAdvise);
public:
    MyDataObject();
    ~MyDataObject();
private:
    LONG refcount;

    FORMATETC* m_AcceptFormat;
    STGMEDIUM* m_StorageMedium;

    HGLOBAL DupGlobalMem(HGLOBAL hMem);

    //Helper function
    HRESULT CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc);
    HRESULT SetBlob(CLIPFORMAT cf, const void *pvBlob, UINT cbBlob);

    LONG m_RefCount;
};
//----------------MyEnumFormatEtc-----------------------------------------------------------
class MyEnumFormatEtc : public IEnumFORMATETC
{
public:
    // IUnknown members
    HRESULT __stdcall  QueryInterface (REFIID iid, void ** ppv)
    {
        if((iid==IID_IUnknown)||(iid==IID_IEnumFORMATETC))
        {
            *ppv=this;
            AddRef();
            return S_OK;
        }
        else
        {
            *ppv=NULL;
            return E_NOINTERFACE;
        }
    }
    ULONG   __stdcall AddRef (void) { return ++_iRefCount; }
    ULONG   __stdcall Release (void)  { if(--_iRefCount==0){delete this;  return 0;} return _iRefCount; }

    // IEnumFormatEtc members
    HRESULT __stdcall  Next  (ULONG celt, FORMATETC * rgelt, ULONG * pceltFetched);
    HRESULT __stdcall  Skip  (ULONG celt)
    {
        _nIndex += celt;
        return (_nIndex <= _nNumFormats) ? S_OK : S_FALSE;
    }
    HRESULT __stdcall  Reset (void)
    {
        _nIndex = 0;
        return S_OK;
    }
    HRESULT __stdcall  Clone (IEnumFORMATETC ** ppEnumFormatEtc)
    {
        HRESULT hResult;
        hResult = CreateEnumFormatEtc(_nNumFormats, _pFormatEtc, ppEnumFormatEtc);
        if(hResult == S_OK)
        {
            ((MyEnumFormatEtc *)*ppEnumFormatEtc)->_nIndex = _nIndex;
        }
        return hResult;
    }

    // Construction / Destruction
    MyEnumFormatEtc(FORMATETC *pFormatEtc, ULONG nNumFormats);
    ~MyEnumFormatEtc();

private:
    LONG        _iRefCount;        
    ULONG       _nIndex;        
    ULONG       _nNumFormats;      
    FORMATETC * _pFormatEtc; 
};
//---------------------------------------------------------------------------

#endif

4.MyDataObject.cpp

 #include "MyDataObject.h"
 #include "MyDropSource.h"
 #include <Urlmon.h>


MyDataObject::MyDataObject(MyDropSource* vDropSource)
{
    m_RefCount = 0;
    m_DropSource = vDropSource;
}


MyDataObject::~MyDataObject()
{
    refcount = 0;

    SAFE_DELETE(m_StorageMedium);
    SAFE_DELETE(m_AcceptFormat);
}


ULONG __stdcall MyDataObject::AddRef()
{
    return InterlockedIncrement(&m_RefCount);
}

ULONG __stdcall MyDataObject::Release()
{
    ULONG nRefCount = InterlockedDecrement(&m_RefCount);

    if (nRefCount == 0)
        delete this;

    return nRefCount;
}

STDMETHODIMP MyDataObject::QueryInterface(REFIID riid, void **ppvObject) {

if (!ppvObject)
        return E_POINTER;

    if (riid == IID_IDataObject)
        *ppvObject = (IDataObject*)this;
    else if (riid == IID_IUnknown)
        *ppvObject = (IUnknown*)this;
    else
    {
        *ppvObject = 0;
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;


}


STDMETHODIMP MyDataObject::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{

    if ( (NULL == pformatetcIn) || (NULL == pmedium) )
    {
        return E_INVALIDARG;
    }

    pmedium->hGlobal = NULL;

    if( (pformatetcIn->tymed & m_AcceptFormat->tymed) &&
        (pformatetcIn->dwAspect == m_AcceptFormat->dwAspect) &&
        (pformatetcIn->cfFormat == m_AcceptFormat->cfFormat) )
        {
            return CopyMedium(pmedium, m_StorageMedium, m_AcceptFormat);
        }

    return DV_E_FORMATETC;
}

STDMETHODIMP MyDataObject::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::QueryGetData(FORMATETC *pformatetc)
{
    if(NULL == pformatetc )
    {
        return E_INVALIDARG;
    }
    if(!(DVASPECT_CONTENT & pformatetc->dwAspect))
    {
        return DV_E_DVASPECT;
    }
    HRESULT hr = DV_E_TYMED;

    if(m_AcceptFormat->tymed & pformatetc->tymed )
        {
        if(m_AcceptFormat->cfFormat == pformatetc->cfFormat )
            {
            return S_OK;
            }
        else
            {
            hr = DV_E_CLIPFORMAT;
            }
        }
    else
        {
            hr = DV_E_TYMED;
        }
    return hr;
}

STDMETHODIMP MyDataObject::GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut)
{
    pformatetcOut->ptd = NULL;
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
    if ( (NULL == pformatetc) || (NULL == pmedium) )
        return E_INVALIDARG;


    if ( pformatetc->tymed != pmedium->tymed )
        return E_FAIL;

    m_AcceptFormat = new FORMATETC;
    m_StorageMedium = new STGMEDIUM;
    ZeroMemory(m_AcceptFormat, sizeof(FORMATETC));
    ZeroMemory(m_StorageMedium, sizeof(STGMEDIUM));

    if ( TRUE == fRelease )
    {
        *m_StorageMedium = *pmedium;
    }
    else
    {
        CopyMedium(m_StorageMedium, pmedium, pformatetc);
    }

    return S_OK;
}

STDMETHODIMP MyDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
{
    if(NULL == ppenumFormatEtc)
    {
        return E_INVALIDARG;
    }
    *ppenumFormatEtc = NULL;
    HRESULT hr = E_NOTIMPL;
    if (DATADIR_GET == dwDirection )
    {
        FORMATETC rgfmtetc[] =
        {
            { CF_HDROP, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL },
        };
        hr = CreateEnumFormatEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc);
    }
    return hr;
}
//Advises:OLE_E_ADVISENOTSUPPORTED
STDMETHODIMP MyDataObject::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
    UNREFERENCED_PARAMETER(pformatetc);
    UNREFERENCED_PARAMETER(advf);
    UNREFERENCED_PARAMETER(pAdvSink);
    UNREFERENCED_PARAMETER(pdwConnection);
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::DUnadvise(DWORD dwConnection)
{
    UNREFERENCED_PARAMETER(dwConnection);
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
{
    UNREFERENCED_PARAMETER(ppenumAdvise);
    return E_NOTIMPL;
}
//Advises:OLE_E_ADVISENOTSUPPORTED

HGLOBAL MyDataObject::DupGlobalMem(HGLOBAL hMem)
{
    DWORD   len    = GlobalSize(hMem);
    PVOID   source = GlobalLock(hMem);
    PVOID   dest   = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, len);

    memcpy(dest, source, len);
    GlobalUnlock(hMem);
    return dest;
}

HRESULT MyDataObject::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)
{
    if ( (NULL == pMedDest) || (NULL ==pMedSrc) || (NULL == pFmtSrc) )
    {
        return E_INVALIDARG;
    }
    switch(pMedSrc->tymed)
    {
    case TYMED_HGLOBAL:
        pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_GDI:
        pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_MFPICT:
        pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_ENHMF:
        pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_FILE:
        pMedSrc->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_ISTREAM:
        pMedDest->pstm = pMedSrc->pstm;
        pMedSrc->pstm->AddRef();
        break;
    case TYMED_ISTORAGE:
        pMedDest->pstg = pMedSrc->pstg;
        pMedSrc->pstg->AddRef();
        break;
    case TYMED_NULL:
    default:
        break;
    }
    pMedDest->tymed = pMedSrc->tymed;
    pMedDest->pUnkForRelease = NULL;
    if(pMedSrc->pUnkForRelease != NULL)
    {
        pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;
        pMedSrc->pUnkForRelease->AddRef();
    }
    return S_OK;
}
HRESULT MyDataObject::SetBlob(CLIPFORMAT cf, const void *pvBlob, UINT cbBlob)
{
    void *pv = GlobalAlloc(GPTR, cbBlob);
    HRESULT hr = pv ? S_OK : E_OUTOFMEMORY;
    if ( SUCCEEDED(hr) )
    {
        CopyMemory(pv, pvBlob, cbBlob);
        FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        STGMEDIUM medium = {};
        medium.tymed = TYMED_HGLOBAL;
        medium.hGlobal = pv;
        hr = this->SetData(&fmte, &medium, TRUE);
        if (FAILED(hr))
        {
            GlobalFree(pv);
        }
    }
    return hr;
}

HRESULT CreateEnumFormatEtc(UINT cfmt, FORMATETC *afmt, IEnumFORMATETC **ppEnumFormatEtc)
{
    if (cfmt == 0 || afmt == 0 || ppEnumFormatEtc == 0)
        return E_INVALIDARG;

    *ppEnumFormatEtc = new MyEnumFormatEtc(afmt, cfmt);
    return (*ppEnumFormatEtc) ? S_OK: E_OUTOFMEMORY;
}

void DeepCopyFormatEtc(FORMATETC *dest, FORMATETC *source)
{
    *dest = *source;
    if(source->ptd)
    {
        dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
        *(dest->ptd) = *(source->ptd);
    }
}

MyEnumFormatEtc::MyEnumFormatEtc(FORMATETC *pFormatEtc, ULONG nNumFormats)
    :_iRefCount(1),_nIndex(0),_nNumFormats(nNumFormats)
{
    _pFormatEtc  = new FORMATETC[nNumFormats];
    // make a new copy of each FORMATETC structure
    for(ULONG i = 0; i < nNumFormats; i++)
    {
        DeepCopyFormatEtc (&_pFormatEtc[i], &pFormatEtc[i]);
    }
}
MyEnumFormatEtc::~MyEnumFormatEtc()
{
    // first free any DVTARGETDEVICE structures
    for(ULONG i = 0; i < _nNumFormats; i++)
    {
        if(_pFormatEtc[i].ptd)
            CoTaskMemFree(_pFormatEtc[i].ptd);
    }
    // now free the main array
    delete[] _pFormatEtc;
}

HRESULT __stdcall MyEnumFormatEtc::Next(ULONG celt, FORMATETC *pFormatEtc, ULONG *pceltFetched)
{
    ULONG copied = 0;
    // copy the FORMATETC structures into the caller's buffer
    while (_nIndex < _nNumFormats && copied < celt)
    {
        DeepCopyFormatEtc (&pFormatEtc [copied], &_pFormatEtc [_nIndex]);
        copied++;
        _nIndex++;
    }
    // store result
    if(pceltFetched != 0)
        *pceltFetched = copied;
    // did we copy all that was requested?
    return (copied == celt) ? S_OK : S_FALSE;
}

5.MyDropSource.h

#ifndef _MYDROPSOURCE_H_
#define _MYDROPSOURCE_H_

#include <stdio.h>
#include "IDragDemo.h"

class MyDropSource : public IDropSource
{
public:

    //IUnknown implementation
    ULONG __stdcall AddRef();
    ULONG __stdcall Release();
    STDMETHODIMP    QueryInterface(REFIID riid, void **ppvObject);

    //IDropSource members
    STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);
    STDMETHODIMP GiveFeedback(DWORD dwEffect);

    //Cons/Destructors
    MyDropSource();
    ~MyDropSource();
private:
    LONG refcount;
};
#endif

6.MyDropSource.cpp

#include "MyDataObject.h"
 #include "MyDropSource.h"
 #include <Urlmon.h>

//Constructors
MyDataObject::MyDataObject(MyDropSource* vDropSource)
{
    m_RefCount = 0;
    m_DropSource = vDropSource;
}

//Destructors
MyDataObject::~MyDataObject()
{
    refcount = 0;

    SAFE_DELETE(m_StorageMedium);
    SAFE_DELETE(m_AcceptFormat);
}

//IUnkown implementation
ULONG __stdcall MyDataObject::AddRef()
{
    return InterlockedIncrement(&m_RefCount);
}

ULONG __stdcall MyDataObject::Release()
{
    ULONG nRefCount = InterlockedDecrement(&m_RefCount);

    if (nRefCount == 0)
        delete this;

    return nRefCount;
}

STDMETHODIMP MyDataObject::QueryInterface(REFIID riid, void **ppvObject) {
    if (!ppvObject)
    return E_POINTER;

if (riid == IID_IDataObject)
    *ppvObject = (IDataObject*)this;
else if (riid == IID_IUnknown)
    *ppvObject = (IUnknown*)this;
else
{
    *ppvObject = 0;
    return E_NOINTERFACE;
}

AddRef();
return S_OK;
}

STDMETHODIMP MyDataObject::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
{
    //入参检查
    if ( (NULL == pformatetcIn) || (NULL == pmedium) )
    {
        return E_INVALIDARG;
    }

    pmedium->hGlobal = NULL;

    if( (pformatetcIn->tymed & m_AcceptFormat->tymed) &&
        (pformatetcIn->dwAspect == m_AcceptFormat->dwAspect) &&
        (pformatetcIn->cfFormat == m_AcceptFormat->cfFormat) )
        {
            return CopyMedium(pmedium, m_StorageMedium, m_AcceptFormat);
        }

    return DV_E_FORMATETC;
}

STDMETHODIMP MyDataObject::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::QueryGetData(FORMATETC *pformatetc)
{
    if(NULL == pformatetc )
    {
        return E_INVALIDARG;
    }
    if(!(DVASPECT_CONTENT & pformatetc->dwAspect))
    {
        return DV_E_DVASPECT;
    }
    HRESULT hr = DV_E_TYMED;

    if(m_AcceptFormat->tymed & pformatetc->tymed )
        {
        if(m_AcceptFormat->cfFormat == pformatetc->cfFormat )
            {
            return S_OK;
            }
        else
            {
            hr = DV_E_CLIPFORMAT;
            }
        }
    else
        {
            hr = DV_E_TYMED;
        }
    return hr;
}

STDMETHODIMP MyDataObject::GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut)
{
    pformatetcOut->ptd = NULL;
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
{
    if ( (NULL == pformatetc) || (NULL == pmedium) )
        return E_INVALIDARG;


    if ( pformatetc->tymed != pmedium->tymed )
        return E_FAIL;

    m_AcceptFormat = new FORMATETC;
    m_StorageMedium = new STGMEDIUM;
    ZeroMemory(m_AcceptFormat, sizeof(FORMATETC));
    ZeroMemory(m_StorageMedium, sizeof(STGMEDIUM));

    if ( TRUE == fRelease )
    {
        *m_StorageMedium = *pmedium;
    }
    else
    {
        CopyMedium(m_StorageMedium, pmedium, pformatetc);
    }

    return S_OK;
}

STDMETHODIMP MyDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
{
    if(NULL == ppenumFormatEtc)
    {
        return E_INVALIDARG;
    }
    *ppenumFormatEtc = NULL;
    HRESULT hr = E_NOTIMPL;
    if (DATADIR_GET == dwDirection )
    {
        FORMATETC rgfmtetc[] =
        {
            { CF_HDROP, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL },
        };
        hr = CreateEnumFormatEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc);
    }
    return hr;
}
//Advises:OLE_E_ADVISENOTSUPPORTED
STDMETHODIMP MyDataObject::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
    UNREFERENCED_PARAMETER(pformatetc);
    UNREFERENCED_PARAMETER(advf);
    UNREFERENCED_PARAMETER(pAdvSink);
    UNREFERENCED_PARAMETER(pdwConnection);
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::DUnadvise(DWORD dwConnection)
{
    UNREFERENCED_PARAMETER(dwConnection);
    return E_NOTIMPL;
}

STDMETHODIMP MyDataObject::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
{
    UNREFERENCED_PARAMETER(ppenumAdvise);
    return E_NOTIMPL;
}
//Advises:OLE_E_ADVISENOTSUPPORTED

HGLOBAL MyDataObject::DupGlobalMem(HGLOBAL hMem)
{
    DWORD   len    = GlobalSize(hMem);
    PVOID   source = GlobalLock(hMem);
    PVOID   dest   = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, len);

    memcpy(dest, source, len);
    GlobalUnlock(hMem);
    return dest;
}

HRESULT MyDataObject::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)
{
    if ( (NULL == pMedDest) || (NULL ==pMedSrc) || (NULL == pFmtSrc) )
    {
        return E_INVALIDARG;
    }
    switch(pMedSrc->tymed)
    {
    case TYMED_HGLOBAL:
        pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_GDI:
        pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_MFPICT:
        pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_ENHMF:
        pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_FILE:
        pMedSrc->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName, pFmtSrc->cfFormat, NULL);
        break;
    case TYMED_ISTREAM:
        pMedDest->pstm = pMedSrc->pstm;
        pMedSrc->pstm->AddRef();
        break;
    case TYMED_ISTORAGE:
        pMedDest->pstg = pMedSrc->pstg;
        pMedSrc->pstg->AddRef();
        break;
    case TYMED_NULL:
    default:
        break;
    }
    pMedDest->tymed = pMedSrc->tymed;
    pMedDest->pUnkForRelease = NULL;
    if(pMedSrc->pUnkForRelease != NULL)
    {
        pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;
        pMedSrc->pUnkForRelease->AddRef();
    }
    return S_OK;
}
HRESULT MyDataObject::SetBlob(CLIPFORMAT cf, const void *pvBlob, UINT cbBlob)
{
    void *pv = GlobalAlloc(GPTR, cbBlob);
    HRESULT hr = pv ? S_OK : E_OUTOFMEMORY;
    if ( SUCCEEDED(hr) )
    {
        CopyMemory(pv, pvBlob, cbBlob);
        FORMATETC fmte = {cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        STGMEDIUM medium = {};
        medium.tymed = TYMED_HGLOBAL;
        medium.hGlobal = pv;
        hr = this->SetData(&fmte, &medium, TRUE);
        if (FAILED(hr))
        {
            GlobalFree(pv);
        }
    }
    return hr;
}

HRESULT CreateEnumFormatEtc(UINT cfmt, FORMATETC *afmt, IEnumFORMATETC **ppEnumFormatEtc)
{
    if (cfmt == 0 || afmt == 0 || ppEnumFormatEtc == 0)
        return E_INVALIDARG;

    *ppEnumFormatEtc = new MyEnumFormatEtc(afmt, cfmt);
    return (*ppEnumFormatEtc) ? S_OK: E_OUTOFMEMORY;
}

void DeepCopyFormatEtc(FORMATETC *dest, FORMATETC *source)
{
    // copy the source FORMATETC into dest
    *dest = *source;
    if(source->ptd)
    {
        // allocate memory for the DVTARGETDEVICE if necessary
        dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(sizeof(DVTARGETDEVICE));
        // copy the contents of the source DVTARGETDEVICE into dest->ptd
        *(dest->ptd) = *(source->ptd);
    }
}

MyEnumFormatEtc::MyEnumFormatEtc(FORMATETC *pFormatEtc, ULONG nNumFormats)
    :_iRefCount(1),_nIndex(0),_nNumFormats(nNumFormats)
{
    _pFormatEtc  = new FORMATETC[nNumFormats];
    // make a new copy of each FORMATETC structure
    for(ULONG i = 0; i < nNumFormats; i++)
    {
        DeepCopyFormatEtc (&_pFormatEtc[i], &pFormatEtc[i]);
    }
}
MyEnumFormatEtc::~MyEnumFormatEtc()
{
    // first free any DVTARGETDEVICE structures
    for(ULONG i = 0; i < _nNumFormats; i++)
    {
        if(_pFormatEtc[i].ptd)
            CoTaskMemFree(_pFormatEtc[i].ptd);
    }
    // now free the main array
    delete[] _pFormatEtc;
}

HRESULT __stdcall MyEnumFormatEtc::Next(ULONG celt, FORMATETC *pFormatEtc, ULONG *pceltFetched)
{
    ULONG copied = 0;
    // copy the FORMATETC structures into the caller's buffer
    while (_nIndex < _nNumFormats && copied < celt)
    {
        DeepCopyFormatEtc (&pFormatEtc [copied], &_pFormatEtc [_nIndex]);
        copied++;
        _nIndex++;
    }
    // store result
    if(pceltFetched != 0)
        *pceltFetched = copied;
    // did we copy all that was requested?
    return (copied == celt) ? S_OK : S_FALSE;
}

7.IDragDemo.h

#ifndef _DRAGDROP_H_
#define _DRAGDROP_H_
#include <windows.h>
#include <ole2.h>
#include <Shlobj.h>
#endif //_DRAGDROP_H_

完整代码可以到达:https://github.com/cyfingm/cb_ole_dragdrop

1 个答案:

答案 0 :(得分:1)

OleIsCurrentClipboard()正在返回S_FALSE,因为您事先正在调用OleFlushClipboard()。阅读文档:

OleFlushClipboard function

  

执行剪贴板关闭序列。 它还会释放由OleSetClipboard函数放置在剪贴板上的IDataObject指针。

     

...

     

OleFlushClipboard将数据对象中的数据呈现到剪贴板,并释放指向数据对象的IDataObject指针。

     

...

     

调用OleFlushClipboard之前,您可以通过调用OleIsCurrentClipboard函数轻松确定数据是否仍在剪贴板上。

基本上,一旦您拨打OleFlushClipboard(),剪贴板就不再包含指向IDataObject的指针。 <{1}}数据会直接复制到剪贴板上,并删除CF_HDROP

你为什么要涉及剪贴板?您无需将IDataObject放在剪贴板上即可使用IDataObject,因此请停止这样做。您将DoDragDrop()直接传递给IDataObject,这就是您需要做的一切。

此代码还存在其他问题。

这一行错了:

DoDragDrop()

应该是这样:

strcpy((char*)(tDropFiles+sizeof(DROPFILES)), tFileName);

或者这个:

strcpy(((char*)tDropFiles)+sizeof(DROPFILES), tFileName);

您也没有正确维护strcpy((char*)(tDropFiles+1), tFileName); IDataObject引用计数。创建这些对象时,它们的引用计数为0. IDropSource将增加OleSetClipboard()引用计数,然后IDataObject将减少它,在调用OleFlushClipboard()之前释放该对象。 DoDragDrop()需要在创建它们之后在两个对象上调用Label1EndDrag()(它具有对它们的引用,毕竟),然后在使用它们时调用AddRef()

Release()

此外,这不起作用:

pDropSource  = new MyDropSource();
pDropSource->AddRef();

pDataObject  = new MyDataObject((MyDropSource*)pDropSource);//(&fmtetc, &stgmed, 1);
pDataObject->AddRef();

...

pDropSource->Release();
pDataObject->Release();

您无法创建(MyDropSource*)pDropSource 实例,将其分配给MyDropSource指针,然后将其强制转换回IDropSource*。此外,没有充分的理由让MyDropSource*包含指向MyDataObject的指针(特别是因为它实际上并没有将它用于任何事情,也没有递增/递减引用计数),所以你需要完全删除它。

最后,您的MyDropSource实现没有返回正确的输出指针地址。它没有正确考虑多态vtable。实现需要看起来更像这样:

QueryInterface()

我确定此代码中还有其他漏洞和漏洞,但在看到这些大漏洞后我停止了审核。一般来说,它不是一个非常干净的实现。

更新STDMETHODIMP MyDataObject::QueryInterface(REFIID riid, void **ppvObject) { if (!ppvObject) return E_POINTER; if (riid == IID_IDataObject) *ppvObject = (IDataObject*)this; else if (riid == IID_IUnknown) *ppvObject = (IUnknown*)this; else { *ppvObject = 0; return E_NOINTERFACE; } AddRef(); return S_OK; } 定义为0,因此语句

DROPEFFECT_NONE
无论if((dwEffect & DROPEFFECT_NONE) == DROPEFFECT_NONE) 的值如何,

始终评估为 true 。不要使用按位dwEffect运算符来测试&,而是使用DROPEFFECT_NONE运算符。对所有其他值使用==

&