VirtualBox

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

最後變更 在這個檔案從95837是 95833,由 vboxsync 提交於 3 年 前

DnD/VBoxTray: Introduced a new and much easier way of debugging drag'n drop issues (also in release mode!) by letting VBoxTray output the current drag'n drop status (along with format information and actions) via systray popups. This allows users to pinpoint issues much easier, without having to install a separate debug build. Disabled by default -- verbose mode needs to be active for this. bugref:10267

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 62.4 KB
 
1/* $Id: VBoxDnD.cpp 95833 2022-07-26 13:53:35Z vboxsync $ */
2/** @file
3 * VBoxDnD.cpp - Windows-specific bits of the drag and drop service.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GUEST_DND
23#include <iprt/win/windows.h>
24#include "VBoxTray.h"
25#include "VBoxHelpers.h"
26#include "VBoxDnD.h"
27
28#include <VBox/VBoxGuestLib.h>
29#include "VBox/HostServices/DragAndDropSvc.h"
30
31using namespace DragAndDropSvc;
32
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/ldr.h>
36#include <iprt/list.h>
37#include <iprt/mem.h>
38
39#include <iprt/cpp/mtlist.h>
40#include <iprt/cpp/ministring.h>
41
42#include <iprt/cpp/mtlist.h>
43
44#include <VBox/err.h>
45#include <VBox/log.h>
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51/** The drag and drop window's window class. */
52#define VBOX_DND_WND_CLASS "VBoxTrayDnDWnd"
53
54/** @todo Merge this with messages from VBoxTray.h. */
55#define WM_VBOXTRAY_DND_MESSAGE WM_APP + 401
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61/** Function pointer for SendInput(). This only is available starting
62 * at NT4 SP3+. */
63typedef BOOL (WINAPI *PFNSENDINPUT)(UINT, LPINPUT, int);
64typedef BOOL (WINAPI* PFNENUMDISPLAYMONITORS)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
65
66
67/*********************************************************************************************************************************
68* Global Variables *
69*********************************************************************************************************************************/
70/** Static pointer to SendInput() function. */
71static PFNSENDINPUT g_pfnSendInput = NULL;
72static PFNENUMDISPLAYMONITORS g_pfnEnumDisplayMonitors = NULL;
73
74static VBOXDNDCONTEXT g_Ctx = { 0 };
75
76
77/*********************************************************************************************************************************
78* Internal Functions *
79*********************************************************************************************************************************/
80static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_PROTO;
81static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_PROTO;
82
83
84
85
86VBoxDnDWnd::VBoxDnDWnd(void)
87 : m_hThread(NIL_RTTHREAD),
88 m_EvtSem(NIL_RTSEMEVENT),
89 m_hWnd(NULL),
90 m_lstActionsAllowed(VBOX_DND_ACTION_IGNORE),
91 m_fMouseButtonDown(false),
92#ifdef VBOX_WITH_DRAG_AND_DROP_GH
93 m_pDropTarget(NULL),
94#endif
95 m_enmMode(Unknown),
96 m_enmState(Uninitialized)
97{
98 RT_ZERO(m_startupInfo);
99
100 LogFlowFunc(("Supported formats:\n"));
101 const RTCString arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
102 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
103 {
104 LogFlowFunc(("\t%s\n", arrEntries[i].c_str()));
105 this->m_lstFmtSup.append(arrEntries[i]);
106 }
107}
108
109VBoxDnDWnd::~VBoxDnDWnd(void)
110{
111 Destroy();
112}
113
114/**
115 * Initializes the proxy window with a given DnD context.
116 *
117 * @return IPRT status code.
118 * @param a_pCtx Pointer to context to use.
119 */
120int VBoxDnDWnd::Initialize(PVBOXDNDCONTEXT a_pCtx)
121{
122 AssertPtrReturn(a_pCtx, VERR_INVALID_POINTER);
123
124 /* Save the context. */
125 this->m_pCtx = a_pCtx;
126
127 int rc = RTSemEventCreate(&m_EvtSem);
128 if (RT_SUCCESS(rc))
129 rc = RTCritSectInit(&m_CritSect);
130
131 if (RT_SUCCESS(rc))
132 {
133 /* Message pump thread for our proxy window. */
134 rc = RTThreadCreate(&m_hThread, VBoxDnDWnd::Thread, this,
135 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
136 "dndwnd"); /** @todo Include ID if there's more than one proxy window. */
137 if (RT_SUCCESS(rc))
138 {
139 int rc2 = RTThreadUserWait(m_hThread, 30 * 1000 /* Timeout in ms */);
140 AssertRC(rc2);
141
142 if (!a_pCtx->fStarted) /* Did the thread fail to start? */
143 rc = VERR_NOT_SUPPORTED; /* Report back DnD as not being supported. */
144 }
145 }
146
147 if (RT_FAILURE(rc))
148 LogRel(("DnD: Failed to initialize proxy window, rc=%Rrc\n", rc));
149
150 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
151 return rc;
152}
153
154/**
155 * Destroys the proxy window and releases all remaining
156 * resources again.
157 */
158void VBoxDnDWnd::Destroy(void)
159{
160 if (m_hThread != NIL_RTTHREAD)
161 {
162 int rcThread = VERR_WRONG_ORDER;
163 int rc = RTThreadWait(m_hThread, 60 * 1000 /* Timeout in ms */, &rcThread);
164 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n",
165 rc, rcThread));
166 NOREF(rc);
167 }
168
169 Reset();
170
171 RTCritSectDelete(&m_CritSect);
172 if (m_EvtSem != NIL_RTSEMEVENT)
173 {
174 RTSemEventDestroy(m_EvtSem);
175 m_EvtSem = NIL_RTSEMEVENT;
176 }
177
178 if (m_pCtx->wndClass != 0)
179 {
180 UnregisterClass(VBOX_DND_WND_CLASS, m_pCtx->pEnv->hInstance);
181 m_pCtx->wndClass = 0;
182 }
183
184 LogFlowFuncLeave();
185}
186
187/**
188 * Thread for handling the window's message pump.
189 *
190 * @return IPRT status code.
191 * @param hThread Handle to this thread.
192 * @param pvUser Pointer to VBoxDnDWnd instance which
193 * is using the thread.
194 */
195/*static*/ DECLCALLBACK(int) VBoxDnDWnd::Thread(RTTHREAD hThread, void *pvUser)
196{
197 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
198
199 LogFlowFuncEnter();
200
201 VBoxDnDWnd *pThis = (VBoxDnDWnd*)pvUser;
202 AssertPtr(pThis);
203
204 PVBOXDNDCONTEXT m_pCtx = pThis->m_pCtx;
205 AssertPtr(m_pCtx);
206 AssertPtr(m_pCtx->pEnv);
207
208 int rc = VINF_SUCCESS;
209
210 AssertPtr(m_pCtx->pEnv);
211 HINSTANCE hInstance = m_pCtx->pEnv->hInstance;
212 Assert(hInstance != 0);
213
214 /* Create our proxy window. */
215 WNDCLASSEX wc = { 0 };
216 wc.cbSize = sizeof(WNDCLASSEX);
217
218 if (!GetClassInfoEx(hInstance, VBOX_DND_WND_CLASS, &wc))
219 {
220 wc.lpfnWndProc = vboxDnDWndProc;
221 wc.lpszClassName = VBOX_DND_WND_CLASS;
222 wc.hInstance = hInstance;
223 wc.style = CS_NOCLOSE;
224
225 if (g_cVerbosity)
226 {
227 /* Make it a solid red color so that we can see the window. */
228 wc.style |= CS_HREDRAW | CS_VREDRAW;
229 wc.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(255, 0, 0)));
230 }
231 else
232 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
233
234 if (!RegisterClassEx(&wc))
235 {
236 DWORD dwErr = GetLastError();
237 LogFlowFunc(("Unable to register proxy window class, error=%ld\n", dwErr));
238 rc = RTErrConvertFromWin32(dwErr);
239 }
240 }
241
242 if (RT_SUCCESS(rc))
243 {
244 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE;
245 DWORD dwStyle = WS_POPUP;
246 if (g_cVerbosity)
247 {
248 dwStyle |= WS_VISIBLE;
249 }
250 else
251 dwExStyle |= WS_EX_TRANSPARENT;
252
253 pThis->m_hWnd = CreateWindowEx(dwExStyle,
254 VBOX_DND_WND_CLASS, VBOX_DND_WND_CLASS,
255 dwStyle,
256 -200, -200, 100, 100, NULL, NULL,
257 hInstance, pThis /* lParm */);
258 if (!pThis->m_hWnd)
259 {
260 DWORD dwErr = GetLastError();
261 LogFlowFunc(("Unable to create proxy window, error=%ld\n", dwErr));
262 rc = RTErrConvertFromWin32(dwErr);
263 }
264 else
265 {
266 BOOL fRc = SetWindowPos(pThis->m_hWnd, HWND_TOPMOST, -200, -200, 0, 0,
267 SWP_NOACTIVATE | SWP_HIDEWINDOW
268 | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
269 AssertMsg(fRc, ("Unable to set window position, error=%ld\n", GetLastError()));
270
271 LogFlowFunc(("Proxy window created, hWnd=0x%x\n", pThis->m_hWnd));
272
273 if (g_cVerbosity)
274 {
275 /*
276 * Install some mouse tracking.
277 */
278 TRACKMOUSEEVENT me;
279 RT_ZERO(me);
280 me.cbSize = sizeof(TRACKMOUSEEVENT);
281 me.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT;
282 me.hwndTrack = pThis->m_hWnd;
283
284 fRc = TrackMouseEvent(&me);
285 AssertMsg(fRc, ("Unable to enable debug mouse tracking, error=%ld\n", GetLastError()));
286 }
287 }
288 }
289
290 HRESULT hr = OleInitialize(NULL);
291 if (SUCCEEDED(hr))
292 {
293#ifdef VBOX_WITH_DRAG_AND_DROP_GH
294 rc = pThis->RegisterAsDropTarget();
295#endif
296 }
297 else
298 {
299 LogRel(("DnD: Unable to initialize OLE, hr=%Rhrc\n", hr));
300 rc = VERR_COM_UNEXPECTED;
301 }
302
303 if (RT_SUCCESS(rc))
304 m_pCtx->fStarted = true; /* Set started indicator on success. */
305
306 int rc2 = RTThreadUserSignal(hThread);
307 bool fSignalled = RT_SUCCESS(rc2);
308
309 if (RT_SUCCESS(rc))
310 {
311 bool fShutdown = false;
312 for (;;)
313 {
314 MSG uMsg;
315 BOOL fRet;
316 while ((fRet = GetMessage(&uMsg, 0, 0, 0)) > 0)
317 {
318 TranslateMessage(&uMsg);
319 DispatchMessage(&uMsg);
320 }
321 Assert(fRet >= 0);
322
323 if (ASMAtomicReadBool(&m_pCtx->fShutdown))
324 fShutdown = true;
325
326 if (fShutdown)
327 {
328 LogFlowFunc(("Closing proxy window ...\n"));
329 break;
330 }
331
332 /** @todo Immediately drop on failure? */
333 }
334
335#ifdef VBOX_WITH_DRAG_AND_DROP_GH
336 rc2 = pThis->UnregisterAsDropTarget();
337 if (RT_SUCCESS(rc))
338 rc = rc2;
339#endif
340 OleUninitialize();
341 }
342
343 if (!fSignalled)
344 {
345 rc2 = RTThreadUserSignal(hThread);
346 AssertRC(rc2);
347 }
348
349 LogFlowFuncLeaveRC(rc);
350 return rc;
351}
352
353/**
354 * Monitor enumeration callback for building up a simple bounding
355 * box, capable of holding all enumerated monitors.
356 *
357 * @return BOOL TRUE if enumeration should continue,
358 * FALSE if not.
359 * @param hMonitor Handle to current monitor being enumerated.
360 * @param hdcMonitor The current monitor's DC (device context).
361 * @param lprcMonitor The current monitor's RECT.
362 * @param lParam Pointer to a RECT structure holding the
363 * bounding box to build.
364 */
365/* static */
366BOOL CALLBACK VBoxDnDWnd::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM lParam)
367{
368 RT_NOREF(hMonitor, hdcMonitor);
369 LPRECT pRect = (LPRECT)lParam;
370 AssertPtrReturn(pRect, FALSE);
371
372 AssertPtr(lprcMonitor);
373 LogFlowFunc(("Monitor is %ld,%ld,%ld,%ld\n",
374 lprcMonitor->left, lprcMonitor->top,
375 lprcMonitor->right, lprcMonitor->bottom));
376
377 /* Build up a simple bounding box to hold the entire (virtual) screen. */
378 if (pRect->left > lprcMonitor->left)
379 pRect->left = lprcMonitor->left;
380 if (pRect->right < lprcMonitor->right)
381 pRect->right = lprcMonitor->right;
382 if (pRect->top > lprcMonitor->top)
383 pRect->top = lprcMonitor->top;
384 if (pRect->bottom < lprcMonitor->bottom)
385 pRect->bottom = lprcMonitor->bottom;
386
387 return TRUE;
388}
389
390/**
391 * The proxy window's WndProc.
392 */
393LRESULT CALLBACK VBoxDnDWnd::WndProc(HWND a_hWnd, UINT a_uMsg, WPARAM a_wParam, LPARAM a_lParam)
394{
395 switch (a_uMsg)
396 {
397 case WM_CREATE:
398 {
399 int rc = OnCreate();
400 if (RT_FAILURE(rc))
401 {
402 LogRel(("DnD: Failed to create proxy window, rc=%Rrc\n", rc));
403 return -1;
404 }
405 return 0;
406 }
407
408 case WM_QUIT:
409 {
410 LogFlowThisFunc(("WM_QUIT\n"));
411 PostQuitMessage(0);
412 return 0;
413 }
414
415 case WM_DESTROY:
416 {
417 LogFlowThisFunc(("WM_DESTROY\n"));
418
419 OnDestroy();
420 return 0;
421 }
422
423 case WM_LBUTTONDOWN:
424 {
425 LogFlowThisFunc(("WM_LBUTTONDOWN\n"));
426 m_fMouseButtonDown = true;
427 return 0;
428 }
429
430 case WM_LBUTTONUP:
431 {
432 LogFlowThisFunc(("WM_LBUTTONUP\n"));
433 m_fMouseButtonDown = false;
434
435 /* As the mouse button was released, Hide the proxy window again.
436 * This can happen if
437 * - the user bumped a guest window to the screen's edges
438 * - there was no drop data from the guest available and the user
439 * enters the guest screen again after this unsuccessful operation */
440 Reset();
441 return 0;
442 }
443
444 case WM_MOUSELEAVE:
445 {
446 LogFlowThisFunc(("WM_MOUSELEAVE\n"));
447 return 0;
448 }
449
450 /* Will only be called once; after the first mouse move, this
451 * window will be hidden! */
452 case WM_MOUSEMOVE:
453 {
454 LogFlowThisFunc(("WM_MOUSEMOVE: mfMouseButtonDown=%RTbool, mMode=%ld, mState=%ld\n",
455 m_fMouseButtonDown, m_enmMode, m_enmState));
456#ifdef DEBUG_andy
457 POINT p;
458 GetCursorPos(&p);
459 LogFlowThisFunc(("WM_MOUSEMOVE: curX=%ld, curY=%ld\n", p.x, p.y));
460#endif
461 int rc = VINF_SUCCESS;
462 if (m_enmMode == HG) /* Host to guest. */
463 {
464 /* Dragging not started yet? Kick it off ... */
465 if ( m_fMouseButtonDown
466 && (m_enmState != Dragging))
467 {
468 m_enmState = Dragging;
469#if 0
470 /* Delay hiding the proxy window a bit when debugging, to see
471 * whether the desired range is covered correctly. */
472 RTThreadSleep(5000);
473#endif
474 Hide();
475
476 LogFlowThisFunc(("Starting drag and drop: dndLstActionsAllowed=0x%x, dwOKEffects=0x%x ...\n",
477 m_lstActionsAllowed, m_startupInfo.dwOKEffects));
478
479 AssertPtr(m_startupInfo.pDataObject);
480 AssertPtr(m_startupInfo.pDropSource);
481 DWORD dwEffect;
482 HRESULT hr = DoDragDrop(m_startupInfo.pDataObject, m_startupInfo.pDropSource,
483 m_startupInfo.dwOKEffects, &dwEffect);
484 LogFlowThisFunc(("hr=%Rhrc, dwEffect=%RI32\n", hr, dwEffect));
485 switch (hr)
486 {
487 case DRAGDROP_S_DROP:
488 m_enmState = Dropped;
489 break;
490
491 case DRAGDROP_S_CANCEL:
492 m_enmState = Canceled;
493 break;
494
495 default:
496 LogFlowThisFunc(("Drag and drop failed with %Rhrc\n", hr));
497 m_enmState = Canceled;
498 rc = VERR_GENERAL_FAILURE; /** @todo Find a better status code. */
499 break;
500 }
501
502 int rc2 = RTCritSectEnter(&m_CritSect);
503 if (RT_SUCCESS(rc2))
504 {
505 m_startupInfo.pDropSource->Release();
506 m_startupInfo.pDataObject->Release();
507
508 RT_ZERO(m_startupInfo);
509
510 rc2 = RTCritSectLeave(&m_CritSect);
511 if (RT_SUCCESS(rc))
512 rc = rc2;
513 }
514
515 m_enmMode = Unknown;
516 }
517 }
518 else if (m_enmMode == GH) /* Guest to host. */
519 {
520 /* Starting here VBoxDnDDropTarget should
521 * take over; was instantiated when registering
522 * this proxy window as a (valid) drop target. */
523 }
524 else
525 rc = VERR_NOT_SUPPORTED;
526
527 LogFlowThisFunc(("WM_MOUSEMOVE: mMode=%ld, mState=%ld, rc=%Rrc\n",
528 m_enmMode, m_enmState, rc));
529 return 0;
530 }
531
532 case WM_NCMOUSEHOVER:
533 LogFlowThisFunc(("WM_NCMOUSEHOVER\n"));
534 return 0;
535
536 case WM_NCMOUSELEAVE:
537 LogFlowThisFunc(("WM_NCMOUSELEAVE\n"));
538 return 0;
539
540 case WM_VBOXTRAY_DND_MESSAGE:
541 {
542 PVBOXDNDEVENT pEvent = (PVBOXDNDEVENT)a_lParam;
543 if (!pEvent)
544 break; /* No event received, bail out. */
545
546 PVBGLR3DNDEVENT pVbglR3Event = pEvent->pVbglR3Event;
547 AssertPtrBreak(pVbglR3Event);
548
549 LogFlowThisFunc(("Received enmType=%RU32\n", pVbglR3Event->enmType));
550
551 int rc;
552 switch (pVbglR3Event->enmType)
553 {
554 case VBGLR3DNDEVENTTYPE_HG_ENTER:
555 {
556 if (pVbglR3Event->u.HG_Enter.cbFormats)
557 {
558 RTCList<RTCString> lstFormats =
559 RTCString(pVbglR3Event->u.HG_Enter.pszFormats, pVbglR3Event->u.HG_Enter.cbFormats - 1).split(DND_FORMATS_SEPARATOR_STR);
560 rc = OnHgEnter(lstFormats, pVbglR3Event->u.HG_Enter.dndLstActionsAllowed);
561 if (RT_FAILURE(rc))
562 break;
563 }
564 else
565 {
566 AssertMsgFailed(("cbFormats is 0\n"));
567 rc = VERR_INVALID_PARAMETER;
568 break;
569 }
570
571 /* Note: After HOST_DND_FN_HG_EVT_ENTER there immediately is a move
572 * event, so fall through is intentional here. */
573 RT_FALL_THROUGH();
574 }
575
576 case VBGLR3DNDEVENTTYPE_HG_MOVE:
577 {
578 rc = OnHgMove(pVbglR3Event->u.HG_Move.uXpos, pVbglR3Event->u.HG_Move.uYpos,
579 pVbglR3Event->u.HG_Move.dndActionDefault);
580 break;
581 }
582
583 case VBGLR3DNDEVENTTYPE_HG_LEAVE:
584 {
585 rc = OnHgLeave();
586 break;
587 }
588
589 case VBGLR3DNDEVENTTYPE_HG_DROP:
590 {
591 rc = OnHgDrop();
592 break;
593 }
594
595 /**
596 * The data header now will contain all the (meta) data the guest needs in
597 * order to complete the DnD operation.
598 */
599 case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
600 {
601 rc = OnHgDataReceive(&pVbglR3Event->u.HG_Received.Meta);
602 break;
603 }
604
605 case VBGLR3DNDEVENTTYPE_HG_CANCEL:
606 {
607 rc = OnHgCancel();
608 break;
609 }
610
611 case VBGLR3DNDEVENTTYPE_QUIT:
612 {
613 LogRel(("DnD: Received quit message, shutting down ...\n"));
614 PostQuitMessage(0);
615 }
616
617#ifdef VBOX_WITH_DRAG_AND_DROP_GH
618 case VBGLR3DNDEVENTTYPE_GH_ERROR:
619 {
620 Reset();
621 rc = VINF_SUCCESS;
622 break;
623 }
624
625 case VBGLR3DNDEVENTTYPE_GH_REQ_PENDING:
626 {
627 rc = OnGhIsDnDPending();
628 break;
629 }
630
631 case VBGLR3DNDEVENTTYPE_GH_DROP:
632 {
633 rc = OnGhDrop(pVbglR3Event->u.GH_Drop.pszFormat, pVbglR3Event->u.GH_Drop.dndActionRequested);
634 break;
635 }
636#endif
637 default:
638 {
639 LogRel(("DnD: Received unsupported message '%RU32'\n", pVbglR3Event->enmType));
640 rc = VERR_NOT_SUPPORTED;
641 break;
642 }
643 }
644
645 LogFlowFunc(("Message %RU32 processed with %Rrc\n", pVbglR3Event->enmType, rc));
646 if (RT_FAILURE(rc))
647 {
648 /* Tell the user. */
649 LogRel(("DnD: Processing message %RU32 failed with %Rrc\n", pVbglR3Event->enmType, rc));
650
651 /* If anything went wrong, do a reset and start over. */
652 Reset();
653 }
654
655 if (pEvent)
656 {
657 VbglR3DnDEventFree(pEvent->pVbglR3Event);
658 pEvent->pVbglR3Event = NULL;
659
660 RTMemFree(pEvent);
661 }
662
663 return 0;
664 }
665
666 default:
667 break;
668 }
669
670 return DefWindowProc(a_hWnd, a_uMsg, a_wParam, a_lParam);
671}
672
673#ifdef VBOX_WITH_DRAG_AND_DROP_GH
674
675/**
676 * Registers this proxy window as a local drop target.
677 *
678 * @return IPRT status code.
679 */
680int VBoxDnDWnd::RegisterAsDropTarget(void)
681{
682 if (m_pDropTarget) /* Already registered as drop target? */
683 return VINF_SUCCESS;
684
685# ifdef RT_EXCEPTIONS_ENABLED
686 try { m_pDropTarget = new VBoxDnDDropTarget(this /* pParent */); }
687 catch (std::bad_alloc &)
688# else
689 m_pDropTarget = new VBoxDnDDropTarget(this /* pParent */);
690 if (!m_pDropTarget)
691# endif
692 {
693 LogFunc(("VERR_NO_MEMORY!\n"));
694 return VERR_NO_MEMORY;
695 }
696
697 HRESULT hrc = CoLockObjectExternal(m_pDropTarget, TRUE /* fLock */, FALSE /* fLastUnlockReleases */);
698 if (SUCCEEDED(hrc))
699 {
700 hrc = RegisterDragDrop(m_hWnd, m_pDropTarget);
701 if (SUCCEEDED(hrc))
702 {
703 LogFlowFuncLeaveRC(VINF_SUCCESS);
704 return VINF_SUCCESS;
705 }
706 }
707 if (hrc != DRAGDROP_E_INVALIDHWND) /* Could be because the DnD host service is not available. */
708 LogRel(("DnD: Creating drop target failed with hr=%Rhrc\n", hrc));
709 LogFlowFuncLeaveRC(VERR_NOT_SUPPORTED);
710 return VERR_NOT_SUPPORTED; /* Report back DnD as not being supported. */
711}
712
713/**
714 * Unregisters this proxy as a drop target.
715 *
716 * @return IPRT status code.
717 */
718int VBoxDnDWnd::UnregisterAsDropTarget(void)
719{
720 LogFlowFuncEnter();
721
722 if (!m_pDropTarget) /* No drop target? Bail out. */
723 return VINF_SUCCESS;
724
725 HRESULT hr = RevokeDragDrop(m_hWnd);
726 if (SUCCEEDED(hr))
727 hr = CoLockObjectExternal(m_pDropTarget, FALSE /* fLock */,
728 TRUE /* fLastUnlockReleases */);
729 if (SUCCEEDED(hr))
730 {
731 ULONG cRefs = m_pDropTarget->Release();
732 Assert(cRefs == 0); NOREF(cRefs);
733 m_pDropTarget = NULL;
734 }
735
736 int rc = SUCCEEDED(hr)
737 ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fix this. */
738
739 LogFlowFuncLeaveRC(rc);
740 return rc;
741}
742
743#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
744
745/**
746 * Handles the creation of a proxy window.
747 *
748 * @return IPRT status code.
749 */
750int VBoxDnDWnd::OnCreate(void)
751{
752 LogFlowFuncEnter();
753 int rc = VbglR3DnDConnect(&m_cmdCtx);
754 if (RT_FAILURE(rc))
755 {
756 LogRel(("DnD: Connection to host service failed, rc=%Rrc\n", rc));
757 return rc;
758 }
759
760 LogFlowThisFunc(("Client ID=%RU32, rc=%Rrc\n", m_cmdCtx.uClientID, rc));
761 return rc;
762}
763
764/**
765 * Handles the destruction of a proxy window.
766 */
767void VBoxDnDWnd::OnDestroy(void)
768{
769 DestroyWindow(m_hWnd);
770
771 VbglR3DnDDisconnect(&m_cmdCtx);
772 LogFlowThisFuncLeave();
773}
774
775/**
776 * Aborts an in-flight DnD operation on the guest.
777 *
778 * @return VBox status code.
779 */
780int VBoxDnDWnd::Abort(void)
781{
782 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", m_enmMode, m_enmState));
783 LogRel(("DnD: Drag and drop operation aborted\n"));
784
785 int rc = RTCritSectEnter(&m_CritSect);
786 if (RT_SUCCESS(rc))
787 {
788 if (m_startupInfo.pDataObject)
789 m_startupInfo.pDataObject->Abort();
790
791 RTCritSectLeave(&m_CritSect);
792 }
793
794 /* Post ESC to our window to officially abort the
795 * drag and drop operation. */
796 this->PostMessage(WM_KEYDOWN, VK_ESCAPE /* wParam */, 0 /* lParam */);
797
798 Reset();
799
800 return rc;
801}
802
803/**
804 * Handles actions required when the host cursor enters
805 * the guest's screen to initiate a host -> guest DnD operation.
806 *
807 * @return IPRT status code.
808 * @param a_lstFormats Supported formats offered by the host.
809 * @param a_fDndLstActionsAllowed Supported actions offered by the host.
810 */
811int VBoxDnDWnd::OnHgEnter(const RTCList<RTCString> &a_lstFormats, VBOXDNDACTIONLIST a_fDndLstActionsAllowed)
812{
813 if (m_enmMode == GH) /* Wrong mode? Bail out. */
814 return VERR_WRONG_ORDER;
815
816#ifdef DEBUG
817 LogFlowThisFunc(("dndActionList=0x%x, a_lstFormats=%zu: ", a_fDndLstActionsAllowed, a_lstFormats.size()));
818 for (size_t i = 0; i < a_lstFormats.size(); i++)
819 LogFlow(("'%s' ", a_lstFormats.at(i).c_str()));
820 LogFlow(("\n"));
821#endif
822
823 Reset();
824 setMode(HG);
825
826 /* Check if the VM session has changed and reconnect to the HGCM service if necessary. */
827 int rc = checkForSessionChange();
828 if (RT_FAILURE(rc))
829 return rc;
830
831 if (g_cVerbosity)
832 {
833 RTCString strMsg("Enter: Host -> Guest\n\n");
834 strMsg += RTCStringFmt("Allowed actions: %#x\n", a_fDndLstActionsAllowed);
835 strMsg += "Formats:\n";
836 for (size_t i = 0; i < this->m_lstFmtSup.size(); i++)
837 {
838 if (i > 0)
839 strMsg += "\n";
840 strMsg += this->m_lstFmtSup.at(i);
841 }
842
843 hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
844 strMsg.c_str(), "VirtualBox Drag'n Drop",
845 15 * 1000 /* Time to display in msec */, NIIF_INFO);
846 }
847
848 /* Save all allowed actions. */
849 this->m_lstActionsAllowed = a_fDndLstActionsAllowed;
850
851 /*
852 * Check if reported formats from host are compatible with this client.
853 */
854 size_t cFormatsSup = this->m_lstFmtSup.size();
855 ULONG cFormatsActive = 0;
856
857 LPFORMATETC paFormatEtc = (LPFORMATETC)RTMemTmpAllocZ(sizeof(paFormatEtc[0]) * cFormatsSup);
858 AssertReturn(paFormatEtc, VERR_NO_TMP_MEMORY);
859
860 LPSTGMEDIUM paStgMeds = (LPSTGMEDIUM)RTMemTmpAllocZ(sizeof(paStgMeds[0]) * cFormatsSup);
861 AssertReturnStmt(paFormatEtc, RTMemTmpFree(paFormatEtc), VERR_NO_TMP_MEMORY);
862
863 LogRel2(("DnD: Reported formats:\n"));
864 for (size_t i = 0; i < a_lstFormats.size(); i++)
865 {
866 bool fSupported = false;
867 for (size_t a = 0; a < this->m_lstFmtSup.size(); a++)
868 {
869 const char *pszFormat = a_lstFormats.at(i).c_str();
870 LogFlowThisFunc(("\t\"%s\" <=> \"%s\"\n", this->m_lstFmtSup.at(a).c_str(), pszFormat));
871
872 fSupported = RTStrICmp(this->m_lstFmtSup.at(a).c_str(), pszFormat) == 0;
873 if (fSupported)
874 {
875 this->m_lstFmtActive.append(a_lstFormats.at(i));
876
877 /** @todo Put this into a \#define / struct. */
878 if (!RTStrICmp(pszFormat, "text/uri-list"))
879 {
880 paFormatEtc[cFormatsActive].cfFormat = CF_HDROP;
881 paFormatEtc[cFormatsActive].dwAspect = DVASPECT_CONTENT;
882 paFormatEtc[cFormatsActive].lindex = -1;
883 paFormatEtc[cFormatsActive].tymed = TYMED_HGLOBAL;
884
885 paStgMeds [cFormatsActive].tymed = TYMED_HGLOBAL;
886 cFormatsActive++;
887 }
888 else if ( !RTStrICmp(pszFormat, "text/plain")
889 || !RTStrICmp(pszFormat, "text/html")
890 || !RTStrICmp(pszFormat, "text/plain;charset=utf-8")
891 || !RTStrICmp(pszFormat, "text/plain;charset=utf-16")
892 || !RTStrICmp(pszFormat, "text/plain")
893 || !RTStrICmp(pszFormat, "text/richtext")
894 || !RTStrICmp(pszFormat, "UTF8_STRING")
895 || !RTStrICmp(pszFormat, "TEXT")
896 || !RTStrICmp(pszFormat, "STRING"))
897 {
898 paFormatEtc[cFormatsActive].cfFormat = CF_TEXT;
899 paFormatEtc[cFormatsActive].dwAspect = DVASPECT_CONTENT;
900 paFormatEtc[cFormatsActive].lindex = -1;
901 paFormatEtc[cFormatsActive].tymed = TYMED_HGLOBAL;
902
903 paStgMeds [cFormatsActive].tymed = TYMED_HGLOBAL;
904 cFormatsActive++;
905 }
906 else /* Should never happen. */
907 AssertReleaseMsgFailedBreak(("Format specification for '%s' not implemented\n", pszFormat));
908 break;
909 }
910 }
911
912 LogRel2(("DnD: \t%s: %RTbool\n", a_lstFormats.at(i).c_str(), fSupported));
913 }
914
915 /*
916 * Warn in the log if this guest does not accept anything.
917 */
918 Assert(cFormatsActive <= cFormatsSup);
919 if (cFormatsActive)
920 {
921 LogRel2(("DnD: %RU32 supported formats found:\n", cFormatsActive));
922 for (size_t i = 0; i < cFormatsActive; i++)
923 LogRel2(("DnD: \t%s\n", this->m_lstFmtActive.at(i).c_str()));
924 }
925 else
926 LogRel(("DnD: Warning: No supported drag and drop formats on the guest found!\n"));
927
928 /*
929 * Prepare the startup info for DoDragDrop().
930 */
931
932 /* Translate our drop actions into allowed Windows drop effects. */
933 m_startupInfo.dwOKEffects = DROPEFFECT_NONE;
934 if (a_fDndLstActionsAllowed)
935 {
936 if (a_fDndLstActionsAllowed & VBOX_DND_ACTION_COPY)
937 m_startupInfo.dwOKEffects |= DROPEFFECT_COPY;
938 if (a_fDndLstActionsAllowed & VBOX_DND_ACTION_MOVE)
939 m_startupInfo.dwOKEffects |= DROPEFFECT_MOVE;
940 if (a_fDndLstActionsAllowed & VBOX_DND_ACTION_LINK)
941 m_startupInfo.dwOKEffects |= DROPEFFECT_LINK;
942 }
943
944 LogRel2(("DnD: Supported drop actions: 0x%x\n", m_startupInfo.dwOKEffects));
945
946#ifdef RT_EXCEPTIONS_ENABLED
947 try
948 {
949 m_startupInfo.pDropSource = new VBoxDnDDropSource(this);
950 m_startupInfo.pDataObject = new VBoxDnDDataObject(paFormatEtc, paStgMeds, cFormatsActive);
951 }
952 catch (std::bad_alloc &)
953#else
954 m_startupInfo.pDropSource = new VBoxDnDDropSource(this);
955 m_startupInfo.pDataObject = new VBoxDnDDataObject(paFormatEtc, paStgMeds, cFormatsActive);
956 if (!m_startupInfo.pDropSource || !m_startupInfo.pDataObject)
957#endif
958 {
959 LogFunc(("VERR_NO_MEMORY!"));
960 rc = VERR_NO_MEMORY;
961 }
962
963 RTMemTmpFree(paFormatEtc);
964 RTMemTmpFree(paStgMeds);
965
966 if (RT_SUCCESS(rc))
967 rc = makeFullscreen();
968
969 LogFlowFuncLeaveRC(rc);
970 return rc;
971}
972
973/**
974 * Handles actions required when the host cursor moves inside
975 * the guest's screen.
976 *
977 * @return IPRT status code.
978 * @param u32xPos Absolute X position (in pixels) of the host cursor
979 * inside the guest.
980 * @param u32yPos Absolute Y position (in pixels) of the host cursor
981 * inside the guest.
982 * @param dndAction Action the host wants to perform while moving.
983 * Currently ignored.
984 */
985int VBoxDnDWnd::OnHgMove(uint32_t u32xPos, uint32_t u32yPos, VBOXDNDACTION dndAction)
986{
987 RT_NOREF(dndAction);
988 int rc;
989
990 uint32_t uActionNotify = VBOX_DND_ACTION_IGNORE;
991 if (m_enmMode == HG)
992 {
993 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, dndAction=0x%x\n",
994 u32xPos, u32yPos, dndAction));
995
996 rc = mouseMove(u32xPos, u32yPos, MOUSEEVENTF_LEFTDOWN);
997
998 if (RT_SUCCESS(rc))
999 rc = RTCritSectEnter(&m_CritSect);
1000 if (RT_SUCCESS(rc))
1001 {
1002 if ( (Dragging == m_enmState)
1003 && m_startupInfo.pDropSource)
1004 uActionNotify = m_startupInfo.pDropSource->GetCurrentAction();
1005
1006 RTCritSectLeave(&m_CritSect);
1007 }
1008 }
1009 else /* Just acknowledge the operation with an ignore action. */
1010 rc = VINF_SUCCESS;
1011
1012 if (RT_SUCCESS(rc))
1013 {
1014 rc = VbglR3DnDHGSendAckOp(&m_cmdCtx, uActionNotify);
1015 if (RT_FAILURE(rc))
1016 LogFlowThisFunc(("Acknowledging operation failed with rc=%Rrc\n", rc));
1017 }
1018
1019 LogFlowThisFunc(("Returning uActionNotify=0x%x, rc=%Rrc\n", uActionNotify, rc));
1020 return rc;
1021}
1022
1023/**
1024 * Handles actions required when the host cursor leaves
1025 * the guest's screen again.
1026 *
1027 * @return IPRT status code.
1028 */
1029int VBoxDnDWnd::OnHgLeave(void)
1030{
1031 if (m_enmMode == GH) /* Wrong mode? Bail out. */
1032 return VERR_WRONG_ORDER;
1033
1034 if (g_cVerbosity)
1035 hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
1036 "Leave: Host -> Guest", "VirtualBox Drag'n Drop",
1037 15 * 1000 /* Time to display in msec */, NIIF_INFO);
1038
1039 int rc = Abort();
1040
1041 LogFlowFuncLeaveRC(rc);
1042 return rc;
1043}
1044
1045/**
1046 * Handles actions required when the host cursor wants to drop
1047 * and therefore start a "drop" action in the guest.
1048 *
1049 * @return IPRT status code.
1050 */
1051int VBoxDnDWnd::OnHgDrop(void)
1052{
1053 if (m_enmMode == GH)
1054 return VERR_WRONG_ORDER;
1055
1056 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", m_enmMode, m_enmState));
1057
1058 int rc = VINF_SUCCESS;
1059 if (m_enmState == Dragging)
1060 {
1061 if (g_cVerbosity)
1062 hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
1063 "Drop: Host -> Guest", "VirtualBox Drag'n Drop",
1064 15 * 1000 /* Time to display in msec */, NIIF_INFO);
1065
1066 if (m_lstFmtActive.size() >= 1)
1067 {
1068 /** @todo What to do when multiple formats are available? */
1069 m_strFmtReq = m_lstFmtActive.at(0);
1070
1071 rc = RTCritSectEnter(&m_CritSect);
1072 if (RT_SUCCESS(rc))
1073 {
1074 if (m_startupInfo.pDataObject)
1075 m_startupInfo.pDataObject->SetStatus(VBoxDnDDataObject::Status_Dropping);
1076 else
1077 rc = VERR_NOT_FOUND;
1078
1079 RTCritSectLeave(&m_CritSect);
1080 }
1081
1082 if (RT_SUCCESS(rc))
1083 {
1084 LogRel(("DnD: Requesting data as '%s' ...\n", m_strFmtReq.c_str()));
1085 rc = VbglR3DnDHGSendReqData(&m_cmdCtx, m_strFmtReq.c_str());
1086 if (RT_FAILURE(rc))
1087 LogFlowThisFunc(("Requesting data failed with rc=%Rrc\n", rc));
1088 }
1089
1090 }
1091 else /* Should never happen. */
1092 LogRel(("DnD: Error: Host did not specify a data format for drop data\n"));
1093 }
1094
1095 LogFlowFuncLeaveRC(rc);
1096 return rc;
1097}
1098
1099/**
1100 * Handles actions required when the host has sent over DnD data
1101 * to the guest after a "drop" event.
1102 *
1103 * @return IPRT status code.
1104 * @param pMeta Pointer to meta data received.
1105 */
1106int VBoxDnDWnd::OnHgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)
1107{
1108 LogFlowThisFunc(("mState=%ld, enmMetaType=%RU32\n", m_enmState, pMeta->enmType));
1109
1110 int rc = RTCritSectEnter(&m_CritSect);
1111 if (RT_SUCCESS(rc))
1112 {
1113 m_enmState = Dropped;
1114
1115 if (m_startupInfo.pDataObject)
1116 {
1117 switch (pMeta->enmType)
1118 {
1119 case VBGLR3GUESTDNDMETADATATYPE_RAW:
1120 {
1121 AssertBreakStmt(pMeta->u.Raw.pvMeta != NULL, rc = VERR_INVALID_POINTER);
1122 AssertBreakStmt(pMeta->u.Raw.cbMeta, rc = VERR_INVALID_PARAMETER);
1123
1124 rc = m_startupInfo.pDataObject->Signal(m_strFmtReq, pMeta->u.Raw.pvMeta, pMeta->u.Raw.cbMeta);
1125 break;
1126 }
1127
1128 case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
1129 {
1130 LogRel2(("DnD: URI transfer root directory is '%s'\n", DnDTransferListGetRootPathAbs(&pMeta->u.URI.Transfer)));
1131
1132 char *pszBuf;
1133 size_t cbBuf;
1134 /* Note: The transfer list already has its root set to a temporary directory, so no need to set/add a new
1135 * path base here. */
1136 rc = DnDTransferListGetRootsEx(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_NATIVE, NULL /* pszPathBase */,
1137 DND_PATH_SEPARATOR_STR, &pszBuf, &cbBuf);
1138 if (RT_SUCCESS(rc))
1139 {
1140 rc = m_startupInfo.pDataObject->Signal(m_strFmtReq, pszBuf, cbBuf);
1141 RTStrFree(pszBuf);
1142 }
1143 break;
1144 }
1145
1146 default:
1147 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1148 break;
1149 }
1150 }
1151 else
1152 rc = VERR_NOT_FOUND;
1153
1154 int rc2 = mouseRelease();
1155 if (RT_SUCCESS(rc))
1156 rc = rc2;
1157
1158 RTCritSectLeave(&m_CritSect);
1159 }
1160
1161 LogFlowFuncLeaveRC(rc);
1162 return rc;
1163}
1164
1165/**
1166 * Handles actions required when the host wants to cancel the current
1167 * host -> guest operation.
1168 *
1169 * @return IPRT status code.
1170 */
1171int VBoxDnDWnd::OnHgCancel(void)
1172{
1173 return Abort();
1174}
1175
1176#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1177/**
1178 * Handles actions required to start a guest -> host DnD operation.
1179 * This works by letting the host ask whether a DnD operation is pending
1180 * on the guest. The guest must not know anything about the host's DnD state
1181 * and/or operations due to security reasons.
1182 *
1183 * To capture a pending DnD operation on the guest which then can be communicated
1184 * to the host the proxy window needs to be registered as a drop target. This drop
1185 * target then will act as a proxy target between the guest OS and the host. In other
1186 * words, the guest OS will use this proxy target as a regular (invisible) window
1187 * which can be used by the regular guest OS' DnD mechanisms, independently of the
1188 * host OS. To make sure this proxy target is able receive an in-progress DnD operation
1189 * on the guest, it will be shown invisibly across all active guest OS screens. Just
1190 * think of an opened umbrella across all screens here.
1191 *
1192 * As soon as the proxy target and its underlying data object receive appropriate
1193 * DnD messages they'll be hidden again, and the control will be transferred back
1194 * this class again.
1195 *
1196 * @return IPRT status code.
1197 */
1198int VBoxDnDWnd::OnGhIsDnDPending(void)
1199{
1200 LogFlowThisFunc(("mMode=%ld, mState=%ld\n", m_enmMode, m_enmState));
1201
1202 if (m_enmMode == Unknown)
1203 setMode(GH);
1204
1205 if (m_enmMode != GH)
1206 return VERR_WRONG_ORDER;
1207
1208 if (m_enmState == Uninitialized)
1209 {
1210 /* Nothing to do here yet. */
1211 m_enmState = Initialized;
1212 }
1213
1214 int rc;
1215 if (m_enmState == Initialized)
1216 {
1217 /* Check if the VM session has changed and reconnect to the HGCM service if necessary. */
1218 rc = checkForSessionChange();
1219 if (RT_SUCCESS(rc))
1220 {
1221 rc = makeFullscreen();
1222 if (RT_SUCCESS(rc))
1223 {
1224 /*
1225 * We have to release the left mouse button to
1226 * get into our (invisible) proxy window.
1227 */
1228 mouseRelease();
1229
1230 /*
1231 * Even if we just released the left mouse button
1232 * we're still in the dragging state to handle our
1233 * own drop target (for the host).
1234 */
1235 m_enmState = Dragging;
1236 }
1237 }
1238 }
1239 else
1240 rc = VINF_SUCCESS;
1241
1242 /**
1243 * Some notes regarding guest cursor movement:
1244 * - The host only sends an HOST_DND_FN_GH_REQ_PENDING message to the guest
1245 * if the mouse cursor is outside the VM's window.
1246 * - The guest does not know anything about the host's cursor
1247 * position / state due to security reasons.
1248 * - The guest *only* knows that the host currently is asking whether a
1249 * guest DnD operation is in progress.
1250 */
1251
1252 if ( RT_SUCCESS(rc)
1253 && m_enmState == Dragging)
1254 {
1255 /** @todo Put this block into a function! */
1256 POINT p;
1257 GetCursorPos(&p);
1258 ClientToScreen(m_hWnd, &p);
1259#ifdef DEBUG_andy
1260 LogFlowThisFunc(("Client to screen curX=%ld, curY=%ld\n", p.x, p.y));
1261#endif
1262
1263 /** @todo Multi-monitor setups? */
1264#if 0 /* unused */
1265 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1266 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1267#endif
1268
1269 LONG px = p.x;
1270 if (px <= 0)
1271 px = 1;
1272 LONG py = p.y;
1273 if (py <= 0)
1274 py = 1;
1275
1276 rc = mouseMove(px, py, 0 /* dwMouseInputFlags */);
1277 }
1278
1279 if (RT_SUCCESS(rc))
1280 {
1281 VBOXDNDACTION dndActionDefault = VBOX_DND_ACTION_IGNORE;
1282
1283 AssertPtr(m_pDropTarget);
1284 RTCString strFormats = m_pDropTarget->Formats();
1285 if (!strFormats.isEmpty())
1286 {
1287 dndActionDefault = VBOX_DND_ACTION_COPY;
1288
1289 LogFlowFunc(("Acknowledging pDropTarget=0x%p, dndActionDefault=0x%x, dndLstActionsAllowed=0x%x, strFormats=%s\n",
1290 m_pDropTarget, dndActionDefault, m_lstActionsAllowed, strFormats.c_str()));
1291 }
1292 else
1293 {
1294 strFormats = "unknown"; /* Prevent VERR_IO_GEN_FAILURE for IOCTL. */
1295 LogFlowFunc(("No format data from proxy window available yet\n"));
1296 }
1297
1298 /** @todo Support more than one action at a time. */
1299 m_lstActionsAllowed = dndActionDefault;
1300
1301 int rc2 = VbglR3DnDGHSendAckPending(&m_cmdCtx,
1302 dndActionDefault, m_lstActionsAllowed,
1303 strFormats.c_str(), (uint32_t)strFormats.length() + 1 /* Include termination */);
1304 if (RT_FAILURE(rc2))
1305 {
1306 char szMsg[256]; /* Sizes according to MSDN. */
1307 char szTitle[64];
1308
1309 /** @todo Add some i18l tr() macros here. */
1310 RTStrPrintf(szTitle, sizeof(szTitle), "VirtualBox Guest Additions Drag and Drop");
1311 RTStrPrintf(szMsg, sizeof(szMsg), "Drag and drop to the host either is not supported or disabled. "
1312 "Please enable Guest to Host or Bidirectional drag and drop mode "
1313 "or re-install the VirtualBox Guest Additions.");
1314 switch (rc2)
1315 {
1316 case VERR_ACCESS_DENIED:
1317 {
1318 rc = hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
1319 szMsg, szTitle,
1320 15 * 1000 /* Time to display in msec */, NIIF_INFO);
1321 AssertRC(rc);
1322 break;
1323 }
1324
1325 default:
1326 break;
1327 }
1328
1329 LogRel2(("DnD: Host refuses drag and drop operation from guest: %Rrc\n", rc2));
1330 Reset();
1331 }
1332 }
1333
1334 if (RT_FAILURE(rc))
1335 Reset(); /* Reset state on failure. */
1336
1337 LogFlowFuncLeaveRC(rc);
1338 return rc;
1339}
1340
1341/**
1342 * Handles actions required to let the guest know that the host
1343 * started a "drop" action on the host. This will tell the guest
1344 * to send data in a specific format the host requested.
1345 *
1346 * @return IPRT status code.
1347 * @param pszFormat Format the host requests the data in.
1348 * @param cbFormat Size (in bytes) of format string.
1349 * @param dndActionDefault Default action on the host.
1350 */
1351int VBoxDnDWnd::OnGhDrop(const RTCString &strFormat, uint32_t dndActionDefault)
1352{
1353 LogFlowThisFunc(("mMode=%ld, mState=%ld, pDropTarget=0x%p, strFormat=%s, dndActionDefault=0x%x\n",
1354 m_enmMode, m_enmState, m_pDropTarget, strFormat.c_str(), dndActionDefault));
1355 int rc;
1356 if (m_enmMode == GH)
1357 {
1358 if (g_cVerbosity)
1359 {
1360 RTCString strMsg("Drop: Guest -> Host\n\n");
1361 strMsg += RTCStringFmt("Action: %#x\n", dndActionDefault);
1362 strMsg += RTCStringFmt("Format: %s\n", strFormat.c_str());
1363
1364 hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
1365 strMsg.c_str(), "VirtualBox Drag'n Drop",
1366 15 * 1000 /* Time to display in msec */, NIIF_INFO);
1367 }
1368
1369 if (m_enmState == Dragging)
1370 {
1371 AssertPtr(m_pDropTarget);
1372 rc = m_pDropTarget->WaitForDrop(5 * 1000 /* 5s timeout */);
1373
1374 Reset();
1375 }
1376 else if (m_enmState == Dropped)
1377 {
1378 rc = VINF_SUCCESS;
1379 }
1380 else
1381 rc = VERR_WRONG_ORDER;
1382
1383 if (RT_SUCCESS(rc))
1384 {
1385 /** @todo Respect uDefAction. */
1386 void *pvData = m_pDropTarget->DataMutableRaw();
1387 uint32_t cbData = (uint32_t)m_pDropTarget->DataSize();
1388 Assert(cbData == m_pDropTarget->DataSize());
1389
1390 if ( pvData
1391 && cbData)
1392 {
1393 rc = VbglR3DnDGHSendData(&m_cmdCtx, strFormat.c_str(), pvData, cbData);
1394 LogFlowFunc(("Sent pvData=0x%p, cbData=%RU32, rc=%Rrc\n", pvData, cbData, rc));
1395 }
1396 else
1397 rc = VERR_NO_DATA;
1398 }
1399 }
1400 else
1401 rc = VERR_WRONG_ORDER;
1402
1403 if (RT_FAILURE(rc))
1404 {
1405 /*
1406 * If an error occurred or the guest is in a wrong DnD mode,
1407 * send an error to the host in any case so that the host does
1408 * not wait for the data it expects from the guest.
1409 */
1410 int rc2 = VbglR3DnDGHSendError(&m_cmdCtx, rc);
1411 AssertRC(rc2);
1412 }
1413
1414 LogFlowFuncLeaveRC(rc);
1415 return rc;
1416}
1417#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1418
1419void VBoxDnDWnd::PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
1420{
1421 LogFlowFunc(("Posting message %u\n"));
1422 BOOL fRc = ::PostMessage(m_hWnd, uMsg, wParam, lParam);
1423 Assert(fRc); NOREF(fRc);
1424}
1425
1426/**
1427 * Injects a DnD event in this proxy window's Windows
1428 * event queue. The (allocated) event will be deleted by
1429 * this class after processing.
1430 *
1431 * @return IPRT status code.
1432 * @param pEvent Event to inject.
1433 */
1434int VBoxDnDWnd::ProcessEvent(PVBOXDNDEVENT pEvent)
1435{
1436 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1437
1438 BOOL fRc = ::PostMessage(m_hWnd, WM_VBOXTRAY_DND_MESSAGE,
1439 0 /* wParm */, (LPARAM)pEvent /* lParm */);
1440 if (!fRc)
1441 {
1442 DWORD dwErr = GetLastError();
1443
1444 static int s_iBitchedAboutFailedDnDMessages = 0;
1445 if (s_iBitchedAboutFailedDnDMessages++ < 32)
1446 {
1447 LogRel(("DnD: Processing event %p failed with %ld (%Rrc), skipping\n",
1448 pEvent, dwErr, RTErrConvertFromWin32(dwErr)));
1449 }
1450
1451 VbglR3DnDEventFree(pEvent->pVbglR3Event);
1452
1453 RTMemFree(pEvent);
1454 pEvent = NULL;
1455
1456 return RTErrConvertFromWin32(dwErr);
1457 }
1458
1459 return VINF_SUCCESS;
1460}
1461
1462/**
1463 * Checks if the VM session has changed (can happen when restoring the VM from a saved state)
1464 * and do a reconnect to the DnD HGCM service.
1465 *
1466 * @returns IPRT status code.
1467 */
1468int VBoxDnDWnd::checkForSessionChange(void)
1469{
1470 uint64_t uSessionID;
1471 int rc = VbglR3GetSessionId(&uSessionID);
1472 if ( RT_SUCCESS(rc)
1473 && uSessionID != m_cmdCtx.uSessionID)
1474 {
1475 LogFlowThisFunc(("VM session has changed to %RU64\n", uSessionID));
1476
1477 rc = VbglR3DnDDisconnect(&m_cmdCtx);
1478 AssertRC(rc);
1479
1480 rc = VbglR3DnDConnect(&m_cmdCtx);
1481 AssertRC(rc);
1482 }
1483
1484 LogFlowFuncLeaveRC(rc);
1485 return rc;
1486}
1487
1488/**
1489 * Hides the proxy window again.
1490 *
1491 * @return IPRT status code.
1492 */
1493int VBoxDnDWnd::Hide(void)
1494{
1495#ifdef DEBUG_andy
1496 LogFlowFunc(("\n"));
1497#endif
1498 ShowWindow(m_hWnd, SW_HIDE);
1499
1500 return VINF_SUCCESS;
1501}
1502
1503/**
1504 * Shows the (invisible) proxy window in fullscreen,
1505 * spawned across all active guest monitors.
1506 *
1507 * @return IPRT status code.
1508 */
1509int VBoxDnDWnd::makeFullscreen(void)
1510{
1511 int rc = VINF_SUCCESS;
1512
1513 RECT r;
1514 RT_ZERO(r);
1515
1516 BOOL fRc;
1517 HDC hDC = GetDC(NULL /* Entire screen */);
1518 if (hDC)
1519 {
1520 fRc = g_pfnEnumDisplayMonitors
1521 /* EnumDisplayMonitors is not available on NT4. */
1522 ? g_pfnEnumDisplayMonitors(hDC, NULL, VBoxDnDWnd::MonitorEnumProc, (LPARAM)&r):
1523 FALSE;
1524
1525 if (!fRc)
1526 rc = VERR_NOT_FOUND;
1527 ReleaseDC(NULL, hDC);
1528 }
1529 else
1530 rc = VERR_ACCESS_DENIED;
1531
1532 if (RT_FAILURE(rc))
1533 {
1534 /* If multi-monitor enumeration failed above, try getting at least the
1535 * primary monitor as a fallback. */
1536 r.left = 0;
1537 r.top = 0;
1538 r.right = GetSystemMetrics(SM_CXSCREEN);
1539 r.bottom = GetSystemMetrics(SM_CYSCREEN);
1540
1541 rc = VINF_SUCCESS;
1542 }
1543
1544 if (RT_SUCCESS(rc))
1545 {
1546 LONG lStyle = GetWindowLong(m_hWnd, GWL_STYLE);
1547 SetWindowLong(m_hWnd, GWL_STYLE,
1548 lStyle & ~(WS_CAPTION | WS_THICKFRAME));
1549 LONG lExStyle = GetWindowLong(m_hWnd, GWL_EXSTYLE);
1550 SetWindowLong(m_hWnd, GWL_EXSTYLE,
1551 lExStyle & ~( WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE
1552 | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1553
1554 fRc = SetWindowPos(m_hWnd, HWND_TOPMOST,
1555 r.left,
1556 r.top,
1557 r.right - r.left,
1558 r.bottom - r.top,
1559 g_cVerbosity
1560 ? SWP_SHOWWINDOW | SWP_FRAMECHANGED
1561 : SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
1562 if (fRc)
1563 {
1564 LogFlowFunc(("Virtual screen is %ld,%ld,%ld,%ld (%ld x %ld)\n",
1565 r.left, r.top, r.right, r.bottom,
1566 r.right - r.left, r.bottom - r.top));
1567 }
1568 else
1569 {
1570 DWORD dwErr = GetLastError();
1571 LogRel(("DnD: Failed to set proxy window position, rc=%Rrc\n",
1572 RTErrConvertFromWin32(dwErr)));
1573 }
1574 }
1575 else
1576 LogRel(("DnD: Failed to determine virtual screen size, rc=%Rrc\n", rc));
1577
1578 LogFlowFuncLeaveRC(rc);
1579 return rc;
1580}
1581
1582/**
1583 * Moves the guest mouse cursor to a specific position.
1584 *
1585 * @return IPRT status code.
1586 * @param x X position (in pixels) to move cursor to.
1587 * @param y Y position (in pixels) to move cursor to.
1588 * @param dwMouseInputFlags Additional movement flags. @sa MOUSEEVENTF_ flags.
1589 */
1590int VBoxDnDWnd::mouseMove(int x, int y, DWORD dwMouseInputFlags)
1591{
1592 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1593 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1594
1595 INPUT Input[1] = { {0} };
1596 Input[0].type = INPUT_MOUSE;
1597 Input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
1598 | dwMouseInputFlags;
1599 Input[0].mi.dx = x * (65535 / iScreenX);
1600 Input[0].mi.dy = y * (65535 / iScreenY);
1601
1602 int rc;
1603 if (g_pfnSendInput(1 /* Number of inputs */,
1604 Input, sizeof(INPUT)))
1605 {
1606#ifdef DEBUG_andy
1607 CURSORINFO ci;
1608 RT_ZERO(ci);
1609 ci.cbSize = sizeof(ci);
1610 BOOL fRc = GetCursorInfo(&ci);
1611 if (fRc)
1612 LogFlowThisFunc(("Cursor shown=%RTbool, cursor=0x%p, x=%d, y=%d\n",
1613 (ci.flags & CURSOR_SHOWING) ? true : false,
1614 ci.hCursor, ci.ptScreenPos.x, ci.ptScreenPos.y));
1615#endif
1616 rc = VINF_SUCCESS;
1617 }
1618 else
1619 {
1620 DWORD dwErr = GetLastError();
1621 rc = RTErrConvertFromWin32(dwErr);
1622 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1623 }
1624
1625 return rc;
1626}
1627
1628/**
1629 * Releases a previously pressed left guest mouse button.
1630 *
1631 * @return IPRT status code.
1632 */
1633int VBoxDnDWnd::mouseRelease(void)
1634{
1635 LogFlowFuncEnter();
1636
1637 int rc;
1638
1639 /* Release mouse button in the guest to start the "drop"
1640 * action at the current mouse cursor position. */
1641 INPUT Input[1] = { {0} };
1642 Input[0].type = INPUT_MOUSE;
1643 Input[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;
1644 if (!g_pfnSendInput(1, Input, sizeof(INPUT)))
1645 {
1646 DWORD dwErr = GetLastError();
1647 rc = RTErrConvertFromWin32(dwErr);
1648 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1649 }
1650 else
1651 rc = VINF_SUCCESS;
1652
1653 return rc;
1654}
1655
1656/**
1657 * Resets the proxy window.
1658 */
1659void VBoxDnDWnd::Reset(void)
1660{
1661 LogFlowThisFunc(("Resetting, old mMode=%ld, mState=%ld\n",
1662 m_enmMode, m_enmState));
1663
1664 /*
1665 * Note: Don't clear this->lstAllowedFormats at the moment, as this value is initialized
1666 * on class creation. We might later want to modify the allowed formats at runtime,
1667 * so keep this in mind when implementing this.
1668 */
1669
1670 this->m_lstFmtActive.clear();
1671 this->m_lstActionsAllowed = VBOX_DND_ACTION_IGNORE;
1672
1673 int rc2 = setMode(Unknown);
1674 AssertRC(rc2);
1675
1676 Hide();
1677}
1678
1679/**
1680 * Sets the current operation mode of this proxy window.
1681 *
1682 * @return IPRT status code.
1683 * @param enmMode New mode to set.
1684 */
1685int VBoxDnDWnd::setMode(Mode enmMode)
1686{
1687 LogFlowThisFunc(("Old mode=%ld, new mode=%ld\n",
1688 m_enmMode, enmMode));
1689
1690 m_enmMode = enmMode;
1691 m_enmState = Initialized;
1692
1693 return VINF_SUCCESS;
1694}
1695
1696/**
1697 * Static helper function for having an own WndProc for proxy
1698 * window instances.
1699 */
1700static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
1701{
1702 LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
1703 AssertPtrReturn(pUserData, 0);
1704
1705 VBoxDnDWnd *pWnd = reinterpret_cast<VBoxDnDWnd *>(pUserData);
1706 if (pWnd)
1707 return pWnd->WndProc(hWnd, uMsg, wParam, lParam);
1708
1709 return 0;
1710}
1711
1712/**
1713 * Static helper function for routing Windows messages to a specific
1714 * proxy window instance.
1715 */
1716static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
1717{
1718 /* Note: WM_NCCREATE is not the first ever message which arrives, but
1719 * early enough for us. */
1720 if (uMsg == WM_NCCREATE)
1721 {
1722 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
1723 AssertPtr(pCS);
1724 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
1725 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxDnDWndProcInstance);
1726
1727 return vboxDnDWndProcInstance(hWnd, uMsg, wParam, lParam);
1728 }
1729
1730 /* No window associated yet. */
1731 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1732}
1733
1734/**
1735 * Initializes drag and drop.
1736 *
1737 * @return IPRT status code.
1738 * @param pEnv The DnD service's environment.
1739 * @param ppInstance The instance pointer which refer to this object.
1740 */
1741DECLCALLBACK(int) VBoxDnDInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
1742{
1743 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
1744 AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
1745
1746 LogFlowFuncEnter();
1747
1748 PVBOXDNDCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
1749 AssertPtr(pCtx);
1750
1751 int rc;
1752 bool fSupportedOS = true;
1753
1754 if (VbglR3AutoLogonIsRemoteSession())
1755 {
1756 /* Do not do drag and drop for remote sessions. */
1757 LogRel(("DnD: Drag and drop has been disabled for a remote session\n"));
1758 rc = VERR_NOT_SUPPORTED;
1759 }
1760 else
1761 rc = VINF_SUCCESS;
1762
1763 if (RT_SUCCESS(rc))
1764 {
1765 g_pfnSendInput = (PFNSENDINPUT)
1766 RTLdrGetSystemSymbol("User32.dll", "SendInput");
1767 fSupportedOS = !RT_BOOL(g_pfnSendInput == NULL);
1768 g_pfnEnumDisplayMonitors = (PFNENUMDISPLAYMONITORS)
1769 RTLdrGetSystemSymbol("User32.dll", "EnumDisplayMonitors");
1770 /* g_pfnEnumDisplayMonitors is optional. */
1771
1772 if (!fSupportedOS)
1773 {
1774 LogRel(("DnD: Not supported Windows version, disabling drag and drop support\n"));
1775 rc = VERR_NOT_SUPPORTED;
1776 }
1777 }
1778
1779 if (RT_SUCCESS(rc))
1780 {
1781 /* Assign service environment to our context. */
1782 pCtx->pEnv = pEnv;
1783
1784 /* Create the proxy window. At the moment we
1785 * only support one window at a time. */
1786 VBoxDnDWnd *pWnd = NULL;
1787#ifdef RT_EXCEPTIONS_ENABLED
1788 try { pWnd = new VBoxDnDWnd(); }
1789 catch (std::bad_alloc &)
1790#else
1791 pWnd = new VBoxDnDWnd();
1792 if (!pWnd)
1793#endif
1794 {
1795 rc = VERR_NO_MEMORY;
1796 }
1797 if (RT_SUCCESS(rc))
1798 {
1799 rc = pWnd->Initialize(pCtx);
1800 if (RT_SUCCESS(rc))
1801 {
1802 /* Add proxy window to our proxy windows list. */
1803#ifdef RT_EXCEPTIONS_ENABLED
1804 try { pCtx->lstWnd.append(pWnd); /** @todo the list implementation sucks wrt exception handling. */ }
1805 catch (std::bad_alloc &)
1806 {
1807 delete pWnd;
1808 rc = VERR_NO_MEMORY;
1809 }
1810#else
1811 pCtx->lstWnd.append(pWnd); /** @todo the list implementation sucks wrt exception handling. */
1812#endif
1813 }
1814 else
1815 delete pWnd;
1816 }
1817 }
1818
1819 if (RT_SUCCESS(rc))
1820 rc = RTSemEventCreate(&pCtx->hEvtQueueSem);
1821 if (RT_SUCCESS(rc))
1822 {
1823 *ppInstance = pCtx;
1824
1825 LogRel(("DnD: Drag and drop service successfully started\n"));
1826 }
1827 else
1828 LogRel(("DnD: Initializing drag and drop service failed with rc=%Rrc\n", rc));
1829
1830 LogFlowFuncLeaveRC(rc);
1831 return rc;
1832}
1833
1834DECLCALLBACK(int) VBoxDnDStop(void *pInstance)
1835{
1836 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
1837
1838 LogFunc(("Stopping pInstance=%p\n", pInstance));
1839
1840 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1841 AssertPtr(pCtx);
1842
1843 /* Set shutdown indicator. */
1844 ASMAtomicWriteBool(&pCtx->fShutdown, true);
1845
1846 /* Disconnect. */
1847 VbglR3DnDDisconnect(&pCtx->cmdCtx);
1848
1849 LogFlowFuncLeaveRC(VINF_SUCCESS);
1850 return VINF_SUCCESS;
1851}
1852
1853DECLCALLBACK(void) VBoxDnDDestroy(void *pInstance)
1854{
1855 AssertPtrReturnVoid(pInstance);
1856
1857 LogFunc(("Destroying pInstance=%p\n", pInstance));
1858
1859 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1860 AssertPtr(pCtx);
1861
1862 /** @todo At the moment we only have one DnD proxy window. */
1863 Assert(pCtx->lstWnd.size() == 1);
1864 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1865 if (pWnd)
1866 {
1867 delete pWnd;
1868 pWnd = NULL;
1869 }
1870
1871 if (pCtx->hEvtQueueSem != NIL_RTSEMEVENT)
1872 {
1873 RTSemEventDestroy(pCtx->hEvtQueueSem);
1874 pCtx->hEvtQueueSem = NIL_RTSEMEVENT;
1875 }
1876
1877 LogFunc(("Destroyed pInstance=%p\n", pInstance));
1878}
1879
1880DECLCALLBACK(int) VBoxDnDWorker(void *pInstance, bool volatile *pfShutdown)
1881{
1882 AssertPtr(pInstance);
1883 AssertPtr(pfShutdown);
1884
1885 LogFlowFunc(("pInstance=%p\n", pInstance));
1886
1887 /*
1888 * Tell the control thread that it can continue
1889 * spawning services.
1890 */
1891 RTThreadUserSignal(RTThreadSelf());
1892
1893 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1894 AssertPtr(pCtx);
1895
1896 int rc = VbglR3DnDConnect(&pCtx->cmdCtx);
1897 if (RT_FAILURE(rc))
1898 return rc;
1899
1900 if (g_cVerbosity)
1901 hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
1902 RTCStringFmt("Running (worker client ID %RU32)", pCtx->cmdCtx.uClientID).c_str(),
1903 "VirtualBox Drag'n Drop",
1904 15 * 1000 /* Time to display in msec */, NIIF_INFO);
1905
1906 /** @todo At the moment we only have one DnD proxy window. */
1907 Assert(pCtx->lstWnd.size() == 1);
1908 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1909 AssertPtr(pWnd);
1910
1911 /* Number of invalid messages skipped in a row. */
1912 int cMsgSkippedInvalid = 0;
1913 PVBOXDNDEVENT pEvent = NULL;
1914
1915 for (;;)
1916 {
1917 pEvent = (PVBOXDNDEVENT)RTMemAllocZ(sizeof(VBOXDNDEVENT));
1918 if (!pEvent)
1919 {
1920 rc = VERR_NO_MEMORY;
1921 break;
1922 }
1923 /* Note: pEvent will be free'd by the consumer later. */
1924
1925 PVBGLR3DNDEVENT pVbglR3Event = NULL;
1926 rc = VbglR3DnDEventGetNext(&pCtx->cmdCtx, &pVbglR3Event);
1927 if (RT_SUCCESS(rc))
1928 {
1929 LogFunc(("enmType=%RU32, rc=%Rrc\n", pVbglR3Event->enmType, rc));
1930
1931 cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
1932
1933 LogRel2(("DnD: Received new event, type=%RU32, rc=%Rrc\n", pVbglR3Event->enmType, rc));
1934
1935 /* pEvent now owns pVbglR3Event. */
1936 pEvent->pVbglR3Event = pVbglR3Event;
1937 pVbglR3Event = NULL;
1938
1939 rc = pWnd->ProcessEvent(pEvent);
1940 if (RT_SUCCESS(rc))
1941 {
1942 /* Event was consumed and the proxy window till take care of the memory -- NULL it. */
1943 pEvent = NULL;
1944 }
1945 else
1946 LogRel(("DnD: Processing proxy window event %RU32 failed with %Rrc\n", pVbglR3Event->enmType, rc));
1947 }
1948
1949 if (RT_FAILURE(rc))
1950 {
1951 if (pEvent)
1952 {
1953 RTMemFree(pEvent);
1954 pEvent = NULL;
1955 }
1956
1957 LogFlowFunc(("Processing next message failed with rc=%Rrc\n", rc));
1958
1959 /* Old(er) hosts either are broken regarding DnD support or otherwise
1960 * don't support the stuff we do on the guest side, so make sure we
1961 * don't process invalid messages forever. */
1962 if (cMsgSkippedInvalid++ > 32)
1963 {
1964 LogRel(("DnD: Too many invalid/skipped messages from host, exiting ...\n"));
1965 break;
1966 }
1967
1968 /* Make sure our proxy window is hidden when an error occured to
1969 * not block the guest's UI. */
1970 int rc2 = pWnd->Abort();
1971 AssertRC(rc2);
1972 }
1973
1974 if (*pfShutdown)
1975 break;
1976
1977 if (ASMAtomicReadBool(&pCtx->fShutdown))
1978 break;
1979
1980 if (RT_FAILURE(rc)) /* Don't hog the CPU on errors. */
1981 RTThreadSleep(1000 /* ms */);
1982 }
1983
1984 if (pEvent)
1985 {
1986 VbglR3DnDEventFree(pEvent->pVbglR3Event);
1987
1988 RTMemFree(pEvent);
1989 pEvent = NULL;
1990 }
1991
1992 VbglR3DnDDisconnect(&pCtx->cmdCtx);
1993
1994 LogRel(("DnD: Ended\n"));
1995
1996 LogFlowFuncLeaveRC(rc);
1997 return rc;
1998}
1999
2000/**
2001 * The service description.
2002 */
2003VBOXSERVICEDESC g_SvcDescDnD =
2004{
2005 /* pszName. */
2006 "draganddrop",
2007 /* pszDescription. */
2008 "Drag and Drop",
2009 /* methods */
2010 VBoxDnDInit,
2011 VBoxDnDWorker,
2012 VBoxDnDStop,
2013 VBoxDnDDestroy
2014};
2015
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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