vbscript: Fix memory leak in owned safearray iterator.
[wine.git] / dlls / winemac.drv / dragdrop.c
blob45615c68b713a19e12713151f2c5eba1e7e90d66
1 /*
2 * MACDRV Drag and drop code
4 * Copyright 2003 Ulrich Czekalla
5 * Copyright 2007 Damjan Jovanovic
6 * Copyright 2013 Ken Thomases for CodeWeavers Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define NONAMELESSUNION
25 #include "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include "macdrv_dll.h"
29 #define COBJMACROS
30 #include "objidl.h"
31 #include "shellapi.h"
32 #include "shlobj.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dragdrop);
38 static IDataObject *active_data_object;
39 static HWND last_droptarget_hwnd;
42 typedef struct
44 IDataObject IDataObject_iface;
45 LONG ref;
46 UINT64 pasteboard;
47 } DragDropDataObject;
50 /**************************************************************************
51 * debugstr_format
53 static const char *debugstr_format(UINT id)
55 WCHAR buffer[256];
57 if (GetClipboardFormatNameW(id, buffer, 256))
58 return wine_dbg_sprintf("0x%04x %s", id, debugstr_w(buffer));
60 switch (id)
62 #define BUILTIN(id) case id: return #id;
63 BUILTIN(CF_TEXT)
64 BUILTIN(CF_BITMAP)
65 BUILTIN(CF_METAFILEPICT)
66 BUILTIN(CF_SYLK)
67 BUILTIN(CF_DIF)
68 BUILTIN(CF_TIFF)
69 BUILTIN(CF_OEMTEXT)
70 BUILTIN(CF_DIB)
71 BUILTIN(CF_PALETTE)
72 BUILTIN(CF_PENDATA)
73 BUILTIN(CF_RIFF)
74 BUILTIN(CF_WAVE)
75 BUILTIN(CF_UNICODETEXT)
76 BUILTIN(CF_ENHMETAFILE)
77 BUILTIN(CF_HDROP)
78 BUILTIN(CF_LOCALE)
79 BUILTIN(CF_DIBV5)
80 BUILTIN(CF_OWNERDISPLAY)
81 BUILTIN(CF_DSPTEXT)
82 BUILTIN(CF_DSPBITMAP)
83 BUILTIN(CF_DSPMETAFILEPICT)
84 BUILTIN(CF_DSPENHMETAFILE)
85 #undef BUILTIN
86 default: return wine_dbg_sprintf("0x%04x", id);
90 static inline DragDropDataObject *impl_from_IDataObject(IDataObject *iface)
92 return CONTAINING_RECORD(iface, DragDropDataObject, IDataObject_iface);
96 static HANDLE get_pasteboard_data(UINT64 pasteboard, UINT desired_format)
98 struct dnd_get_data_params params = { .handle = pasteboard, .format = desired_format, .size = 2048 };
99 HANDLE handle;
100 NTSTATUS status;
102 for (;;)
104 if (!(handle = GlobalAlloc(GMEM_FIXED, params.size))) return 0;
105 params.data = GlobalLock(handle);
106 status = MACDRV_CALL(dnd_get_data, &params);
107 GlobalUnlock(handle);
108 if (!status) return GlobalReAlloc(handle, params.size, GMEM_MOVEABLE);
109 GlobalFree(handle);
110 if (status != STATUS_BUFFER_OVERFLOW) return 0;
114 static HRESULT WINAPI dddo_QueryInterface(IDataObject* iface, REFIID riid, LPVOID *ppvObj)
116 DragDropDataObject *This = impl_from_IDataObject(iface);
118 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppvObj);
120 if (IsEqualIID(riid, &IID_IUnknown) || (IsEqualIID(riid, &IID_IDataObject)))
122 *ppvObj = iface;
123 IDataObject_AddRef(iface);
124 return S_OK;
127 *ppvObj = NULL;
128 return E_NOINTERFACE;
132 static ULONG WINAPI dddo_AddRef(IDataObject* iface)
134 DragDropDataObject *This = impl_from_IDataObject(iface);
135 ULONG refCount = InterlockedIncrement(&This->ref);
137 TRACE("(%p)->(count=%lu)\n", This, refCount - 1);
139 return refCount;
143 static ULONG WINAPI dddo_Release(IDataObject* iface)
145 DragDropDataObject *This = impl_from_IDataObject(iface);
146 ULONG refCount = InterlockedDecrement(&This->ref);
148 TRACE("(%p)->(count=%lu)\n", This, refCount + 1);
149 if (refCount)
150 return refCount;
152 TRACE("-- destroying DragDropDataObject (%p)\n", This);
153 MACDRV_CALL(dnd_release, &This->pasteboard);
154 HeapFree(GetProcessHeap(), 0, This);
155 return 0;
159 static HRESULT WINAPI dddo_GetData(IDataObject* iface, FORMATETC* formatEtc, STGMEDIUM* medium)
161 DragDropDataObject *This = impl_from_IDataObject(iface);
162 HRESULT hr;
164 TRACE("This %p formatEtc %s\n", This, debugstr_format(formatEtc->cfFormat));
166 hr = IDataObject_QueryGetData(iface, formatEtc);
167 if (SUCCEEDED(hr))
169 medium->tymed = TYMED_HGLOBAL;
170 medium->u.hGlobal = get_pasteboard_data(This->pasteboard, formatEtc->cfFormat);
171 medium->pUnkForRelease = NULL;
172 hr = medium->u.hGlobal ? S_OK : E_OUTOFMEMORY;
175 return hr;
179 static HRESULT WINAPI dddo_GetDataHere(IDataObject* iface, FORMATETC* formatEtc,
180 STGMEDIUM* medium)
182 FIXME("iface %p formatEtc %p medium %p; stub\n", iface, formatEtc, medium);
183 return DATA_E_FORMATETC;
187 static HRESULT WINAPI dddo_QueryGetData(IDataObject* iface, FORMATETC* formatEtc)
189 DragDropDataObject *This = impl_from_IDataObject(iface);
190 struct dnd_have_format_params params;
191 HRESULT hr = DV_E_FORMATETC;
193 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s}\n",
194 This, formatEtc, formatEtc->tymed, formatEtc->dwAspect,
195 debugstr_format(formatEtc->cfFormat));
197 if (formatEtc->tymed && !(formatEtc->tymed & TYMED_HGLOBAL))
199 FIXME("only HGLOBAL medium types supported right now\n");
200 return DV_E_TYMED;
202 if (formatEtc->dwAspect != DVASPECT_CONTENT)
204 FIXME("only the content aspect is supported right now\n");
205 return E_NOTIMPL;
208 params.handle = This->pasteboard;
209 params.format = formatEtc->cfFormat;
210 if (MACDRV_CALL(dnd_have_format, &params))
211 hr = S_OK;
213 TRACE(" -> 0x%lx\n", hr);
214 return hr;
218 static HRESULT WINAPI dddo_GetConicalFormatEtc(IDataObject* iface, FORMATETC* formatEtc,
219 FORMATETC* formatEtcOut)
221 DragDropDataObject *This = impl_from_IDataObject(iface);
223 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s}\n",
224 This, formatEtc, formatEtc->tymed, formatEtc->dwAspect,
225 debugstr_format(formatEtc->cfFormat));
227 *formatEtcOut = *formatEtc;
228 formatEtcOut->ptd = NULL;
229 return DATA_S_SAMEFORMATETC;
233 static HRESULT WINAPI dddo_SetData(IDataObject* iface, FORMATETC* formatEtc,
234 STGMEDIUM* medium, BOOL fRelease)
236 DragDropDataObject *This = impl_from_IDataObject(iface);
238 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s} medium %p fRelease %d\n",
239 This, formatEtc, formatEtc->tymed, formatEtc->dwAspect,
240 debugstr_format(formatEtc->cfFormat), medium, fRelease);
242 return E_NOTIMPL;
246 static HRESULT WINAPI dddo_EnumFormatEtc(IDataObject* iface, DWORD direction,
247 IEnumFORMATETC** enumFormatEtc)
249 DragDropDataObject *This = impl_from_IDataObject(iface);
250 struct dnd_get_formats_params params;
251 UINT count;
252 HRESULT hr;
254 TRACE("This %p direction %lu enumFormatEtc %p\n", This, direction, enumFormatEtc);
256 if (direction != DATADIR_GET)
258 WARN("only the get direction is implemented\n");
259 return E_NOTIMPL;
262 params.handle = This->pasteboard;
263 count = MACDRV_CALL(dnd_get_formats, &params);
264 if (count)
266 FORMATETC *formatEtcs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(FORMATETC));
267 if (formatEtcs)
269 INT i;
271 for (i = 0; i < count; i++)
273 formatEtcs[i].cfFormat = params.formats[i];
274 formatEtcs[i].ptd = NULL;
275 formatEtcs[i].dwAspect = DVASPECT_CONTENT;
276 formatEtcs[i].lindex = -1;
277 formatEtcs[i].tymed = TYMED_HGLOBAL;
280 hr = SHCreateStdEnumFmtEtc(count, formatEtcs, enumFormatEtc);
281 HeapFree(GetProcessHeap(), 0, formatEtcs);
283 else
284 hr = E_OUTOFMEMORY;
286 else
287 hr = SHCreateStdEnumFmtEtc(0, NULL, enumFormatEtc);
289 TRACE(" -> 0x%lx\n", hr);
290 return hr;
294 static HRESULT WINAPI dddo_DAdvise(IDataObject* iface, FORMATETC* formatEtc, DWORD advf,
295 IAdviseSink* pAdvSink, DWORD* pdwConnection)
297 FIXME("(%p, %p, %lu, %p, %p): stub\n", iface, formatEtc, advf,
298 pAdvSink, pdwConnection);
299 return OLE_E_ADVISENOTSUPPORTED;
303 static HRESULT WINAPI dddo_DUnadvise(IDataObject* iface, DWORD dwConnection)
305 FIXME("(%p, %lu): stub\n", iface, dwConnection);
306 return OLE_E_ADVISENOTSUPPORTED;
310 static HRESULT WINAPI dddo_EnumDAdvise(IDataObject* iface, IEnumSTATDATA** pEnumAdvise)
312 FIXME("(%p, %p): stub\n", iface, pEnumAdvise);
313 return OLE_E_ADVISENOTSUPPORTED;
317 static const IDataObjectVtbl dovt =
319 dddo_QueryInterface,
320 dddo_AddRef,
321 dddo_Release,
322 dddo_GetData,
323 dddo_GetDataHere,
324 dddo_QueryGetData,
325 dddo_GetConicalFormatEtc,
326 dddo_SetData,
327 dddo_EnumFormatEtc,
328 dddo_DAdvise,
329 dddo_DUnadvise,
330 dddo_EnumDAdvise
334 static IDataObject *create_data_object_for_pasteboard(UINT64 pasteboard)
336 DragDropDataObject *dddo;
338 dddo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dddo));
339 if (!dddo)
340 return NULL;
342 dddo->ref = 1;
343 dddo->IDataObject_iface.lpVtbl = &dovt;
344 dddo->pasteboard = pasteboard;
345 MACDRV_CALL(dnd_retain, &dddo->pasteboard);
347 return &dddo->IDataObject_iface;
351 /* Based on functions in dlls/ole32/ole2.c */
352 static HANDLE get_droptarget_local_handle(HWND hwnd)
354 static const WCHAR prop_marshalleddroptarget[] =
355 {'W','i','n','e','M','a','r','s','h','a','l','l','e','d','D','r','o','p','T','a','r','g','e','t',0};
356 HANDLE handle;
357 HANDLE local_handle = 0;
359 handle = GetPropW(hwnd, prop_marshalleddroptarget);
360 if (handle)
362 DWORD pid;
363 HANDLE process;
365 GetWindowThreadProcessId(hwnd, &pid);
366 process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
367 if (process)
369 DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
370 CloseHandle(process);
373 return local_handle;
377 static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
379 HRESULT hr = E_OUTOFMEMORY;
380 HGLOBAL hmem;
381 void *data;
382 MEMORY_BASIC_INFORMATION info;
384 data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
385 if(!data) return hr;
387 VirtualQuery(data, &info, sizeof(info));
388 TRACE("size %d\n", (int)info.RegionSize);
390 hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
391 if(hmem)
393 memcpy(GlobalLock(hmem), data, info.RegionSize);
394 GlobalUnlock(hmem);
395 hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
397 UnmapViewOfFile(data);
398 return hr;
402 static IDropTarget* get_droptarget_pointer(HWND hwnd)
404 IDropTarget *droptarget = NULL;
405 HANDLE map;
406 IStream *stream;
408 map = get_droptarget_local_handle(hwnd);
409 if(!map) return NULL;
411 if(SUCCEEDED(create_stream_from_map(map, &stream)))
413 CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
414 IStream_Release(stream);
416 CloseHandle(map);
417 return droptarget;
421 /**************************************************************************
422 * macdrv_dnd_query_drop
424 NTSTATUS WINAPI macdrv_dnd_query_drop(void *arg, ULONG size)
426 struct dnd_query_drop_params *params = arg;
427 IDropTarget *droptarget;
428 BOOL ret = FALSE;
429 POINT pt;
431 TRACE("win %x x,y %d,%d effect %x pasteboard %s\n", params->hwnd, params->x, params->y,
432 params->effect, wine_dbgstr_longlong(params->handle));
434 pt.x = params->x;
435 pt.y = params->y;
437 droptarget = get_droptarget_pointer(last_droptarget_hwnd);
438 if (droptarget)
440 HRESULT hr;
441 POINTL pointl;
442 DWORD effect = params->effect;
444 if (!active_data_object)
446 WARN("shouldn't happen: no active IDataObject\n");
447 active_data_object = create_data_object_for_pasteboard(params->handle);
450 pointl.x = pt.x;
451 pointl.y = pt.y;
452 TRACE("Drop hwnd %p droptarget %p pointl (%ld,%ld) effect 0x%08lx\n", last_droptarget_hwnd,
453 droptarget, pointl.x, pointl.y, effect);
454 hr = IDropTarget_Drop(droptarget, active_data_object, MK_LBUTTON, pointl, &effect);
455 if (SUCCEEDED(hr))
457 if (effect != DROPEFFECT_NONE)
459 TRACE("drop succeeded\n");
460 ret = TRUE;
462 else
463 TRACE("the application refused the drop\n");
465 else
466 WARN("drop failed, error 0x%08lx\n", hr);
467 IDropTarget_Release(droptarget);
469 else
471 HWND hwnd = WindowFromPoint(pt);
472 while (hwnd && !(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES))
473 hwnd = GetParent(hwnd);
474 if (hwnd)
476 HDROP hdrop = get_pasteboard_data(params->handle, CF_HDROP);
477 DROPFILES *dropfiles = GlobalLock(hdrop);
478 if (dropfiles)
480 RECT rect;
481 dropfiles->pt.x = pt.x;
482 dropfiles->pt.y = pt.y;
483 dropfiles->fNC = !(ScreenToClient(hwnd, &dropfiles->pt) &&
484 GetClientRect(hwnd, &rect) &&
485 PtInRect(&rect, dropfiles->pt));
487 TRACE("sending WM_DROPFILES: hwnd %p nc %d pt %s %s\n", hwnd,
488 dropfiles->fNC, wine_dbgstr_point(&dropfiles->pt),
489 debugstr_w((WCHAR*)((char*)dropfiles + dropfiles->pFiles)));
491 GlobalUnlock(hdrop);
493 ret = PostMessageW(hwnd, WM_DROPFILES, (WPARAM)hdrop, 0L);
494 /* hdrop is owned by the message and freed when the recipient calls DragFinish(). */
497 if (!ret) GlobalFree(hdrop);
501 if (active_data_object) IDataObject_Release(active_data_object);
502 active_data_object = NULL;
503 last_droptarget_hwnd = NULL;
504 return ret;
508 /**************************************************************************
509 * macdrv_dnd_query_exited
511 NTSTATUS WINAPI macdrv_dnd_query_exited(void *arg, ULONG size)
513 struct dnd_query_exited_params *params = arg;
514 HWND hwnd = UlongToHandle(params->hwnd);
515 IDropTarget *droptarget;
517 TRACE("win %p\n", hwnd);
519 droptarget = get_droptarget_pointer(last_droptarget_hwnd);
520 if (droptarget)
522 HRESULT hr;
524 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd, droptarget);
525 hr = IDropTarget_DragLeave(droptarget);
526 if (FAILED(hr))
527 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr);
528 IDropTarget_Release(droptarget);
531 if (active_data_object) IDataObject_Release(active_data_object);
532 active_data_object = NULL;
533 last_droptarget_hwnd = NULL;
535 return TRUE;
539 /**************************************************************************
540 * query_drag_operation
542 NTSTATUS WINAPI macdrv_dnd_query_drag(void *arg, ULONG size)
544 struct dnd_query_drag_params *params = arg;
545 HWND hwnd = UlongToHandle(params->hwnd);
546 BOOL ret = FALSE;
547 POINT pt;
548 DWORD effect;
549 IDropTarget *droptarget;
550 HRESULT hr;
552 TRACE("win %p x,y %d,%d effect %x pasteboard %s\n", hwnd, params->x, params->y,
553 params->effect, wine_dbgstr_longlong(params->handle));
555 pt.x = params->x;
556 pt.y = params->y;
557 effect = params->effect;
559 /* Instead of the top-level window we got in the query, start with the deepest
560 child under the cursor. Travel up the hierarchy looking for a window that
561 has an associated IDropTarget. */
562 hwnd = WindowFromPoint(pt);
565 droptarget = get_droptarget_pointer(hwnd);
566 } while (!droptarget && (hwnd = GetParent(hwnd)));
568 if (last_droptarget_hwnd != hwnd)
570 if (last_droptarget_hwnd)
572 IDropTarget *old_droptarget = get_droptarget_pointer(last_droptarget_hwnd);
573 if (old_droptarget)
575 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd, old_droptarget);
576 hr = IDropTarget_DragLeave(old_droptarget);
577 if (FAILED(hr))
578 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr);
579 IDropTarget_Release(old_droptarget);
583 last_droptarget_hwnd = hwnd;
585 if (droptarget)
587 POINTL pointl = { pt.x, pt.y };
589 if (!active_data_object)
590 active_data_object = create_data_object_for_pasteboard(params->handle);
592 TRACE("DragEnter hwnd %p droptarget %p\n", hwnd, droptarget);
593 hr = IDropTarget_DragEnter(droptarget, active_data_object, MK_LBUTTON,
594 pointl, &effect);
595 if (SUCCEEDED(hr))
597 TRACE(" effect %ld\n", effect);
598 ret = TRUE;
600 else
601 WARN("IDropTarget_DragEnter failed, error 0x%08lx\n", hr);
602 IDropTarget_Release(droptarget);
605 else if (droptarget)
607 POINTL pointl = { pt.x, pt.y };
609 TRACE("DragOver hwnd %p droptarget %p\n", hwnd, droptarget);
610 hr = IDropTarget_DragOver(droptarget, MK_LBUTTON, pointl, &effect);
611 if (SUCCEEDED(hr))
613 TRACE(" effect %ld\n", effect);
614 ret = TRUE;
616 else
617 WARN("IDropTarget_DragOver failed, error 0x%08lx\n", hr);
618 IDropTarget_Release(droptarget);
621 if (!ret)
623 hwnd = WindowFromPoint(pt);
624 while (hwnd && !(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES))
625 hwnd = GetParent(hwnd);
626 if (hwnd)
628 FORMATETC formatEtc;
630 if (!active_data_object)
631 active_data_object = create_data_object_for_pasteboard(params->handle);
633 formatEtc.cfFormat = CF_HDROP;
634 formatEtc.ptd = NULL;
635 formatEtc.dwAspect = DVASPECT_CONTENT;
636 formatEtc.lindex = -1;
637 formatEtc.tymed = TYMED_HGLOBAL;
638 if (SUCCEEDED(IDataObject_QueryGetData(active_data_object, &formatEtc)))
640 TRACE("WS_EX_ACCEPTFILES hwnd %p\n", hwnd);
641 effect = DROPEFFECT_COPY | DROPEFFECT_LINK;
642 ret = TRUE;
647 TRACE(" -> %s\n", ret ? "TRUE" : "FALSE");
648 return ret ? effect : 0;