VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.cpp@ 51924

Last change on this file since 51924 was 51924, checked in by vboxsync, 10 years ago

VBoxTray/DnD: Assign service context before starting internal thread.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.8 KB
Line 
1/* $Id: VBoxDnD.cpp 51924 2014-07-08 11:08:53Z vboxsync $ */
2/** @file
3 * VBoxDnD.cpp - Windows-specific bits of the drag'n drop service.
4 */
5
6/*
7 * Copyright (C) 2013-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include <windows.h>
18#include "VBoxTray.h"
19#include "VBoxHelpers.h"
20#include "VBoxDnD.h"
21
22#include <VBox/VBoxGuestLib.h>
23#include "VBox/HostServices/DragAndDropSvc.h"
24
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/err.h>
28#include <iprt/ldr.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31
32#include <iprt/cpp/mtlist.h>
33#include <iprt/cpp/ministring.h>
34
35#include <iprt/cpp/mtlist.h>
36
37#include <VBoxGuestInternal.h>
38#ifdef LOG_GROUP
39# undef LOG_GROUP
40#endif
41#define LOG_GROUP LOG_GROUP_GUEST_DND
42#include <VBox/log.h>
43
44/* Enable this define to see the proxy window(s) when debugging
45 * their behavior. Don't have this enabled in release builds! */
46#ifdef DEBUG
47//# define VBOX_DND_DEBUG_WND
48#endif
49
50/** @todo Merge this with messages from VBoxTray.h. */
51#define WM_VBOXTRAY_DND_MESSAGE WM_APP + 401
52
53/** Function pointer for SendInput(). This only is available starting
54 * at NT4 SP3+. */
55typedef BOOL (WINAPI *PFNSENDINPUT)(UINT, LPINPUT, int);
56typedef BOOL (WINAPI* PFNENUMDISPLAYMONITORS)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
57
58/** Static pointer to SendInput() function. */
59static PFNSENDINPUT s_pfnSendInput = NULL;
60static PFNENUMDISPLAYMONITORS s_pfnEnumDisplayMonitors = NULL;
61
62static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
63static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
64
65VBoxDnDWnd::VBoxDnDWnd(void)
66 : hThread(NIL_RTTHREAD),
67 mEventSem(NIL_RTSEMEVENT),
68 hWnd(NULL),
69 uAllActions(DND_IGNORE_ACTION),
70 mfMouseButtonDown(false),
71#ifdef VBOX_WITH_DRAG_AND_DROP_GH
72 pDropTarget(NULL),
73#endif
74 mClientID(UINT32_MAX),
75 mMode(Unknown),
76 mState(Uninitialized)
77{
78 RT_ZERO(startupInfo);
79
80 const RTCString arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
81 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
82 this->lstAllowedFormats.append(arrEntries[i]);
83}
84
85VBoxDnDWnd::~VBoxDnDWnd(void)
86{
87 Destroy();
88}
89
90/**
91 * Initializes the proxy window with a given DnD context.
92 *
93 * @return IPRT status code.
94 * @param pContext Pointer to context to use.
95 */
96int VBoxDnDWnd::Initialize(PVBOXDNDCONTEXT pContext)
97{
98 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
99
100 /* Save the context. */
101 this->pContext = pContext;
102
103 int rc = RTSemEventCreate(&mEventSem);
104 if (RT_SUCCESS(rc))
105 rc = RTCritSectInit(&mCritSect);
106
107 if (RT_SUCCESS(rc))
108 {
109 /* Message pump thread for our proxy window. */
110 rc = RTThreadCreate(&hThread, VBoxDnDWnd::Thread, this,
111 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
112 "VBoxTrayDnDWnd");
113 if (RT_SUCCESS(rc))
114 rc = RTThreadUserWait(hThread, 30 * 1000 /* Timeout in ms */);
115 }
116
117 if (RT_FAILURE(rc))
118 LogRel(("DnD: Failed to initialize proxy window, rc=%Rrc\n", rc));
119
120 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
121 return rc;
122}
123
124/**
125 * Destroys the proxy window and releases all remaining
126 * resources again.
127 *
128 */
129void VBoxDnDWnd::Destroy(void)
130{
131 if (hThread != NIL_RTTHREAD)
132 {
133 int rcThread = VERR_WRONG_ORDER;
134 int rc = RTThreadWait(hThread, 60 * 1000 /* Timeout in ms */, &rcThread);
135 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n",
136 rc, rcThread));
137 }
138
139 reset();
140
141 RTCritSectDelete(&mCritSect);
142 if (mEventSem != NIL_RTSEMEVENT)
143 RTSemEventDestroy(mEventSem);
144
145 LogFlowFuncLeave();
146}
147
148/**
149 * Thread for handling the window's message pump.
150 *
151 * @return IPRT status code.
152 * @param hThread Handle to this thread.
153 * @param pvUser Pointer to VBoxDnDWnd instance which
154 * is using the thread.
155 */
156/* static */
157int VBoxDnDWnd::Thread(RTTHREAD hThread, void *pvUser)
158{
159 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
160
161 VBoxDnDWnd *pThis = (VBoxDnDWnd*)pvUser;
162 AssertPtr(pThis);
163
164 PVBOXDNDCONTEXT pContext = pThis->pContext;
165 AssertPtr(pContext);
166 AssertPtr(pContext->pEnv);
167
168 HINSTANCE hInstance = pContext->pEnv->hInstance;
169 Assert(hInstance != 0);
170
171 /* Create our proxy window. */
172 WNDCLASSEX wndClass;
173 RT_ZERO(wndClass);
174
175 wndClass.cbSize = sizeof(WNDCLASSEX);
176 wndClass.lpfnWndProc = vboxDnDWndProc;
177 wndClass.lpszClassName = "VBoxTrayDnDWnd";
178 wndClass.hInstance = hInstance;
179 wndClass.style = CS_NOCLOSE;
180#ifdef VBOX_DND_DEBUG_WND
181 wndClass.style |= CS_HREDRAW | CS_VREDRAW;
182 wndClass.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(255, 0, 0)));
183#else
184 wndClass.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
185#endif
186
187 bool fSignalled = false; /* Thread signalled? */
188
189 int rc = VINF_SUCCESS;
190 if (!RegisterClassEx(&wndClass))
191 {
192 DWORD dwErr = GetLastError();
193 LogFlowFunc(("Unable to register proxy window class, error=%ld\n", dwErr));
194 rc = RTErrConvertFromWin32(dwErr);
195 }
196
197 if (RT_SUCCESS(rc))
198 {
199 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
200 DWORD dwStyle = WS_POPUP;
201#ifdef VBOX_DND_DEBUG_WND
202 dwExStyle &= ~WS_EX_TRANSPARENT; /* Remove transparency bit. */
203 dwStyle |= WS_VISIBLE; /* Make the window visible. */
204#endif
205 pThis->hWnd =
206 CreateWindowEx(dwExStyle,
207 "VBoxTrayDnDWnd", "VBoxTrayDnDWnd",
208 dwStyle,
209#ifdef VBOX_DND_DEBUG_WND
210 CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL,
211#else
212 -200, -200, 100, 100, NULL, NULL,
213#endif
214 hInstance, pThis /* lParm */);
215 if (!pThis->hWnd)
216 {
217 DWORD dwErr = GetLastError();
218 LogFlowFunc(("Unable to create proxy window, error=%ld\n", dwErr));
219 rc = RTErrConvertFromWin32(dwErr);
220 }
221 else
222 {
223#ifndef VBOX_DND_DEBUG_WND
224 SetWindowPos(pThis->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
225 SWP_NOACTIVATE | SWP_HIDEWINDOW
226 | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
227 LogFlowFunc(("Proxy window created, hWnd=0x%x\n", pThis->hWnd));
228#else
229 LogFlowFunc(("Debug proxy window created, hWnd=0x%x\n", pThis->hWnd));
230
231 /*
232 * Install some mouse tracking.
233 */
234 TRACKMOUSEEVENT me;
235 RT_ZERO(me);
236 me.cbSize = sizeof(TRACKMOUSEEVENT);
237 me.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT;
238 me.hwndTrack = pThis->hWnd;
239 BOOL fRc = TrackMouseEvent(&me);
240 Assert(fRc);
241#endif
242 }
243 }
244
245 HRESULT hr = OleInitialize(NULL);
246 if (SUCCEEDED(hr))
247 {
248#ifdef VBOX_WITH_DRAG_AND_DROP_GH
249 rc = pThis->RegisterAsDropTarget();
250#endif
251 }
252 else
253 {
254 LogRel(("DnD: Unable to initialize OLE, hr=%Rhrc\n", hr));
255 rc = VERR_COM_UNEXPECTED;
256 }
257
258 if (RT_SUCCESS(rc))
259 {
260 rc = RTThreadUserSignal(hThread);
261 fSignalled = RT_SUCCESS(rc);
262
263 bool fShutdown = false;
264 while (RT_SUCCESS(rc))
265 {
266 MSG uMsg;
267 while (GetMessage(&uMsg, 0, 0, 0))
268 {
269 TranslateMessage(&uMsg);
270 DispatchMessage(&uMsg);
271 }
272
273 if (ASMAtomicReadBool(&pContext->fShutdown))
274 fShutdown = true;
275
276 if (fShutdown)
277 {
278 LogFlowFunc(("Cancelling ...\n"));
279 break;
280 }
281
282 /** @todo Immediately drop on failure? */
283 }
284
285#ifdef VBOX_WITH_DRAG_AND_DROP_GH
286 int rc2 = pThis->UnregisterAsDropTarget();
287 if (RT_SUCCESS(rc))
288 rc = rc2;
289#endif
290 OleUninitialize();
291 }
292
293 if (!fSignalled)
294 {
295 int rc2 = RTThreadUserSignal(hThread);
296 AssertRC(rc2);
297 }
298
299 LogFlowFuncLeaveRC(rc);
300 return rc;
301}
302
303/**
304 * Monitor enumeration callback for building up a simple bounding
305 * box, capable of holding all enumerated monitors.
306 *
307 * @return BOOL TRUE if enumeration should continue,
308 * FALSE if not.
309 * @param hMonitor Handle to current monitor being enumerated.
310 * @param hdcMonitor The current monitor's DC (device context).
311 * @param lprcMonitor The current monitor's RECT.
312 * @param lParam Pointer to a RECT structure holding the
313 * bounding box to build.
314 */
315/* static */
316BOOL CALLBACK VBoxDnDWnd::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor,
317 LPRECT lprcMonitor, LPARAM lParam)
318{
319 LPRECT pRect = (LPRECT)lParam;
320 AssertPtrReturn(pRect, FALSE);
321
322 AssertPtr(lprcMonitor);
323 LogFlowFunc(("Monitor is %ld,%ld,%ld,%ld\n",
324 lprcMonitor->left, lprcMonitor->top,
325 lprcMonitor->right, lprcMonitor->bottom));
326
327 /* Build up a simple bounding box to hold the entire (virtual) screen. */
328 if (pRect->left > lprcMonitor->left)
329 pRect->left = lprcMonitor->left;
330 if (pRect->right < lprcMonitor->right)
331 pRect->right = lprcMonitor->right;
332 if (pRect->top > lprcMonitor->top)
333 pRect->top = lprcMonitor->top;
334 if (pRect->bottom < lprcMonitor->bottom)
335 pRect->bottom = lprcMonitor->bottom;
336
337 return TRUE;
338}
339
340/**
341 * The proxy window's WndProc.
342 */
343LRESULT CALLBACK VBoxDnDWnd::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
344{
345 switch (uMsg)
346 {
347 case WM_CREATE:
348 {
349 int rc = OnCreate();
350 if (RT_FAILURE(rc))
351 return FALSE;
352 return TRUE;
353 }
354
355 case WM_CLOSE:
356 {
357 OnDestroy();
358
359 DestroyWindow(hWnd);
360 PostQuitMessage(0);
361 return 0;
362 }
363
364 case WM_LBUTTONDOWN:
365 LogFlowThisFunc(("WM_LBUTTONDOWN\n"));
366 mfMouseButtonDown = true;
367 return 0;
368
369 case WM_LBUTTONUP:
370 LogFlowThisFunc(("WM_LBUTTONUP\n"));
371 mfMouseButtonDown = false;
372
373 /* As the mouse button was released, Hide the proxy window again.
374 * This can happen if
375 * - the user bumped a guest window to the screen's edges
376 * - there was no drop data from the guest available and the user
377 * enters the guest screen again after this unsuccessful operation */
378 reset();
379 return 0;
380
381 case WM_MOUSELEAVE:
382 LogFlowThisFunc(("WM_MOUSELEAVE\n"));
383 return 0;
384
385 /* Will only be called once; after the first mouse move, this
386 * window will be hidden! */
387 case WM_MOUSEMOVE:
388 {
389 LogFlowThisFunc(("WM_MOUSEMOVE: mfMouseButtonDown=%RTbool, mMode=%ld, mState=%ld\n",
390 mfMouseButtonDown, mMode, mState));
391#ifdef DEBUG_andy
392 POINT p;
393 GetCursorPos(&p);
394 LogFlowThisFunc(("WM_MOUSEMOVE: curX=%ld, curY=%ld\n", p.x, p.y));
395#endif
396 int rc = VINF_SUCCESS;
397 if (mMode == HG) /* Host to guest. */
398 {
399 /* Dragging not started yet? Kick it off ... */
400 if ( mfMouseButtonDown
401 && (mState != Dragging))
402 {
403 mState = Dragging;
404#if 0
405 /* Delay hiding the proxy window a bit when debugging, to see
406 * whether the desired range is covered correctly. */
407 RTThreadSleep(5000);
408#endif
409 hide();
410
411 LogFlowThisFunc(("Starting drag'n drop: uAllActions=0x%x, dwOKEffects=0x%x ...\n",
412 uAllActions, startupInfo.dwOKEffects));
413
414 AssertPtr(startupInfo.pDataObject);
415 AssertPtr(startupInfo.pDropSource);
416 DWORD dwEffect;
417 HRESULT hr = DoDragDrop(startupInfo.pDataObject, startupInfo.pDropSource,
418 startupInfo.dwOKEffects, &dwEffect);
419 LogFlowThisFunc(("hr=%Rhrc, dwEffect=%RI32\n", hr, dwEffect));
420 switch (hr)
421 {
422 case DRAGDROP_S_DROP:
423 mState = Dropped;
424 break;
425
426 case DRAGDROP_S_CANCEL:
427 mState = Canceled;
428 break;
429
430 default:
431 LogFlowThisFunc(("Drag'n drop failed with %Rhrc\n", hr));
432 mState = Canceled;
433 rc = VERR_GENERAL_FAILURE; /** @todo Find a better status code. */
434 break;
435 }
436
437 int rc2 = RTCritSectEnter(&mCritSect);
438 if (RT_SUCCESS(rc2))
439 {
440 startupInfo.pDropSource->Release();
441 startupInfo.pDataObject->Release();
442
443 RT_ZERO(startupInfo);
444
445 rc2 = RTCritSectLeave(&mCritSect);
446 if (RT_SUCCESS(rc))
447 rc = rc2;
448 }
449
450 mMode = Unknown;
451 }
452 }
453 else if (mMode == GH) /* Guest to host. */
454 {
455 /* Starting here VBoxDnDDropTarget should
456 * take over; was instantiated when registering
457 * this proxy window as a (valid) drop target. */
458 }
459 else
460 rc = VERR_NOT_SUPPORTED;
461
462 LogFlowThisFunc(("WM_MOUSEMOVE: mMode=%ld, mState=%ld, rc=%Rrc\n",
463 mMode, mState, rc));
464 return 0;
465 }
466
467 case WM_NCMOUSEHOVER:
468 LogFlowThisFunc(("WM_NCMOUSEHOVER\n"));
469 return 0;
470
471 case WM_NCMOUSELEAVE:
472 LogFlowThisFunc(("WM_NCMOUSELEAVE\n"));
473 return 0;
474
475 case WM_VBOXTRAY_DND_MESSAGE:
476 {
477 VBOXDNDEVENT *pEvent = (PVBOXDNDEVENT)lParam;
478 if (!pEvent)
479 break; /* No event received, bail out. */
480
481 LogFlowThisFunc(("Received uType=%RU32, uScreenID=%RU32\n",
482 pEvent->Event.uType, pEvent->Event.uScreenId));
483
484 int rc;
485 switch (pEvent->Event.uType)
486 {
487 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
488 {
489 LogFlowThisFunc(("HOST_DND_HG_EVT_ENTER\n"));
490
491 if (pEvent->Event.cbFormats)
492 {
493 RTCList<RTCString> lstFormats =
494 RTCString(pEvent->Event.pszFormats, pEvent->Event.cbFormats - 1).split("\r\n");
495 rc = OnHgEnter(lstFormats, pEvent->Event.u.a.uAllActions);
496 }
497 else
498 {
499 AssertMsgFailed(("cbFormats is 0\n"));
500 rc = VERR_INVALID_PARAMETER;
501 }
502
503 /* Note: After HOST_DND_HG_EVT_ENTER there immediately is a move
504 * event, so fall through is intentional here. */
505 }
506
507 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
508 {
509 LogFlowThisFunc(("HOST_DND_HG_EVT_MOVE: %d,%d\n",
510 pEvent->Event.u.a.uXpos, pEvent->Event.u.a.uYpos));
511
512 rc = OnHgMove(pEvent->Event.u.a.uXpos, pEvent->Event.u.a.uYpos,
513 pEvent->Event.u.a.uDefAction);
514 break;
515 }
516
517 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
518 {
519 LogFlowThisFunc(("HOST_DND_HG_EVT_LEAVE\n"));
520
521 rc = OnHgLeave();
522 break;
523 }
524
525 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
526 {
527 LogFlowThisFunc(("HOST_DND_HG_EVT_DROPPED\n"));
528
529 rc = OnHgDrop();
530 break;
531 }
532
533 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
534 {
535 LogFlowThisFunc(("HOST_DND_HG_SND_DATA\n"));
536
537 rc = OnHgDataReceived(pEvent->Event.u.b.pvData,
538 pEvent->Event.u.b.cbData);
539 break;
540 }
541
542 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
543 {
544 LogFlowThisFunc(("HOST_DND_HG_EVT_CANCEL\n"));
545
546 rc = OnHgCancel();
547 break;
548 }
549
550 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
551 {
552 LogFlowThisFunc(("HOST_DND_GH_REQ_PENDING\n"));
553#ifdef VBOX_WITH_DRAG_AND_DROP_GH
554 rc = OnGhIsDnDPending(pEvent->Event.uScreenId);
555
556#else
557 rc = VERR_NOT_SUPPORTED;
558#endif
559 break;
560 }
561
562 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
563 {
564 LogFlowThisFunc(("HOST_DND_GH_EVT_DROPPED\n"));
565#ifdef VBOX_WITH_DRAG_AND_DROP_GH
566 rc = OnGhDropped(pEvent->Event.pszFormats,
567 pEvent->Event.cbFormats,
568 pEvent->Event.u.a.uDefAction);
569#else
570 rc = VERR_NOT_SUPPORTED;
571#endif
572 break;
573 }
574
575 default:
576 rc = VERR_NOT_SUPPORTED;
577 break;
578 }
579
580 /* Some messages require cleanup. */
581 switch (pEvent->Event.uType)
582 {
583 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
584 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
585 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
586#ifdef VBOX_WITH_DRAG_AND_DROP_GH
587 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
588#endif
589 {
590 if (pEvent->Event.pszFormats)
591 RTMemFree(pEvent->Event.pszFormats);
592 break;
593 }
594
595 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
596 {
597 if (pEvent->Event.pszFormats)
598 RTMemFree(pEvent->Event.pszFormats);
599 if (pEvent->Event.u.b.pvData)
600 RTMemFree(pEvent->Event.u.b.pvData);
601 break;
602 }
603
604 default:
605 /* Ignore. */
606 break;
607 }
608
609 if (pEvent)
610 {
611 LogFlowThisFunc(("Processing event %RU32 resulted in rc=%Rrc\n",
612 pEvent->Event.uType, rc));
613
614 RTMemFree(pEvent);
615 }
616 return 0;
617 }
618
619 default:
620 break;
621 }
622
623 return DefWindowProc(hWnd, uMsg, wParam, lParam);
624}
625
626#ifdef VBOX_WITH_DRAG_AND_DROP_GH
627/**
628 * Registers this proxy window as a local drop target.
629 *
630 * @return IPRT status code.
631 */
632int VBoxDnDWnd::RegisterAsDropTarget(void)
633{
634 if (pDropTarget) /* Already registered as drop target? */
635 return VINF_SUCCESS;
636
637 int rc;
638 try
639 {
640 pDropTarget = new VBoxDnDDropTarget(this /* pParent */);
641 AssertPtr(pDropTarget);
642 HRESULT hr = CoLockObjectExternal(pDropTarget, TRUE /* fLock */,
643 FALSE /* fLastUnlockReleases */);
644 if (SUCCEEDED(hr))
645 hr = RegisterDragDrop(hWnd, pDropTarget);
646
647 if (FAILED(hr))
648 {
649 LogRel(("DnD: Creating drop target failed with hr=%Rhrc\n", hr));
650 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
651 }
652 else
653 {
654 rc = VINF_SUCCESS;
655 }
656 }
657 catch (std::bad_alloc)
658 {
659 rc = VERR_NO_MEMORY;
660 }
661
662 LogFlowFuncLeaveRC(rc);
663 return rc;
664}
665
666/**
667 * Unregisters this proxy as a drop target.
668 *
669 * @return IPRT status code.
670 */
671int VBoxDnDWnd::UnregisterAsDropTarget(void)
672{
673 LogFlowFuncEnter();
674
675 if (!pDropTarget) /* No drop target? Bail out. */
676 return VINF_SUCCESS;
677
678 HRESULT hr = RevokeDragDrop(hWnd);
679 if (SUCCEEDED(hr))
680 hr = CoLockObjectExternal(pDropTarget, FALSE /* fLock */,
681 TRUE /* fLastUnlockReleases */);
682 if (SUCCEEDED(hr))
683 {
684 ULONG cRefs = pDropTarget->Release();
685
686 Assert(cRefs == 0);
687 pDropTarget = NULL;
688 }
689
690 int rc = SUCCEEDED(hr)
691 ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fix this. */
692
693 LogFlowFuncLeaveRC(rc);
694 return rc;
695}
696#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
697
698/**
699 * Handles the creation of a proxy window.
700 *
701 * @return IPRT status code.
702 */
703int VBoxDnDWnd::OnCreate(void)
704{
705 int rc = VbglR3DnDConnect(&mClientID);
706 if (RT_FAILURE(rc))
707 {
708 LogFlowThisFunc(("Connection to host service failed, rc=%Rrc\n", rc));
709 return rc;
710 }
711
712 LogFlowThisFunc(("Client ID=%RU32, rc=%Rrc\n", mClientID, rc));
713 return rc;
714}
715
716/**
717 * Handles the destruction of a proxy window.
718 */
719void VBoxDnDWnd::OnDestroy(void)
720{
721 VbglR3DnDDisconnect(mClientID);
722 LogFlowThisFuncLeave();
723}
724
725/**
726 * Handles actions required when the host cursor enters
727 * the guest's screen to initiate a host -> guest DnD operation.
728 *
729 * @return IPRT status code.
730 * @param lstFormats Supported formats offered by the host.
731 * @param uAllActions Supported actions offered by the host.
732 */
733int VBoxDnDWnd::OnHgEnter(const RTCList<RTCString> &lstFormats, uint32_t uAllActions)
734{
735 if (mMode == GH) /* Wrong mode? Bail out. */
736 return VERR_WRONG_ORDER;
737
738#ifdef DEBUG
739 LogFlowThisFunc(("uActions=0x%x, lstFormats=%zu: ", uAllActions, lstFormats.size()));
740 for (size_t i = 0; i < lstFormats.size(); i++)
741 LogFlow(("'%s' ", lstFormats.at(i).c_str()));
742 LogFlow(("\n"));
743#endif
744
745 reset();
746 setMode(HG);
747
748 /* Save all allowed actions. */
749 this->uAllActions = uAllActions;
750
751 /*
752 * Check if requested formats are compatible with this client.
753 */
754 LogFlowThisFunc(("Supported formats:\n"));
755 for (size_t i = 0; i < lstFormats.size(); i++)
756 {
757 bool fSupported = this->lstAllowedFormats.contains(lstFormats.at(i));
758 if (fSupported)
759 this->lstFormats.append(lstFormats.at(i));
760 LogFlowThisFunc(("\t%s: %RTbool\n", lstFormats.at(i).c_str(), fSupported));
761 }
762
763 /*
764 * Prepare the startup info for DoDragDrop().
765 */
766 int rc = VINF_SUCCESS;
767 try
768 {
769 /* Translate our drop actions into
770 * allowed Windows drop effects. */
771 startupInfo.dwOKEffects = DROPEFFECT_NONE;
772 if (uAllActions)
773 {
774 if (uAllActions & DND_COPY_ACTION)
775 startupInfo.dwOKEffects |= DROPEFFECT_COPY;
776 if (uAllActions & DND_MOVE_ACTION)
777 startupInfo.dwOKEffects |= DROPEFFECT_MOVE;
778 if (uAllActions & DND_LINK_ACTION)
779 startupInfo.dwOKEffects |= DROPEFFECT_LINK;
780 }
781
782 startupInfo.pDropSource = new VBoxDnDDropSource(this);
783 startupInfo.pDataObject = new VBoxDnDDataObject();
784 }
785 catch (std::bad_alloc)
786 {
787 rc = VERR_NO_MEMORY;
788 }
789
790 if (RT_SUCCESS(rc))
791 rc = makeFullscreen();
792
793 LogFlowFuncLeaveRC(rc);
794 return rc;
795}
796
797/**
798 * Handles actions required when the host cursor moves inside
799 * the guest's screen.
800 *
801 * @return IPRT status code.
802 * @param u32xPos Absolute X position (in pixels) of the host cursor
803 * inside the guest.
804 * @param u32yPos Absolute Y position (in pixels) of the host cursor
805 * inside the guest.
806 * @param uAction Action the host wants to perform while moving.
807 * Currently ignored.
808 */
809int VBoxDnDWnd::OnHgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t uAction)
810{
811 int rc;
812
813 uint32_t uActionNotify = DND_IGNORE_ACTION;
814 if (mMode == HG)
815 {
816 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, uAction=0x%x\n",
817 u32xPos, u32yPos, uAction));
818
819 rc = mouseMove(u32xPos, u32yPos, MOUSEEVENTF_LEFTDOWN);
820
821 if (RT_SUCCESS(rc))
822 rc = RTCritSectEnter(&mCritSect);
823 if (RT_SUCCESS(rc))
824 {
825 if ( (Dragging == mState)
826 && startupInfo.pDropSource)
827 uActionNotify = startupInfo.pDropSource->GetCurrentAction();
828
829 RTCritSectLeave(&mCritSect);
830 }
831 }
832 else /* Just acknowledge the operation with an ignore action. */
833 rc = VINF_SUCCESS;
834
835 if (RT_SUCCESS(rc))
836 {
837 rc = VbglR3DnDHGAcknowledgeOperation(mClientID, uActionNotify);
838 if (RT_FAILURE(rc))
839 LogFlowThisFunc(("Acknowledging operation failed with rc=%Rrc\n", rc));
840 }
841
842 LogFlowThisFunc(("Returning uActionNotify=0x%x, rc=%Rrc\n", uActionNotify, rc));
843 return rc;
844}
845
846/**
847 * Handles actions required when the host cursor leaves
848 * the guest's screen again.
849 *
850 * @return IPRT status code.
851 */
852int VBoxDnDWnd::OnHgLeave(void)
853{
854 if (mMode == GH) /* Wrong mode? Bail out. */
855 return VERR_WRONG_ORDER;
856
857 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", mMode, mState));
858 LogRel(("DnD: Drag'n drop operation aborted\n"));
859
860 reset();
861
862 int rc = VINF_SUCCESS;
863
864 /* Post ESC to our window to officially abort the
865 * drag'n drop operation. */
866 PostMessage(hWnd, WM_KEYDOWN, VK_ESCAPE, 0 /* lParam */);
867
868 LogFlowFuncLeaveRC(rc);
869 return rc;
870}
871
872/**
873 * Handles actions required when the host cursor wants to drop
874 * and therefore start a "drop" action in the guest.
875 *
876 * @return IPRT status code.
877 */
878int VBoxDnDWnd::OnHgDrop(void)
879{
880 if (mMode == GH)
881 return VERR_WRONG_ORDER;
882
883 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", mMode, mState));
884
885 int rc = VINF_SUCCESS;
886 if (mState == Dragging)
887 {
888 Assert(lstFormats.size() >= 1);
889
890 /** @todo What to do when multiple formats are available? */
891 mFormatRequested = lstFormats.at(0);
892
893 rc = RTCritSectEnter(&mCritSect);
894 if (RT_SUCCESS(rc))
895 {
896 if (startupInfo.pDataObject)
897 startupInfo.pDataObject->SetStatus(VBoxDnDDataObject::Dropping);
898 else
899 rc = VERR_NOT_FOUND;
900
901 RTCritSectLeave(&mCritSect);
902 }
903
904 if (RT_SUCCESS(rc))
905 {
906 LogRel(("DnD: Requesting data as '%s' ...\n", mFormatRequested.c_str()));
907 rc = VbglR3DnDHGRequestData(mClientID, mFormatRequested.c_str());
908 if (RT_FAILURE(rc))
909 LogFlowThisFunc(("Requesting data failed with rc=%Rrc\n", rc));
910 }
911 }
912
913 LogFlowFuncLeaveRC(rc);
914 return rc;
915}
916
917/**
918 * Handles actions required when the host has sent over DnD data
919 * to the guest after a "drop" event.
920 *
921 * @return IPRT status code.
922 * @param pvData Pointer to raw data received.
923 * @param cbData Size of data (in bytes) received.
924 */
925int VBoxDnDWnd::OnHgDataReceived(const void *pvData, uint32_t cbData)
926{
927 LogFlowThisFunc(("mState=%ld, pvData=%p, cbData=%RU32\n",
928 mState, pvData, cbData));
929
930 mState = Dropped;
931
932 int rc = VINF_SUCCESS;
933 if (pvData)
934 {
935 Assert(cbData);
936 rc = RTCritSectEnter(&mCritSect);
937 if (RT_SUCCESS(rc))
938 {
939 if (startupInfo.pDataObject)
940 rc = startupInfo.pDataObject->Signal(mFormatRequested, pvData, cbData);
941 else
942 rc = VERR_NOT_FOUND;
943
944 RTCritSectLeave(&mCritSect);
945 }
946 }
947
948 int rc2 = mouseRelease();
949 if (RT_SUCCESS(rc))
950 rc = rc2;
951
952 LogFlowFuncLeaveRC(rc);
953 return rc;
954}
955
956/**
957 * Handles actions required when the host wants to cancel an action
958 * host -> guest operation.
959 *
960 * @return IPRT status code.
961 */
962int VBoxDnDWnd::OnHgCancel(void)
963{
964 int rc = RTCritSectEnter(&mCritSect);
965 if (RT_SUCCESS(rc))
966 {
967 if (startupInfo.pDataObject)
968 startupInfo.pDataObject->Abort();
969
970 RTCritSectLeave(&mCritSect);
971 }
972
973 int rc2 = mouseRelease();
974 if (RT_SUCCESS(rc))
975 rc = rc2;
976
977 reset();
978
979 return rc;
980}
981
982#ifdef VBOX_WITH_DRAG_AND_DROP_GH
983/**
984 * Handles actions required to start a guest -> host DnD operation.
985 * This works by letting the host ask whether a DnD operation is pending
986 * on the guest. The guest must not know anything about the host's DnD state
987 * and/or operations due to security reasons.
988 *
989 * To capture a pending DnD operation on the guest which then can be communicated
990 * to the host the proxy window needs to be registered as a drop target. This drop
991 * target then will act as a proxy target between the guest OS and the host. In other
992 * words, the guest OS will use this proxy target as a regular (invisible) window
993 * which can be used by the regular guest OS' DnD mechanisms, independently of the
994 * host OS. To make sure this proxy target is able receive an in-progress DnD operation
995 * on the guest, it will be shown invisibly across all active guest OS screens. Just
996 * think of an opened umbrella across all screens here.
997 *
998 * As soon as the proxy target and its underlying data object receive appropriate
999 * DnD messages they'll be hidden again, and the control will be transferred back
1000 * this class again.
1001 *
1002 * @return IPRT status code.
1003 * @param uScreenID Screen ID the host wants to query a pending operation
1004 * for. Currently not used/needed here.
1005 */
1006int VBoxDnDWnd::OnGhIsDnDPending(uint32_t uScreenID)
1007{
1008 LogFlowThisFunc(("mMode=%ld, mState=%ld, uScreenID=%RU32\n",
1009 mMode, mState, uScreenID));
1010
1011 if (mMode == Unknown)
1012 setMode(GH);
1013
1014 if (mMode != GH)
1015 return VERR_WRONG_ORDER;
1016
1017 if (mState == Uninitialized)
1018 {
1019 /* Nothing to do here yet. */
1020 mState = Initialized;
1021 }
1022
1023 int rc;
1024 if (mState == Initialized)
1025 {
1026 rc = makeFullscreen();
1027 if (RT_SUCCESS(rc))
1028 {
1029 /*
1030 * We have to release the left mouse button to
1031 * get into our (invisible) proxy window.
1032 */
1033 mouseRelease();
1034
1035 /*
1036 * Even if we just released the left mouse button
1037 * we're still in the dragging state to handle our
1038 * own drop target (for the host).
1039 */
1040 mState = Dragging;
1041 }
1042 }
1043 else
1044 rc = VINF_SUCCESS;
1045
1046 /**
1047 * Some notes regarding guest cursor movement:
1048 * - The host only sends an HOST_DND_GH_REQ_PENDING message to the guest
1049 * if the mouse cursor is outside the VM's window.
1050 * - The guest does not know anything about the host's cursor
1051 * position / state due to security reasons.
1052 * - The guest *only* knows that the host currently is asking whether a
1053 * guest DnD operation is in progress.
1054 */
1055
1056 if ( RT_SUCCESS(rc)
1057 && mState == Dragging)
1058 {
1059 /** @todo Put this block into a function! */
1060 POINT p;
1061 GetCursorPos(&p);
1062 ClientToScreen(hWnd, &p);
1063#ifdef DEBUG_andy
1064 LogFlowThisFunc(("Client to screen curX=%ld, curY=%ld\n", p.x, p.y));
1065#endif
1066
1067 /** @todo Multi-monitor setups? */
1068 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1069 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1070
1071 static LONG px = p.x;
1072 if (px <= 0)
1073 px = 1;
1074 static LONG py = p.y;
1075 if (py <= 0)
1076 py = 1;
1077
1078 rc = mouseMove(px, py, 0 /* dwMouseInputFlags */);
1079 }
1080
1081 if (RT_SUCCESS(rc))
1082 {
1083 uint32_t uDefAction = DND_IGNORE_ACTION;
1084
1085 AssertPtr(pDropTarget);
1086 RTCString strFormats = pDropTarget->Formats();
1087 if (!strFormats.isEmpty())
1088 {
1089 uDefAction = DND_COPY_ACTION;
1090
1091 LogFlowFunc(("Acknowledging pDropTarget=0x%p, uDefAction=0x%x, uAllActions=0x%x, strFormats=%s\n",
1092 pDropTarget, uDefAction, uAllActions, strFormats.c_str()));
1093 }
1094 else
1095 {
1096 strFormats = "unknown"; /* Prevent VERR_IO_GEN_FAILURE for IOCTL. */
1097 LogFlowFunc(("No format data available yet\n"));
1098 }
1099
1100 /** @todo Support more than one action at a time. */
1101 uAllActions = uDefAction;
1102
1103 rc = VbglR3DnDGHAcknowledgePending(mClientID,
1104 uDefAction, uAllActions, strFormats.c_str());
1105 if (RT_FAILURE(rc))
1106 {
1107 char szMsg[256]; /* Sizes according to MSDN. */
1108 char szTitle[64];
1109
1110 /** @todo Add some i18l tr() macros here. */
1111 RTStrPrintf(szTitle, sizeof(szTitle), "VirtualBox Guest Additions Drag'n Drop");
1112 RTStrPrintf(szMsg, sizeof(szMsg), "Drag'n drop to the host either is not supported or disabled. "
1113 "Please enable Guest to Host or Bidirectional drag'n drop mode "
1114 "or re-install the VirtualBox Guest Additions.");
1115 switch (rc)
1116 {
1117 case VERR_ACCESS_DENIED:
1118 rc = hlpShowBalloonTip(ghInstance, ghwndToolWindow, ID_TRAYICON,
1119 szMsg, szTitle,
1120 15 * 1000 /* Time to display in msec */, NIIF_INFO);
1121 AssertRC(rc);
1122 break;
1123
1124 default:
1125 break;
1126 }
1127 }
1128 }
1129
1130 if (RT_FAILURE(rc))
1131 reset(); /* Reset state on failure. */
1132
1133 LogFlowFuncLeaveRC(rc);
1134 return rc;
1135}
1136
1137/**
1138 * Handles actions required to let the guest know that the host
1139 * started a "drop" action on the host. This will tell the guest
1140 * to send data in a specific format the host requested.
1141 *
1142 * @return IPRT status code.
1143 * @param pszFormat Format the host requests the data in.
1144 * @param cbFormat Size (in bytes) of format string.
1145 * @param uDefAction Default action on the host.
1146 */
1147int VBoxDnDWnd::OnGhDropped(const char *pszFormat, uint32_t cbFormat,
1148 uint32_t uDefAction)
1149{
1150 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1151 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
1152
1153 LogFlowThisFunc(("mMode=%ld, mState=%ld, pDropTarget=0x%p, pszFormat=%s, uDefAction=0x%x\n",
1154 mMode, mState, pDropTarget, pszFormat, uDefAction));
1155 int rc;
1156 if (mMode == GH)
1157 {
1158 if (mState == Dragging)
1159 {
1160 AssertPtr(pDropTarget);
1161 rc = pDropTarget->WaitForDrop(30 * 1000 /* Timeout in ms */);
1162
1163 reset();
1164 }
1165 else if (mState == Dropped)
1166 {
1167 rc = VINF_SUCCESS;
1168 }
1169 else
1170 rc = VERR_WRONG_ORDER;
1171
1172 if (RT_SUCCESS(rc))
1173 {
1174 /** @todo Respect uDefAction. */
1175 void *pvData = pDropTarget->DataMutableRaw();
1176 AssertPtr(pvData);
1177 uint32_t cbData = pDropTarget->DataSize();
1178 Assert(cbData);
1179
1180 rc = VbglR3DnDGHSendData(mClientID, pszFormat, pvData, cbData);
1181 LogFlowFunc(("Sent pvData=0x%p, cbData=%RU32, rc=%Rrc\n",
1182 pvData, cbData, rc));
1183
1184
1185 }
1186 }
1187 else
1188 rc = VERR_WRONG_ORDER;
1189
1190 LogFlowFuncLeaveRC(rc);
1191 return rc;
1192}
1193#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1194
1195/**
1196 * Injects a DnD event in this proxy window's Windows
1197 * event queue. The (allocated) event will be deleted by
1198 * this class after processing.
1199 *
1200 * @return IPRT status code.
1201 * @param pEvent Event to inject.
1202 */
1203int VBoxDnDWnd::ProcessEvent(PVBOXDNDEVENT pEvent)
1204{
1205 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1206
1207 BOOL fRc = PostMessage(hWnd, WM_VBOXTRAY_DND_MESSAGE,
1208 0 /* wParm */, (LPARAM)pEvent /* lParm */);
1209 if (!fRc)
1210 {
1211 static int s_iBitchedAboutFailedDnDMessages = 0;
1212 if (s_iBitchedAboutFailedDnDMessages++ < 10)
1213 {
1214 DWORD dwErr = GetLastError();
1215 LogRel(("DnD: Processing event %p failed with %ld (%Rrc), skpping\n",
1216 pEvent, dwErr, RTErrConvertFromWin32(dwErr)));
1217 }
1218
1219 RTMemFree(pEvent);
1220 pEvent = NULL;
1221
1222 return VERR_NO_MEMORY;
1223 }
1224
1225 return VINF_SUCCESS;
1226}
1227
1228/**
1229 * Hides the proxy window again.
1230 *
1231 * @return IPRT status code.
1232 */
1233int VBoxDnDWnd::hide(void)
1234{
1235#ifdef DEBUG_andy
1236 LogFlowFunc(("\n"));
1237#endif
1238 ShowWindow(hWnd, SW_HIDE);
1239
1240 return VINF_SUCCESS;
1241}
1242
1243/**
1244 * Shows the (invisible) proxy window in fullscreen,
1245 * spawned across all active guest monitors.
1246 *
1247 * @return IPRT status code.
1248 */
1249int VBoxDnDWnd::makeFullscreen(void)
1250{
1251 int rc = VINF_SUCCESS;
1252
1253 RECT r;
1254 RT_ZERO(r);
1255
1256 BOOL fRc;
1257 HDC hDC = GetDC(NULL /* Entire screen */);
1258 if (hDC)
1259 {
1260 fRc = s_pfnEnumDisplayMonitors
1261 /* EnumDisplayMonitors is not available on NT4. */
1262 ? s_pfnEnumDisplayMonitors(hDC, NULL, VBoxDnDWnd::MonitorEnumProc, (LPARAM)&r):
1263 FALSE;
1264
1265 if (!fRc)
1266 rc = VERR_NOT_FOUND;
1267 ReleaseDC(NULL, hDC);
1268 }
1269 else
1270 rc = VERR_ACCESS_DENIED;
1271
1272 if (RT_FAILURE(rc))
1273 {
1274 /* If multi-monitor enumeration failed above, try getting at least the
1275 * primary monitor as a fallback. */
1276 r.left = 0;
1277 r.top = 0;
1278 r.right = GetSystemMetrics(SM_CXSCREEN);
1279 r.bottom = GetSystemMetrics(SM_CYSCREEN);
1280 rc = VINF_SUCCESS;
1281 }
1282
1283 if (RT_SUCCESS(rc))
1284 {
1285 LONG lStyle = GetWindowLong(hWnd, GWL_STYLE);
1286 SetWindowLong(hWnd, GWL_STYLE,
1287 lStyle & ~(WS_CAPTION | WS_THICKFRAME));
1288 LONG lExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
1289 SetWindowLong(hWnd, GWL_EXSTYLE,
1290 lExStyle & ~( WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE
1291 | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1292
1293 fRc = SetWindowPos(hWnd, HWND_TOPMOST,
1294 r.left,
1295 r.top,
1296 r.right - r.left,
1297 r.bottom - r.top,
1298#ifdef VBOX_DND_DEBUG_WND
1299 SWP_SHOWWINDOW | SWP_FRAMECHANGED);
1300#else
1301 SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
1302#endif
1303 if (fRc)
1304 {
1305 LogFlowFunc(("Virtual screen is %ld,%ld,%ld,%ld (%ld x %ld)\n",
1306 r.left, r.top, r.right, r.bottom,
1307 r.right - r.left, r.bottom - r.top));
1308 }
1309 else
1310 {
1311 DWORD dwErr = GetLastError();
1312 LogRel(("DnD: Failed to set proxy window position, rc=%Rrc\n",
1313 RTErrConvertFromWin32(dwErr)));
1314 }
1315 }
1316 else
1317 LogRel(("DnD: Failed to determine virtual screen size, rc=%Rrc\n", rc));
1318
1319 LogFlowFuncLeaveRC(rc);
1320 return rc;
1321}
1322
1323/**
1324 * Moves the guest mouse cursor to a specific position.
1325 *
1326 * @return IPRT status code.
1327 * @param x X position (in pixels) to move cursor to.
1328 * @param y Y position (in pixels) to move cursor to.
1329 * @param dwMouseInputFlags Additional movement flags. @sa MOUSEEVENTF_ flags.
1330 */
1331int VBoxDnDWnd::mouseMove(int x, int y, DWORD dwMouseInputFlags)
1332{
1333 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1334 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1335
1336 INPUT Input[1] = { 0 };
1337 Input[0].type = INPUT_MOUSE;
1338 Input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
1339 | dwMouseInputFlags;
1340 Input[0].mi.dx = x * (65535 / iScreenX);
1341 Input[0].mi.dy = y * (65535 / iScreenY);
1342
1343 int rc;
1344 if (s_pfnSendInput(1 /* Number of inputs */,
1345 Input, sizeof(INPUT)))
1346 {
1347#ifdef DEBUG_andy
1348 CURSORINFO ci;
1349 RT_ZERO(ci);
1350 ci.cbSize = sizeof(ci);
1351 BOOL fRc = GetCursorInfo(&ci);
1352 if (fRc)
1353 LogFlowThisFunc(("Cursor shown=%RTbool, cursor=0x%p, x=%d, y=%d\n",
1354 (ci.flags & CURSOR_SHOWING) ? true : false,
1355 ci.hCursor, ci.ptScreenPos.x, ci.ptScreenPos.y));
1356#endif
1357 rc = VINF_SUCCESS;
1358 }
1359 else
1360 {
1361 DWORD dwErr = GetLastError();
1362 rc = RTErrConvertFromWin32(dwErr);
1363 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1364 }
1365
1366 return rc;
1367}
1368
1369/**
1370 * Releases a previously pressed left guest mouse button.
1371 *
1372 * @return IPRT status code.
1373 */
1374int VBoxDnDWnd::mouseRelease(void)
1375{
1376#ifdef DEBUG_andy
1377 LogFlowFunc(("\n"));
1378#endif
1379 int rc;
1380
1381 /* Release mouse button in the guest to start the "drop"
1382 * action at the current mouse cursor position. */
1383 INPUT Input[1] = { 0 };
1384 Input[0].type = INPUT_MOUSE;
1385 Input[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;
1386 if (!s_pfnSendInput(1, Input, sizeof(INPUT)))
1387 {
1388 DWORD dwErr = GetLastError();
1389 rc = RTErrConvertFromWin32(dwErr);
1390 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1391 }
1392 else
1393 rc = VINF_SUCCESS;
1394
1395 return rc;
1396}
1397
1398/**
1399 * Resets the proxy window.
1400 */
1401void VBoxDnDWnd::reset(void)
1402{
1403 LogFlowThisFunc(("Resetting, old mMode=%ld, mState=%ld\n",
1404 mMode, mState));
1405
1406 lstAllowedFormats.clear();
1407 lstFormats.clear();
1408 uAllActions = DND_IGNORE_ACTION;
1409
1410 int rc2 = setMode(Unknown);
1411 AssertRC(rc2);
1412
1413 hide();
1414}
1415
1416/**
1417 * Sets the current operation mode of this proxy window.
1418 *
1419 * @return IPRT status code.
1420 * @param enmMode New mode to set.
1421 */
1422int VBoxDnDWnd::setMode(Mode enmMode)
1423{
1424 LogFlowThisFunc(("Old mode=%ld, new mode=%ld\n",
1425 mMode, enmMode));
1426
1427 mMode = enmMode;
1428 mState = Initialized;
1429
1430 return VINF_SUCCESS;
1431}
1432
1433/**
1434 * Static helper function for having an own WndProc for proxy
1435 * window instances.
1436 */
1437static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg,
1438 WPARAM wParam, LPARAM lParam)
1439{
1440 LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
1441 AssertPtrReturn(pUserData, 0);
1442
1443 VBoxDnDWnd *pWnd = reinterpret_cast<VBoxDnDWnd *>(pUserData);
1444 if (pWnd)
1445 return pWnd->WndProc(hWnd, uMsg, wParam, lParam);
1446
1447 return 0;
1448}
1449
1450/**
1451 * Static helper function for routing Windows messages to a specific
1452 * proxy window instance.
1453 */
1454static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg,
1455 WPARAM wParam, LPARAM lParam)
1456{
1457 /* Note: WM_NCCREATE is not the first ever message which arrives, but
1458 * early enough for us. */
1459 if (uMsg == WM_NCCREATE)
1460 {
1461 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
1462 AssertPtr(pCS);
1463 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
1464 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxDnDWndProcInstance);
1465
1466 return vboxDnDWndProcInstance(hWnd, uMsg, wParam, lParam);
1467 }
1468
1469 /* No window associated yet. */
1470 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1471}
1472
1473/**
1474 * Initializes drag'n drop.
1475 *
1476 * @return IPRT status code.
1477 * @param pEnv The DnD service's environment.
1478 * @param ppInstance The instance pointer which refer to this object.
1479 * @param pfStartThread Pointer to flag whether the DnD service can be started or not.
1480 */
1481int VBoxDnDInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
1482{
1483 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
1484 /** ppInstance not used here. */
1485 AssertPtrReturn(pfStartThread, VERR_INVALID_POINTER);
1486
1487 LogFlowFuncEnter();
1488
1489 *pfStartThread = false;
1490
1491 PVBOXDNDCONTEXT pCtx = &gCtx;
1492
1493 int rc;
1494 bool fSupportedOS = true;
1495
1496 s_pfnSendInput = (PFNSENDINPUT)
1497 RTLdrGetSystemSymbol("User32.dll", "SendInput");
1498 fSupportedOS = !RT_BOOL(s_pfnSendInput == NULL);
1499 s_pfnEnumDisplayMonitors = (PFNENUMDISPLAYMONITORS)
1500 RTLdrGetSystemSymbol("User32.dll", "EnumDisplayMonitors");
1501 /* g_pfnEnumDisplayMonitors is optional. */
1502
1503 if (!fSupportedOS)
1504 {
1505 LogRel(("DnD: Not supported Windows version, disabling drag'n drop support\n"));
1506 rc = VERR_NOT_SUPPORTED;
1507 }
1508 else
1509 rc = VINF_SUCCESS;
1510
1511 if (RT_SUCCESS(rc))
1512 {
1513 /* Assign service environment to our context. */
1514 pCtx->pEnv = pEnv;
1515
1516 /* Create the proxy window. At the moment we
1517 * only support one window at a time. */
1518 VBoxDnDWnd *pWnd = NULL;
1519 try
1520 {
1521 pWnd = new VBoxDnDWnd();
1522 rc = pWnd->Initialize(pCtx);
1523
1524 /* Add proxy window to our proxy windows list. */
1525 if (RT_SUCCESS(rc))
1526 pCtx->lstWnd.append(pWnd);
1527 }
1528 catch (std::bad_alloc)
1529 {
1530 rc = VERR_NO_MEMORY;
1531 }
1532 }
1533
1534 if (RT_SUCCESS(rc))
1535 rc = RTSemEventCreate(&pCtx->hEvtQueueSem);
1536 if (RT_SUCCESS(rc))
1537 {
1538 *ppInstance = pCtx;
1539 *pfStartThread = true;
1540
1541 LogRel(("DnD: Drag'n drop service successfully started\n"));
1542 return VINF_SUCCESS;
1543 }
1544
1545 LogRel(("DnD: Initializing drag'n drop service failed with rc=%Rrc\n", rc));
1546 return rc;
1547}
1548
1549void VBoxDnDStop(const VBOXSERVICEENV *pEnv, void *pInstance)
1550{
1551 AssertPtrReturnVoid(pEnv);
1552 AssertPtrReturnVoid(pInstance);
1553
1554 LogFunc(("Stopping pInstance=%p\n", pInstance));
1555
1556 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1557 AssertPtr(pCtx);
1558
1559 /* Set shutdown indicator. */
1560 ASMAtomicWriteBool(&pCtx->fShutdown, true);
1561}
1562
1563void VBoxDnDDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
1564{
1565 AssertPtr(pEnv);
1566 AssertPtr(pInstance);
1567
1568 LogFunc(("Destroying pInstance=%p\n", pInstance));
1569
1570 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1571 AssertPtr(pCtx);
1572
1573 int rc = VINF_SUCCESS;
1574
1575 /** @todo At the moment we only have one DnD proxy window. */
1576 Assert(pCtx->lstWnd.size() == 1);
1577 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1578 if (pWnd)
1579 delete pWnd;
1580
1581 if (pCtx->hEvtQueueSem != NIL_RTSEMEVENT)
1582 RTSemEventDestroy(pCtx->hEvtQueueSem);
1583
1584 LogFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
1585 pInstance, rc));
1586}
1587
1588unsigned __stdcall VBoxDnDThread(void *pInstance)
1589{
1590 LogFlowFunc(("pInstance=%p\n", pInstance));
1591
1592 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1593 AssertPtr(pCtx);
1594
1595 uint32_t uClientID;
1596 int rc = VbglR3DnDConnect(&uClientID);
1597 if (RT_FAILURE(rc))
1598 return rc;
1599
1600 /** @todo At the moment we only have one DnD proxy window. */
1601 Assert(pCtx->lstWnd.size() == 1);
1602 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1603 AssertPtr(pWnd);
1604
1605 /* Number of invalid messages skipped in a row. */
1606 int cMsgSkippedInvalid = 0;
1607
1608 do
1609 {
1610 PVBOXDNDEVENT pEvent = (PVBOXDNDEVENT)RTMemAlloc(sizeof(VBOXDNDEVENT));
1611 if (!pEvent)
1612 {
1613 rc = VERR_NO_MEMORY;
1614 break;
1615 }
1616 /* Note: pEvent will be free'd by the consumer later. */
1617
1618 rc = VbglR3DnDProcessNextMessage(uClientID, &pEvent->Event);
1619 LogFlowFunc(("VbglR3DnDProcessNextMessage returned uType=%RU32, rc=%Rrc\n",
1620 pEvent->Event.uType, rc));
1621
1622 if (ASMAtomicReadBool(&pCtx->fShutdown))
1623 break;
1624
1625 if (RT_SUCCESS(rc))
1626 {
1627 cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
1628
1629 LogFlowFunc(("Received new event, type=%RU32\n", pEvent->Event.uType));
1630
1631 int rc2 = pWnd->ProcessEvent(pEvent);
1632 if (RT_FAILURE(rc2))
1633 LogFlowFunc(("Processing event failed with rc=%Rrc\n", rc2));
1634 }
1635 else if (rc == VERR_CANCELLED)
1636 {
1637 int rc2 = pWnd->OnHgCancel();
1638 if (RT_FAILURE(rc2))
1639 LogFlowFunc(("Cancelling failed with rc=%Rrc\n", rc2));
1640 break;
1641 }
1642 else
1643 {
1644 LogFlowFunc(("Processing next message failed with rc=%Rrc\n", rc));
1645
1646 /* Old(er) hosts either are broken regarding DnD support or otherwise
1647 * don't support the stuff we do on the guest side, so make sure we
1648 * don't process invalid messages forever. */
1649 if (cMsgSkippedInvalid++ > 3)
1650 {
1651 LogRel(("DnD: Too many invalid/skipped messages from host, exiting ...\n"));
1652 break;
1653 }
1654
1655 int rc2 = VbglR3DnDGHSendError(uClientID, rc);
1656 AssertRC(rc2);
1657 }
1658
1659 if (ASMAtomicReadBool(&pCtx->fShutdown))
1660 break;
1661
1662 if (RT_FAILURE(rc)) /* Don't hog the CPU on errors. */
1663 RTThreadSleep(1000 /* ms */);
1664
1665 } while (true);
1666
1667 LogFlowFunc(("Shutting down ...\n"));
1668
1669 VbglR3DnDDisconnect(uClientID);
1670
1671 LogFlowFuncLeaveRC(rc);
1672 return rc;
1673}
1674
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette