资源pipe理器在执行拖放操作时不会释放IDataObject

我在我的应用程序中执行拖放操作。 我遇到了Windows资源pipe理器在拖放操作后不能释放我的IDataObject的问题。 为了隔离这个问题,我实现了一个非常简单的拖放源代码,它应该在大多数Win32编译器中编译。 数据对象不包含数据; 正如你所看到的一切非常简单。 数据对象包含可以使用DebugView查看的跟踪,以指示何时创build以及何时销毁。

重现:

  1. 按住鼠标键开始拖动。
  2. 将对象拖放到打开的Windows资源pipe理器窗口中。
  3. 观察DebugView中的输出; 样本输出:

    [4964] gdo ctor [4964] gds ctor [4964] gds dtor 

    这个输出表明数据源被破坏了,但有人仍然持有对我的IDataObject的引用!

  4. 开始在同一浏览器窗口中拖动文件。 即使我现在还没有和我的项目进行交互,它会导致gdo dtor被打印 – 表示对IDataObject的最终引用被释放。

我正在运行Windows 7 64位。 有意思的是,一些资源pipe理器的窗口在释放之后立即释放数据对象; 其他人似乎没有这样做,直到你开始拖动一个不同的对象到资源pipe理器窗口,如步骤#4所示。 它也似乎取决于在窗口中的哪个位置放置对象 – 有些地方会立即释放对象,有些则不会。 这很奇怪!

我的问题是这些:

  1. 资源pipe理器正常吗? 为什么是这样? 或者我的代码中有一个错误? 当我的应用程序终止时,看到COM对象仍然被引用是非常令人不安的! 这也意味着IDataObject所拥有的资源是捆绑的,直到资源pipe理器决定释放该对象。
  2. 如果这确实是正常的行为(即使它不是,我想我应该处理不合适的放置目标),那么当应用程序终止时清理未释放COM对象的最佳做法是什么? 我正在用C ++ Builder编写和使用ATL,当用户试图closures应用程序,他们得到一个非常不友好的“在这个应用程序仍然有活动的COM对象,等等等等等等,你确定要closures这个应用程序?” – 大概由ATL生成,它注意到有未发布的COM对象 – 通常在应用程序closures时是一件坏事。

这是一些示例代码。 它实现了一个不提供数据的IDataObject,以及一个非常基本的IDropSource。 当然,真正的应用程序通过IDataObject提供数据,但是我发现这个基本的实现足以重现这个问题。 我用C ++ Builder写的,但是其中90%是可移植的Win32代码。 只要将标签或其他对象添加到select的GUI工具包(MFC,WinForms,C ++ / CLI,Qt,wxWidgets,Win32等等),并将合适的代码绑定到MouseDown事件。

我不能想到这个代码中的任何错误会导致这种行为,但这并不意味着我没有错过任何!

 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(); } 

Solutions Collecting From Web of "资源pipe理器在执行拖放操作时不会释放IDataObject"