VirtualBox

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

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

VBoxTray/DnD: Make the code build w/o exceptions enabled. bugref:10261

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 60.6 KB
 
1/* $Id: VBoxDnD.cpp 95734 2022-07-19 21:50:40Z vboxsync $ */
2/** @file
3 * VBoxDnD.cpp - Windows-specific bits of the drag and drop service.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GUEST_DND
23#include <iprt/win/windows.h>
24#include "VBoxTray.h"
25#include "VBoxHelpers.h"
26#include "VBoxDnD.h"
27
28#include <VBox/VBoxGuestLib.h>
29#include "VBox/HostServices/DragAndDropSvc.h"
30
31using namespace DragAndDropSvc;
32
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/ldr.h>
36#include <iprt/list.h>
37#include <iprt/mem.h>
38
39#include <iprt/cpp/mtlist.h>
40#include <iprt/cpp/ministring.h>
41
42#include <iprt/cpp/mtlist.h>
43
44#include <VBox/err.h>
45#include <VBox/log.h>
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51/* Enable this define to see the proxy window(s) when debugging
52 * their behavior. Don't have this enabled in release builds! */
53#ifdef DEBUG
54//# define VBOX_DND_DEBUG_WND
55#endif
56
57/** The drag and drop window's window class. */
58#define VBOX_DND_WND_CLASS "VBoxTrayDnDWnd"
59
60/** @todo Merge this with messages from VBoxTray.h. */
61#define WM_VBOXTRAY_DND_MESSAGE WM_APP + 401
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67/** Function pointer for SendInput(). This only is available starting
68 * at NT4 SP3+. */
69typedef BOOL (WINAPI *PFNSENDINPUT)(UINT, LPINPUT, int);
70typedef BOOL (WINAPI* PFNENUMDISPLAYMONITORS)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
71
72
73/*********************************************************************************************************************************
74* Global Variables *
75*********************************************************************************************************************************/
76/** Static pointer to SendInput() function. */
77static PFNSENDINPUT g_pfnSendInput = NULL;
78static PFNENUMDISPLAYMONITORS g_pfnEnumDisplayMonitors = NULL;
79
80static VBOXDNDCONTEXT g_Ctx = { 0 };
81
82
83/*********************************************************************************************************************************
84* Internal Functions *
85*********************************************************************************************************************************/
86static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_PROTO;
87static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_PROTO;
88
89
90
91
92VBoxDnDWnd::VBoxDnDWnd(void)
93 : m_hThread(NIL_RTTHREAD),
94 m_EvtSem(NIL_RTSEMEVENT),
95 m_hWnd(NULL),
96 m_lstActionsAllowed(VBOX_DND_ACTION_IGNORE),
97 m_fMouseButtonDown(false),
98#ifdef VBOX_WITH_DRAG_AND_DROP_GH
99 m_pDropTarget(NULL),
100#endif
101 m_enmMode(Unknown),
102 m_enmState(Uninitialized)
103{
104 RT_ZERO(m_startupInfo);
105
106 LogFlowFunc(("Supported formats:\n"));
107 const RTCString arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
108 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
109 {
110 LogFlowFunc(("\t%s\n", arrEntries[i].c_str()));
111 this->m_lstFmtSup.append(arrEntries[i]);
112 }
113}
114
115VBoxDnDWnd::~VBoxDnDWnd(void)
116{
117 Destroy();
118}
119
120/**
121 * Initializes the proxy window with a given DnD context.
122 *
123 * @return IPRT status code.
124 * @param a_pCtx Pointer to context to use.
125 */
126int VBoxDnDWnd::Initialize(PVBOXDNDCONTEXT a_pCtx)
127{
128 AssertPtrReturn(a_pCtx, VERR_INVALID_POINTER);
129
130 /* Save the context. */
131 this->m_pCtx = a_pCtx;
132
133 int rc = RTSemEventCreate(&m_EvtSem);
134 if (RT_SUCCESS(rc))
135 rc = RTCritSectInit(&m_CritSect);
136
137 if (RT_SUCCESS(rc))
138 {
139 /* Message pump thread for our proxy window. */
140 rc = RTThreadCreate(&m_hThread, VBoxDnDWnd::Thread, this,
141 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
142 "dndwnd"); /** @todo Include ID if there's more than one proxy window. */
143 if (RT_SUCCESS(rc))
144 {
145 int rc2 = RTThreadUserWait(m_hThread, 30 * 1000 /* Timeout in ms */);
146 AssertRC(rc2);
147
148 if (!a_pCtx->fStarted) /* Did the thread fail to start? */
149 rc = VERR_NOT_SUPPORTED; /* Report back DnD as not being supported. */
150 }
151 }
152
153 if (RT_FAILURE(rc))
154 LogRel(("DnD: Failed to initialize proxy window, rc=%Rrc\n", rc));
155
156 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
157 return rc;
158}
159
160/**
161 * Destroys the proxy window and releases all remaining
162 * resources again.
163 */
164void VBoxDnDWnd::Destroy(void)
165{
166 if (m_hThread != NIL_RTTHREAD)
167 {
168 int rcThread = VERR_WRONG_ORDER;
169 int rc = RTThreadWait(m_hThread, 60 * 1000 /* Timeout in ms */, &rcThread);
170 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n",
171 rc, rcThread));
172 NOREF(rc);
173 }
174
175 Reset();
176
177 RTCritSectDelete(&m_CritSect);
178 if (m_EvtSem != NIL_RTSEMEVENT)
179 {
180 RTSemEventDestroy(m_EvtSem);
181 m_EvtSem = NIL_RTSEMEVENT;
182 }
183
184 if (m_pCtx->wndClass != 0)
185 {
186 UnregisterClass(VBOX_DND_WND_CLASS, m_pCtx->pEnv->hInstance);
187 m_pCtx->wndClass = 0;
188 }
189
190 LogFlowFuncLeave();
191}
192
193/**
194 * Thread for handling the window's message pump.
195 *
196 * @return IPRT status code.
197 * @param hThread Handle to this thread.
198 * @param pvUser Pointer to VBoxDnDWnd instance which
199 * is using the thread.
200 */
201/*static*/ DECLCALLBACK(int) VBoxDnDWnd::Thread(RTTHREAD hThread, void *pvUser)
202{
203 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
204
205 LogFlowFuncEnter();
206
207 VBoxDnDWnd *pThis = (VBoxDnDWnd*)pvUser;
208 AssertPtr(pThis);
209
210 PVBOXDNDCONTEXT m_pCtx = pThis->m_pCtx;
211 AssertPtr(m_pCtx);
212 AssertPtr(m_pCtx->pEnv);
213
214 int rc = VINF_SUCCESS;
215
216 AssertPtr(m_pCtx->pEnv);
217 HINSTANCE hInstance = m_pCtx->pEnv->hInstance;
218 Assert(hInstance != 0);
219
220 /* Create our proxy window. */
221 WNDCLASSEX wc = { 0 };
222 wc.cbSize = sizeof(WNDCLASSEX);
223
224 if (!GetClassInfoEx(hInstance, VBOX_DND_WND_CLASS, &wc))
225 {
226 wc.lpfnWndProc = vboxDnDWndProc;
227 wc.lpszClassName = VBOX_DND_WND_CLASS;
228 wc.hInstance = hInstance;
229 wc.style = CS_NOCLOSE;
230#ifdef VBOX_DND_DEBUG_WND
231 wc.style |= CS_HREDRAW | CS_VREDRAW;
232 wc.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(255, 0, 0)));
233#else
234 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
235#endif
236 if (!RegisterClassEx(&wc))
237 {
238 DWORD dwErr = GetLastError();
239 LogFlowFunc(("Unable to register proxy window class, error=%ld\n", dwErr));
240 rc = RTErrConvertFromWin32(dwErr);
241 }
242 }
243
244 if (RT_SUCCESS(rc))
245 {
246 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
247 DWORD dwStyle = WS_POPUP;
248#ifdef VBOX_DND_DEBUG_WND
249 dwExStyle &= ~WS_EX_TRANSPARENT; /* Remove transparency bit. */
250 dwStyle |= WS_VISIBLE; /* Make the window visible. */
251#endif
252 pThis->m_hWnd = CreateWindowEx(dwExStyle,
253 VBOX_DND_WND_CLASS, VBOX_DND_WND_CLASS,
254 dwStyle,
255#ifdef VBOX_DND_DEBUG_WND
256 CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL,
257#else
258 -200, -200, 100, 100, NULL, NULL,
259#endif
260 hInstance, pThis /* lParm */);
261 if (!pThis->m_hWnd)
262 {
263 DWORD dwErr = GetLastError();
264 LogFlowFunc(("Unable to create proxy window, error=%ld\n", dwErr));
265 rc = RTErrConvertFromWin32(dwErr);
266 }
267 else
268 {
269#ifndef VBOX_DND_DEBUG_WND
270 SetWindowPos(pThis->m_hWnd, HWND_TOPMOST, -200, -200, 0, 0,
271 SWP_NOACTIVATE | SWP_HIDEWINDOW
272 | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
273 LogFlowFunc(("Proxy window created, hWnd=0x%x\n", pThis->m_hWnd));
274#else
275 LogFlowFunc(("Debug proxy window created, hWnd=0x%x\n", pThis->m_hWnd));
276
277 /*
278 * Install some mouse tracking.
279 */
280 TRACKMOUSEEVENT me;
281 RT_ZERO(me);
282 me.cbSize = sizeof(TRACKMOUSEEVENT);
283 me.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT;
284 me.hwndTrack = pThis->m_hWnd;
285 BOOL fRc = TrackMouseEvent(&me);
286 Assert(fRc);
287#endif
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 /* Save all allowed actions. */
833 this->m_lstActionsAllowed = a_fDndLstActionsAllowed;
834
835 /*
836 * Check if reported formats from host are compatible with this client.
837 */
838 size_t cFormatsSup = this->m_lstFmtSup.size();
839 ULONG cFormatsActive = 0;
840
841 LPFORMATETC paFormatEtc = (LPFORMATETC)RTMemTmpAllocZ(sizeof(paFormatEtc[0]) * cFormatsSup);
842 AssertReturn(paFormatEtc, VERR_NO_TMP_MEMORY);
843
844 LPSTGMEDIUM paStgMeds = (LPSTGMEDIUM)RTMemTmpAllocZ(sizeof(paStgMeds[0]) * cFormatsSup);
845 AssertReturnStmt(paFormatEtc, RTMemTmpFree(paFormatEtc), VERR_NO_TMP_MEMORY);
846
847 LogRel2(("DnD: Reported formats:\n"));
848 for (size_t i = 0; i < a_lstFormats.size(); i++)
849 {
850 bool fSupported = false;
851 for (size_t a = 0; a < this->m_lstFmtSup.size(); a++)
852 {
853 const char *pszFormat = a_lstFormats.at(i).c_str();
854 LogFlowThisFunc(("\t\"%s\" <=> \"%s\"\n", this->m_lstFmtSup.at(a).c_str(), pszFormat));
855
856 fSupported = RTStrICmp(this->m_lstFmtSup.at(a).c_str(), pszFormat) == 0;
857 if (fSupported)
858 {
859 this->m_lstFmtActive.append(a_lstFormats.at(i));
860
861 /** @todo Put this into a \#define / struct. */
862 if (!RTStrICmp(pszFormat, "text/uri-list"))
863 {
864 paFormatEtc[cFormatsActive].cfFormat = CF_HDROP;
865 paFormatEtc[cFormatsActive].dwAspect = DVASPECT_CONTENT;
866 paFormatEtc[cFormatsActive].lindex = -1;
867 paFormatEtc[cFormatsActive].tymed = TYMED_HGLOBAL;
868
869 paStgMeds [cFormatsActive].tymed = TYMED_HGLOBAL;
870 cFormatsActive++;
871 }
872 else if ( !RTStrICmp(pszFormat, "text/plain")
873 || !RTStrICmp(pszFormat, "text/html")
874 || !RTStrICmp(pszFormat, "text/plain;charset=utf-8")
875 || !RTStrICmp(pszFormat, "text/plain;charset=utf-16")
876 || !RTStrICmp(pszFormat, "text/plain")
877 || !RTStrICmp(pszFormat, "text/richtext")
878 || !RTStrICmp(pszFormat, "UTF8_STRING")
879 || !RTStrICmp(pszFormat, "TEXT")
880 || !RTStrICmp(pszFormat, "STRING"))
881 {
882 paFormatEtc[cFormatsActive].cfFormat = CF_TEXT;
883 paFormatEtc[cFormatsActive].dwAspect = DVASPECT_CONTENT;
884 paFormatEtc[cFormatsActive].lindex = -1;
885 paFormatEtc[cFormatsActive].tymed = TYMED_HGLOBAL;
886
887 paStgMeds [cFormatsActive].tymed = TYMED_HGLOBAL;
888 cFormatsActive++;
889 }
890 else /* Should never happen. */
891 AssertReleaseMsgFailedBreak(("Format specification for '%s' not implemented\n", pszFormat));
892 break;
893 }
894 }
895
896 LogRel2(("DnD: \t%s: %RTbool\n", a_lstFormats.at(i).c_str(), fSupported));
897 }
898
899 /*
900 * Warn in the log if this guest does not accept anything.
901 */
902 Assert(cFormatsActive <= cFormatsSup);
903 if (cFormatsActive)
904 {
905 LogRel2(("DnD: %RU32 supported formats found:\n", cFormatsActive));
906 for (size_t i = 0; i < cFormatsActive; i++)
907 LogRel2(("DnD: \t%s\n", this->m_lstFmtActive.at(i).c_str()));
908 }
909 else
910 LogRel(("DnD: Warning: No supported drag and drop formats on the guest found!\n"));
911
912 /*
913 * Prepare the startup info for DoDragDrop().
914 */
915
916 /* Translate our drop actions into allowed Windows drop effects. */
917 m_startupInfo.dwOKEffects = DROPEFFECT_NONE;
918 if (a_fDndLstActionsAllowed)
919 {
920 if (a_fDndLstActionsAllowed & VBOX_DND_ACTION_COPY)
921 m_startupInfo.dwOKEffects |= DROPEFFECT_COPY;
922 if (a_fDndLstActionsAllowed & VBOX_DND_ACTION_MOVE)
923 m_startupInfo.dwOKEffects |= DROPEFFECT_MOVE;
924 if (a_fDndLstActionsAllowed & VBOX_DND_ACTION_LINK)
925 m_startupInfo.dwOKEffects |= DROPEFFECT_LINK;
926 }
927
928 LogRel2(("DnD: Supported drop actions: 0x%x\n", m_startupInfo.dwOKEffects));
929
930#ifdef RT_EXCEPTIONS_ENABLED
931 try
932 {
933 m_startupInfo.pDropSource = new VBoxDnDDropSource(this);
934 m_startupInfo.pDataObject = new VBoxDnDDataObject(paFormatEtc, paStgMeds, cFormatsActive);
935 }
936 catch (std::bad_alloc &)
937#else
938 m_startupInfo.pDropSource = new VBoxDnDDropSource(this);
939 m_startupInfo.pDataObject = new VBoxDnDDataObject(paFormatEtc, paStgMeds, cFormatsActive);
940 if (!m_startupInfo.pDropSource || !m_startupInfo.pDataObject)
941#endif
942 {
943 LogFunc(("VERR_NO_MEMORY!"));
944 rc = VERR_NO_MEMORY;
945 }
946
947 RTMemTmpFree(paFormatEtc);
948 RTMemTmpFree(paStgMeds);
949
950 if (RT_SUCCESS(rc))
951 rc = makeFullscreen();
952
953 LogFlowFuncLeaveRC(rc);
954 return rc;
955}
956
957/**
958 * Handles actions required when the host cursor moves inside
959 * the guest's screen.
960 *
961 * @return IPRT status code.
962 * @param u32xPos Absolute X position (in pixels) of the host cursor
963 * inside the guest.
964 * @param u32yPos Absolute Y position (in pixels) of the host cursor
965 * inside the guest.
966 * @param dndAction Action the host wants to perform while moving.
967 * Currently ignored.
968 */
969int VBoxDnDWnd::OnHgMove(uint32_t u32xPos, uint32_t u32yPos, VBOXDNDACTION dndAction)
970{
971 RT_NOREF(dndAction);
972 int rc;
973
974 uint32_t uActionNotify = VBOX_DND_ACTION_IGNORE;
975 if (m_enmMode == HG)
976 {
977 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, dndAction=0x%x\n",
978 u32xPos, u32yPos, dndAction));
979
980 rc = mouseMove(u32xPos, u32yPos, MOUSEEVENTF_LEFTDOWN);
981
982 if (RT_SUCCESS(rc))
983 rc = RTCritSectEnter(&m_CritSect);
984 if (RT_SUCCESS(rc))
985 {
986 if ( (Dragging == m_enmState)
987 && m_startupInfo.pDropSource)
988 uActionNotify = m_startupInfo.pDropSource->GetCurrentAction();
989
990 RTCritSectLeave(&m_CritSect);
991 }
992 }
993 else /* Just acknowledge the operation with an ignore action. */
994 rc = VINF_SUCCESS;
995
996 if (RT_SUCCESS(rc))
997 {
998 rc = VbglR3DnDHGSendAckOp(&m_cmdCtx, uActionNotify);
999 if (RT_FAILURE(rc))
1000 LogFlowThisFunc(("Acknowledging operation failed with rc=%Rrc\n", rc));
1001 }
1002
1003 LogFlowThisFunc(("Returning uActionNotify=0x%x, rc=%Rrc\n", uActionNotify, rc));
1004 return rc;
1005}
1006
1007/**
1008 * Handles actions required when the host cursor leaves
1009 * the guest's screen again.
1010 *
1011 * @return IPRT status code.
1012 */
1013int VBoxDnDWnd::OnHgLeave(void)
1014{
1015 if (m_enmMode == GH) /* Wrong mode? Bail out. */
1016 return VERR_WRONG_ORDER;
1017
1018 int rc = Abort();
1019
1020 LogFlowFuncLeaveRC(rc);
1021 return rc;
1022}
1023
1024/**
1025 * Handles actions required when the host cursor wants to drop
1026 * and therefore start a "drop" action in the guest.
1027 *
1028 * @return IPRT status code.
1029 */
1030int VBoxDnDWnd::OnHgDrop(void)
1031{
1032 if (m_enmMode == GH)
1033 return VERR_WRONG_ORDER;
1034
1035 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", m_enmMode, m_enmState));
1036
1037 int rc = VINF_SUCCESS;
1038 if (m_enmState == Dragging)
1039 {
1040 if (m_lstFmtActive.size() >= 1)
1041 {
1042 /** @todo What to do when multiple formats are available? */
1043 m_strFmtReq = m_lstFmtActive.at(0);
1044
1045 rc = RTCritSectEnter(&m_CritSect);
1046 if (RT_SUCCESS(rc))
1047 {
1048 if (m_startupInfo.pDataObject)
1049 m_startupInfo.pDataObject->SetStatus(VBoxDnDDataObject::Status_Dropping);
1050 else
1051 rc = VERR_NOT_FOUND;
1052
1053 RTCritSectLeave(&m_CritSect);
1054 }
1055
1056 if (RT_SUCCESS(rc))
1057 {
1058 LogRel(("DnD: Requesting data as '%s' ...\n", m_strFmtReq.c_str()));
1059 rc = VbglR3DnDHGSendReqData(&m_cmdCtx, m_strFmtReq.c_str());
1060 if (RT_FAILURE(rc))
1061 LogFlowThisFunc(("Requesting data failed with rc=%Rrc\n", rc));
1062 }
1063
1064 }
1065 else /* Should never happen. */
1066 LogRel(("DnD: Error: Host did not specify a data format for drop data\n"));
1067 }
1068
1069 LogFlowFuncLeaveRC(rc);
1070 return rc;
1071}
1072
1073/**
1074 * Handles actions required when the host has sent over DnD data
1075 * to the guest after a "drop" event.
1076 *
1077 * @return IPRT status code.
1078 * @param pMeta Pointer to meta data received.
1079 */
1080int VBoxDnDWnd::OnHgDataReceive(PVBGLR3GUESTDNDMETADATA pMeta)
1081{
1082 LogFlowThisFunc(("mState=%ld, enmMetaType=%RU32\n", m_enmState, pMeta->enmType));
1083
1084 int rc = RTCritSectEnter(&m_CritSect);
1085 if (RT_SUCCESS(rc))
1086 {
1087 m_enmState = Dropped;
1088
1089 if (m_startupInfo.pDataObject)
1090 {
1091 switch (pMeta->enmType)
1092 {
1093 case VBGLR3GUESTDNDMETADATATYPE_RAW:
1094 {
1095 AssertBreakStmt(pMeta->u.Raw.pvMeta != NULL, rc = VERR_INVALID_POINTER);
1096 AssertBreakStmt(pMeta->u.Raw.cbMeta, rc = VERR_INVALID_PARAMETER);
1097
1098 rc = m_startupInfo.pDataObject->Signal(m_strFmtReq, pMeta->u.Raw.pvMeta, pMeta->u.Raw.cbMeta);
1099 break;
1100 }
1101
1102 case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
1103 {
1104 LogRel2(("DnD: URI transfer root directory is '%s'\n", DnDTransferListGetRootPathAbs(&pMeta->u.URI.Transfer)));
1105
1106 char *pszBuf;
1107 size_t cbBuf;
1108 /* Note: The transfer list already has its root set to a temporary directory, so no need to set/add a new
1109 * path base here. */
1110 rc = DnDTransferListGetRootsEx(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_NATIVE, NULL /* pszPathBase */,
1111 DND_PATH_SEPARATOR_STR, &pszBuf, &cbBuf);
1112 if (RT_SUCCESS(rc))
1113 {
1114 rc = m_startupInfo.pDataObject->Signal(m_strFmtReq, pszBuf, cbBuf);
1115 RTStrFree(pszBuf);
1116 }
1117 break;
1118 }
1119
1120 default:
1121 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1122 break;
1123 }
1124 }
1125 else
1126 rc = VERR_NOT_FOUND;
1127
1128 int rc2 = mouseRelease();
1129 if (RT_SUCCESS(rc))
1130 rc = rc2;
1131
1132 RTCritSectLeave(&m_CritSect);
1133 }
1134
1135 LogFlowFuncLeaveRC(rc);
1136 return rc;
1137}
1138
1139/**
1140 * Handles actions required when the host wants to cancel the current
1141 * host -> guest operation.
1142 *
1143 * @return IPRT status code.
1144 */
1145int VBoxDnDWnd::OnHgCancel(void)
1146{
1147 return Abort();
1148}
1149
1150#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1151/**
1152 * Handles actions required to start a guest -> host DnD operation.
1153 * This works by letting the host ask whether a DnD operation is pending
1154 * on the guest. The guest must not know anything about the host's DnD state
1155 * and/or operations due to security reasons.
1156 *
1157 * To capture a pending DnD operation on the guest which then can be communicated
1158 * to the host the proxy window needs to be registered as a drop target. This drop
1159 * target then will act as a proxy target between the guest OS and the host. In other
1160 * words, the guest OS will use this proxy target as a regular (invisible) window
1161 * which can be used by the regular guest OS' DnD mechanisms, independently of the
1162 * host OS. To make sure this proxy target is able receive an in-progress DnD operation
1163 * on the guest, it will be shown invisibly across all active guest OS screens. Just
1164 * think of an opened umbrella across all screens here.
1165 *
1166 * As soon as the proxy target and its underlying data object receive appropriate
1167 * DnD messages they'll be hidden again, and the control will be transferred back
1168 * this class again.
1169 *
1170 * @return IPRT status code.
1171 */
1172int VBoxDnDWnd::OnGhIsDnDPending(void)
1173{
1174 LogFlowThisFunc(("mMode=%ld, mState=%ld\n", m_enmMode, m_enmState));
1175
1176 if (m_enmMode == Unknown)
1177 setMode(GH);
1178
1179 if (m_enmMode != GH)
1180 return VERR_WRONG_ORDER;
1181
1182 if (m_enmState == Uninitialized)
1183 {
1184 /* Nothing to do here yet. */
1185 m_enmState = Initialized;
1186 }
1187
1188 int rc;
1189 if (m_enmState == Initialized)
1190 {
1191 /* Check if the VM session has changed and reconnect to the HGCM service if necessary. */
1192 rc = checkForSessionChange();
1193 if (RT_SUCCESS(rc))
1194 {
1195 rc = makeFullscreen();
1196 if (RT_SUCCESS(rc))
1197 {
1198 /*
1199 * We have to release the left mouse button to
1200 * get into our (invisible) proxy window.
1201 */
1202 mouseRelease();
1203
1204 /*
1205 * Even if we just released the left mouse button
1206 * we're still in the dragging state to handle our
1207 * own drop target (for the host).
1208 */
1209 m_enmState = Dragging;
1210 }
1211 }
1212 }
1213 else
1214 rc = VINF_SUCCESS;
1215
1216 /**
1217 * Some notes regarding guest cursor movement:
1218 * - The host only sends an HOST_DND_FN_GH_REQ_PENDING message to the guest
1219 * if the mouse cursor is outside the VM's window.
1220 * - The guest does not know anything about the host's cursor
1221 * position / state due to security reasons.
1222 * - The guest *only* knows that the host currently is asking whether a
1223 * guest DnD operation is in progress.
1224 */
1225
1226 if ( RT_SUCCESS(rc)
1227 && m_enmState == Dragging)
1228 {
1229 /** @todo Put this block into a function! */
1230 POINT p;
1231 GetCursorPos(&p);
1232 ClientToScreen(m_hWnd, &p);
1233#ifdef DEBUG_andy
1234 LogFlowThisFunc(("Client to screen curX=%ld, curY=%ld\n", p.x, p.y));
1235#endif
1236
1237 /** @todo Multi-monitor setups? */
1238#if 0 /* unused */
1239 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1240 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1241#endif
1242
1243 LONG px = p.x;
1244 if (px <= 0)
1245 px = 1;
1246 LONG py = p.y;
1247 if (py <= 0)
1248 py = 1;
1249
1250 rc = mouseMove(px, py, 0 /* dwMouseInputFlags */);
1251 }
1252
1253 if (RT_SUCCESS(rc))
1254 {
1255 VBOXDNDACTION dndActionDefault = VBOX_DND_ACTION_IGNORE;
1256
1257 AssertPtr(m_pDropTarget);
1258 RTCString strFormats = m_pDropTarget->Formats();
1259 if (!strFormats.isEmpty())
1260 {
1261 dndActionDefault = VBOX_DND_ACTION_COPY;
1262
1263 LogFlowFunc(("Acknowledging pDropTarget=0x%p, dndActionDefault=0x%x, dndLstActionsAllowed=0x%x, strFormats=%s\n",
1264 m_pDropTarget, dndActionDefault, m_lstActionsAllowed, strFormats.c_str()));
1265 }
1266 else
1267 {
1268 strFormats = "unknown"; /* Prevent VERR_IO_GEN_FAILURE for IOCTL. */
1269 LogFlowFunc(("No format data from proxy window available yet\n"));
1270 }
1271
1272 /** @todo Support more than one action at a time. */
1273 m_lstActionsAllowed = dndActionDefault;
1274
1275 int rc2 = VbglR3DnDGHSendAckPending(&m_cmdCtx,
1276 dndActionDefault, m_lstActionsAllowed,
1277 strFormats.c_str(), (uint32_t)strFormats.length() + 1 /* Include termination */);
1278 if (RT_FAILURE(rc2))
1279 {
1280 char szMsg[256]; /* Sizes according to MSDN. */
1281 char szTitle[64];
1282
1283 /** @todo Add some i18l tr() macros here. */
1284 RTStrPrintf(szTitle, sizeof(szTitle), "VirtualBox Guest Additions Drag and Drop");
1285 RTStrPrintf(szMsg, sizeof(szMsg), "Drag and drop to the host either is not supported or disabled. "
1286 "Please enable Guest to Host or Bidirectional drag and drop mode "
1287 "or re-install the VirtualBox Guest Additions.");
1288 switch (rc2)
1289 {
1290 case VERR_ACCESS_DENIED:
1291 {
1292 rc = hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
1293 szMsg, szTitle,
1294 15 * 1000 /* Time to display in msec */, NIIF_INFO);
1295 AssertRC(rc);
1296 break;
1297 }
1298
1299 default:
1300 break;
1301 }
1302
1303 LogRel2(("DnD: Host refuses drag and drop operation from guest: %Rrc\n", rc2));
1304 Reset();
1305 }
1306 }
1307
1308 if (RT_FAILURE(rc))
1309 Reset(); /* Reset state on failure. */
1310
1311 LogFlowFuncLeaveRC(rc);
1312 return rc;
1313}
1314
1315/**
1316 * Handles actions required to let the guest know that the host
1317 * started a "drop" action on the host. This will tell the guest
1318 * to send data in a specific format the host requested.
1319 *
1320 * @return IPRT status code.
1321 * @param pszFormat Format the host requests the data in.
1322 * @param cbFormat Size (in bytes) of format string.
1323 * @param dndActionDefault Default action on the host.
1324 */
1325int VBoxDnDWnd::OnGhDrop(const RTCString &strFormat, uint32_t dndActionDefault)
1326{
1327 RT_NOREF(dndActionDefault);
1328
1329 LogFlowThisFunc(("mMode=%ld, mState=%ld, pDropTarget=0x%p, strFormat=%s, dndActionDefault=0x%x\n",
1330 m_enmMode, m_enmState, m_pDropTarget, strFormat.c_str(), dndActionDefault));
1331 int rc;
1332 if (m_enmMode == GH)
1333 {
1334 if (m_enmState == Dragging)
1335 {
1336 AssertPtr(m_pDropTarget);
1337 rc = m_pDropTarget->WaitForDrop(5 * 1000 /* 5s timeout */);
1338
1339 Reset();
1340 }
1341 else if (m_enmState == Dropped)
1342 {
1343 rc = VINF_SUCCESS;
1344 }
1345 else
1346 rc = VERR_WRONG_ORDER;
1347
1348 if (RT_SUCCESS(rc))
1349 {
1350 /** @todo Respect uDefAction. */
1351 void *pvData = m_pDropTarget->DataMutableRaw();
1352 uint32_t cbData = (uint32_t)m_pDropTarget->DataSize();
1353 Assert(cbData == m_pDropTarget->DataSize());
1354
1355 if ( pvData
1356 && cbData)
1357 {
1358 rc = VbglR3DnDGHSendData(&m_cmdCtx, strFormat.c_str(), pvData, cbData);
1359 LogFlowFunc(("Sent pvData=0x%p, cbData=%RU32, rc=%Rrc\n", pvData, cbData, rc));
1360 }
1361 else
1362 rc = VERR_NO_DATA;
1363 }
1364 }
1365 else
1366 rc = VERR_WRONG_ORDER;
1367
1368 if (RT_FAILURE(rc))
1369 {
1370 /*
1371 * If an error occurred or the guest is in a wrong DnD mode,
1372 * send an error to the host in any case so that the host does
1373 * not wait for the data it expects from the guest.
1374 */
1375 int rc2 = VbglR3DnDGHSendError(&m_cmdCtx, rc);
1376 AssertRC(rc2);
1377 }
1378
1379 LogFlowFuncLeaveRC(rc);
1380 return rc;
1381}
1382#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1383
1384void VBoxDnDWnd::PostMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
1385{
1386 LogFlowFunc(("Posting message %u\n"));
1387 BOOL fRc = ::PostMessage(m_hWnd, uMsg, wParam, lParam);
1388 Assert(fRc); NOREF(fRc);
1389}
1390
1391/**
1392 * Injects a DnD event in this proxy window's Windows
1393 * event queue. The (allocated) event will be deleted by
1394 * this class after processing.
1395 *
1396 * @return IPRT status code.
1397 * @param pEvent Event to inject.
1398 */
1399int VBoxDnDWnd::ProcessEvent(PVBOXDNDEVENT pEvent)
1400{
1401 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1402
1403 BOOL fRc = ::PostMessage(m_hWnd, WM_VBOXTRAY_DND_MESSAGE,
1404 0 /* wParm */, (LPARAM)pEvent /* lParm */);
1405 if (!fRc)
1406 {
1407 DWORD dwErr = GetLastError();
1408
1409 static int s_iBitchedAboutFailedDnDMessages = 0;
1410 if (s_iBitchedAboutFailedDnDMessages++ < 32)
1411 {
1412 LogRel(("DnD: Processing event %p failed with %ld (%Rrc), skipping\n",
1413 pEvent, dwErr, RTErrConvertFromWin32(dwErr)));
1414 }
1415
1416 VbglR3DnDEventFree(pEvent->pVbglR3Event);
1417
1418 RTMemFree(pEvent);
1419 pEvent = NULL;
1420
1421 return RTErrConvertFromWin32(dwErr);
1422 }
1423
1424 return VINF_SUCCESS;
1425}
1426
1427/**
1428 * Checks if the VM session has changed (can happen when restoring the VM from a saved state)
1429 * and do a reconnect to the DnD HGCM service.
1430 *
1431 * @returns IPRT status code.
1432 */
1433int VBoxDnDWnd::checkForSessionChange(void)
1434{
1435 uint64_t uSessionID;
1436 int rc = VbglR3GetSessionId(&uSessionID);
1437 if ( RT_SUCCESS(rc)
1438 && uSessionID != m_cmdCtx.uSessionID)
1439 {
1440 LogFlowThisFunc(("VM session has changed to %RU64\n", uSessionID));
1441
1442 rc = VbglR3DnDDisconnect(&m_cmdCtx);
1443 AssertRC(rc);
1444
1445 rc = VbglR3DnDConnect(&m_cmdCtx);
1446 AssertRC(rc);
1447 }
1448
1449 LogFlowFuncLeaveRC(rc);
1450 return rc;
1451}
1452
1453/**
1454 * Hides the proxy window again.
1455 *
1456 * @return IPRT status code.
1457 */
1458int VBoxDnDWnd::Hide(void)
1459{
1460#ifdef DEBUG_andy
1461 LogFlowFunc(("\n"));
1462#endif
1463 ShowWindow(m_hWnd, SW_HIDE);
1464
1465 return VINF_SUCCESS;
1466}
1467
1468/**
1469 * Shows the (invisible) proxy window in fullscreen,
1470 * spawned across all active guest monitors.
1471 *
1472 * @return IPRT status code.
1473 */
1474int VBoxDnDWnd::makeFullscreen(void)
1475{
1476 int rc = VINF_SUCCESS;
1477
1478 RECT r;
1479 RT_ZERO(r);
1480
1481 BOOL fRc;
1482 HDC hDC = GetDC(NULL /* Entire screen */);
1483 if (hDC)
1484 {
1485 fRc = g_pfnEnumDisplayMonitors
1486 /* EnumDisplayMonitors is not available on NT4. */
1487 ? g_pfnEnumDisplayMonitors(hDC, NULL, VBoxDnDWnd::MonitorEnumProc, (LPARAM)&r):
1488 FALSE;
1489
1490 if (!fRc)
1491 rc = VERR_NOT_FOUND;
1492 ReleaseDC(NULL, hDC);
1493 }
1494 else
1495 rc = VERR_ACCESS_DENIED;
1496
1497 if (RT_FAILURE(rc))
1498 {
1499 /* If multi-monitor enumeration failed above, try getting at least the
1500 * primary monitor as a fallback. */
1501 r.left = 0;
1502 r.top = 0;
1503 r.right = GetSystemMetrics(SM_CXSCREEN);
1504 r.bottom = GetSystemMetrics(SM_CYSCREEN);
1505 rc = VINF_SUCCESS;
1506 }
1507
1508 if (RT_SUCCESS(rc))
1509 {
1510 LONG lStyle = GetWindowLong(m_hWnd, GWL_STYLE);
1511 SetWindowLong(m_hWnd, GWL_STYLE,
1512 lStyle & ~(WS_CAPTION | WS_THICKFRAME));
1513 LONG lExStyle = GetWindowLong(m_hWnd, GWL_EXSTYLE);
1514 SetWindowLong(m_hWnd, GWL_EXSTYLE,
1515 lExStyle & ~( WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE
1516 | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1517
1518 fRc = SetWindowPos(m_hWnd, HWND_TOPMOST,
1519 r.left,
1520 r.top,
1521 r.right - r.left,
1522 r.bottom - r.top,
1523#ifdef VBOX_DND_DEBUG_WND
1524 SWP_SHOWWINDOW | SWP_FRAMECHANGED);
1525#else
1526 SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
1527#endif
1528 if (fRc)
1529 {
1530 LogFlowFunc(("Virtual screen is %ld,%ld,%ld,%ld (%ld x %ld)\n",
1531 r.left, r.top, r.right, r.bottom,
1532 r.right - r.left, r.bottom - r.top));
1533 }
1534 else
1535 {
1536 DWORD dwErr = GetLastError();
1537 LogRel(("DnD: Failed to set proxy window position, rc=%Rrc\n",
1538 RTErrConvertFromWin32(dwErr)));
1539 }
1540 }
1541 else
1542 LogRel(("DnD: Failed to determine virtual screen size, rc=%Rrc\n", rc));
1543
1544 LogFlowFuncLeaveRC(rc);
1545 return rc;
1546}
1547
1548/**
1549 * Moves the guest mouse cursor to a specific position.
1550 *
1551 * @return IPRT status code.
1552 * @param x X position (in pixels) to move cursor to.
1553 * @param y Y position (in pixels) to move cursor to.
1554 * @param dwMouseInputFlags Additional movement flags. @sa MOUSEEVENTF_ flags.
1555 */
1556int VBoxDnDWnd::mouseMove(int x, int y, DWORD dwMouseInputFlags)
1557{
1558 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1559 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1560
1561 INPUT Input[1] = { {0} };
1562 Input[0].type = INPUT_MOUSE;
1563 Input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
1564 | dwMouseInputFlags;
1565 Input[0].mi.dx = x * (65535 / iScreenX);
1566 Input[0].mi.dy = y * (65535 / iScreenY);
1567
1568 int rc;
1569 if (g_pfnSendInput(1 /* Number of inputs */,
1570 Input, sizeof(INPUT)))
1571 {
1572#ifdef DEBUG_andy
1573 CURSORINFO ci;
1574 RT_ZERO(ci);
1575 ci.cbSize = sizeof(ci);
1576 BOOL fRc = GetCursorInfo(&ci);
1577 if (fRc)
1578 LogFlowThisFunc(("Cursor shown=%RTbool, cursor=0x%p, x=%d, y=%d\n",
1579 (ci.flags & CURSOR_SHOWING) ? true : false,
1580 ci.hCursor, ci.ptScreenPos.x, ci.ptScreenPos.y));
1581#endif
1582 rc = VINF_SUCCESS;
1583 }
1584 else
1585 {
1586 DWORD dwErr = GetLastError();
1587 rc = RTErrConvertFromWin32(dwErr);
1588 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1589 }
1590
1591 return rc;
1592}
1593
1594/**
1595 * Releases a previously pressed left guest mouse button.
1596 *
1597 * @return IPRT status code.
1598 */
1599int VBoxDnDWnd::mouseRelease(void)
1600{
1601 LogFlowFuncEnter();
1602
1603 int rc;
1604
1605 /* Release mouse button in the guest to start the "drop"
1606 * action at the current mouse cursor position. */
1607 INPUT Input[1] = { {0} };
1608 Input[0].type = INPUT_MOUSE;
1609 Input[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;
1610 if (!g_pfnSendInput(1, Input, sizeof(INPUT)))
1611 {
1612 DWORD dwErr = GetLastError();
1613 rc = RTErrConvertFromWin32(dwErr);
1614 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1615 }
1616 else
1617 rc = VINF_SUCCESS;
1618
1619 return rc;
1620}
1621
1622/**
1623 * Resets the proxy window.
1624 */
1625void VBoxDnDWnd::Reset(void)
1626{
1627 LogFlowThisFunc(("Resetting, old mMode=%ld, mState=%ld\n",
1628 m_enmMode, m_enmState));
1629
1630 /*
1631 * Note: Don't clear this->lstAllowedFormats at the moment, as this value is initialized
1632 * on class creation. We might later want to modify the allowed formats at runtime,
1633 * so keep this in mind when implementing this.
1634 */
1635
1636 this->m_lstFmtActive.clear();
1637 this->m_lstActionsAllowed = VBOX_DND_ACTION_IGNORE;
1638
1639 int rc2 = setMode(Unknown);
1640 AssertRC(rc2);
1641
1642 Hide();
1643}
1644
1645/**
1646 * Sets the current operation mode of this proxy window.
1647 *
1648 * @return IPRT status code.
1649 * @param enmMode New mode to set.
1650 */
1651int VBoxDnDWnd::setMode(Mode enmMode)
1652{
1653 LogFlowThisFunc(("Old mode=%ld, new mode=%ld\n",
1654 m_enmMode, enmMode));
1655
1656 m_enmMode = enmMode;
1657 m_enmState = Initialized;
1658
1659 return VINF_SUCCESS;
1660}
1661
1662/**
1663 * Static helper function for having an own WndProc for proxy
1664 * window instances.
1665 */
1666static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
1667{
1668 LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
1669 AssertPtrReturn(pUserData, 0);
1670
1671 VBoxDnDWnd *pWnd = reinterpret_cast<VBoxDnDWnd *>(pUserData);
1672 if (pWnd)
1673 return pWnd->WndProc(hWnd, uMsg, wParam, lParam);
1674
1675 return 0;
1676}
1677
1678/**
1679 * Static helper function for routing Windows messages to a specific
1680 * proxy window instance.
1681 */
1682static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
1683{
1684 /* Note: WM_NCCREATE is not the first ever message which arrives, but
1685 * early enough for us. */
1686 if (uMsg == WM_NCCREATE)
1687 {
1688 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
1689 AssertPtr(pCS);
1690 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
1691 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxDnDWndProcInstance);
1692
1693 return vboxDnDWndProcInstance(hWnd, uMsg, wParam, lParam);
1694 }
1695
1696 /* No window associated yet. */
1697 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1698}
1699
1700/**
1701 * Initializes drag and drop.
1702 *
1703 * @return IPRT status code.
1704 * @param pEnv The DnD service's environment.
1705 * @param ppInstance The instance pointer which refer to this object.
1706 */
1707DECLCALLBACK(int) VBoxDnDInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
1708{
1709 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
1710 AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
1711
1712 LogFlowFuncEnter();
1713
1714 PVBOXDNDCONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
1715 AssertPtr(pCtx);
1716
1717 int rc;
1718 bool fSupportedOS = true;
1719
1720 if (VbglR3AutoLogonIsRemoteSession())
1721 {
1722 /* Do not do drag and drop for remote sessions. */
1723 LogRel(("DnD: Drag and drop has been disabled for a remote session\n"));
1724 rc = VERR_NOT_SUPPORTED;
1725 }
1726 else
1727 rc = VINF_SUCCESS;
1728
1729 if (RT_SUCCESS(rc))
1730 {
1731 g_pfnSendInput = (PFNSENDINPUT)
1732 RTLdrGetSystemSymbol("User32.dll", "SendInput");
1733 fSupportedOS = !RT_BOOL(g_pfnSendInput == NULL);
1734 g_pfnEnumDisplayMonitors = (PFNENUMDISPLAYMONITORS)
1735 RTLdrGetSystemSymbol("User32.dll", "EnumDisplayMonitors");
1736 /* g_pfnEnumDisplayMonitors is optional. */
1737
1738 if (!fSupportedOS)
1739 {
1740 LogRel(("DnD: Not supported Windows version, disabling drag and drop support\n"));
1741 rc = VERR_NOT_SUPPORTED;
1742 }
1743 }
1744
1745 if (RT_SUCCESS(rc))
1746 {
1747 /* Assign service environment to our context. */
1748 pCtx->pEnv = pEnv;
1749
1750 /* Create the proxy window. At the moment we
1751 * only support one window at a time. */
1752 VBoxDnDWnd *pWnd = NULL;
1753#ifdef RT_EXCEPTIONS_ENABLED
1754 try { pWnd = new VBoxDnDWnd(); }
1755 catch (std::bad_alloc &)
1756#else
1757 pWnd = new VBoxDnDWnd();
1758 if (!pWnd)
1759#endif
1760 {
1761 rc = VERR_NO_MEMORY;
1762 }
1763 if (RT_SUCCESS(rc))
1764 {
1765 rc = pWnd->Initialize(pCtx);
1766 if (RT_SUCCESS(rc))
1767 {
1768 /* Add proxy window to our proxy windows list. */
1769#ifdef RT_EXCEPTIONS_ENABLED
1770 try { pCtx->lstWnd.append(pWnd); /** @todo the list implementation sucks wrt exception handling. */ }
1771 catch (std::bad_alloc &)
1772 {
1773 delete pWnd;
1774 rc = VERR_NO_MEMORY;
1775 }
1776#else
1777 pCtx->lstWnd.append(pWnd); /** @todo the list implementation sucks wrt exception handling. */
1778#endif
1779 }
1780 else
1781 delete pWnd;
1782 }
1783 }
1784
1785 if (RT_SUCCESS(rc))
1786 rc = RTSemEventCreate(&pCtx->hEvtQueueSem);
1787 if (RT_SUCCESS(rc))
1788 {
1789 *ppInstance = pCtx;
1790
1791 LogRel(("DnD: Drag and drop service successfully started\n"));
1792 }
1793 else
1794 LogRel(("DnD: Initializing drag and drop service failed with rc=%Rrc\n", rc));
1795
1796 LogFlowFuncLeaveRC(rc);
1797 return rc;
1798}
1799
1800DECLCALLBACK(int) VBoxDnDStop(void *pInstance)
1801{
1802 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
1803
1804 LogFunc(("Stopping pInstance=%p\n", pInstance));
1805
1806 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1807 AssertPtr(pCtx);
1808
1809 /* Set shutdown indicator. */
1810 ASMAtomicWriteBool(&pCtx->fShutdown, true);
1811
1812 /* Disconnect. */
1813 VbglR3DnDDisconnect(&pCtx->cmdCtx);
1814
1815 LogFlowFuncLeaveRC(VINF_SUCCESS);
1816 return VINF_SUCCESS;
1817}
1818
1819DECLCALLBACK(void) VBoxDnDDestroy(void *pInstance)
1820{
1821 AssertPtrReturnVoid(pInstance);
1822
1823 LogFunc(("Destroying pInstance=%p\n", pInstance));
1824
1825 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1826 AssertPtr(pCtx);
1827
1828 /** @todo At the moment we only have one DnD proxy window. */
1829 Assert(pCtx->lstWnd.size() == 1);
1830 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1831 if (pWnd)
1832 {
1833 delete pWnd;
1834 pWnd = NULL;
1835 }
1836
1837 if (pCtx->hEvtQueueSem != NIL_RTSEMEVENT)
1838 {
1839 RTSemEventDestroy(pCtx->hEvtQueueSem);
1840 pCtx->hEvtQueueSem = NIL_RTSEMEVENT;
1841 }
1842
1843 LogFunc(("Destroyed pInstance=%p\n", pInstance));
1844}
1845
1846DECLCALLBACK(int) VBoxDnDWorker(void *pInstance, bool volatile *pfShutdown)
1847{
1848 AssertPtr(pInstance);
1849 AssertPtr(pfShutdown);
1850
1851 LogFlowFunc(("pInstance=%p\n", pInstance));
1852
1853 /*
1854 * Tell the control thread that it can continue
1855 * spawning services.
1856 */
1857 RTThreadUserSignal(RTThreadSelf());
1858
1859 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1860 AssertPtr(pCtx);
1861
1862 int rc = VbglR3DnDConnect(&pCtx->cmdCtx);
1863 if (RT_FAILURE(rc))
1864 return rc;
1865
1866 /** @todo At the moment we only have one DnD proxy window. */
1867 Assert(pCtx->lstWnd.size() == 1);
1868 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1869 AssertPtr(pWnd);
1870
1871 /* Number of invalid messages skipped in a row. */
1872 int cMsgSkippedInvalid = 0;
1873 PVBOXDNDEVENT pEvent = NULL;
1874
1875 for (;;)
1876 {
1877 pEvent = (PVBOXDNDEVENT)RTMemAllocZ(sizeof(VBOXDNDEVENT));
1878 if (!pEvent)
1879 {
1880 rc = VERR_NO_MEMORY;
1881 break;
1882 }
1883 /* Note: pEvent will be free'd by the consumer later. */
1884
1885 PVBGLR3DNDEVENT pVbglR3Event = NULL;
1886 rc = VbglR3DnDEventGetNext(&pCtx->cmdCtx, &pVbglR3Event);
1887 if (RT_SUCCESS(rc))
1888 {
1889 LogFunc(("enmType=%RU32, rc=%Rrc\n", pVbglR3Event->enmType, rc));
1890
1891 cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
1892
1893 LogRel2(("DnD: Received new event, type=%RU32, rc=%Rrc\n", pVbglR3Event->enmType, rc));
1894
1895 /* pEvent now owns pVbglR3Event. */
1896 pEvent->pVbglR3Event = pVbglR3Event;
1897 pVbglR3Event = NULL;
1898
1899 rc = pWnd->ProcessEvent(pEvent);
1900 if (RT_SUCCESS(rc))
1901 {
1902 /* Event was consumed and the proxy window till take care of the memory -- NULL it. */
1903 pEvent = NULL;
1904 }
1905 else
1906 LogRel(("DnD: Processing proxy window event %RU32 failed with %Rrc\n", pVbglR3Event->enmType, rc));
1907 }
1908
1909 if (RT_FAILURE(rc))
1910 {
1911 if (pEvent)
1912 {
1913 RTMemFree(pEvent);
1914 pEvent = NULL;
1915 }
1916
1917 LogFlowFunc(("Processing next message failed with rc=%Rrc\n", rc));
1918
1919 /* Old(er) hosts either are broken regarding DnD support or otherwise
1920 * don't support the stuff we do on the guest side, so make sure we
1921 * don't process invalid messages forever. */
1922 if (cMsgSkippedInvalid++ > 32)
1923 {
1924 LogRel(("DnD: Too many invalid/skipped messages from host, exiting ...\n"));
1925 break;
1926 }
1927
1928 /* Make sure our proxy window is hidden when an error occured to
1929 * not block the guest's UI. */
1930 int rc2 = pWnd->Abort();
1931 AssertRC(rc2);
1932 }
1933
1934 if (*pfShutdown)
1935 break;
1936
1937 if (ASMAtomicReadBool(&pCtx->fShutdown))
1938 break;
1939
1940 if (RT_FAILURE(rc)) /* Don't hog the CPU on errors. */
1941 RTThreadSleep(1000 /* ms */);
1942 }
1943
1944 if (pEvent)
1945 {
1946 VbglR3DnDEventFree(pEvent->pVbglR3Event);
1947
1948 RTMemFree(pEvent);
1949 pEvent = NULL;
1950 }
1951
1952 VbglR3DnDDisconnect(&pCtx->cmdCtx);
1953
1954 LogRel(("DnD: Ended\n"));
1955
1956 LogFlowFuncLeaveRC(rc);
1957 return rc;
1958}
1959
1960/**
1961 * The service description.
1962 */
1963VBOXSERVICEDESC g_SvcDescDnD =
1964{
1965 /* pszName. */
1966 "draganddrop",
1967 /* pszDescription. */
1968 "Drag and Drop",
1969 /* methods */
1970 VBoxDnDInit,
1971 VBoxDnDWorker,
1972 VBoxDnDStop,
1973 VBoxDnDDestroy
1974};
1975
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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