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.
-- v2: 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..fa5ab354375 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) + { + /*Windows Vista or above expect this function to create a IDropSource object if the 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; }
/*************************************************************************
v2: change name of the internal IDropSource to InternalDropSource, add IUnknown QueryInterface, use AddRef inside QueryInterface, Release after DoDragDrop exit, return properly E_OUTOFMEMORY, change some comments descriptions
@Alcaro