VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDImpl.cpp@ 51092

最後變更 在這個檔案從51092是 51092,由 vboxsync 提交於 11 年 前

6813 src-client/MachineDebuggerImpl.cpp + various formatting changes

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 48.3 KB
 
1/* $Id: GuestDnDImpl.cpp 51092 2014-04-16 17:57:25Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest Drag and Drop parts
4 */
5
6/*
7 * Copyright (C) 2011-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "GuestImpl.h"
19#include "AutoCaller.h"
20
21#ifdef VBOX_WITH_DRAG_AND_DROP
22# include "ConsoleImpl.h"
23# include "ProgressImpl.h"
24# include "GuestDnDImpl.h"
25
26# include <iprt/dir.h>
27# include <iprt/path.h>
28# include <iprt/stream.h>
29# include <iprt/semaphore.h>
30# include <iprt/cpp/utils.h>
31
32# include <VMMDev.h>
33
34# include <VBox/com/list.h>
35# include <VBox/GuestHost/DragAndDrop.h>
36# include <VBox/HostServices/DragAndDropSvc.h>
37
38# ifdef LOG_GROUP
39 # undef LOG_GROUP
40# endif
41# define LOG_GROUP LOG_GROUP_GUEST_DND
42# include <VBox/log.h>
43
44/* How does this work:
45 *
46 * Drag and Drop is handled over the internal HGCM service for the host <->
47 * guest communication. Beside that we need to map the Drag and Drop protocols
48 * of the various OS's we support to our internal channels, this is also highly
49 * communicative in both directions. Unfortunately HGCM isn't really designed
50 * for that. Next we have to foul some of the components. This includes to
51 * trick X11 on the guest side, but also Qt needs to be tricked on the host
52 * side a little bit.
53 *
54 * The following components are involved:
55 *
56 * 1. GUI: Uses the Qt classes for Drag and Drop and mainly forward the content
57 * of it to the Main IGuest interface (see UIDnDHandler.cpp).
58 * 2. Main: Public interface for doing Drag and Drop. Also manage the IProgress
59 * interfaces for blocking the caller by showing a progress dialog (see
60 * this file).
61 * 3. HGCM service: Handle all messages from the host to the guest at once and
62 * encapsulate the internal communication details (see dndmanager.cpp and
63 * friends).
64 * 4. Guest additions: Split into the platform neutral part (see
65 * VBoxGuestR3LibDragAndDrop.cpp) and the guest OS specific parts.
66 * Receive/send message from/to the HGCM service and does all guest specific
67 * operations. Currently only X11 is supported (see draganddrop.cpp within
68 * VBoxClient).
69 *
70 * Host -> Guest:
71 * 1. There are DnD Enter, Move, Leave events which are send exactly like this
72 * to the guest. The info includes the pos, mimetypes and allowed actions.
73 * The guest has to respond with an action it would accept, so the GUI could
74 * change the cursor.
75 * 2. On drop, first a drop event is send. If this is accepted a drop data
76 * event follows. This blocks the GUI and shows some progress indicator.
77 *
78 * Guest -> Host:
79 * 1. The GUI is asking the guest if a DnD event is pending when the user moves
80 * the cursor out of the view window. If so, this returns the mimetypes and
81 * allowed actions.
82 * (2. On every mouse move this is asked again, to make sure the DnD event is
83 * still valid.)
84 * 3. On drop the host request the data from the guest. This blocks the GUI and
85 * shows some progress indicator.
86 *
87 * Some hints:
88 * m_sstrAllowedMimeTypes here in this file defines the allowed mime-types.
89 * This is necessary because we need special handling for some of the
90 * mime-types. E.g. for URI lists we need to transfer the actual dirs and
91 * files. Text EOL may to be changed. Also unknown mime-types may need special
92 * handling as well, which may lead to undefined behavior in the host/guest, if
93 * not done.
94 *
95 * Dropping of a directory, means recursively transferring _all_ the content.
96 *
97 * Directories and files are placed into the user's temporary directory on the
98 * guest (e.g. /tmp/VirtualBox Dropped Files). We can't delete them after the
99 * DnD operation, because we didn't know what the DnD target does with it. E.g.
100 * it could just be opened in place. This could lead ofc to filling up the disk
101 * within the guest. To inform the user about this, a small app could be
102 * developed which scans this directory regularly and inform the user with a
103 * tray icon hint (and maybe the possibility to clean this up instantly). The
104 * same has to be done in the G->H direction when it is implemented.
105 *
106 * Of course only regularly files are supported. Symlinks are resolved and
107 * transfered as regularly files. First we don't know if the other side support
108 * symlinks at all and second they could point to somewhere in a directory tree
109 * which not exists on the other side.
110 *
111 * The code tries to preserve the file modes of the transfered dirs/files. This
112 * is useful (and maybe necessary) for two things:
113 * 1. If a file is executable, it should be also after the transfer, so the
114 * user can just execute it, without manually tweaking the modes first.
115 * 2. If a dir/file is not accessible by group/others in the host, it shouldn't
116 * be in the guest.
117 * In any case, the user mode is always set to rwx (so that we can access it
118 * ourself, in e.g. for a cleanup case after cancel).
119 *
120 * Cancel is supported in both directions and cleans up all previous steps
121 * (thats is: deleting already transfered dirs/files).
122 *
123 * In general I propose the following changes in the VBox HGCM infrastructure
124 * for the future:
125 * - Currently it isn't really possible to send messages to the guest from the
126 * host. The host informs the guest just that there is something, the guest
127 * than has to ask which message and depending on that send the appropriate
128 * message to the host, which is filled with the right data.
129 * - There is no generic interface for sending bigger memory blocks to/from the
130 * guest. This is now done here, but I guess was also necessary for e.g.
131 * guest execution. So something generic which brake this up into smaller
132 * blocks and send it would be nice (with all the error handling and such
133 * ofc).
134 * - I developed a "protocol" for the DnD communication here. So the host and
135 * the guest have always to match in the revision. This is ofc bad, because
136 * the additions could be outdated easily. So some generic protocol number
137 * support in HGCM for asking the host and the guest of the support version,
138 * would be nice. Ofc at least the host should be able to talk to the guest,
139 * even when the version is below the host one.
140 * All this stuff would be useful for the current services, but also for future
141 * onces.
142 *
143 * Todo:
144 * - Dragging out of the guest (partly done)
145 * - ESC doesn't really work (on Windows guests it's already implemented)
146 * - transfer of URIs (that is the files and patching of the data)
147 * - testing in a multi monitor setup
148 * ... in any case it seems a little bit difficult to handle from the Qt
149 * side. Maybe also a host specific implementation becomes necessary ...
150 * this would be really worst ofc.
151 * - Win guest support (maybe there have to be done a mapping between the
152 * official mime-types and Win Clipboard formats (see QWindowsMime, for an
153 * idea), for VBox internally only mime-types should be used)
154 * - EOL handling on text (should be shared with the clipboard code)
155 * - add configuration (GH, HG, Bidirectional, None), like for the clipboard
156 * - HG->GH and GH->HG-switch: Handle the case the user drags something out of
157 * the guest and than return to the source view (or another window in the
158 * multiple guest screen scenario).
159 * - add support for more mime-types (especially images, csv)
160 * - test unusual behavior:
161 * - DnD service crash in the guest during a DnD op (e.g. crash of VBoxClient or X11)
162 * - not expected order of the events between HGCM and the guest
163 * - Security considerations: We transfer a lot of memory between the guest and
164 * the host and even allow the creation of dirs/files. Maybe there should be
165 * limits introduced to preventing DOS attacks or filling up all the memory
166 * (both in the host and the guest).
167 * - test, test, test ...
168 */
169
170class DnDGuestResponse
171{
172
173public:
174
175 DnDGuestResponse(const ComObjPtr<Guest>& pGuest);
176
177 virtual ~DnDGuestResponse(void);
178
179public:
180
181 int notifyAboutGuestResponse(void);
182 int waitForGuestResponse(RTMSINTERVAL msTimeout = 500);
183
184 void setDefAction(uint32_t a) { m_defAction = a; }
185 uint32_t defAction(void) const { return m_defAction; }
186
187 void setAllActions(uint32_t a) { m_allActions = a; }
188 uint32_t allActions() const { return m_allActions; }
189
190 void setFormat(const Utf8Str &strFormat) { m_strFormat = strFormat; }
191 Utf8Str format(void) const { return m_strFormat; }
192
193 void setDropDir(const Utf8Str &strDropDir) { m_strDropDir = strDropDir; }
194 Utf8Str dropDir(void) const { return m_strDropDir; }
195
196 int dataAdd(const void *pvData, uint32_t cbData, uint32_t *pcbCurSize);
197 int dataSetStatus(size_t cbDataAdd, size_t cbDataTotal = 0);
198 void reset(void);
199 const void *data(void) { return m_pvData; }
200 size_t size(void) const { return m_cbData; }
201
202 int setProgress(unsigned uPercentage, uint32_t uState, int rcOp = VINF_SUCCESS);
203 HRESULT resetProgress(const ComObjPtr<Guest>& pParent);
204 HRESULT queryProgressTo(IProgress **ppProgress);
205
206 int writeToFile(const char *pszPath, size_t cbPath, void *pvData, size_t cbData, uint32_t fMode);
207
208public:
209
210 Utf8Str errorToString(const ComObjPtr<Guest>& pGuest, int guestRc);
211
212private:
213 RTSEMEVENT m_EventSem;
214 uint32_t m_defAction;
215 uint32_t m_allActions;
216 Utf8Str m_strFormat;
217
218 /** The actual MIME data.*/
219 void *m_pvData;
220 /** Size (in bytes) of MIME data. */
221 uint32_t m_cbData;
222
223 size_t m_cbDataCurrent;
224 size_t m_cbDataTotal;
225 /** Dropped files directory on the host. */
226 Utf8Str m_strDropDir;
227 /** The handle of the currently opened file being written to
228 * or read from. */
229 RTFILE m_hFile;
230 Utf8Str m_strFile;
231
232 ComObjPtr<Guest> m_parent;
233 ComObjPtr<Progress> m_progress;
234};
235
236/** @todo This class needs a major cleanup. Later. */
237class GuestDnDPrivate
238{
239private:
240
241 /** @todo Currently we only support one response. Maybe this needs to be extended at some time. */
242 GuestDnDPrivate(GuestDnD *q, const ComObjPtr<Guest>& pGuest)
243 : q_ptr(q)
244 , p(pGuest)
245 , m_pDnDResponse(new DnDGuestResponse(pGuest))
246 {}
247 virtual ~GuestDnDPrivate(void) { delete m_pDnDResponse; }
248
249 DnDGuestResponse *response(void) const { return m_pDnDResponse; }
250
251 HRESULT adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const;
252 int hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const;
253
254 /* Static helpers. */
255 static RTCString toFormatString(ComSafeArrayIn(IN_BSTR, formats));
256 static void toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats));
257
258 static DragAndDropAction_T toMainAction(uint32_t uAction);
259 static void toMainActions(uint32_t uActions, ComSafeArrayOut(DragAndDropAction_T, actions));
260 static uint32_t toHGCMAction(DragAndDropAction_T action);
261 static void toHGCMActions(DragAndDropAction_T inDefAction, uint32_t *pOutDefAction,
262 ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
263 uint32_t *pOutAllowedActions);
264
265 /* Private q and parent pointer */
266 GuestDnD *q_ptr;
267 ComObjPtr<Guest> p;
268
269 /* Private helper members. */
270 static const RTCList<RTCString> m_sstrAllowedMimeTypes;
271 DnDGuestResponse *m_pDnDResponse;
272
273 friend class GuestDnD;
274};
275
276/* What mime-types are supported by VirtualBox.
277 * Note: If you add something here, make sure you test it with all guest OS's!
278 ** @todo Make this MIME list configurable / extendable (by extra data?). Currently
279 * this is done hardcoded on every guest platform (POSIX/Windows).
280 */
281/* static */
282const RTCList<RTCString> GuestDnDPrivate::m_sstrAllowedMimeTypes = RTCList<RTCString>()
283 /* Uri's */
284 << "text/uri-list"
285 /* Text */
286 << "text/plain;charset=utf-8"
287 << "UTF8_STRING"
288 << "text/plain"
289 << "COMPOUND_TEXT"
290 << "TEXT"
291 << "STRING"
292 /* OpenOffice formates */
293 << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
294 << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
295
296DnDGuestResponse::DnDGuestResponse(const ComObjPtr<Guest>& pGuest)
297 : m_EventSem(NIL_RTSEMEVENT)
298 , m_defAction(0)
299 , m_allActions(0)
300 , m_pvData(0)
301 , m_cbData(0)
302 , m_cbDataCurrent(0)
303 , m_cbDataTotal(0)
304 , m_hFile(NIL_RTFILE)
305 , m_parent(pGuest)
306{
307 int rc = RTSemEventCreate(&m_EventSem);
308 AssertRC(rc);
309}
310
311DnDGuestResponse::~DnDGuestResponse(void)
312{
313 reset();
314
315 int rc = RTSemEventDestroy(m_EventSem);
316 AssertRC(rc);
317}
318
319int DnDGuestResponse::dataAdd(const void *pvData, uint32_t cbData,
320 uint32_t *pcbCurSize)
321{
322 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
323 AssertReturn(cbData, VERR_INVALID_PARAMETER);
324 /* pcbCurSize is optional. */
325
326 int rc = VINF_SUCCESS;
327
328 /** @todo Make reallocation scheme a bit smarter here. */
329 m_pvData = RTMemRealloc(m_pvData, m_cbData + cbData);
330 if (m_pvData)
331 {
332 memcpy(&static_cast<uint8_t*>(m_pvData)[m_cbData],
333 pvData, cbData);
334 m_cbData += cbData;
335
336 if (pcbCurSize)
337 *pcbCurSize = m_cbData;
338 }
339 else
340 rc = VERR_NO_MEMORY;
341
342 return rc;
343}
344
345/* static */
346Utf8Str DnDGuestResponse::errorToString(const ComObjPtr<Guest>& pGuest, int guestRc)
347{
348 Utf8Str strError;
349
350 switch (guestRc)
351 {
352 case VERR_ACCESS_DENIED:
353 strError += Utf8StrFmt(pGuest->tr("For one or more guest files or directories selected for transferring to the host your guest "
354 "user does not have the appropriate access rights for. Please make sure that all selected "
355 "elements can be accessed and that your guest user has the appropriate rights."));
356 break;
357
358 case VERR_NOT_FOUND:
359 /* Should not happen due to file locking on the guest, but anyway ... */
360 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were not"
361 "found on the guest anymore. This can be the case if the guest files were moved and/or"
362 "altered while the drag'n drop operation was in progress."));
363 break;
364
365 case VERR_SHARING_VIOLATION:
366 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were locked. "
367 "Please make sure that all selected elements can be accessed and that your guest user has "
368 "the appropriate rights."));
369 break;
370
371 default:
372 strError += Utf8StrFmt("Drag'n drop guest error (%Rrc)", guestRc);
373 break;
374 }
375
376 return strError;
377}
378
379int DnDGuestResponse::notifyAboutGuestResponse(void)
380{
381 return RTSemEventSignal(m_EventSem);
382}
383
384void DnDGuestResponse::reset(void)
385{
386 LogFlowThisFuncEnter();
387
388 m_defAction = 0;
389 m_allActions = 0;
390
391 m_strDropDir = "";
392 m_strFormat = "";
393
394 if (m_pvData)
395 {
396 RTMemFree(m_pvData);
397 m_pvData = NULL;
398 }
399 m_cbData = 0;
400 m_cbDataCurrent = 0;
401 m_cbDataTotal = 0;
402
403 if (m_hFile != NIL_RTFILE)
404 {
405 RTFileClose(m_hFile);
406 m_hFile = NIL_RTFILE;
407 }
408 m_strFile = "";
409}
410
411HRESULT DnDGuestResponse::resetProgress(const ComObjPtr<Guest>& pParent)
412{
413 m_progress.setNull();
414 HRESULT rc = m_progress.createObject();
415 if (SUCCEEDED(rc))
416 {
417 rc = m_progress->init(static_cast<IGuest*>(pParent),
418 Bstr(pParent->tr("Dropping data")).raw(),
419 TRUE);
420 }
421 return rc;
422}
423
424int DnDGuestResponse::setProgress(unsigned uPercentage,
425 uint32_t uState, int rcOp /* = VINF_SUCCESS */)
426{
427 LogFlowFunc(("uPercentage=%RU32, uState=%RU32, rcOp=%Rrc\n",
428 uPercentage, uState, rcOp));
429
430 int vrc = VINF_SUCCESS;
431 if (!m_progress.isNull())
432 {
433 BOOL fCompleted;
434 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
435 if (!fCompleted)
436 {
437 if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)
438 {
439 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR,
440 COM_IIDOF(IGuest),
441 m_parent->getComponentName(),
442 DnDGuestResponse::errorToString(m_parent, rcOp).c_str());
443 reset();
444 }
445 else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED)
446 {
447 hr = m_progress->Cancel();
448 if (SUCCEEDED(hr))
449 vrc = VERR_CANCELLED;
450
451 reset();
452 }
453 else /* uState == DragAndDropSvc::DND_PROGRESS_RUNNING */
454 {
455 hr = m_progress->SetCurrentOperationProgress(uPercentage);
456 AssertComRC(hr);
457 if ( uState == DragAndDropSvc::DND_PROGRESS_COMPLETE
458 || uPercentage >= 100)
459 hr = m_progress->i_notifyComplete(S_OK);
460 }
461 }
462 }
463
464 return vrc;
465}
466
467int DnDGuestResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
468{
469 if (cbDataTotal)
470 {
471#ifndef DEBUG_andy
472 AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size must not be smaller (%zu) than old value (%zu)\n",
473 cbDataTotal, m_cbDataTotal));
474#endif
475 LogFlowFunc(("Updating total data size from %zu to %zu\n", m_cbDataTotal, cbDataTotal));
476 m_cbDataTotal = cbDataTotal;
477 }
478 AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
479
480 m_cbDataCurrent += cbDataAdd;
481 unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
482
483 /** @todo Don't use anonymous enums (uint32_t). */
484 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
485 if (m_cbDataCurrent >= m_cbDataTotal)
486 uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
487
488#ifdef DEBUG_andy
489 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n",
490 m_cbDataCurrent, m_cbDataTotal, uStatus));
491#else
492 AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
493 ("More data transferred (%zu) than initially announced (%zu), cbDataAdd=%zu\n",
494 m_cbDataCurrent, m_cbDataTotal, cbDataAdd));
495#endif
496 int rc = setProgress(cPercentage, uStatus);
497
498 /** @todo For now we instantly confirm the cancel. Check if the
499 * guest should first clean up stuff itself and than really confirm
500 * the cancel request by an extra message. */
501 if (rc == VERR_CANCELLED)
502 rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
503
504 LogFlowFuncLeaveRC(rc);
505 return rc;
506}
507
508HRESULT DnDGuestResponse::queryProgressTo(IProgress **ppProgress)
509{
510 return m_progress.queryInterfaceTo(ppProgress);
511}
512
513int DnDGuestResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */)
514{
515 int rc = RTSemEventWait(m_EventSem, msTimeout);
516#ifdef DEBUG_andy
517 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
518#endif
519 return rc;
520}
521
522int DnDGuestResponse::writeToFile(const char *pszPath, size_t cbPath,
523 void *pvData, size_t cbData, uint32_t fMode)
524{
525 /** @todo Support locking more than one file at a time! We
526 * might want to have a table in DnDGuestImpl which
527 * keeps those file pointers around, or extend the
528 * actual protocol for explicit open calls.
529 *
530 * For now we only keep one file open at a time, so if
531 * a client does alternating writes to different files
532 * this function will close the old and re-open the new
533 * file on every call. */
534 int rc;
535 if ( m_hFile == NIL_RTFILE
536 || m_strFile != pszPath)
537 {
538 char *pszFile = RTPathJoinA(m_strDropDir.c_str(), pszPath);
539 if (pszFile)
540 {
541 RTFILE hFile;
542 /** @todo Respect fMode! */
543 rc = RTFileOpen(&hFile, pszFile,
544 RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE
545 | RTFILE_O_WRITE | RTFILE_O_APPEND);
546 if (RT_SUCCESS(rc))
547 {
548 LogFlowFunc(("Opening \"%s\" (fMode=0x%x) for writing ...\n",
549 pszFile, fMode));
550
551 m_hFile = hFile;
552 m_strFile = pszPath;
553 }
554
555 RTStrFree(pszFile);
556 }
557 else
558 rc = VERR_NO_MEMORY;
559 }
560 else
561 rc = VINF_SUCCESS;
562
563 if (RT_SUCCESS(rc))
564 {
565 rc = RTFileWrite(m_hFile, pvData, cbData,
566 NULL /* No partial writes */);
567
568 if (RT_SUCCESS(rc))
569 rc = dataSetStatus(cbData);
570 }
571
572 return rc;
573}
574
575///////////////////////////////////////////////////////////////////////////////
576
577HRESULT GuestDnDPrivate::adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const
578{
579 /* For multi-monitor support we need to add shift values to the coordinates
580 * (depending on the screen number). */
581 ComPtr<IDisplay> pDisplay;
582 HRESULT hr = p->mParent->COMGETTER(Display)(pDisplay.asOutParam());
583 if (FAILED(hr))
584 return hr;
585
586 ComPtr<IFramebuffer> pFramebuffer;
587 LONG xShift, yShift;
588 hr = pDisplay->GetFramebuffer(uScreenId, pFramebuffer.asOutParam(),
589 &xShift, &yShift);
590 if (FAILED(hr))
591 return hr;
592
593 *puX += xShift;
594 *puY += yShift;
595
596 return hr;
597}
598
599int GuestDnDPrivate::hostCall(uint32_t u32Function, uint32_t cParms,
600 PVBOXHGCMSVCPARM paParms) const
601{
602 VMMDev *pVMMDev = NULL;
603 {
604 /* Make sure mParent is valid, so set the read lock while using.
605 * Do not keep this lock while doing the actual call, because in the meanwhile
606 * another thread could request a write lock which would be a bad idea ... */
607 AutoReadLock alock(p COMMA_LOCKVAL_SRC_POS);
608
609 /* Forward the information to the VMM device. */
610 AssertPtr(p->mParent);
611 pVMMDev = p->mParent->getVMMDev();
612 }
613
614 if (!pVMMDev)
615 throw p->setError(VBOX_E_VM_ERROR,
616 p->tr("VMM device is not available (is the VM running?)"));
617
618 LogFlowFunc(("hgcmHostCall uMsg=%RU32, cParms=%RU32\n", u32Function, cParms));
619 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc",
620 u32Function,
621 cParms, paParms);
622 if (RT_FAILURE(rc))
623 {
624 LogFlowFunc(("hgcmHostCall failed with rc=%Rrc\n", rc));
625 throw p->setError(VBOX_E_IPRT_ERROR,
626 p->tr("hgcmHostCall failed (%Rrc)"), rc);
627 }
628
629 return rc;
630}
631
632/* static */
633RTCString GuestDnDPrivate::toFormatString(ComSafeArrayIn(IN_BSTR, formats))
634{
635 const RTCList<Utf8Str> formatList(ComSafeArrayInArg(formats));
636 RTCString strFormat;
637 for (size_t i = 0; i < formatList.size(); ++i)
638 {
639 const RTCString &f = formatList.at(i);
640 /* Only keep allowed format types. */
641 if (m_sstrAllowedMimeTypes.contains(f))
642 strFormat += f + "\r\n";
643 }
644 return strFormat;
645}
646
647/* static */
648void GuestDnDPrivate::toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats))
649{
650 RTCList<RTCString> list = strFormats.split("\r\n");
651 size_t i = 0;
652 while (i < list.size())
653 {
654 /* Only keep allowed format types. */
655 if (!m_sstrAllowedMimeTypes.contains(list.at(i)))
656 list.removeAt(i);
657 else
658 ++i;
659 }
660 /* Create a safe array out of the cleaned list. */
661 com::SafeArray<BSTR> sfaFormats(list.size());
662 for (i = 0; i < list.size(); ++i)
663 {
664 const RTCString &f = list.at(i);
665 if (m_sstrAllowedMimeTypes.contains(f))
666 {
667 Bstr bstr(f);
668 bstr.detachTo(&sfaFormats[i]);
669 }
670 }
671 sfaFormats.detachTo(ComSafeArrayOutArg(formats));
672}
673
674/* static */
675uint32_t GuestDnDPrivate::toHGCMAction(DragAndDropAction_T action)
676{
677 uint32_t a = DND_IGNORE_ACTION;
678 switch (action)
679 {
680 case DragAndDropAction_Copy: a = DND_COPY_ACTION; break;
681 case DragAndDropAction_Move: a = DND_MOVE_ACTION; break;
682 case DragAndDropAction_Link: /* For now it doesn't seems useful to allow a link
683 action between host & guest. Maybe later! */
684 case DragAndDropAction_Ignore: /* Ignored */ break;
685 default: AssertMsgFailed(("Action %u not recognized!\n", action)); break;
686 }
687 return a;
688}
689
690/* static */
691void GuestDnDPrivate::toHGCMActions(DragAndDropAction_T inDefAction,
692 uint32_t *pOutDefAction,
693 ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
694 uint32_t *pOutAllowedActions)
695{
696 const com::SafeArray<DragAndDropAction_T> sfaInActions(ComSafeArrayInArg(inAllowedActions));
697
698 /* Defaults */
699 *pOutDefAction = toHGCMAction(inDefAction);;
700 *pOutAllowedActions = DND_IGNORE_ACTION;
701
702 /* First convert the allowed actions to a bit array. */
703 for (size_t i = 0; i < sfaInActions.size(); ++i)
704 *pOutAllowedActions |= toHGCMAction(sfaInActions[i]);
705
706 /* Second check if the default action is a valid action and if not so try
707 * to find an allowed action. */
708 if (isDnDIgnoreAction(*pOutDefAction))
709 {
710 if (hasDnDCopyAction(*pOutAllowedActions))
711 *pOutDefAction = DND_COPY_ACTION;
712 else if (hasDnDMoveAction(*pOutAllowedActions))
713 *pOutDefAction = DND_MOVE_ACTION;
714 }
715}
716
717/* static */
718DragAndDropAction_T GuestDnDPrivate::toMainAction(uint32_t uAction)
719{
720 /* For now it doesn't seems useful to allow a
721 * link action between host & guest. Maybe later! */
722 return (isDnDCopyAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Copy :
723 isDnDMoveAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Move :
724 (DragAndDropAction_T)DragAndDropAction_Ignore);
725}
726
727/* static */
728void GuestDnDPrivate::toMainActions(uint32_t uActions,
729 ComSafeArrayOut(DragAndDropAction_T, actions))
730{
731 /* For now it doesn't seems useful to allow a
732 * link action between host & guest. Maybe later! */
733 RTCList<DragAndDropAction_T> list;
734 if (hasDnDCopyAction(uActions))
735 list.append(DragAndDropAction_Copy);
736 if (hasDnDMoveAction(uActions))
737 list.append(DragAndDropAction_Move);
738
739 com::SafeArray<DragAndDropAction_T> sfaActions(list.size());
740 for (size_t i = 0; i < list.size(); ++i)
741 sfaActions[i] = list.at(i);
742 sfaActions.detachTo(ComSafeArrayOutArg(actions));
743}
744
745GuestDnD::GuestDnD(const ComObjPtr<Guest>& pGuest)
746 : d_ptr(new GuestDnDPrivate(this, pGuest))
747{
748}
749
750GuestDnD::~GuestDnD(void)
751{
752 delete d_ptr;
753}
754
755HRESULT GuestDnD::dragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY,
756 DragAndDropAction_T defaultAction,
757 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
758 ComSafeArrayIn(IN_BSTR, formats),
759 DragAndDropAction_T *pResultAction)
760{
761 DPTR(GuestDnD);
762 const ComObjPtr<Guest> &p = d->p;
763
764 /* Default is ignoring */
765 *pResultAction = DragAndDropAction_Ignore;
766
767 /* Check & convert the drag & drop actions */
768 uint32_t uDefAction = 0;
769 uint32_t uAllowedActions = 0;
770 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
771 /* If there is no usable action, ignore this request. */
772 if (isDnDIgnoreAction(uDefAction))
773 return S_OK;
774
775 /* Make a flat data string out of the mime-type list. */
776 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
777 /* If there is no valid mime-type, ignore this request. */
778 if (strFormats.isEmpty())
779 return S_OK;
780
781 HRESULT hr = S_OK;
782
783 try
784 {
785 /* Adjust the coordinates in a multi-monitor setup. */
786 d->adjustCoords(uScreenId, &uX, &uY);
787
788 VBOXHGCMSVCPARM paParms[7];
789 int i = 0;
790 paParms[i++].setUInt32(uScreenId);
791 paParms[i++].setUInt32(uX);
792 paParms[i++].setUInt32(uY);
793 paParms[i++].setUInt32(uDefAction);
794 paParms[i++].setUInt32(uAllowedActions);
795 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
796 paParms[i++].setUInt32(strFormats.length() + 1);
797
798 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_ENTER,
799 i,
800 paParms);
801
802 DnDGuestResponse *pResp = d->response();
803 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
804 return S_OK;
805
806 /* Copy the response info */
807 *pResultAction = d->toMainAction(pResp->defAction());
808 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
809 }
810 catch (HRESULT hr2)
811 {
812 hr = hr2;
813 }
814
815 return hr;
816}
817
818HRESULT GuestDnD::dragHGMove(ULONG uScreenId, ULONG uX, ULONG uY,
819 DragAndDropAction_T defaultAction,
820 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
821 ComSafeArrayIn(IN_BSTR, formats),
822 DragAndDropAction_T *pResultAction)
823{
824 DPTR(GuestDnD);
825 const ComObjPtr<Guest> &p = d->p;
826
827 /* Default is ignoring */
828 *pResultAction = DragAndDropAction_Ignore;
829
830 /* Check & convert the drag & drop actions */
831 uint32_t uDefAction = 0;
832 uint32_t uAllowedActions = 0;
833 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
834 /* If there is no usable action, ignore this request. */
835 if (isDnDIgnoreAction(uDefAction))
836 return S_OK;
837
838 /* Make a flat data string out of the mime-type list. */
839 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
840 /* If there is no valid mime-type, ignore this request. */
841 if (strFormats.isEmpty())
842 return S_OK;
843
844 HRESULT hr = S_OK;
845
846 try
847 {
848 /* Adjust the coordinates in a multi-monitor setup. */
849 d->adjustCoords(uScreenId, &uX, &uY);
850
851 VBOXHGCMSVCPARM paParms[7];
852 int i = 0;
853 paParms[i++].setUInt32(uScreenId);
854 paParms[i++].setUInt32(uX);
855 paParms[i++].setUInt32(uY);
856 paParms[i++].setUInt32(uDefAction);
857 paParms[i++].setUInt32(uAllowedActions);
858 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
859 paParms[i++].setUInt32(strFormats.length() + 1);
860
861 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_MOVE,
862 i,
863 paParms);
864
865 DnDGuestResponse *pResp = d->response();
866 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
867 return S_OK;
868
869 /* Copy the response info */
870 *pResultAction = d->toMainAction(pResp->defAction());
871 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
872 }
873 catch (HRESULT hr2)
874 {
875 hr = hr2;
876 }
877
878 return hr;
879}
880
881HRESULT GuestDnD::dragHGLeave(ULONG uScreenId)
882{
883 DPTR(GuestDnD);
884 const ComObjPtr<Guest> &p = d->p;
885
886 HRESULT hr = S_OK;
887
888 try
889 {
890 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_LEAVE,
891 0,
892 NULL);
893
894 DnDGuestResponse *pResp = d->response();
895 pResp->waitForGuestResponse();
896 }
897 catch (HRESULT hr2)
898 {
899 hr = hr2;
900 }
901
902 return hr;
903}
904
905HRESULT GuestDnD::dragHGDrop(ULONG uScreenId, ULONG uX, ULONG uY,
906 DragAndDropAction_T defaultAction,
907 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
908 ComSafeArrayIn(IN_BSTR, formats),
909 BSTR *pstrFormat,
910 DragAndDropAction_T *pResultAction)
911{
912 DPTR(GuestDnD);
913 const ComObjPtr<Guest> &p = d->p;
914
915 /* Default is ignoring */
916 *pResultAction = DragAndDropAction_Ignore;
917
918 /* Check & convert the drag & drop actions */
919 uint32_t uDefAction = 0;
920 uint32_t uAllowedActions = 0;
921 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
922 /* If there is no usable action, ignore this request. */
923 if (isDnDIgnoreAction(uDefAction))
924 return S_OK;
925
926 /* Make a flat data string out of the mime-type list. */
927 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
928 /* If there is no valid mime-type, ignore this request. */
929 if (strFormats.isEmpty())
930 return S_OK;
931
932 HRESULT hr = S_OK;
933
934 try
935 {
936 /* Adjust the coordinates in a multi-monitor setup. */
937 d->adjustCoords(uScreenId, &uX, &uY);
938
939 VBOXHGCMSVCPARM paParms[7];
940 int i = 0;
941 paParms[i++].setUInt32(uScreenId);
942 paParms[i++].setUInt32(uX);
943 paParms[i++].setUInt32(uY);
944 paParms[i++].setUInt32(uDefAction);
945 paParms[i++].setUInt32(uAllowedActions);
946 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
947 paParms[i++].setUInt32(strFormats.length() + 1);
948
949 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_DROPPED,
950 i,
951 paParms);
952
953 DnDGuestResponse *pResp = d->response();
954 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
955 return S_OK;
956
957 /* Get the resulting action from the guest. */
958 *pResultAction = d->toMainAction(pResp->defAction());
959
960 LogFlowFunc(("resFormat=%s, resAction=%RU32\n",
961 pResp->format().c_str(), pResp->defAction()));
962
963 Bstr(pResp->format()).cloneTo(pstrFormat);
964 }
965 catch (HRESULT hr2)
966 {
967 hr = hr2;
968 }
969
970 return hr;
971}
972
973HRESULT GuestDnD::dragHGPutData(ULONG uScreenId, IN_BSTR bstrFormat,
974 ComSafeArrayIn(BYTE, data), IProgress **ppProgress)
975{
976 DPTR(GuestDnD);
977 const ComObjPtr<Guest> &p = d->p;
978
979 HRESULT hr = S_OK;
980
981 try
982 {
983 Utf8Str strFormat(bstrFormat);
984 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(data));
985
986 VBOXHGCMSVCPARM paParms[5];
987 int i = 0;
988 paParms[i++].setUInt32(uScreenId);
989 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
990 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
991 paParms[i++].setPointer((void*)sfaData.raw(), (uint32_t)sfaData.size());
992 paParms[i++].setUInt32((uint32_t)sfaData.size());
993
994 DnDGuestResponse *pResp = d->response();
995 /* Reset any old progress status. */
996 pResp->resetProgress(p);
997
998 /* Note: The actual data transfer of files/directoies is performed by the
999 * DnD host service. */
1000 d->hostCall(DragAndDropSvc::HOST_DND_HG_SND_DATA,
1001 i,
1002 paParms);
1003
1004 /* Query the progress object to the caller. */
1005 pResp->queryProgressTo(ppProgress);
1006 }
1007 catch (HRESULT hr2)
1008 {
1009 hr = hr2;
1010 }
1011
1012 return hr;
1013}
1014
1015#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1016HRESULT GuestDnD::dragGHPending(ULONG uScreenId,
1017 ComSafeArrayOut(BSTR, formats),
1018 ComSafeArrayOut(DragAndDropAction_T, allowedActions),
1019 DragAndDropAction_T *pDefaultAction)
1020{
1021 DPTR(GuestDnD);
1022 const ComObjPtr<Guest> &p = d->p;
1023
1024 /* Default is ignoring */
1025 *pDefaultAction = DragAndDropAction_Ignore;
1026
1027 HRESULT hr = S_OK;
1028
1029 try
1030 {
1031 VBOXHGCMSVCPARM paParms[1];
1032 int i = 0;
1033 paParms[i++].setUInt32(uScreenId);
1034
1035 d->hostCall(DragAndDropSvc::HOST_DND_GH_REQ_PENDING,
1036 i,
1037 paParms);
1038
1039 DnDGuestResponse *pResp = d->response();
1040 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
1041 return S_OK;
1042
1043 if (isDnDIgnoreAction(pResp->defAction()))
1044 return S_OK;
1045
1046 /* Fetch the default action to use. */
1047 *pDefaultAction = d->toMainAction(pResp->defAction());
1048 d->toFormatSafeArray(pResp->format(), ComSafeArrayOutArg(formats));
1049 d->toMainActions(pResp->allActions(), ComSafeArrayOutArg(allowedActions));
1050
1051 LogFlowFunc(("*pDefaultAction=0x%x\n", *pDefaultAction));
1052 }
1053 catch (HRESULT hr2)
1054 {
1055 hr = hr2;
1056 }
1057
1058 return hr;
1059}
1060
1061HRESULT GuestDnD::dragGHDropped(IN_BSTR bstrFormat, DragAndDropAction_T action,
1062 IProgress **ppProgress)
1063{
1064 DPTR(GuestDnD);
1065 const ComObjPtr<Guest> &p = d->p;
1066
1067 Utf8Str strFormat(bstrFormat);
1068 HRESULT hr = S_OK;
1069
1070 uint32_t uAction = d->toHGCMAction(action);
1071 /* If there is no usable action, ignore this request. */
1072 if (isDnDIgnoreAction(uAction))
1073 return S_OK;
1074
1075 const char *pcszFormat = strFormat.c_str();
1076 bool fNeedsDropDir = DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat));
1077 LogFlowFunc(("strFormat=%s, uAction=0x%x, fNeedsDropDir=%RTbool\n",
1078 pcszFormat, uAction, fNeedsDropDir));
1079
1080 DnDGuestResponse *pResp = d->response();
1081 AssertPtr(pResp);
1082
1083 /* Reset any old data. */
1084 pResp->reset();
1085 pResp->resetProgress(p);
1086
1087 /* Set the format we are going to retrieve to have it around
1088 * when retrieving the data later. */
1089 pResp->setFormat(strFormat);
1090
1091 if (fNeedsDropDir)
1092 {
1093 char szDropDir[RTPATH_MAX];
1094 int rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
1095 if (RT_FAILURE(rc))
1096 return p->setError(VBOX_E_IPRT_ERROR,
1097 p->tr("Unable to create the temporary drag'n drop directory \"%s\" (%Rrc)\n"),
1098 szDropDir, rc);
1099 LogFlowFunc(("Dropped files directory on the host is: %s\n", szDropDir));
1100
1101 pResp->setDropDir(szDropDir);
1102 }
1103
1104 try
1105 {
1106 VBOXHGCMSVCPARM paParms[3];
1107 int i = 0;
1108 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
1109 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
1110 paParms[i++].setUInt32(uAction);
1111
1112 d->hostCall(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED,
1113 i,
1114 paParms);
1115
1116 /* Query the progress object to the caller. */
1117 pResp->queryProgressTo(ppProgress);
1118 }
1119 catch (HRESULT hr2)
1120 {
1121 hr = hr2;
1122 }
1123
1124 return hr;
1125}
1126
1127HRESULT GuestDnD::dragGHGetData(ComSafeArrayOut(BYTE, data))
1128{
1129 DPTR(GuestDnD);
1130 const ComObjPtr<Guest> &p = d->p;
1131
1132 HRESULT hr = S_OK;
1133
1134 DnDGuestResponse *pResp = d->response();
1135 if (pResp)
1136 {
1137 com::SafeArray<BYTE> sfaData;
1138
1139 size_t cbData = pResp->size();
1140 if (cbData)
1141 {
1142 const void *pvData = pResp->data();
1143 AssertPtr(pvData);
1144
1145 Utf8Str strFormat = pResp->format();
1146 LogFlowFunc(("strFormat=%s, cbData=%zu, pvData=0x%p\n",
1147 strFormat.c_str(), cbData, pvData));
1148
1149 if (DnDMIMEHasFileURLs(strFormat.c_str(), strFormat.length()))
1150 {
1151 LogFlowFunc(("strDropDir=%s\n", pResp->dropDir().c_str()));
1152
1153 DnDURIList lstURI;
1154 int rc2 = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1155 if (RT_SUCCESS(rc2))
1156 {
1157 Utf8Str strURIs = lstURI.RootToString(pResp->dropDir());
1158 size_t cbURIs = strURIs.length();
1159 if (sfaData.resize(cbURIs + 1 /* Include termination */))
1160 memcpy(sfaData.raw(), strURIs.c_str(), cbURIs);
1161 else
1162 hr = E_OUTOFMEMORY;
1163 }
1164 else
1165 hr = VBOX_E_IPRT_ERROR;
1166
1167 LogFlowFunc(("Found %zu root URIs, rc=%Rrc\n", lstURI.RootCount(), rc2));
1168 }
1169 else
1170 {
1171 /* Copy the data into a safe array of bytes. */
1172 if (sfaData.resize(cbData))
1173 memcpy(sfaData.raw(), pvData, cbData);
1174 else
1175 hr = E_OUTOFMEMORY;
1176 }
1177 }
1178
1179 /* Detach in any case, regardless of data size. */
1180 sfaData.detachTo(ComSafeArrayOutArg(data));
1181
1182 /* Delete the data. */
1183 pResp->reset();
1184 }
1185 else
1186 hr = VBOX_E_INVALID_OBJECT_STATE;
1187
1188 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
1189 return hr;
1190}
1191
1192int GuestDnD::onGHSendData(DnDGuestResponse *pResp,
1193 const void *pvData, size_t cbData,
1194 size_t cbTotalSize)
1195{
1196 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1197 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1198 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1199 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
1200
1201 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
1202 if (RT_SUCCESS(rc))
1203 rc = pResp->dataSetStatus(cbData, cbTotalSize);
1204
1205 LogFlowFuncLeaveRC(rc);
1206 return rc;
1207}
1208
1209int GuestDnD::onGHSendDir(DnDGuestResponse *pResp,
1210 const char *pszPath, size_t cbPath,
1211 uint32_t fMode)
1212{
1213 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1214 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1215 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1216
1217 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1218 pszPath, cbPath, fMode));
1219
1220 int rc;
1221 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
1222 if (pszDir)
1223 {
1224 rc = RTDirCreateFullPath(pszDir, fMode);
1225 RTStrFree(pszDir);
1226 }
1227 else
1228 rc = VERR_NO_MEMORY;
1229
1230 if (RT_SUCCESS(rc))
1231 rc = pResp->dataSetStatus(cbPath);
1232
1233 LogFlowFuncLeaveRC(rc);
1234 return rc;
1235}
1236
1237int GuestDnD::onGHSendFile(DnDGuestResponse *pResp,
1238 const char *pszPath, size_t cbPath,
1239 void *pvData, size_t cbData, uint32_t fMode)
1240{
1241 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1242 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1243 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1244
1245 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1246 pszPath, cbPath, fMode));
1247
1248 int rc = pResp->writeToFile(pszPath, cbPath, pvData, cbData, fMode);
1249
1250 LogFlowFuncLeaveRC(rc);
1251 return rc;
1252}
1253#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1254
1255/* static */
1256DECLCALLBACK(int) GuestDnD::notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function,
1257 void *pvParms, uint32_t cbParms)
1258{
1259 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
1260 pvExtension, u32Function, pvParms, cbParms));
1261
1262 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest*>(pvExtension);
1263 if (!pGuest->m_pGuestDnD)
1264 return VINF_SUCCESS;
1265
1266 GuestDnD *pGuestDnD = pGuest->m_pGuestDnD;
1267 AssertPtr(pGuestDnD);
1268
1269 GuestDnDPrivate *d = static_cast<GuestDnDPrivate*>(pGuest->m_pGuestDnD->d_ptr);
1270 const ComObjPtr<Guest> &p = d->p;
1271
1272 DnDGuestResponse *pResp = d->response();
1273 if (pResp == NULL)
1274 return VERR_INVALID_PARAMETER;
1275
1276 int rc = VINF_SUCCESS;
1277 switch (u32Function)
1278 {
1279 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
1280 {
1281 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
1282 AssertPtr(pCBData);
1283 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
1284 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1285
1286 pResp->setDefAction(pCBData->uAction);
1287
1288 rc = pResp->notifyAboutGuestResponse();
1289 break;
1290 }
1291
1292 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
1293 {
1294 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
1295 AssertPtr(pCBData);
1296 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1297 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1298
1299 pResp->setFormat(pCBData->pszFormat);
1300
1301 rc = pResp->notifyAboutGuestResponse();
1302 break;
1303 }
1304
1305 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
1306 {
1307 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
1308 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
1309 AssertPtr(pCBData);
1310 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
1311 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1312
1313 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
1314 break;
1315 }
1316
1317#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1318 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
1319 {
1320 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
1321 reinterpret_cast <DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
1322 AssertPtr(pCBData);
1323 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
1324 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1325
1326 pResp->setFormat(pCBData->pszFormat);
1327 pResp->setDefAction(pCBData->uDefAction);
1328 pResp->setAllActions(pCBData->uAllActions);
1329
1330 rc = pResp->notifyAboutGuestResponse();
1331 break;
1332 }
1333
1334 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1335 {
1336 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1337 AssertPtr(pCBData);
1338 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1339 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1340
1341 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
1342 pCBData->cbTotalSize);
1343 break;
1344 }
1345
1346 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1347 {
1348 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1349 AssertPtr(pCBData);
1350 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1351 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1352
1353 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1354 break;
1355 }
1356
1357 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
1358 {
1359 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
1360 AssertPtr(pCBData);
1361 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
1362 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1363
1364 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
1365 pCBData->pvData, pCBData->cbData, pCBData->fMode);
1366 break;
1367 }
1368
1369 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1370 {
1371 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1372 AssertPtr(pCBData);
1373 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1374 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1375
1376 /* Cleanup. */
1377 pResp->reset();
1378 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1379 break;
1380 }
1381#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1382 default:
1383 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
1384 break;
1385 }
1386
1387 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1388 return rc;
1389}
1390#endif /* VBOX_WITH_DRAG_AND_DROP */
1391
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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