ole32: Implement cross-process drag and drop.

This commit is contained in:
Huw Davies 2010-07-22 13:37:19 +01:00 committed by Alexandre Julliard
parent a2e1dd29dc
commit 6d1ef3a6a6
1 changed files with 189 additions and 32 deletions

View File

@ -116,6 +116,10 @@ static const WCHAR prop_olemenuW[] =
static const WCHAR prop_oledroptarget[] =
{'O','l','e','D','r','o','p','T','a','r','g','e','t','I','n','t','e','r','f','a','c','e',0};
/* property to store Marshalled IDropTarget pointer */
static const WCHAR prop_marshalleddroptarget[] =
{'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};
static const WCHAR clsidfmtW[] =
{'C','L','S','I','D','\\','{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-',
'%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x',
@ -266,14 +270,134 @@ HRESULT WINAPI OleInitializeWOW(DWORD x, DWORD y) {
return 0;
}
/***
* OLEDD_FindDropTarget()
/*************************************************************
* get_droptarget_handle
*
* Returns IDropTarget pointer registered for this window.
* Retrieve a handle to the map containing the marshalled IDropTarget.
* This handle belongs to the process that called RegisterDragDrop.
* See get_droptarget_local_handle().
*/
static inline IDropTarget* OLEDD_FindDropTarget(HWND hwnd)
static inline HANDLE get_droptarget_handle(HWND hwnd)
{
return GetPropW(hwnd, prop_oledroptarget);
return GetPropW(hwnd, prop_marshalleddroptarget);
}
/*************************************************************
* is_droptarget
*
* Is the window a droptarget.
*/
static inline BOOL is_droptarget(HWND hwnd)
{
return get_droptarget_handle(hwnd) ? TRUE : FALSE;
}
/*************************************************************
* get_droptarget_local_handle
*
* Retrieve a handle to the map containing the marshalled IDropTarget.
* The handle should be closed when finished with.
*/
static HANDLE get_droptarget_local_handle(HWND hwnd)
{
HANDLE handle, local_handle = 0;
handle = get_droptarget_handle(hwnd);
if(handle)
{
DWORD pid;
HANDLE process;
GetWindowThreadProcessId(hwnd, &pid);
process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
if(process)
{
DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(process);
}
}
return local_handle;
}
/***********************************************************************
* create_map_from_stream
*
* Helper for RegisterDragDrop. Creates a file mapping object
* with the contents of the provided stream. The stream must
* be a global memory backed stream.
*/
static HRESULT create_map_from_stream(IStream *stream, HANDLE *map)
{
HGLOBAL hmem;
DWORD size;
HRESULT hr;
void *data;
hr = GetHGlobalFromStream(stream, &hmem);
if(FAILED(hr)) return hr;
size = GlobalSize(hmem);
*map = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL);
if(!*map) return E_OUTOFMEMORY;
data = MapViewOfFile(*map, FILE_MAP_WRITE, 0, 0, size);
memcpy(data, GlobalLock(hmem), size);
GlobalUnlock(hmem);
UnmapViewOfFile(data);
return S_OK;
}
/***********************************************************************
* create_stream_from_map
*
* Creates a stream from the provided map.
*/
static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
{
HRESULT hr = E_OUTOFMEMORY;
HGLOBAL hmem;
void *data;
MEMORY_BASIC_INFORMATION info;
data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
if(!data) return hr;
VirtualQuery(data, &info, sizeof(info));
TRACE("size %d\n", (int)info.RegionSize);
hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
if(hmem)
{
memcpy(GlobalLock(hmem), data, info.RegionSize);
GlobalUnlock(hmem);
hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
}
UnmapViewOfFile(data);
return hr;
}
/***********************************************************************
* get_droptarget_pointer
*
* Retrieves the marshalled IDropTarget from the window.
*/
static IDropTarget* get_droptarget_pointer(HWND hwnd)
{
IDropTarget *droptarget = NULL;
HANDLE map;
IStream *stream;
map = get_droptarget_local_handle(hwnd);
if(!map) return NULL;
if(SUCCEEDED(create_stream_from_map(map, &stream)))
{
CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
IStream_Release(stream);
}
CloseHandle(map);
return droptarget;
}
/***********************************************************************
@ -282,6 +406,9 @@ static inline IDropTarget* OLEDD_FindDropTarget(HWND hwnd)
HRESULT WINAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
{
DWORD pid = 0;
HRESULT hr;
IStream *stream;
HANDLE map;
TRACE("(%p,%p)\n", hwnd, pDropTarget);
@ -309,13 +436,40 @@ HRESULT WINAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
}
/* check if the window is already registered */
if (OLEDD_FindDropTarget(hwnd))
if (is_droptarget(hwnd))
return DRAGDROP_E_ALREADYREGISTERED;
IDropTarget_AddRef(pDropTarget);
SetPropW(hwnd, prop_oledroptarget, pDropTarget);
/*
* Marshal the drop target pointer into a shared memory map and
* store the map's handle in a Wine specific window prop. We also
* store the drop target pointer itself in the
* "OleDropTargetInterface" prop for compatibility with Windows.
*/
return S_OK;
hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
if(FAILED(hr)) return hr;
hr = CoMarshalInterface(stream, &IID_IDropTarget, (IUnknown*)pDropTarget, MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
if(SUCCEEDED(hr))
{
hr = create_map_from_stream(stream, &map);
if(SUCCEEDED(hr))
{
IDropTarget_AddRef(pDropTarget);
SetPropW(hwnd, prop_oledroptarget, pDropTarget);
SetPropW(hwnd, prop_marshalleddroptarget, map);
}
else
{
LARGE_INTEGER zero;
zero.QuadPart = 0;
IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
CoReleaseMarshalData(stream);
}
}
IStream_Release(stream);
return hr;
}
/***********************************************************************
@ -323,7 +477,10 @@ HRESULT WINAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
*/
HRESULT WINAPI RevokeDragDrop(HWND hwnd)
{
IDropTarget* droptarget;
HANDLE map;
IStream *stream;
IDropTarget *drop_target;
HRESULT hr;
TRACE("(%p)\n", hwnd);
@ -334,13 +491,24 @@ HRESULT WINAPI RevokeDragDrop(HWND hwnd)
}
/* no registration data */
if (!(droptarget = OLEDD_FindDropTarget(hwnd)))
if (!(map = get_droptarget_handle(hwnd)))
return DRAGDROP_E_NOTREGISTERED;
IDropTarget_Release(droptarget);
RemovePropW(hwnd, prop_oledroptarget);
drop_target = GetPropW(hwnd, prop_oledroptarget);
if(drop_target) IDropTarget_Release(drop_target);
return S_OK;
RemovePropW(hwnd, prop_oledroptarget);
RemovePropW(hwnd, prop_marshalleddroptarget);
hr = create_stream_from_map(map, &stream);
if(SUCCEEDED(hr))
{
CoReleaseMarshalData(stream);
IStream_Release(stream);
}
CloseHandle(map);
return hr;
}
/***********************************************************************
@ -1975,30 +2143,17 @@ static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo)
* Find-out if there is a drag target under the mouse
*/
HWND next_target_wnd = hwndNewTarget;
IDropTarget *new_target;
DWORD pid;
trackerInfo->curTargetHWND = hwndNewTarget;
do {
new_target = OLEDD_FindDropTarget(next_target_wnd);
} while (!new_target && (next_target_wnd = GetParent(next_target_wnd)));
while (next_target_wnd && !is_droptarget(next_target_wnd))
next_target_wnd = GetParent(next_target_wnd);
if (next_target_wnd) hwndNewTarget = next_target_wnd;
GetWindowThreadProcessId(hwndNewTarget, &pid);
if (pid != GetCurrentProcessId())
{
FIXME("drop to another process window is unsupported\n");
trackerInfo->curDragTargetHWND = 0;
trackerInfo->curTargetHWND = 0;
trackerInfo->curDragTarget = 0;
}
else
{
trackerInfo->curDragTargetHWND = hwndNewTarget;
trackerInfo->curDragTarget = new_target;
}
trackerInfo->curDragTargetHWND = hwndNewTarget;
if(trackerInfo->curDragTarget) IDropTarget_Release(trackerInfo->curDragTarget);
trackerInfo->curDragTarget = get_droptarget_pointer(hwndNewTarget);
/*
* If there is, notify it that we just dragged-in
@ -2016,6 +2171,7 @@ static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo)
{
trackerInfo->curDragTargetHWND = 0;
trackerInfo->curTargetHWND = 0;
IDropTarget_Release(trackerInfo->curDragTarget);
trackerInfo->curDragTarget = 0;
}
}
@ -2027,6 +2183,7 @@ static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo)
*/
trackerInfo->curDragTargetHWND = 0;
trackerInfo->curTargetHWND = 0;
if(trackerInfo->curDragTarget) IDropTarget_Release(trackerInfo->curDragTarget);
trackerInfo->curDragTarget = 0;
}
}