VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use