VirtualBox

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

Last change on this file since 97764 was 97764, checked in by vboxsync, 2 years ago

DnD: Renamed the VbglR3 event name VBGLR3DNDEVENTTYPE_HG_CANCEL -> VBGLR3DNDEVENTTYPE_CANCEL, to emphasize that this is an event for both directions (host -> guest / guest -> host). Fixed a memory leak in vbglR3DnDHGRecvDataMain().

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