VirtualBox

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

Last change on this file since 50508 was 50508, checked in by vboxsync, 11 years ago

DnD: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.0 KB
Line 
1/* $Id: VBoxDnD.cpp 50508 2014-02-19 15:45:58Z vboxsync $ */
2/** @file
3 * VBoxDnD.cpp - Windows-specific bits of the drag'n drop service.
4 */
5
6/*
7 * Copyright (C) 2013-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include <windows.h>
18#include "VBoxTray.h"
19#include "VBoxHelpers.h"
20#include "VBoxDnD.h"
21
22#include <VBox/VBoxGuestLib.h>
23#include "VBox/HostServices/DragAndDropSvc.h"
24
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/err.h>
28#include <iprt/ldr.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31
32#include <iprt/cpp/mtlist.h>
33#include <iprt/cpp/ministring.h>
34
35#include <iprt/cpp/mtlist.h>
36
37#include <VBoxGuestInternal.h>
38
39/* Enable this define to see the proxy window(s) when debugging
40 * their behavior. Don't have this enabled in release builds! */
41#ifdef DEBUG
42//# define VBOX_DND_DEBUG_WND
43#endif
44
45/** @todo Merge this with messages from VBoxTray.h. */
46#define WM_VBOXTRAY_DND_MESSAGE WM_APP + 401
47
48/** Function pointer for SendInput(). This only is available starting
49 * at NT4 SP3+. */
50typedef BOOL (WINAPI *PFNSENDINPUT)(UINT, LPINPUT, int);
51typedef BOOL (WINAPI* PFNENUMDISPLAYMONITORS)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
52
53/** Static pointer to SendInput() function. */
54static PFNSENDINPUT s_pfnSendInput = NULL;
55static PFNENUMDISPLAYMONITORS s_pfnEnumDisplayMonitors = NULL;
56
57static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
58static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
59
60VBoxDnDWnd::VBoxDnDWnd(void)
61 : hWnd(NULL),
62 mfMouseButtonDown(false),
63#ifdef VBOX_WITH_DRAG_AND_DROP_GH
64 pDropTarget(NULL),
65#endif
66 mClientID(UINT32_MAX),
67 mMode(Unknown),
68 mState(Uninitialized)
69{
70 RT_ZERO(startupInfo);
71
72 reset();
73}
74
75VBoxDnDWnd::~VBoxDnDWnd(void)
76{
77 /** @todo Shutdown crit sect / event etc! */
78
79 reset();
80}
81
82int VBoxDnDWnd::Initialize(PVBOXDNDCONTEXT pContext)
83{
84 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
85
86 /* Save the context. */
87 this->pContext = pContext;
88
89 int rc = RTSemEventCreate(&mEventSem);
90 if (RT_SUCCESS(rc))
91 rc = RTCritSectInit(&mCritSect);
92
93 if (RT_SUCCESS(rc))
94 {
95 /* Message pump thread for our proxy window. */
96 rc = RTThreadCreate(&gCtx.hEvtQueue, VBoxDnDWnd::Thread, this,
97 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
98 "VBoxTrayDnDWnd");
99 if (RT_FAILURE(rc))
100 LogRel(("DnD: Failed to start proxy window thread, rc=%Rrc\n", rc));
101 /** @todo Wait for thread to be started! */
102 }
103
104 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
105 return rc;
106}
107
108/**
109 * Thread for handling the window's message pump.
110 *
111 * @return IPRT status code.
112 * @param hThread
113 * @param pvUser
114 */
115/* static */
116int VBoxDnDWnd::Thread(RTTHREAD hThread, void *pvUser)
117{
118 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
119
120 VBoxDnDWnd *pThis = (VBoxDnDWnd*)pvUser;
121 AssertPtr(pThis);
122
123 PVBOXDNDCONTEXT pContext = pThis->pContext;
124 AssertPtr(pContext);
125 AssertPtr(pContext->pEnv);
126
127 HINSTANCE hInstance = pContext->pEnv->hInstance;
128 Assert(hInstance != 0);
129
130 /* Create our proxy window. */
131 WNDCLASSEX wndClass;
132 RT_ZERO(wndClass);
133
134 wndClass.cbSize = sizeof(WNDCLASSEX);
135 wndClass.lpfnWndProc = vboxDnDWndProc;
136 wndClass.lpszClassName = "VBoxTrayDnDWnd";
137 wndClass.hInstance = hInstance;
138 wndClass.style = CS_NOCLOSE;
139#ifdef VBOX_DND_DEBUG_WND
140 wndClass.style |= CS_HREDRAW | CS_VREDRAW;
141 wndClass.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(255, 0, 0)));
142#else
143 wndClass.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
144#endif
145
146 int rc = VINF_SUCCESS;
147 if (!RegisterClassEx(&wndClass))
148 {
149 DWORD dwErr = GetLastError();
150 LogFlowFunc(("Unable to register proxy window class, error=%ld\n", dwErr));
151 rc = RTErrConvertFromWin32(dwErr);
152 }
153
154 if (RT_SUCCESS(rc))
155 {
156 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
157 DWORD dwStyle = WS_POPUP;
158#ifdef VBOX_DND_DEBUG_WND
159 dwExStyle &= ~WS_EX_TRANSPARENT; /* Remove transparency bit. */
160 dwStyle |= WS_VISIBLE; /* Make the window visible. */
161#endif
162 pThis->hWnd =
163 CreateWindowEx(dwExStyle,
164 "VBoxTrayDnDWnd", "VBoxTrayDnDWnd",
165 dwStyle,
166#ifdef VBOX_DND_DEBUG_WND
167 CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL,
168#else
169 -200, -200, 100, 100, NULL, NULL,
170#endif
171 hInstance, pThis /* lParm */);
172 if (!pThis->hWnd)
173 {
174 DWORD dwErr = GetLastError();
175 LogFlowFunc(("Unable to create proxy window, error=%ld\n", dwErr));
176 rc = RTErrConvertFromWin32(dwErr);
177 }
178 else
179 {
180#ifndef VBOX_DND_DEBUG_WND
181 SetWindowPos(pThis->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
182 SWP_NOACTIVATE | SWP_HIDEWINDOW
183 | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
184 LogFlowFunc(("Proxy window created, hWnd=0x%x\n", pThis->hWnd));
185#else
186 LogFlowFunc(("Debug proxy window created, hWnd=0x%x\n", pThis->hWnd));
187
188 /*
189 * Install some mouse tracking.
190 */
191 TRACKMOUSEEVENT me;
192 RT_ZERO(me);
193 me.cbSize = sizeof(TRACKMOUSEEVENT);
194 me.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT;
195 me.hwndTrack = pThis->hWnd;
196 BOOL fRc = TrackMouseEvent(&me);
197 Assert(fRc);
198#endif
199 }
200 }
201
202 HRESULT hr = OleInitialize(NULL);
203 if (SUCCEEDED(hr))
204 {
205#ifdef VBOX_WITH_DRAG_AND_DROP_GH
206 rc = pThis->RegisterAsDropTarget();
207#endif
208 }
209 else
210 {
211 LogRelFunc(("Unable to initialize OLE, hr=%Rhrc\n", hr));
212 rc = VERR_COM_UNEXPECTED;
213 }
214
215 if (RT_SUCCESS(rc))
216 {
217 bool fShutdown = false;
218 do
219 {
220 MSG uMsg;
221 while (GetMessage(&uMsg, 0, 0, 0))
222 {
223 TranslateMessage(&uMsg);
224 DispatchMessage(&uMsg);
225 }
226
227 if (ASMAtomicReadBool(&pContext->fShutdown))
228 fShutdown = true;
229
230 if (fShutdown)
231 {
232 LogFlowFunc(("Cancelling ...\n"));
233 break;
234 }
235
236 /** @todo Immediately drop on failure? */
237
238 } while (RT_SUCCESS(rc));
239
240#ifdef VBOX_WITH_DRAG_AND_DROP_GH
241 int rc2 = pThis->UnregisterAsDropTarget();
242 if (RT_SUCCESS(rc))
243 rc = rc2;
244#endif
245 OleUninitialize();
246 }
247
248 LogFlowFuncLeaveRC(rc);
249 return rc;
250}
251
252/* static */
253BOOL CALLBACK VBoxDnDWnd::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor,
254 LPRECT lprcMonitor, LPARAM lParam)
255{
256 LPRECT pRect = (LPRECT)lParam;
257 AssertPtrReturn(pRect, FALSE);
258
259 AssertPtr(lprcMonitor);
260 LogFlowFunc(("Monitor is %ld,%ld,%ld,%ld\n",
261 lprcMonitor->left, lprcMonitor->top,
262 lprcMonitor->right, lprcMonitor->bottom));
263
264 /* Build up a simple bounding box to hold the entire (virtual) screen. */
265 if (pRect->left > lprcMonitor->left)
266 pRect->left = lprcMonitor->left;
267 if (pRect->right < lprcMonitor->right)
268 pRect->right = lprcMonitor->right;
269 if (pRect->top > lprcMonitor->top)
270 pRect->top = lprcMonitor->top;
271 if (pRect->bottom < lprcMonitor->bottom)
272 pRect->bottom = lprcMonitor->bottom;
273
274 return TRUE;
275}
276
277LRESULT CALLBACK VBoxDnDWnd::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
278{
279 switch (uMsg)
280 {
281 case WM_CREATE:
282 {
283 int rc = OnCreate();
284 if (RT_FAILURE(rc))
285 return FALSE;
286 return TRUE;
287 }
288
289 case WM_CLOSE:
290 {
291 OnDestroy();
292
293 DestroyWindow(hWnd);
294 PostQuitMessage(0);
295 return 0;
296 }
297
298 case WM_LBUTTONDOWN:
299 LogFlowThisFunc(("WM_LBUTTONDOWN\n"));
300 mfMouseButtonDown = true;
301 return 0;
302
303 case WM_LBUTTONUP:
304 LogFlowThisFunc(("WM_LBUTTONUP\n"));
305 mfMouseButtonDown = false;
306 return 0;
307
308 case WM_MOUSELEAVE:
309 LogFlowThisFunc(("WM_MOUSELEAVE\n"));
310 return 0;
311
312 /* Will only be called once; after the first mouse move, this
313 * window will be hidden! */
314 case WM_MOUSEMOVE:
315 {
316 LogFlowThisFunc(("WM_MOUSEMOVE: mfMouseButtonDown=%RTbool, mMode=%ld, mState=%ld\n",
317 mfMouseButtonDown, mMode, mState));
318#ifdef DEBUG_andy
319 POINT p;
320 GetCursorPos(&p);
321 LogFlowThisFunc(("WM_MOUSEMOVE: curX=%ld, curY=%ld\n", p.x, p.y));
322#endif
323 int rc = VINF_SUCCESS;
324 if (mMode == HG) /* Host to guest. */
325 {
326 /* Dragging not started yet? Kick it off ... */
327 if ( mfMouseButtonDown
328 && (mState != Dragging))
329 {
330 mState = Dragging;
331#if 0
332 /* Delay hiding the proxy window a bit when debugging, to see
333 * whether the desired range is covered correctly. */
334 RTThreadSleep(5000);
335#endif
336 hide();
337
338 LogFlowThisFunc(("Starting drag'n drop: uAllActions=0x%x, dwOKEffects=0x%x ...\n",
339 uAllActions, startupInfo.dwOKEffects));
340
341 AssertPtr(startupInfo.pDataObject);
342 AssertPtr(startupInfo.pDropSource);
343 DWORD dwEffect;
344 HRESULT hr = DoDragDrop(startupInfo.pDataObject, startupInfo.pDropSource,
345 startupInfo.dwOKEffects, &dwEffect);
346 LogFlowThisFunc(("hr=%Rhrc, dwEffect=%RI32\n", hr, dwEffect));
347 switch (hr)
348 {
349 case DRAGDROP_S_DROP:
350 mState = Dropped;
351 break;
352
353 case DRAGDROP_S_CANCEL:
354 mState = Canceled;
355 break;
356
357 default:
358 LogFlowThisFunc(("Drag'n drop failed with %Rhrc\n", hr));
359 mState = Canceled;
360 rc = VERR_GENERAL_FAILURE; /** @todo Find a better status code. */
361 break;
362 }
363
364 int rc2 = RTCritSectEnter(&mCritSect);
365 if (RT_SUCCESS(rc2))
366 {
367 startupInfo.pDropSource->Release();
368 startupInfo.pDataObject->Release();
369
370 RT_ZERO(startupInfo);
371
372 rc2 = RTCritSectLeave(&mCritSect);
373 if (RT_SUCCESS(rc))
374 rc = rc2;
375 }
376
377 mMode = Unknown;
378 }
379 }
380 else if (mMode == GH) /* Guest to host. */
381 {
382 //hide();
383
384 /* Starting here VBoxDnDDropTarget should
385 * take over; was instantiated when registering
386 * this proxy window as a (valid) drop target. */
387 }
388 else
389 rc = VERR_NOT_SUPPORTED;
390
391 LogFlowThisFunc(("WM_MOUSEMOVE: mMode=%ld, mState=%ld, rc=%Rrc\n",
392 mMode, mState, rc));
393 return 0;
394 }
395
396 case WM_NCMOUSEHOVER:
397 LogFlowThisFunc(("WM_NCMOUSEHOVER\n"));
398 return 0;
399
400 case WM_NCMOUSELEAVE:
401 LogFlowThisFunc(("WM_NCMOUSELEAVE\n"));
402 return 0;
403
404 case WM_VBOXTRAY_DND_MESSAGE:
405 {
406 VBOXDNDEVENT *pEvent = (PVBOXDNDEVENT)lParam;
407 AssertPtr(pEvent);
408
409 LogFlowThisFunc(("Received uType=%RU32, uScreenID=%RU32\n",
410 pEvent->Event.uType, pEvent->Event.uScreenId));
411
412 int rc;
413 switch (pEvent->Event.uType)
414 {
415 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
416 {
417 LogFlowThisFunc(("HOST_DND_HG_EVT_ENTER\n"));
418
419 reset();
420
421 mMode = HG;
422
423 if (pEvent->Event.cbFormats)
424 {
425 RTCList<RTCString> lstFormats =
426 RTCString(pEvent->Event.pszFormats, pEvent->Event.cbFormats - 1).split("\r\n");
427 rc = OnHgEnter(lstFormats, pEvent->Event.u.a.uAllActions);
428 }
429 else
430 {
431 AssertMsgFailed(("cbFormats is 0\n"));
432 rc = VERR_INVALID_PARAMETER;
433 }
434
435 /* Note: After HOST_DND_HG_EVT_ENTER there immediately is a move
436 * event, so fall through is intentional here. */
437 }
438
439 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
440 {
441 LogFlowThisFunc(("HOST_DND_HG_EVT_MOVE: %d,%d\n",
442 pEvent->Event.u.a.uXpos, pEvent->Event.u.a.uYpos));
443
444 rc = OnHgMove(pEvent->Event.u.a.uXpos, pEvent->Event.u.a.uYpos,
445 pEvent->Event.u.a.uDefAction);
446 break;
447 }
448
449 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
450 {
451 LogFlowThisFunc(("HOST_DND_HG_EVT_LEAVE\n"));
452
453 rc = OnHgLeave();
454 break;
455 }
456
457 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
458 {
459 LogFlowThisFunc(("HOST_DND_HG_EVT_DROPPED\n"));
460
461 rc = OnHgDrop();
462 break;
463 }
464
465 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
466 {
467 LogFlowThisFunc(("HOST_DND_HG_SND_DATA\n"));
468
469 rc = OnHgDataReceived(pEvent->Event.u.b.pvData,
470 pEvent->Event.u.b.cbData);
471 break;
472 }
473
474 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
475 {
476 LogFlowThisFunc(("HOST_DND_HG_EVT_CANCEL\n"));
477
478 rc = OnHgCancel();
479 break;
480 }
481
482 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
483 {
484 LogFlowThisFunc(("HOST_DND_GH_REQ_PENDING\n"));
485#ifdef VBOX_WITH_DRAG_AND_DROP_GH
486 if ( mMode == Unknown
487 /* There can be more than one HOST_DND_GH_REQ_PENDING
488 * messages coming in. */
489 || mMode == GH)
490 {
491 mMode = GH;
492 rc = OnGhIsDnDPending(pEvent->Event.uScreenId);
493 }
494 else
495 rc = VERR_WRONG_ORDER;
496#else
497 rc = VERR_NOT_SUPPORTED;
498#endif
499 break;
500 }
501
502 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
503 {
504 LogFlowThisFunc(("HOST_DND_GH_EVT_DROPPED\n"));
505#ifdef VBOX_WITH_DRAG_AND_DROP_GH
506 if (mMode == GH)
507 {
508 rc = OnGhDropped(pEvent->Event.pszFormats,
509 pEvent->Event.cbFormats,
510 pEvent->Event.u.a.uDefAction);
511 }
512 else
513 rc = VERR_WRONG_ORDER;
514#else
515 rc = VERR_NOT_SUPPORTED;
516#endif
517 break;
518 }
519
520 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
521 {
522 LogFlowThisFunc(("GUEST_DND_GH_EVT_ERROR\n"));
523#ifdef VBOX_WITH_DRAG_AND_DROP_GH
524 reset();
525 rc = VINF_SUCCESS; /** @todo GUEST_DND_GH_EVT_ERROR */
526#else
527 rc = VERR_NOT_SUPPORTED;
528#endif
529 break;
530 }
531
532 case DragAndDropSvc::HOST_DND_GH_RECV_DIR:
533 {
534 LogFlowThisFunc(("HOST_DND_GH_RECV_DIR\n"));
535#ifdef VBOX_WITH_DRAG_AND_DROP_GH
536 if (mMode == GH)
537 {
538 rc = OnGhSendDir(pEvent->Event.pszFormats,
539 pEvent->Event.cbFormats,
540 pEvent->Event.u.a.uDefAction);
541 }
542 else
543 rc = VERR_WRONG_ORDER;
544#else
545 rc = VERR_NOT_SUPPORTED;
546#endif
547 break;
548 }
549
550 case DragAndDropSvc::HOST_DND_GH_RECV_FILE:
551 {
552 LogFlowThisFunc(("HOST_DND_GH_RECV_FILE\n"));
553#ifdef VBOX_WITH_DRAG_AND_DROP_GH
554 if (mMode == GH)
555 {
556 rc = OnGhSendFile(pEvent->Event.pszFormats,
557 pEvent->Event.cbFormats,
558 pEvent->Event.u.a.uDefAction);
559 }
560 else
561 rc = VERR_WRONG_ORDER;
562#else
563 rc = VERR_NOT_SUPPORTED;
564#endif
565 break;
566 }
567
568 default:
569 rc = VERR_NOT_SUPPORTED;
570 break;
571 }
572
573 /* Some messages require cleanup. */
574 switch (pEvent->Event.uType)
575 {
576 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
577 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
578 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
579#ifdef VBOX_WITH_DRAG_AND_DROP_GH
580 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
581#endif
582 {
583 if (pEvent->Event.pszFormats)
584 RTMemFree(pEvent->Event.pszFormats);
585 break;
586 }
587
588 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
589 {
590 if (pEvent->Event.pszFormats)
591 RTMemFree(pEvent->Event.pszFormats);
592 if (pEvent->Event.u.b.pvData)
593 RTMemFree(pEvent->Event.u.b.pvData);
594 break;
595 }
596
597 default:
598 /* Ignore. */
599 break;
600 }
601
602 LogFlowThisFunc(("Processing event %RU32 resulted in rc=%Rrc\n",
603 pEvent->Event.uType, rc));
604 if (pEvent)
605 RTMemFree(pEvent);
606 return 0;
607 }
608
609 default:
610 break;
611 }
612
613 return DefWindowProc(hWnd, uMsg, wParam, lParam);
614}
615
616#ifdef VBOX_WITH_DRAG_AND_DROP_GH
617/**
618 * Registers this proxy window as a local drop target.
619 *
620 * @return IPRT status code.
621 */
622int VBoxDnDWnd::RegisterAsDropTarget(void)
623{
624 if (pDropTarget) /* Already registered as drop target? */
625 return VINF_SUCCESS;
626
627 int rc;
628 try
629 {
630 pDropTarget = new VBoxDnDDropTarget(this /* pParent */);
631 AssertPtr(pDropTarget);
632 HRESULT hr = CoLockObjectExternal(pDropTarget, TRUE /* fLock */,
633 FALSE /* fLastUnlockReleases */);
634 if (SUCCEEDED(hr))
635 hr = RegisterDragDrop(hWnd, pDropTarget);
636
637 if (FAILED(hr))
638 {
639 LogRel(("DnD: Creating drop target failed with hr=%Rhrc\n", hr));
640 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
641 }
642 else
643 {
644 rc = VINF_SUCCESS;
645 }
646 }
647 catch (std::bad_alloc)
648 {
649 rc = VERR_NO_MEMORY;
650 }
651
652 LogFlowFuncLeaveRC(rc);
653 return rc;
654}
655
656int VBoxDnDWnd::UnregisterAsDropTarget(void)
657{
658 LogFlowFuncEnter();
659
660 if (!pDropTarget) /* No drop target? Bail out. */
661 return VINF_SUCCESS;
662
663 HRESULT hr = RevokeDragDrop(hWnd);
664 if (SUCCEEDED(hr))
665 hr = CoLockObjectExternal(pDropTarget, FALSE /* fLock */,
666 TRUE /* fLastUnlockReleases */);
667 if (SUCCEEDED(hr))
668 {
669 ULONG cRefs = pDropTarget->Release();
670
671 Assert(cRefs == 0);
672 pDropTarget = NULL;
673 }
674
675 int rc = SUCCEEDED(hr)
676 ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fix this. */
677
678 LogFlowFuncLeaveRC(rc);
679 return rc;
680}
681#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
682
683int VBoxDnDWnd::OnCreate(void)
684{
685 int rc = VbglR3DnDConnect(&mClientID);
686 if (RT_FAILURE(rc))
687 {
688 LogFlowThisFunc(("Connection to host service failed, rc=%Rrc\n", rc));
689 return rc;
690 }
691
692 LogFlowThisFunc(("Client ID=%RU32, rc=%Rrc\n", mClientID, rc));
693 return rc;
694}
695
696void VBoxDnDWnd::OnDestroy(void)
697{
698 VbglR3DnDDisconnect(mClientID);
699 LogFlowThisFuncLeave();
700}
701
702int VBoxDnDWnd::OnHgEnter(const RTCList<RTCString> &lstFormats, uint32_t uAllActions)
703{
704#ifdef DEBUG
705 LogFlowThisFunc(("uActions=0x%x, lstFormats=%zu: ", uAllActions, lstFormats.size()));
706 for (size_t i = 0; i < lstFormats.size(); i++)
707 LogFlow(("'%s' ", lstFormats.at(i).c_str()));
708 LogFlow(("\n"));
709#endif
710
711 /* Save all allowed actions. */
712 this->uAllActions = uAllActions;
713
714 /*
715 * Install our allowed MIME types.
716 ** @todo See todo for m_sstrAllowedMimeTypes in GuestDnDImpl.cpp.
717 */
718 const RTCList<RTCString> lstAllowedMimeTypes = RTCList<RTCString>()
719 /* URI's */
720 << "text/uri-list"
721 /* Text */
722 << "text/plain;charset=utf-8"
723 << "UTF8_STRING"
724 << "text/plain"
725 << "COMPOUND_TEXT"
726 << "TEXT"
727 << "STRING"
728 /* OpenOffice formats */
729 << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
730 << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
731 this->lstAllowedFormats = lstAllowedMimeTypes;
732
733 /*
734 * Check MIME compatibility with this client.
735 */
736 LogFlowThisFunc(("Supported MIME types:\n"));
737 for (size_t i = 0; i < lstFormats.size(); i++)
738 {
739 bool fSupported = lstAllowedFormats.contains(lstFormats.at(i));
740 if (fSupported)
741 this->lstFormats.append(lstFormats.at(i));
742 LogFlowThisFunc(("\t%s: %RTbool\n", lstFormats.at(i).c_str(), fSupported));
743 }
744
745 /*
746 * Prepare the startup info for DoDragDrop().
747 */
748 int rc = VINF_SUCCESS;
749 try
750 {
751 /* Translate our drop actions into
752 * allowed Windows drop effects. */
753 startupInfo.dwOKEffects = DROPEFFECT_NONE;
754 if (uAllActions)
755 {
756 if (uAllActions & DND_COPY_ACTION)
757 startupInfo.dwOKEffects |= DROPEFFECT_COPY;
758 if (uAllActions & DND_MOVE_ACTION)
759 startupInfo.dwOKEffects |= DROPEFFECT_MOVE;
760 if (uAllActions & DND_LINK_ACTION)
761 startupInfo.dwOKEffects |= DROPEFFECT_LINK;
762 }
763
764 startupInfo.pDropSource = new VBoxDnDDropSource(this);
765 startupInfo.pDataObject = new VBoxDnDDataObject();
766 }
767 catch (std::bad_alloc)
768 {
769 rc = VERR_NO_MEMORY;
770 }
771
772 if (RT_SUCCESS(rc))
773 rc = makeFullscreen();
774
775 LogFlowFuncLeaveRC(rc);
776 return rc;
777}
778
779int VBoxDnDWnd::OnHgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t uAction)
780{
781 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, uAction=0x%x\n",
782 u32xPos, u32yPos, uAction));
783
784 int rc = mouseMove(u32xPos, u32yPos, MOUSEEVENTF_LEFTDOWN);
785
786 uint32_t uActionNotify = DND_IGNORE_ACTION;
787 if (RT_SUCCESS(rc))
788 rc = RTCritSectEnter(&mCritSect);
789 if (RT_SUCCESS(rc))
790 {
791 if ( (Dragging == mState)
792 && startupInfo.pDropSource)
793 uActionNotify = startupInfo.pDropSource->GetCurrentAction();
794
795 RTCritSectLeave(&mCritSect);
796 }
797
798 if (RT_SUCCESS(rc))
799 {
800 rc = VbglR3DnDHGAcknowledgeOperation(mClientID, uActionNotify);
801 if (RT_FAILURE(rc))
802 LogFlowThisFunc(("Acknowledging operation failed with rc=%Rrc\n", rc));
803 }
804
805 LogFlowThisFunc(("Returning uActionNotify=0x%x, rc=%Rrc\n", uActionNotify, rc));
806 return rc;
807}
808
809int VBoxDnDWnd::OnHgLeave(void)
810{
811 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", mMode, mState));
812 LogRel(("DnD: Drag'n drop operation aborted\n"));
813
814 reset();
815
816 int rc = VINF_SUCCESS;
817
818 /* Post ESC to our window to officially abort the
819 * drag'n drop operation. */
820 PostMessage(hWnd, WM_KEYDOWN, VK_ESCAPE, 0 /* lParam */);
821
822 LogFlowFuncLeaveRC(rc);
823 return rc;
824}
825
826int VBoxDnDWnd::OnHgDrop(void)
827{
828 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", mMode, mState));
829
830 int rc = VINF_SUCCESS;
831 if (mState == Dragging)
832 {
833 Assert(lstFormats.size() >= 1);
834
835 /** @todo What to do when multiple formats are available? */
836 mFormatRequested = lstFormats.at(0);
837
838 rc = RTCritSectEnter(&mCritSect);
839 if (RT_SUCCESS(rc))
840 {
841 if (startupInfo.pDataObject)
842 startupInfo.pDataObject->SetStatus(VBoxDnDDataObject::Dropping);
843 else
844 rc = VERR_NOT_FOUND;
845
846 RTCritSectLeave(&mCritSect);
847 }
848
849 if (RT_SUCCESS(rc))
850 {
851 LogRel(("DnD: Requesting data as '%s' ...\n", mFormatRequested.c_str()));
852 rc = VbglR3DnDHGRequestData(mClientID, mFormatRequested.c_str());
853 if (RT_FAILURE(rc))
854 LogFlowThisFunc(("Requesting data failed with rc=%Rrc\n", rc));
855 }
856 }
857
858 LogFlowFuncLeaveRC(rc);
859 return rc;
860}
861
862int VBoxDnDWnd::OnHgDataReceived(const void *pvData, uint32_t cbData)
863{
864 LogFlowThisFunc(("mState=%ld, pvData=%p, cbData=%RU32\n",
865 mState, pvData, cbData));
866
867 mState = Dropped;
868
869 int rc = VINF_SUCCESS;
870 if (pvData)
871 {
872 Assert(cbData);
873 rc = RTCritSectEnter(&mCritSect);
874 if (RT_SUCCESS(rc))
875 {
876 if (startupInfo.pDataObject)
877 rc = startupInfo.pDataObject->Signal(mFormatRequested, pvData, cbData);
878 else
879 rc = VERR_NOT_FOUND;
880
881 RTCritSectLeave(&mCritSect);
882 }
883 }
884
885 int rc2 = mouseRelease();
886 if (RT_SUCCESS(rc))
887 rc = rc2;
888
889 LogFlowFuncLeaveRC(rc);
890 return rc;
891}
892
893int VBoxDnDWnd::OnHgCancel(void)
894{
895 int rc = RTCritSectEnter(&mCritSect);
896 if (RT_SUCCESS(rc))
897 {
898 if (startupInfo.pDataObject)
899 startupInfo.pDataObject->Abort();
900
901 RTCritSectLeave(&mCritSect);
902 }
903
904 int rc2 = mouseRelease();
905 if (RT_SUCCESS(rc))
906 rc = rc2;
907
908 reset();
909
910 return rc;
911}
912
913#ifdef VBOX_WITH_DRAG_AND_DROP_GH
914int VBoxDnDWnd::OnGhIsDnDPending(uint32_t uScreenID)
915{
916 LogFlowThisFunc(("mMode=%ld, mState=%ld, uScreenID=%RU32\n",
917 mMode, mState, uScreenID));
918
919 if (mState == Uninitialized)
920 reset();
921
922 int rc;
923 if (mState == Initialized)
924 {
925 rc = makeFullscreen();
926 if (RT_SUCCESS(rc))
927 {
928 /*
929 * We have to release the left mouse button to
930 * get into our (invisible) proxy window.
931 */
932 mouseRelease();
933
934 /*
935 * Even if we just released the left mouse button
936 * we're still in the dragging state to handle our
937 * own drop target (for the host).
938 */
939 mState = Dragging;
940 }
941 }
942 else
943 rc = VINF_SUCCESS;
944
945 /**
946 * Some notes regarding guest cursor movement:
947 * - The host only sends an HOST_DND_GH_REQ_PENDING message to the guest
948 * if the mouse cursor is outside the VM's window.
949 * - The guest does not know anything about the host's cursor
950 * position / state due to security reasons.
951 * - The guest *only* knows that the host currently is asking whether a
952 * guest DnD operation is in progress.
953 */
954
955 if ( RT_SUCCESS(rc)
956 && (mState == Dragging))
957 {
958 /** @todo Put this block into a function! */
959 POINT p;
960 GetCursorPos(&p);
961 ClientToScreen(hWnd, &p);
962#ifdef DEBUG_andy
963 LogFlowThisFunc(("Client to screen curX=%ld, curY=%ld\n", p.x, p.y));
964#endif
965
966 /** @todo Multi-monitor setups? */
967 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
968 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
969
970 static LONG px = p.x;
971 if (px <= 0)
972 px = 1;
973 static LONG py = p.y;
974 if (py <= 0)
975 py = 1;
976
977 rc = mouseMove(px, py, 0 /* dwMouseInputFlags */);
978 }
979
980 if (RT_SUCCESS(rc))
981 {
982 uint32_t uDefAction = DND_IGNORE_ACTION;
983
984 AssertPtr(pDropTarget);
985 RTCString strFormats = pDropTarget->Formats();
986 if (!strFormats.isEmpty())
987 {
988 uDefAction = DND_COPY_ACTION;
989 uAllActions = uDefAction;
990
991 LogFlowFunc(("Acknowledging pDropTarget=0x%p, uDefAction=0x%x, uAllActions=0x%x, strFormats=%s\n",
992 pDropTarget, uDefAction, uAllActions, strFormats.c_str()));
993 rc = VbglR3DnDGHAcknowledgePending(mClientID,
994 uDefAction, uAllActions, strFormats.c_str());
995 }
996 else
997 LogFlowFunc(("No format data available yet\n"));
998 }
999
1000 LogFlowFuncLeaveRC(rc);
1001 return rc;
1002}
1003
1004int VBoxDnDWnd::OnGhDropped(const char *pszFormat, uint32_t cbFormats,
1005 uint32_t uDefAction)
1006{
1007 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1008 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1009
1010 LogFlowThisFunc(("mMode=%ld, mState=%ld, pDropTarget=0x%p, pszFormat=%s, uDefAction=0x%x\n",
1011 mMode, mState, pDropTarget, pszFormat, uDefAction));
1012
1013 int rc;
1014 if (mState == Dragging)
1015 {
1016 AssertPtr(pDropTarget);
1017 rc = pDropTarget->WaitForDrop(30 * 1000 /* Timeout in ms */);
1018 if (RT_SUCCESS(rc))
1019 {
1020 /** @todo Respect uDefAction. */
1021 void *pvData = pDropTarget->DataMutableRaw();
1022 AssertPtr(pvData);
1023 uint32_t cbData = pDropTarget->DataSize();
1024 Assert(cbData);
1025
1026 rc = VbglR3DnDGHSendData(mClientID, pszFormat, pvData, cbData);
1027 LogFlowFunc(("Sent pvData=0x%p, cbData=%RU32, rc=%Rrc\n",
1028 pvData, cbData, rc));
1029 }
1030
1031 reset();
1032 }
1033 else
1034 rc = VERR_WRONG_ORDER;
1035
1036 LogFlowFuncLeaveRC(rc);
1037 return rc;
1038}
1039
1040int VBoxDnDWnd::OnGhSendDir(const char *pszFormats, uint32_t cbFormats,
1041 uint32_t uDefAction)
1042{
1043 int rc = 0;
1044 LogFlowFuncLeaveRC(rc);
1045 return rc;
1046}
1047
1048int VBoxDnDWnd::OnGhSendFile(const char *pszFormats, uint32_t cbFormats,
1049 uint32_t uDefAction)
1050{
1051 int rc = 0;
1052 LogFlowFuncLeaveRC(rc);
1053 return rc;
1054}
1055#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1056
1057int VBoxDnDWnd::ProcessEvent(PVBOXDNDEVENT pEvent)
1058{
1059 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1060
1061 PostMessage(hWnd, WM_VBOXTRAY_DND_MESSAGE,
1062 0 /* wParm */, (LPARAM)pEvent /* lParm */);
1063
1064 return VINF_SUCCESS;
1065}
1066
1067int VBoxDnDWnd::hide(void)
1068{
1069#ifdef DEBUG_andy
1070 LogFlowFunc(("\n"));
1071#endif
1072 ShowWindow(hWnd, SW_HIDE);
1073
1074 return VINF_SUCCESS;
1075}
1076
1077int VBoxDnDWnd::makeFullscreen(void)
1078{
1079 int rc = VINF_SUCCESS;
1080
1081 RECT r;
1082 RT_ZERO(r);
1083
1084 BOOL fRc;
1085 HDC hDC = GetDC(NULL /* Entire screen */);
1086 if (hDC)
1087 {
1088 fRc = s_pfnEnumDisplayMonitors
1089 /* EnumDisplayMonitors is not available on NT4. */
1090 ? s_pfnEnumDisplayMonitors(hDC, NULL, VBoxDnDWnd::MonitorEnumProc, (LPARAM)&r):
1091 FALSE;
1092
1093 if (!fRc)
1094 rc = VERR_NOT_FOUND;
1095 ReleaseDC(NULL, hDC);
1096 }
1097 else
1098 rc = VERR_ACCESS_DENIED;
1099
1100 if (RT_FAILURE(rc))
1101 {
1102 /* If multi-monitor enumeration failed above, try getting at least the
1103 * primary monitor as a fallback. */
1104 r.left = 0;
1105 r.top = 0;
1106 r.right = GetSystemMetrics(SM_CXSCREEN);
1107 r.bottom = GetSystemMetrics(SM_CYSCREEN);
1108 rc = VINF_SUCCESS;
1109 }
1110
1111 if (RT_SUCCESS(rc))
1112 {
1113 LONG lStyle = GetWindowLong(hWnd, GWL_STYLE);
1114 SetWindowLong(hWnd, GWL_STYLE,
1115 lStyle & ~(WS_CAPTION | WS_THICKFRAME));
1116 LONG lExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
1117 SetWindowLong(hWnd, GWL_EXSTYLE,
1118 lExStyle & ~( WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE
1119 | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1120
1121 fRc = SetWindowPos(hWnd, HWND_TOPMOST,
1122 r.left,
1123 r.top,
1124 r.right - r.left,
1125 r.bottom - r.top,
1126#ifdef VBOX_DND_DEBUG_WND
1127 SWP_SHOWWINDOW | SWP_FRAMECHANGED);
1128#else
1129 SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
1130#endif
1131 if (fRc)
1132 {
1133 LogFlowFunc(("Virtual screen is %ld,%ld,%ld,%ld (%ld x %ld)\n",
1134 r.left, r.top, r.right, r.bottom,
1135 r.right - r.left, r.bottom - r.top));
1136 }
1137 else
1138 {
1139 DWORD dwErr = GetLastError();
1140 LogRel(("DnD: Failed to set proxy window position, rc=%Rrc\n",
1141 RTErrConvertFromWin32(dwErr)));
1142 }
1143 }
1144 else
1145 LogRel(("DnD: Failed to determine virtual screen size, rc=%Rrc\n", rc));
1146
1147 LogFlowFuncLeaveRC(rc);
1148 return rc;
1149}
1150
1151int VBoxDnDWnd::mouseMove(int x, int y, DWORD dwMouseInputFlags)
1152{
1153 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1154 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1155
1156 INPUT Input[1] = { 0 };
1157 Input[0].type = INPUT_MOUSE;
1158 Input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
1159 | dwMouseInputFlags;
1160 Input[0].mi.dx = x * (65535 / iScreenX);
1161 Input[0].mi.dy = y * (65535 / iScreenY);
1162
1163 int rc;
1164 if (s_pfnSendInput(1 /* Number of inputs */,
1165 Input, sizeof(INPUT)))
1166 {
1167#ifdef DEBUG_andy
1168 CURSORINFO ci;
1169 RT_ZERO(ci);
1170 ci.cbSize = sizeof(ci);
1171 BOOL fRc = GetCursorInfo(&ci);
1172 if (fRc)
1173 LogFlowThisFunc(("Cursor shown=%RTbool, cursor=0x%p, x=%d, y=%d\n",
1174 (ci.flags & CURSOR_SHOWING) ? true : false,
1175 ci.hCursor, ci.ptScreenPos.x, ci.ptScreenPos.y));
1176#endif
1177 rc = VINF_SUCCESS;
1178 }
1179 else
1180 {
1181 DWORD dwErr = GetLastError();
1182 rc = RTErrConvertFromWin32(dwErr);
1183 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1184 }
1185
1186 return rc;
1187}
1188
1189int VBoxDnDWnd::mouseRelease(void)
1190{
1191#ifdef DEBUG_andy
1192 LogFlowFunc(("\n"));
1193#endif
1194 int rc;
1195
1196 /* Release mouse button in the guest to start the "drop"
1197 * action at the current mouse cursor position. */
1198 INPUT Input[1] = { 0 };
1199 Input[0].type = INPUT_MOUSE;
1200 Input[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;
1201 if (!s_pfnSendInput(1, Input, sizeof(INPUT)))
1202 {
1203 DWORD dwErr = GetLastError();
1204 rc = RTErrConvertFromWin32(dwErr);
1205 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1206 }
1207 else
1208 rc = VINF_SUCCESS;
1209
1210 return rc;
1211}
1212
1213void VBoxDnDWnd::reset(void)
1214{
1215 LogFlowThisFunc(("Old mState=%ld\n", mState));
1216
1217 lstAllowedFormats.clear();
1218 lstFormats.clear();
1219 uAllActions = DND_IGNORE_ACTION;
1220
1221 mMode = Unknown;
1222 mState = Initialized;
1223}
1224
1225static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1226{
1227 LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
1228 AssertPtrReturn(pUserData, 0);
1229
1230 VBoxDnDWnd *pWnd = reinterpret_cast<VBoxDnDWnd *>(pUserData);
1231 if (pWnd)
1232 return pWnd->WndProc(hWnd, uMsg, wParam, lParam);
1233
1234 return 0;
1235}
1236
1237static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1238{
1239 /* Note: WM_NCCREATE is not the first ever message which arrives, but
1240 * early enough for us. */
1241 if (uMsg == WM_NCCREATE)
1242 {
1243 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
1244 AssertPtr(pCS);
1245 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
1246 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxDnDWndProcInstance);
1247
1248 return vboxDnDWndProcInstance(hWnd, uMsg, wParam, lParam);
1249 }
1250
1251 /* No window associated yet. */
1252 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1253}
1254
1255/**
1256 * Initializes drag'n drop.
1257 *
1258 * @return IPRT status code.
1259 * @param pEnv The DnD service's environment.
1260 * @param ppInstance The instance pointer which refer to this object.
1261 * @param pfStartThread Pointer to flag whether the DnD service can be started or not.
1262 */
1263int VBoxDnDInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
1264{
1265 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
1266 /** ppInstance not used here. */
1267 AssertPtrReturn(pfStartThread, VERR_INVALID_POINTER);
1268
1269 LogFlowFuncEnter();
1270
1271 *pfStartThread = false;
1272
1273 PVBOXDNDCONTEXT pCtx = &gCtx;
1274
1275 int rc;
1276 bool fSupportedOS = true;
1277
1278 s_pfnSendInput = (PFNSENDINPUT)
1279 RTLdrGetSystemSymbol("User32.dll", "SendInput");
1280 fSupportedOS = !RT_BOOL(s_pfnSendInput == NULL);
1281 s_pfnEnumDisplayMonitors = (PFNENUMDISPLAYMONITORS)
1282 RTLdrGetSystemSymbol("User32.dll", "EnumDisplayMonitors");
1283 /* g_pfnEnumDisplayMonitors is optional. */
1284
1285 if (!fSupportedOS)
1286 {
1287 LogRel(("DnD: Not supported Windows version, disabling drag'n drop support\n"));
1288 rc = VERR_NOT_SUPPORTED;
1289 }
1290 else
1291 rc = VINF_SUCCESS;
1292
1293 if (RT_SUCCESS(rc))
1294 {
1295 /* Create the proxy window. At the moment we
1296 * only support one window at a time. */
1297 VBoxDnDWnd *pWnd = NULL;
1298 try
1299 {
1300 pWnd = new VBoxDnDWnd();
1301 rc = pWnd->Initialize(pCtx);
1302
1303 /* Add proxy window to our proxy windows list. */
1304 if (RT_SUCCESS(rc))
1305 pCtx->lstWnd.append(pWnd);
1306 }
1307 catch (std::bad_alloc)
1308 {
1309 rc = VERR_NO_MEMORY;
1310 }
1311 }
1312
1313 if (RT_SUCCESS(rc))
1314 rc = RTSemEventCreate(&pCtx->hEvtQueueSem);
1315 if (RT_SUCCESS(rc))
1316 {
1317 /* Assign service environment to our context. */
1318 gCtx.pEnv = pEnv;
1319 }
1320
1321 if (RT_SUCCESS(rc))
1322 {
1323 *ppInstance = pCtx;
1324 *pfStartThread = true;
1325
1326 LogRel(("DnD: Drag'n drop service successfully started\n"));
1327 return VINF_SUCCESS;
1328 }
1329
1330 LogRel(("DnD: Initializing drag'n drop service failed with rc=%Rrc\n", rc));
1331 return rc;
1332}
1333
1334void VBoxDnDStop(const VBOXSERVICEENV *pEnv, void *pInstance)
1335{
1336 AssertPtrReturnVoid(pEnv);
1337 AssertPtrReturnVoid(pInstance);
1338
1339 LogFunc(("Stopping pInstance=%p\n", pInstance));
1340
1341 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1342 AssertPtr(pCtx);
1343
1344 /* Set shutdown indicator. */
1345 ASMAtomicWriteBool(&pCtx->fShutdown, true);
1346
1347 /** @todo Notify / wait for HGCM thread! */
1348}
1349
1350void VBoxDnDDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
1351{
1352 AssertPtr(pEnv);
1353 AssertPtr(pInstance);
1354
1355 LogFunc(("Destroying pInstance=%p\n", pInstance));
1356
1357 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1358 AssertPtr(pCtx);
1359
1360 int rc = VINF_SUCCESS;
1361
1362 /** @todo At the moment we only have one DnD proxy window. */
1363 Assert(pCtx->lstWnd.size() == 1);
1364 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1365 if (pWnd)
1366 delete pWnd;
1367
1368 if (pCtx->hEvtQueueSem != NIL_RTSEMEVENT)
1369 RTSemEventDestroy(pCtx->hEvtQueueSem);
1370
1371 LogFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
1372 pInstance, rc));
1373}
1374
1375unsigned __stdcall VBoxDnDThread(void *pInstance)
1376{
1377 LogFlowFunc(("pInstance=%p\n", pInstance));
1378
1379 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1380 AssertPtr(pCtx);
1381
1382 uint32_t uClientID;
1383 int rc = VbglR3DnDConnect(&uClientID);
1384 if (RT_FAILURE(rc))
1385 return rc;
1386
1387 /** @todo At the moment we only have one DnD proxy window. */
1388 Assert(pCtx->lstWnd.size() == 1);
1389 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1390 AssertPtr(pWnd);
1391
1392 /* Number of invalid messages skipped in a row. */
1393 int cMsgSkippedInvalid = 0;
1394
1395 do
1396 {
1397 PVBOXDNDEVENT pEvent = (PVBOXDNDEVENT)RTMemAlloc(sizeof(VBOXDNDEVENT));
1398 if (!pEvent)
1399 {
1400 rc = VERR_NO_MEMORY;
1401 break;
1402 }
1403 /* Note: pEvent will be free'd by the consumer later. */
1404
1405 rc = VbglR3DnDProcessNextMessage(uClientID, &pEvent->Event);
1406 LogFlowFunc(("VbglR3DnDProcessNextMessage returned uType=%RU32, rc=%Rrc\n",
1407 pEvent->Event.uType, rc));
1408
1409 if (ASMAtomicReadBool(&pCtx->fShutdown))
1410 break;
1411
1412 if (RT_SUCCESS(rc))
1413 {
1414 cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
1415
1416 LogFlowFunc(("Received new event, type=%RU32\n", pEvent->Event.uType));
1417
1418 int rc2 = pWnd->ProcessEvent(pEvent);
1419 if (RT_FAILURE(rc2))
1420 LogFlowFunc(("Processing event failed with rc=%Rrc\n", rc2));
1421 }
1422 else if (rc == VERR_CANCELLED)
1423 {
1424 int rc2 = pWnd->OnHgCancel();
1425 if (RT_FAILURE(rc2))
1426 LogFlowFunc(("Cancelling failed with rc=%Rrc\n", rc2));
1427 }
1428 else
1429 {
1430 LogFlowFunc(("Processing next message failed with rc=%Rrc\n", rc));
1431
1432 /* Old(er) hosts either are broken regarding DnD support or otherwise
1433 * don't support the stuff we do on the guest side, so make sure we
1434 * don't process invalid messages forever. */
1435 if (rc == VERR_INVALID_PARAMETER)
1436 cMsgSkippedInvalid++;
1437 if (cMsgSkippedInvalid > 3)
1438 {
1439 LogFlowFunc(("Too many invalid/skipped messages from host, exiting ...\n"));
1440 break;
1441 }
1442 }
1443
1444 if (ASMAtomicReadBool(&pCtx->fShutdown))
1445 break;
1446
1447 } while (true);
1448
1449 LogFlowFunc(("Shutting down ...\n"));
1450
1451 VbglR3DnDDisconnect(uClientID);
1452
1453 LogFlowFuncLeaveRC(rc);
1454 return rc;
1455}
1456
Note: See TracBrowser for help on using the repository browser.

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