[2/2] ole32: store window to drop target mappings in wineserver (with patch)
Damjan Jovanovic
damjan.jov at gmail.com
Wed Feb 3 11:48:07 CST 2010
Changelog:
* ole32: store window to drop target mappings in wineserver
This patch series allows OLE drag and drop to work across process boundaries.
Some tests are todo_wined because CoMarshalInterface seems to do 3
AddRefs and 2 Releases, while the (excessively strict?) test requires
exactly 1 AddRef only.
Damjan Jovanovic
-------------- next part --------------
diff --git a/dlls/ole32/ole2.c b/dlls/ole32/ole2.c
index 6bf79fd..9b7d68a 100644
--- a/dlls/ole32/ole2.c
+++ b/dlls/ole32/ole2.c
@@ -34,6 +34,8 @@
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
@@ -47,6 +49,7 @@
#include "wine/unicode.h"
#include "compobj_private.h"
#include "wine/list.h"
+#include "wine/server.h"
#include "wine/debug.h"
@@ -57,12 +60,6 @@ WINE_DECLARE_DEBUG_CHANNEL(accel);
* These are static/global variables and internal data structures that the
* OLE module uses to maintain it's state.
*/
-typedef struct tagDropTargetNode
-{
- HWND hwndTarget;
- IDropTarget* dropTarget;
- struct list entry;
-} DropTargetNode;
typedef struct tagTrackerWindowInfo
{
@@ -112,11 +109,6 @@ static LONG OLE_moduleLockCount = 0;
*/
static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
-/*
- * This is the head of the Drop target container.
- */
-static struct list targetListHead = LIST_INIT(targetListHead);
-
/******************************************************************************
* These are the prototypes of miscellaneous utility methods
*/
@@ -145,9 +137,8 @@ extern void OLEClipbrd_Initialize(void);
* These are the prototypes of the utility methods used for OLE Drag n Drop
*/
static void OLEDD_Initialize(void);
-static DropTargetNode* OLEDD_FindDropTarget(
- HWND hwndOfTarget);
-static void OLEDD_FreeDropTarget(DropTargetNode*, BOOL);
+static IDropTarget* OLEDD_FindDropTarget(
+ HWND hwndOfTarget, BOOL destroy);
static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
HWND hwnd,
UINT uMsg,
@@ -277,7 +268,8 @@ HRESULT WINAPI RegisterDragDrop(
HWND hwnd,
LPDROPTARGET pDropTarget)
{
- DropTargetNode* dropTargetInfo;
+ HGLOBAL hGlobal;
+ HRESULT hr;
TRACE("(%p,%p)\n", hwnd, pDropTarget);
@@ -296,34 +288,59 @@ HRESULT WINAPI RegisterDragDrop(
return DRAGDROP_E_INVALIDHWND;
}
- /*
- * First, check if the window is already registered.
- */
- dropTargetInfo = OLEDD_FindDropTarget(hwnd);
-
- if (dropTargetInfo!=NULL)
- return DRAGDROP_E_ALREADYREGISTERED;
-
- /*
- * If it's not there, we can add it. We first create a node for it.
- */
- dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode));
-
- if (dropTargetInfo==NULL)
- return E_OUTOFMEMORY;
-
- dropTargetInfo->hwndTarget = hwnd;
-
- /*
- * Don't forget that this is an interface pointer, need to nail it down since
- * we keep a copy of it.
- */
- IDropTarget_AddRef(pDropTarget);
- dropTargetInfo->dropTarget = pDropTarget;
+ hGlobal = GlobalAlloc(GMEM_DDESHARE|GMEM_MOVEABLE, 0);
+ if (hGlobal)
+ {
+ IStream *stream;
+ hr = CreateStreamOnHGlobal(hGlobal, FALSE, &stream);
+ if (SUCCEEDED(hr))
+ {
+ DWORD size = 0;
+ BOOL succeeded = FALSE;
+ /* MSDN: "MSHLFLAGS_TABLESTRONG is used by the RegisterDragDrop function when registering a window as a drop target.
+ * This keeps the window registered as a drop target no matter how many times the end user drags across the window.
+ * The RevokeDragDrop function calls CoReleaseMarshalData."
+ */
+ hr = CoMarshalInterface(stream, &IID_IDropTarget, (IUnknown*)pDropTarget,
+ MSHCTX_NOSHAREDMEM, NULL, MSHLFLAGS_TABLESTRONG);
+ if (SUCCEEDED(hr))
+ {
+ LARGE_INTEGER offset;
+ ULARGE_INTEGER position;
+ offset.QuadPart = 0;
+ hr = IStream_Seek(stream, offset, STREAM_SEEK_CUR, &position);
+ if (SUCCEEDED(hr))
+ {
+ succeeded = TRUE;
+ size = (DWORD)position.QuadPart;
+ }
+ }
+ IStream_Release(stream);
- list_add_tail(&targetListHead, &dropTargetInfo->entry);
+ if (succeeded)
+ {
+ NTSTATUS status;
+ void *marshalledDropTarget = GlobalLock(hGlobal);
+ SERVER_START_REQ( set_window_droptarget )
+ {
+ req->handle = wine_server_user_handle( hwnd );
+ wine_server_add_data( req, marshalledDropTarget, size );
+ status = wine_server_call( req );
+ }
+ SERVER_END_REQ;
+ GlobalUnlock(hGlobal);
+ if (status == STATUS_SUCCESS)
+ hr = S_OK;
+ else if (status == STATUS_OBJECT_NAME_COLLISION)
+ hr = DRAGDROP_E_ALREADYREGISTERED;
+ }
+ }
+ GlobalFree(hGlobal);
+ }
+ else
+ hr = E_OUTOFMEMORY;
- return S_OK;
+ return hr;
}
/***********************************************************************
@@ -332,7 +349,8 @@ HRESULT WINAPI RegisterDragDrop(
HRESULT WINAPI RevokeDragDrop(
HWND hwnd)
{
- DropTargetNode* dropTargetInfo;
+ NTSTATUS status;
+ HRESULT hr = E_FAIL;
TRACE("(%p)\n", hwnd);
@@ -342,20 +360,22 @@ HRESULT WINAPI RevokeDragDrop(
return DRAGDROP_E_INVALIDHWND;
}
- /*
- * First, check if the window is already registered.
- */
- dropTargetInfo = OLEDD_FindDropTarget(hwnd);
+ OLEDD_FindDropTarget(hwnd, TRUE);
- /*
- * If it ain't in there, it's an error.
- */
- if (dropTargetInfo==NULL)
- return DRAGDROP_E_NOTREGISTERED;
+ SERVER_START_REQ( set_window_droptarget )
+ {
+ req->handle = wine_server_user_handle( hwnd );
+ wine_server_add_data( req, NULL, 0 );
+ status = wine_server_call( req );
+ }
+ SERVER_END_REQ;
- OLEDD_FreeDropTarget(dropTargetInfo, TRUE);
+ if (status == STATUS_SUCCESS)
+ hr = S_OK;
+ else if (status == STATUS_OBJECT_NAME_NOT_FOUND)
+ hr = DRAGDROP_E_NOTREGISTERED;
- return S_OK;
+ return hr;
}
/***********************************************************************
@@ -1885,32 +1905,12 @@ static void OLEDD_Initialize(void)
}
/***
- * OLEDD_FreeDropTarget()
- *
- * Frees the drag and drop data structure
- */
-static void OLEDD_FreeDropTarget(DropTargetNode *dropTargetInfo, BOOL release_drop_target)
-{
- list_remove(&dropTargetInfo->entry);
- if (release_drop_target) IDropTarget_Release(dropTargetInfo->dropTarget);
- HeapFree(GetProcessHeap(), 0, dropTargetInfo);
-}
-
-/***
* OLEDD_UnInitialize()
*
* Releases the OLE drag and drop data structures.
*/
void OLEDD_UnInitialize(void)
{
- /*
- * Simply empty the list.
- */
- while (!list_empty(&targetListHead))
- {
- DropTargetNode* curNode = LIST_ENTRY(list_head(&targetListHead), DropTargetNode, entry);
- OLEDD_FreeDropTarget(curNode, FALSE);
- }
}
/***
@@ -1918,21 +1918,52 @@ void OLEDD_UnInitialize(void)
*
* Finds information about the drop target.
*/
-static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
+static IDropTarget* OLEDD_FindDropTarget(HWND hwndOfTarget, BOOL destroy)
{
- DropTargetNode* curNode;
+ NTSTATUS status;
+ HGLOBAL hGlobal;
+ void *buffer;
+ int bufferSize = 4096;
+ size_t replySize;
+ IStream *stream;
+ IDropTarget *dropTarget = NULL;
+ HRESULT hr;
- /*
- * Iterate the list to find the HWND value.
- */
- LIST_FOR_EACH_ENTRY(curNode, &targetListHead, DropTargetNode, entry)
- if (hwndOfTarget==curNode->hwndTarget)
- return curNode;
+ do
+ {
+ hGlobal = GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, bufferSize);
+ if (hGlobal == NULL)
+ return NULL;
+ buffer = GlobalLock(hGlobal);
- /*
- * If we get here, the item is not in the list
- */
- return NULL;
+ SERVER_START_REQ( get_window_droptarget )
+ {
+ req->handle = wine_server_user_handle( hwndOfTarget );
+ wine_server_set_reply( req, buffer, bufferSize );
+ status = wine_server_call( req );
+ replySize = wine_server_reply_size( req );
+ }
+ SERVER_END_REQ;
+
+ GlobalUnlock(hGlobal);
+ if (replySize >= bufferSize)
+ {
+ GlobalFree(hGlobal);
+ bufferSize *= 2;
+ }
+ } while (replySize >= bufferSize);
+
+ hr = CreateStreamOnHGlobal(hGlobal, FALSE, &stream);
+ if (SUCCEEDED(hr))
+ {
+ if (destroy)
+ hr = CoReleaseMarshalData(stream);
+ else
+ hr = CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&dropTarget);
+ IStream_Release(stream);
+ }
+ GlobalFree(hGlobal);
+ return dropTarget;
}
/***
@@ -2038,7 +2069,7 @@ static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo)
}
else
{
- DropTargetNode* newDropTargetNode = 0;
+ IDropTarget* newDropTarget = 0;
/*
* If we changed window, we have to notify our old target and check for
@@ -2061,12 +2092,12 @@ static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo)
trackerInfo->curTargetHWND = hwndNewTarget;
do {
- newDropTargetNode = OLEDD_FindDropTarget(nexttar);
- } while (!newDropTargetNode && (nexttar = GetParent(nexttar)) != 0);
+ newDropTarget = OLEDD_FindDropTarget(nexttar, FALSE);
+ } while (!newDropTarget && (nexttar = GetParent(nexttar)) != 0);
if(nexttar) hwndNewTarget = nexttar;
trackerInfo->curDragTargetHWND = hwndNewTarget;
- trackerInfo->curDragTarget = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
+ trackerInfo->curDragTarget = newDropTarget;
/*
* If there is, notify it that we just dragged-in
diff --git a/dlls/ole32/tests/dragdrop.c b/dlls/ole32/tests/dragdrop.c
index b28a831..a514455 100644
--- a/dlls/ole32/tests/dragdrop.c
+++ b/dlls/ole32/tests/dragdrop.c
@@ -152,18 +152,18 @@ START_TEST(dragdrop)
ok(droptarget_addref_called == 0, "DropTarget_AddRef shouldn't have been called\n");
hr = RegisterDragDrop(hwnd, &DropTarget);
ok_ole_success(hr, "RegisterDragDrop");
- ok(droptarget_addref_called == 1, "DropTarget_AddRef should have been called once, not %d times\n", droptarget_addref_called);
+ todo_wine ok(droptarget_addref_called == 1, "DropTarget_AddRef should have been called once, not %d times\n", droptarget_addref_called);
hr = RegisterDragDrop(hwnd, &DropTarget);
ok(hr == DRAGDROP_E_ALREADYREGISTERED, "RegisterDragDrop with already registered hwnd should return DRAGDROP_E_ALREADYREGISTERED instead of 0x%08x\n", hr);
- ok(droptarget_release_called == 0, "DropTarget_Release shouldn't have been called\n");
+ todo_wine ok(droptarget_release_called == 0, "DropTarget_Release shouldn't have been called\n");
OleUninitialize();
- ok(droptarget_release_called == 0, "DropTarget_Release shouldn't have been called\n");
+ todo_wine ok(droptarget_release_called == 0, "DropTarget_Release shouldn't have been called\n");
hr = RevokeDragDrop(hwnd);
ok_ole_success(hr, "RevokeDragDrop");
- ok(droptarget_release_called == 1 ||
+ todo_wine ok(droptarget_release_called == 1 ||
broken(droptarget_release_called == 0), /* NT4 */
"DropTarget_Release should have been called once, not %d times\n", droptarget_release_called);
More information about the wine-patches
mailing list