VirtualBox

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

Last change on this file since 95959 was 95959, checked in by vboxsync, 3 years ago

Additions/VBoxTray: Enable (optional) release logging, handle log groups a bit more elegant. bugref:10267

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

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