VirtualBox

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

最後變更 在這個檔案從106468是 106468,由 vboxsync 提交於 5 月 前

Additions/VBoxTray: Doxygen fixes. ​bugref:10763

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

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