Application using Delphi runtime with Virtual-TreeView module, like xEdit, pass a NULL IDropSource to SHDoDragDrop, prevnting the drag and drop to works at all, when used in Windos Vista and above compatiblity mode, while windows xp compatibility mode works (as it use a dfferent system). Documentation stated that Windows Vista and above will create an internal IDropSource in this case. Creating a dummy IDropSource with minimal functionality allow the drag and drop to works using the default cursor. In case of xEdit it won't show the cell that it's being dragged like on windows, a functionality that require a full implementation of IDropSource::GiveFeedback and DragSourceHelper2::InitializeFromBitmap.
Solve the functional part of #54923
I'm unsure on how to write tests for this.
-- v3: shell32: Create an internal IDropSource in SHDoDragDrop if it wasn't passed by the caller
From: Lorenzo Ferrillo lorenzofersteam@live.it
--- dlls/shell32/shellord.c | 108 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-)
diff --git a/dlls/shell32/shellord.c b/dlls/shell32/shellord.c index a007c0e5c60..7c262e6436f 100644 --- a/dlls/shell32/shellord.c +++ b/dlls/shell32/shellord.c @@ -534,6 +534,90 @@ HRESULT WINAPI SHRevokeDragDrop(HWND hWnd) return RevokeDragDrop(hWnd); }
+ +typedef struct InternalDropSource{ + IDropSource IDropSource_iface; + LONG Ref; +} InternalDropSource; + +static inline InternalDropSource *impl_from_IDropSource(IDropSource *iface) +{ + return CONTAINING_RECORD(iface, InternalDropSource, IDropSource_iface); +} + +static HRESULT WINAPI InternalDropSource_QueryInterface(IDropSource *iface, REFIID riid, void **ppvObj) +{ + InternalDropSource *This = impl_from_IDropSource(iface); + TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppvObj); + + *ppvObj = NULL; + + if(IsEqualIID(riid, &IID_IDropSource) || IsEqualIID(riid, &IID_IUnknown) ) + { + *ppvObj = &This->IDropSource_iface; + IUnknown_AddRef ((IUnknown *) (*ppvObj)); + TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj); + return S_OK; + } + + WARN("-- Interface: E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI InternalDropSource_AddRef(IDropSource *iface) +{ + InternalDropSource *This = impl_from_IDropSource(iface); + TRACE("(%p)->(%lu)\n", This, This->Ref); + return InterlockedIncrement(&This->Ref); +} + +static ULONG WINAPI InternalDropSource_Release(IDropSource *iface) +{ + ULONG refcount; + InternalDropSource *This = impl_from_IDropSource(iface); + TRACE("(%p)->(%lu)\n", This, This->Ref); + refcount = InterlockedDecrement(&This->Ref); + + if(refcount == 0) + free(This); + + return refcount; +} + +static HRESULT WINAPI InternalDropSource_QueryContinueDrag( + IDropSource *iface, + BOOL fEscapePressed, + DWORD grfKeyState) +{ + InternalDropSource *This = impl_from_IDropSource(iface); + TRACE("(%p)\n",This); + + if (fEscapePressed) + return DRAGDROP_S_CANCEL; + else if (!(grfKeyState & MK_LBUTTON) && !(grfKeyState & MK_RBUTTON)) + return DRAGDROP_S_DROP; + else + return S_OK; +} + +static HRESULT WINAPI InternalDropSource_GiveFeedback( + IDropSource *iface, + DWORD dwEffect) +{ + InternalDropSource *This = impl_from_IDropSource(iface); + TRACE("(%p)\n",This); + + return DRAGDROP_S_USEDEFAULTCURSORS; +} + +static const IDropSourceVtbl dropsourcevtbl = +{ + InternalDropSource_QueryInterface, + InternalDropSource_AddRef, + InternalDropSource_Release, + InternalDropSource_QueryContinueDrag, + InternalDropSource_GiveFeedback +}; /************************************************************************* * SHDoDragDrop [SHELL32.88] * @@ -554,9 +638,29 @@ HRESULT WINAPI SHDoDragDrop( DWORD dwOKEffect, LPDWORD pdwEffect) { + HRESULT hr; + LPDROPSOURCE dropSource = lpDropSource; FIXME("(%p %p %p 0x%08lx %p):stub.\n", - hWnd, lpDataObject, lpDropSource, dwOKEffect, pdwEffect); - return DoDragDrop(lpDataObject, lpDropSource, dwOKEffect, pdwEffect); + hWnd, lpDataObject, lpDropSource, dwOKEffect, pdwEffect); + if(lpDropSource == NULL) + { + /*This function should create a IDropSource object if the lpDropSource parameter was NULL. + * Delphi's drag and drop functions expect this to work when it detects Windows Vista or above */ + InternalDropSource* dummy = calloc(1, sizeof(InternalDropSource)); + if(!dummy) + return E_OUTOFMEMORY; + + dummy->IDropSource_iface.lpVtbl = &dropsourcevtbl; + dummy->Ref = 1; + dropSource = &dummy->IDropSource_iface; + } + hr = DoDragDrop(lpDataObject, dropSource, dwOKEffect, pdwEffect); + if(lpDropSource == NULL) + { + /*Source was created internally, free*/ + InternalDropSource_Release(dropSource); + } + return hr; }
/*************************************************************************