C++ Explorer不';进行拖放操作时,不要释放IDataObject

C++ Explorer不';进行拖放操作时,不要释放IDataObject,c++,windows,winapi,com,drag-and-drop,C++,Windows,Winapi,Com,Drag And Drop,我正在我的应用程序中实现拖放。Windows资源管理器在拖放操作后未释放IDataObject时出现问题。为了解决这个问题,我实现了一个非常简单的拖放源代码,它应该在大多数Win32编译器中编译。数据对象不包含任何数据;正如你所见,一切都很简单。数据对象包含可使用DebugView查看的跟踪,以指示创建和销毁数据对象的时间 复制: 按住鼠标按钮开始拖动 将对象拖放到打开的Windows资源管理器窗口中 在调试视图中观察输出;样本输出: [4964] gdo ctor [4964] gds cto

我正在我的应用程序中实现拖放。Windows资源管理器在拖放操作后未释放IDataObject时出现问题。为了解决这个问题,我实现了一个非常简单的拖放源代码,它应该在大多数Win32编译器中编译。数据对象不包含任何数据;正如你所见,一切都很简单。数据对象包含可使用DebugView查看的跟踪,以指示创建和销毁数据对象的时间

复制:

  • 按住鼠标按钮开始拖动
  • 将对象拖放到打开的Windows资源管理器窗口中
  • 在调试视图中观察输出;样本输出:

    [4964] gdo ctor
    [4964] gds ctor
    [4964] gds dtor
    
    此输出表示数据源已被破坏,但仍有人持有对我的IDataObject的引用

  • 开始在同一资源管理器窗口中拖动文件。尽管此时我根本没有与我的项目交互,但它会导致打印
    gdo dtor
    ,这表明对IDataObject的最终引用已发布
  • 我正在运行64位Windows 7。有趣的是,有些资源管理器窗口在删除数据对象后会立即释放数据对象;其他人似乎不会这样做,直到您开始将另一个对象拖动到资源管理器窗口中,如步骤4所示。这似乎还取决于我在窗口中放置对象的位置——某些位置会导致对象立即释放,而其他位置则不会。真奇怪

    我的问题是:

  • Explorer这样做正常吗?为什么会这样?还是我的代码中有bug?当我的应用程序终止时,看到COM对象仍然被引用,这是非常令人不安的!这也意味着IDataObject持有的资源将被占用,直到Explorer决定释放该对象为止
  • 如果这确实是正常行为(即使不是,我想我也应该处理行为不端的丢弃目标),那么在应用程序终止时清理这个未发布的COM对象的最佳实践是什么?我用C++ Builder编写,使用ATL,当用户试图关闭应用程序时,他们会非常不友好,“在这个应用程序中仍然有活动的COM对象,BLAH BLAH BLAH。您确实想关闭这个应用程序吗?”-可能由ATL生成,ATL注意到存在未发布的COM对象-通常在应用程序关闭时是一件坏事
  • 下面是一些示例代码。它实现了一个不提供数据的IDataObject和一个非常基本的IDropSource。当然,真正的应用程序通过IDataObject提供数据,但我发现这个基本实现足以重现这个问题。我用C++ Builder编写的,但其中90%是便携式Win32代码。只需将标签或其他对象添加到所选的GUI工具包(MFC、WinForms with C++/CLI、Qt、wxWidgets、straight Win32等),并将适当的代码绑定到MouseDown事件

    我想不出这段代码中有任何bug会导致这种行为,但这并不意味着我没有遗漏任何bug

    class GenericDataObject : public IDataObject
    {
    public:
        // basic IUnknown implementation
        ULONG __stdcall AddRef() { return InterlockedIncrement(&refcount); }
        ULONG __stdcall Release() {
            ULONG nRefCount = InterlockedDecrement(&refcount);
            if (nRefCount == 0) delete this;
            return nRefCount;
        }
        STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) {
            if (!ppvObject) return E_POINTER;
            if (riid == IID_IUnknown) {
                *ppvObject = static_cast<IUnknown*>(this);
                AddRef();
                return S_OK;
            } else if (riid == IID_IDataObject) {
                *ppvObject = static_cast<IDataObject*>(this);
                AddRef();
                return S_OK;
            } else {
                *ppvObject = NULL;
                return E_NOINTERFACE;
            }
        }
        // IDataObject members
        STDMETHODIMP GetData               (FORMATETC *pformatetcIn, STGMEDIUM *pmedium) { return DV_E_FORMATETC; }
        STDMETHODIMP GetDataHere           (FORMATETC *pformatetc,   STGMEDIUM *pmedium) { return E_NOTIMPL; }
        STDMETHODIMP QueryGetData          (FORMATETC *pformatetc) { return DV_E_FORMATETC; }
        STDMETHODIMP GetCanonicalFormatEtc (FORMATETC *pformatectIn, FORMATETC *pformatetcOut) { return DV_E_FORMATETC; }
        STDMETHODIMP SetData               (FORMATETC *pformatetc,   STGMEDIUM *pmedium,  BOOL fRelease) { return E_NOTIMPL; }
        STDMETHODIMP EnumFormatEtc         (DWORD     dwDirection,   IEnumFORMATETC **ppenumFormatEtc) { return E_NOTIMPL; }
        STDMETHODIMP DAdvise               (FORMATETC *pformatetc,   DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; }
        STDMETHODIMP DUnadvise             (DWORD     dwConnection) { return OLE_E_ADVISENOTSUPPORTED; }
        STDMETHODIMP EnumDAdvise           (IEnumSTATDATA **ppenumAdvise) { return OLE_E_ADVISENOTSUPPORTED; }
    public:
        GenericDataObject() : refcount(1) {OutputDebugString("gdo ctor");}
        ~GenericDataObject() {OutputDebugString("gdo dtor");}
    private:
        LONG refcount;
    };
    
    class GenericDropSource : public IDropSource
    {
    public:
        // basic IUnknown implementation
        ULONG __stdcall AddRef() { return InterlockedIncrement(&refcount); }
        ULONG __stdcall Release() {
            ULONG nRefCount = InterlockedDecrement(&refcount);
            if (nRefCount == 0) delete this;
            return nRefCount;
        }
        STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) {
            if (!ppvObject) return E_POINTER;
            if (riid == IID_IUnknown) {
                *ppvObject = static_cast<IUnknown*>(this);
                AddRef();
                return S_OK;
            } else if (riid == IID_IDropSource) {
                *ppvObject = static_cast<IDropSource*>(this);
                AddRef();
                return S_OK;
            } else {
                *ppvObject = NULL;
                return E_NOINTERFACE;
            }
        }
        // IDropSource members
        STDMETHODIMP QueryContinueDrag     (BOOL fEscapePressed, DWORD grfKeyState) {
            if (fEscapePressed) {
                return DRAGDROP_S_CANCEL;
            }
            if (!(grfKeyState & (MK_LBUTTON | MK_RBUTTON))) {
                return DRAGDROP_S_DROP;
            }
            return S_OK;
        }
        STDMETHODIMP GiveFeedback          (DWORD dwEffect) { return DRAGDROP_S_USEDEFAULTCURSORS; }
    public:
        GenericDropSource() : refcount(1) {OutputDebugString("gds ctor");}
        ~GenericDropSource() {OutputDebugString("gds dtor");}
    private:
        LONG refcount;
    };
    
    // This is the C++ Builder-specific part; all I did was add a label to the default form
    // and tie this event to it.
    void __fastcall TForm1::Label1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
    {
        OleInitialize(NULL);
        GenericDataObject *o = new GenericDataObject;
        GenericDropSource *s = new GenericDropSource;
        DWORD effect = 0;
        DoDragDrop(o, s, DROPEFFECT_COPY, &effect);
        o->Release();
        s->Release();
    }
    
    class GenericDataObject:公共IDataObject
    {
    公众:
    //基本IUnknown实现
    ULONG u stdcall AddRef(){return InterlockedIncrement(&refcount);}
    ULONG uu stdcall发布(){
    ULONG nRefCount=联锁减量(&refcount);
    如果(nRefCount==0)删除此项;
    返回nRefCount;
    }
    STDMETHODIMP查询接口(refid-riid,void**ppvObject){
    如果(!ppvObject)返回E_指针;
    如果(riid==IID_IUnknown){
    *ppvObject=静态投影(此);
    AddRef();
    返回S_OK;
    }else if(riid==IID_IDataObject){
    *ppvObject=静态投影(此);
    AddRef();
    返回S_OK;
    }否则{
    *ppvObject=NULL;
    返回E_NOINTERFACE;
    }
    }
    //IDataObject成员
    STDMETHODIMP GetData(FORMATETC*pformatetcIn,STGMEDIUM*pmedium){return DV_E_FORMATETC;}
    STDMETHODIMP GetDataHere(FORMATETC*pformatetc,STGMEDIUM*pmedium){返回E_NOTIMPL;}
    STDMETHODIMP QueryGetData(FORMATETC*pformatetc){return DV_E_FORMATETC;}
    STDMETHODIMP GetCanonicalFormatEtc(FORMATETC*pformatectIn,FORMATETC*pformatetcOut){return DV_E_FORMATETC;}
    STDMETHODIMP SetData(FORMATETC*pformatetc、STGMEDIUM*pmedium、BOOL fRelease){return E_NOTIMPL;}
    STDMETHODIMP EnumFormatEtc(DWORD dwDirection、IEnumFORMATETC**ppenumFormatEtc){return E_NOTIMPL;}
    STDMETHODIMP-DAdvise(FORMATETC*pformatetc、DWORD advf、IAdviseSink*pAdvSink、DWORD*pdwConnection){返回OLE_E_advisionnotsupported;}
    STDMETHODIMP DUnadvise(DWORD dwConnection){return OLE_E_advisionnotsupported;}
    STDMETHODIMP enumdavise(IEnumSTATDATA**ppenumAdvise){返回OLE_E_advisionnotsupported;}
    公众:
    GenericDataObject():refcount(1){OutputDebugString(“gdo ctor”);}
    ~GenericDataObject(){OutputDebugString(“gdo dtor”);}
    私人:
    长参考计数;
    };
    类GenericDropSource:公共IDropSource
    {
    公众:
    //基本IUnknown实现
    ULONG u stdcall AddRef(){return InterlockedIncrement(&refcount);}
    ULONG uu stdcall发布(){
    ULONG nRefCount=联锁减量(&refcount);
    如果(nRefCount==0)删除此项;
    返回nRefCount;
    }
    STDMETHODIMP查询接口(refid-riid,void**ppvObject){
    如果(!ppvObject)返回E_指针;
    如果(riid==IID_IUnknown){
    *ppvObject=静态投影(此);
    AddRef();
    返回S_OK;
    }else if(riid==IID_IDropSource){
    *ppvObject=静态投影(此);
    AddRef();
    返回S_OK;
    }否则{
    *ppvObject=NULL;
    返回E_NOINTERFACE;
    }
    }
    //IDropSource成员
    标准方法