VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/service.cpp@ 50529

Last change on this file since 50529 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: 22.8 KB
Line 
1/* $Id: service.cpp 50508 2014-02-19 15:45:58Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
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.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
18/** @page pg_svc_guest_control Guest Control HGCM Service
19 *
20 * This service acts as a proxy for handling and buffering host command requests
21 * and clients on the guest. It tries to be as transparent as possible to let
22 * the guest (client) and host side do their protocol handling as desired.
23 *
24 * The following terms are used:
25 * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
26 * which wants to control something on the guest.
27 * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
28 * new host commands to perform. There can be multiple clients connected
29 * to a service. A client is represented by its HGCM client ID.
30 * - Context ID: An (almost) unique ID automatically generated on the host (Main API)
31 * to not only distinguish clients but individual requests. Because
32 * the host does not know anything about connected clients it needs
33 * an indicator which it can refer to later. This context ID gets
34 * internally bound by the service to a client which actually processes
35 * the command in order to have a relationship between client<->context ID(s).
36 *
37 * The host can trigger commands which get buffered by the service (with full HGCM
38 * parameter info). As soon as a client connects (or is ready to do some new work)
39 * it gets a buffered host command to process it. This command then will be immediately
40 * removed from the command list. If there are ready clients but no new commands to be
41 * processed, these clients will be set into a deferred state (that is being blocked
42 * to return until a new command is available).
43 *
44 * If a client needs to inform the host that something happened, it can send a
45 * message to a low level HGCM callback registered in Main. This callback contains
46 * the actual data as well as the context ID to let the host do the next necessary
47 * steps for this context. This context ID makes it possible to wait for an event
48 * inside the host's Main API function (like starting a process on the guest and
49 * wait for getting its PID returned by the client) as well as cancelling blocking
50 * host calls in order the client terminated/crashed (HGCM detects disconnected
51 * clients and reports it to this service's callback).
52 */
53
54/******************************************************************************
55 * Header Files *
56 ******************************************************************************/
57#ifdef LOG_GROUP
58 #undef LOG_GROUP
59#endif
60#define LOG_GROUP LOG_GROUP_GUEST_DND
61
62#include "dndmanager.h"
63
64/******************************************************************************
65 * Service class declaration *
66 ******************************************************************************/
67
68/**
69 * Specialized drag & drop service class.
70 */
71class DragAndDropService: public HGCM::AbstractService<DragAndDropService>
72{
73public:
74 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
75 : HGCM::AbstractService<DragAndDropService>(pHelpers)
76 , m_pManager(0)
77 , m_cClients(0)
78 {}
79
80protected:
81 /* HGCM service implementation */
82 int init(VBOXHGCMSVCFNTABLE *pTable);
83 int uninit();
84 int clientConnect(uint32_t u32ClientID, void *pvClient);
85 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
86 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
87 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
88
89 static DECLCALLBACK(int) progressCallback(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser);
90 int hostMessage(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
91 void modeSet(uint32_t u32Mode);
92 inline uint32_t modeGet() { return m_u32Mode; };
93
94 DnDManager *m_pManager;
95
96 uint32_t m_cClients;
97 RTCList<HGCM::Client*> m_clientQueue;
98 uint32_t m_u32Mode;
99};
100
101/******************************************************************************
102 * Service class implementation *
103 ******************************************************************************/
104
105int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
106{
107 /* Register functions. */
108 pTable->pfnHostCall = svcHostCall;
109 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
110 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
111 pTable->pfnRegisterExtension = svcRegisterExtension;
112 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
113
114 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
115
116 return VINF_SUCCESS;
117}
118
119int DragAndDropService::uninit()
120{
121 delete m_pManager;
122
123 return VINF_SUCCESS;
124}
125
126int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
127{
128 LogFlowFunc(("New client (%ld) connected\n", u32ClientID));
129 if (m_cClients < UINT32_MAX)
130 m_cClients++;
131 else
132 AssertMsgFailed(("Maximum number of clients reached\n"));
133 return VINF_SUCCESS;
134}
135
136int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
137{
138 /* Remove all waiters with this clientId. */
139 for (size_t i = 0; i < m_clientQueue.size(); )
140 {
141 HGCM::Client *pClient = m_clientQueue.at(i);
142 if (pClient->clientId() == u32ClientID)
143 {
144 m_pHelpers->pfnCallComplete(pClient->handle(), VERR_INTERRUPTED);
145 m_clientQueue.removeAt(i);
146 delete pClient;
147 }
148 else
149 i++;
150 }
151
152 return VINF_SUCCESS;
153}
154
155void DragAndDropService::modeSet(uint32_t u32Mode)
156{
157 switch (u32Mode)
158 {
159 case VBOX_DRAG_AND_DROP_MODE_OFF:
160 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
161 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
162 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
163 m_u32Mode = u32Mode;
164 break;
165
166 default:
167 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
168 }
169}
170
171void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
172 void *pvClient, uint32_t u32Function,
173 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
174{
175 LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
176 u32ClientID, u32Function, cParms));
177
178 /* Check if we've the right mode set. */
179 bool fIgnoreRequest = true; /* Play safe. */
180 switch (u32Function)
181 {
182 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
183 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
184 {
185 fIgnoreRequest = false;
186 }
187 else
188 LogFlowFunc(("Drag'n drop disabled, ignoring request\n"));
189 break;
190 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
191 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
192 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
193 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
194 {
195 fIgnoreRequest = false;
196 }
197 else
198 LogFlowFunc(("Host -> guest DnD mode disabled, ignoring request\n"));
199 break;
200#ifdef VBOX_WITH_DRAG_AND_DROP_GH
201 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
202 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
203 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
204 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
205 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
206 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
207 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
208 {
209 fIgnoreRequest = false;
210 }
211 else
212 LogFlowFunc(("Guest -> host DnD mode disabled, ignoring request\n"));
213 break;
214#endif
215 default:
216 /* Reach through to DnD manager. */
217 fIgnoreRequest = false;
218 break;
219 }
220
221 int rc;
222 if (!fIgnoreRequest)
223 {
224 rc = VINF_SUCCESS;
225 switch (u32Function)
226 {
227 /* Note: Older VBox versions with enabled DnD guest->host support (< 4.4)
228 * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
229 * HOST_DND_GH_REQ_PENDING, which led this service returning
230 * VERR_INVALID_PARAMETER when the guest wanted to actually
231 * handle HOST_DND_GH_REQ_PENDING. */
232 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
233 {
234 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
235 if ( cParms != 3
236 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* message */
237 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* parameter count */
238 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* blocking */)
239 rc = VERR_INVALID_PARAMETER;
240 else
241 {
242 rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32, &paParms[1].u.uint32);
243 if ( RT_FAILURE(rc)
244 && paParms[2].u.uint32) /* Blocking? */
245 {
246 m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle, u32Function, cParms, paParms));
247 rc = VINF_HGCM_ASYNC_EXECUTE;
248 }
249 }
250 break;
251 }
252 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
253 {
254 LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
255 if ( cParms != 1
256 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
257 rc = VERR_INVALID_PARAMETER;
258 else
259 {
260 DragAndDropSvc::VBOXDNDCBHGACKOPDATA data;
261 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP;
262 paParms[0].getUInt32(&data.uAction);
263 if (m_pfnHostCallback)
264 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
265 // m_pHelpers->pfnCallComplete(callHandle, rc);
266 }
267 break;
268 }
269 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
270 {
271 LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
272 if ( cParms != 1
273 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
274 rc = VERR_INVALID_PARAMETER;
275 else
276 {
277 DragAndDropSvc::VBOXDNDCBHGREQDATADATA data;
278 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA;
279 uint32_t cTmp;
280 paParms[0].getPointer((void**)&data.pszFormat, &cTmp);
281 if (m_pfnHostCallback)
282 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
283 // m_pHelpers->pfnCallComplete(callHandle, rc);
284 // if (data.pszFormat)
285 // RTMemFree(data.pszFormat);
286 // if (data.pszTmpPath)
287 // RTMemFree(data.pszTmpPath);
288 }
289 break;
290 }
291#ifdef VBOX_WITH_DRAG_AND_DROP_GH
292 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
293 {
294 LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
295 if ( cParms != 3
296 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* defaction */
297 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* allactions */
298 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
299 rc = VERR_INVALID_PARAMETER;
300 else
301 {
302 DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA data;
303 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING;
304 paParms[0].getUInt32(&data.uDefAction);
305 paParms[1].getUInt32(&data.uAllActions);
306 uint32_t cTmp;
307 paParms[2].getPointer((void**)&data.pszFormat, &cTmp);
308 if (m_pfnHostCallback)
309 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
310 }
311 break;
312 }
313 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
314 {
315 LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
316 if ( cParms != 2
317 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* data */
318 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
319 rc = VERR_INVALID_PARAMETER;
320 else
321 {
322 DragAndDropSvc::VBOXDNDCBSNDDATADATA data;
323 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA;
324 paParms[0].getPointer((void**)&data.pvData, &data.cbData);
325 paParms[1].getUInt32(&data.cbTotalSize);
326 if (m_pfnHostCallback)
327 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
328 }
329 break;
330 }
331 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
332 {
333 LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
334 if ( cParms != 3
335 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
336 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* path length */
337 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
338 rc = VERR_INVALID_PARAMETER;
339 else
340 {
341 DragAndDropSvc::VBOXDNDCBSNDDIRDATA data;
342 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR;
343 uint32_t cTmp;
344 paParms[0].getPointer((void**)&data.pszPath, &cTmp);
345 paParms[1].getUInt32(&data.cbPath);
346 paParms[2].getUInt32(&data.fMode);
347#ifdef DEBUG_andy
348 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n",
349 data.pszPath, data.cbPath, data.fMode));
350#endif
351 if (m_pfnHostCallback)
352 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
353 }
354 break;
355 }
356 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
357 {
358 LogFlowFunc(("GUEST_DND_GH_SND_FILE\n"));
359 if ( cParms != 5
360 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* file path */
361 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* file path length */
362 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* file data */
363 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* file data length */
364 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
365 rc = VERR_INVALID_PARAMETER;
366 else
367 {
368 DragAndDropSvc::VBOXDNDCBSNDFILEDATA data;
369 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE;
370 uint32_t cTmp;
371 paParms[0].getPointer((void**)&data.pszFilePath, &cTmp);
372 paParms[1].getUInt32(&data.cbFilePath);
373 paParms[2].getPointer((void**)&data.pvData, &data.cbData);
374 /* paParms[3] is cbData. */
375 paParms[4].getUInt32(&data.fMode);
376#ifdef DEBUG_andy
377 LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
378 data.pszFilePath, data.cbData, data.pvData, data.fMode));
379#endif
380 if (m_pfnHostCallback)
381 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
382 }
383 break;
384 }
385 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
386 {
387 LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
388 if ( cParms != 1
389 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
390 rc = VERR_INVALID_PARAMETER;
391 else
392 {
393 DragAndDropSvc::VBOXDNDCBEVTERRORDATA data;
394 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR;
395 uint32_t rcOp;
396 paParms[0].getUInt32(&rcOp);
397 data.rc = rcOp;
398 if (m_pfnHostCallback)
399 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
400 }
401 break;
402 }
403#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
404 default:
405 {
406 /* All other messages are handled by the DnD manager. */
407 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
408 break;
409 }
410 }
411 }
412 else
413 rc = VERR_NOT_SUPPORTED;
414
415 /* If async execute is requested, we didn't notify the guest about
416 * completion. The client is queued into the waiters list and will be
417 * notified as soon as a new event is available. */
418 if (rc != VINF_HGCM_ASYNC_EXECUTE)
419 m_pHelpers->pfnCallComplete(callHandle, rc);
420 LogFlowFunc(("Returning rc=%Rrc\n", rc));
421}
422
423int DragAndDropService::hostMessage(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
424{
425 int rc = VINF_SUCCESS;
426#if 0
427 HGCM::Message *pMessage = new HGCM::Message(u32Function, cParms, paParms);
428 m_hostQueue.push(pMessage);
429// bool fPush = true;
430 RTPrintf("client queue %u\n", m_clientQueue.size());
431 RTPrintf("host queue %u\n", m_hostQueue.size());
432 if (!m_clientQueue.empty())
433 {
434 pMessage = m_hostQueue.front();
435 HGCM::Client *pClient = m_clientQueue.front();
436 /* Check if this was a request for getting the next host
437 * message. If so, return the message id and the parameter
438 * count. The message itself has to be queued. */
439 if (pClient->message() == DragAndDropSvc::GUEST_GET_NEXT_HOST_MSG)
440 {
441 RTPrintf("client is waiting for next host msg\n");
442// rc = VERR_TOO_MUCH_DATA;
443 pClient->addMessageInfo(pMessage);
444 /* temp */
445// m_pHelpers->pfnCallComplete(pClient->handle(), rc);
446// m_clientQueue.pop();
447// delete pClient;
448 }
449 else
450 {
451 RTPrintf("client is waiting for host msg (%d)\n", u32Function);
452 /* There is a request for a host message pending. Check
453 * if this is the correct message and if so deliver. If
454 * not the message will be queued. */
455 rc = pClient->addMessage(pMessage);
456 m_hostQueue.pop();
457 delete pMessage;
458// if (RT_SUCCESS(rc))
459// fPush = false;
460 }
461 /* In any case mark this client request as done. */
462 m_pHelpers->pfnCallComplete(pClient->handle(), rc);
463 m_clientQueue.pop_front();
464 delete pClient;
465 }
466// if (fPush)
467// {
468// RTPrintf("push message\n");
469// m_hostQueue.push(pMessage);
470// }
471// else
472// delete pMessage;
473#endif
474
475 return rc;
476}
477
478int DragAndDropService::hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
479{
480 LogFlowFunc(("u32Function=%RU32, cParms=%RU32\n", u32Function, cParms));
481
482 int rc = VINF_SUCCESS;
483 if (u32Function == DragAndDropSvc::HOST_DND_SET_MODE)
484 {
485 if (cParms != 1)
486 rc = VERR_INVALID_PARAMETER;
487 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
488 rc = VERR_INVALID_PARAMETER;
489 else
490 modeSet(paParms[0].u.uint32);
491 }
492 else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
493 {
494 rc = m_pManager->addMessage(u32Function, cParms, paParms);
495 if ( RT_SUCCESS(rc)
496 && !m_clientQueue.isEmpty())
497 {
498 HGCM::Client *pClient = m_clientQueue.first();
499 AssertPtr(pClient);
500 /* Check if this was a request for getting the next host
501 * message. If so, return the message id and the parameter
502 * count. The message itself has to be queued. */
503 if (pClient->message() == DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG)
504 {
505 LogFlowFunc(("Client %RU32 is waiting for next host msg\n", pClient->clientId()));
506
507 uint32_t uMsg1;
508 uint32_t cParms1;
509 rc = m_pManager->nextMessageInfo(&uMsg1, &cParms1);
510 if (RT_SUCCESS(rc))
511 {
512 pClient->addMessageInfo(uMsg1, cParms1);
513 m_pHelpers->pfnCallComplete(pClient->handle(), rc);
514 m_clientQueue.removeFirst();
515 delete pClient;
516 }
517 else
518 AssertMsgFailed(("Should not happen!"));
519 }
520 else
521 AssertMsgFailed(("Should not happen!"));
522 }
523// else
524// AssertMsgFailed(("Should not happen %Rrc!", rc));
525 }
526
527 LogFlowFunc(("rc=%Rrc\n", rc));
528 return rc;
529}
530
531DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser)
532{
533 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
534
535 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
536
537 if (pSelf->m_pfnHostCallback)
538 {
539 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uPercentage=%RU32, uState=%RU32, rc=%Rrc\n",
540 uPercentage, uState, rc));
541 DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA data;
542 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS;
543 data.uPercentage = RT_MIN(uPercentage, 100);
544 data.uState = uState;
545 data.rc = rc;
546
547 return pSelf->m_pfnHostCallback(pSelf->m_pvHostData, DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS, &data, sizeof(data));
548 }
549
550 return VINF_SUCCESS;
551}
552
553/**
554 * @copydoc VBOXHGCMSVCLOAD
555 */
556extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
557{
558 return DragAndDropService::svcLoad(pTable);
559}
560
Note: See TracBrowser for help on using the repository browser.

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