VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDropTarget.cpp@ 106945

Last change on this file since 106945 was 106411, checked in by vboxsync, 8 weeks ago

Additions/VBoxTray: Implemented ability for easier user-controllable logging (also via verbose levels), support for running in foreground mode (with a console window attached to) and selective starting of sub services to easier pinpoint errors in release builds. Cleaned up initialization / termination code a little. See command line help for new options. bugref:10763

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.5 KB
Line 
1/* $Id: VBoxDnDDropTarget.cpp 106411 2024-10-17 07:44:43Z vboxsync $ */
2/** @file
3 * VBoxDnDTarget.cpp - IDropTarget implementation.
4 */
5
6/*
7 * Copyright (C) 2014-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_GUEST_DND
29#include <VBox/log.h>
30
31#include <iprt/win/windows.h>
32#include <new> /* For bad_alloc. */
33#include <iprt/win/shlobj.h> /* For DROPFILES and friends. */
34
35#include "VBoxTray.h"
36#include "VBoxTrayInternal.h"
37#include "VBoxHelpers.h"
38#include "VBoxDnD.h"
39
40#include "VBox/GuestHost/DragAndDrop.h"
41#include "VBox/HostServices/DragAndDropSvc.h"
42
43#include <iprt/path.h>
44#include <iprt/utf16.h>
45#include <iprt/uri.h>
46
47
48VBoxDnDDropTarget::VBoxDnDDropTarget(VBoxDnDWnd *pParent)
49 : m_cRefs(1),
50 m_pWndParent(pParent),
51 m_dwCurEffect(0),
52 m_pvData(NULL),
53 m_cbData(0),
54 m_EvtDrop(NIL_RTSEMEVENT)
55{
56 int rc = RTSemEventCreate(&m_EvtDrop);
57 LogFlowFunc(("rc=%Rrc\n", rc)); NOREF(rc);
58}
59
60VBoxDnDDropTarget::~VBoxDnDDropTarget(void)
61{
62 reset();
63
64 int rc2 = RTSemEventDestroy(m_EvtDrop);
65 AssertRC(rc2);
66
67 LogFlowFunc(("rc=%Rrc, mRefCount=%RI32\n", rc2, m_cRefs));
68}
69
70/*
71 * IUnknown methods.
72 */
73
74STDMETHODIMP_(ULONG) VBoxDnDDropTarget::AddRef(void)
75{
76 return InterlockedIncrement(&m_cRefs);
77}
78
79STDMETHODIMP_(ULONG) VBoxDnDDropTarget::Release(void)
80{
81 LONG lCount = InterlockedDecrement(&m_cRefs);
82 if (lCount == 0)
83 {
84 delete this;
85 return 0;
86 }
87
88 return lCount;
89}
90
91STDMETHODIMP VBoxDnDDropTarget::QueryInterface(REFIID iid, void **ppvObject)
92{
93 AssertPtrReturn(ppvObject, E_INVALIDARG);
94
95 if ( iid == IID_IDropSource
96 || iid == IID_IUnknown)
97 {
98 AddRef();
99 *ppvObject = this;
100 return S_OK;
101 }
102
103 *ppvObject = 0;
104 return E_NOINTERFACE;
105}
106
107/**
108 * Static helper function to dump supported formats of a data object.
109 *
110 * @param pDataObject Pointer to data object to dump formats for.
111 */
112/* static */
113void VBoxDnDDropTarget::DumpFormats(IDataObject *pDataObject)
114{
115 AssertPtrReturnVoid(pDataObject);
116
117 /* Enumerate supported source formats. This shouldn't happen too often
118 * on day to day use, but still keep it in here. */
119 IEnumFORMATETC *pEnumFormats;
120 HRESULT hr2 = pDataObject->EnumFormatEtc(DATADIR_GET, &pEnumFormats);
121 if (SUCCEEDED(hr2))
122 {
123 VBoxTrayVerbose(1, "DnD: The following formats were offered to us:\n");
124
125 FORMATETC curFormatEtc;
126 while (pEnumFormats->Next(1, &curFormatEtc,
127 NULL /* pceltFetched */) == S_OK)
128 {
129 WCHAR wszCfName[128]; /* 128 chars should be enough, rest will be truncated. */
130 hr2 = GetClipboardFormatNameW(curFormatEtc.cfFormat, wszCfName,
131 sizeof(wszCfName) / sizeof(WCHAR));
132 VBoxTrayVerbose(1, "\tcfFormat=%RI16 (%s), tyMed=%RI32, dwAspect=%RI32, strCustomName=%ls, hr=%Rhrc\n",
133 curFormatEtc.cfFormat,
134 VBoxDnDDataObject::ClipboardFormatToString(curFormatEtc.cfFormat),
135 curFormatEtc.tymed,
136 curFormatEtc.dwAspect,
137 wszCfName, hr2);
138 }
139
140 pEnumFormats->Release();
141 }
142}
143
144/*
145 * IDropTarget methods.
146 */
147
148STDMETHODIMP VBoxDnDDropTarget::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
149{
150 RT_NOREF(pt);
151 AssertPtrReturn(pDataObject, E_INVALIDARG);
152 AssertPtrReturn(pdwEffect, E_INVALIDARG);
153
154 LogFlowFunc(("pDataObject=0x%p, grfKeyState=0x%x, x=%ld, y=%ld, dwEffect=%RU32\n",
155 pDataObject, grfKeyState, pt.x, pt.y, *pdwEffect));
156
157 reset();
158
159 /** @todo At the moment we only support one DnD format at a time. */
160
161#ifdef DEBUG
162 VBoxDnDDropTarget::DumpFormats(pDataObject);
163#endif
164
165 /* Try different formats.
166 * CF_HDROP is the most common one, so start with this. */
167 FORMATETC fmtEtc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
168 HRESULT hr = pDataObject->QueryGetData(&fmtEtc);
169 if (hr == S_OK)
170 {
171 m_strFormat = "text/uri-list";
172 }
173 else
174 {
175 LogFlowFunc(("CF_HDROP not wanted, hr=%Rhrc\n", hr));
176
177 /* So we couldn't retrieve the data in CF_HDROP format; try with
178 * CF_UNICODETEXT + CF_TEXT formats now. Rest stays the same. */
179 fmtEtc.cfFormat = CF_UNICODETEXT;
180 hr = pDataObject->QueryGetData(&fmtEtc);
181 if (hr == S_OK)
182 {
183 m_strFormat = "text/plain;charset=utf-8";
184 }
185 else
186 {
187 LogFlowFunc(("CF_UNICODETEXT not wanted, hr=%Rhrc\n", hr));
188
189 fmtEtc.cfFormat = CF_TEXT;
190 hr = pDataObject->QueryGetData(&fmtEtc);
191 if (hr == S_OK)
192 {
193 m_strFormat = "text/plain;charset=utf-8";
194 }
195 else
196 {
197 LogFlowFunc(("CF_TEXT not wanted, hr=%Rhrc\n", hr));
198 fmtEtc.cfFormat = 0; /* Set it to non-supported. */
199
200 /* Clean up. */
201 reset();
202 }
203 }
204 }
205
206 /* Did we find a format that we support? */
207 if (fmtEtc.cfFormat)
208 {
209 LogFlowFunc(("Found supported format %RI16 (%s)\n",
210 fmtEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(fmtEtc.cfFormat)));
211
212 /* Make a copy of the FORMATETC structure so that we later can
213 * use this for comparrison and stuff. */
214 /** @todo The DVTARGETDEVICE member only is a shallow copy for now! */
215 memcpy(&m_FormatEtc, &fmtEtc, sizeof(FORMATETC));
216
217 /* Which drop effect we're going to use? */
218 /* Note: pt is not used since we don't need to differentiate within our
219 * proxy window. */
220 *pdwEffect = VBoxDnDDropTarget::GetDropEffect(grfKeyState, *pdwEffect);
221 }
222 else
223 {
224 /* No or incompatible data -- so no drop effect required. */
225 *pdwEffect = DROPEFFECT_NONE;
226
227 switch (hr)
228 {
229 case ERROR_INVALID_FUNCTION:
230 {
231 VBoxTrayError("DnD: Drag and drop format is not supported by VBoxTray\n");
232 VBoxDnDDropTarget::DumpFormats(pDataObject);
233 break;
234 }
235
236 default:
237 break;
238 }
239 }
240
241 LogFlowFunc(("Returning mstrFormats=%s, cfFormat=%RI16, pdwEffect=%ld, hr=%Rhrc\n",
242 m_strFormat.c_str(), fmtEtc.cfFormat, *pdwEffect, hr));
243 return hr;
244}
245
246STDMETHODIMP VBoxDnDDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
247{
248 RT_NOREF(pt);
249 AssertPtrReturn(pdwEffect, E_INVALIDARG);
250
251#ifdef DEBUG_andy
252 LogFlowFunc(("cfFormat=%RI16, grfKeyState=0x%x, x=%ld, y=%ld\n",
253 m_FormatEtc.cfFormat, grfKeyState, pt.x, pt.y));
254#endif
255
256 if (m_FormatEtc.cfFormat)
257 {
258 /* Note: pt is not used since we don't need to differentiate within our
259 * proxy window. */
260 *pdwEffect = VBoxDnDDropTarget::GetDropEffect(grfKeyState, *pdwEffect);
261 }
262 else
263 {
264 *pdwEffect = DROPEFFECT_NONE;
265 }
266
267#ifdef DEBUG_andy
268 LogFlowFunc(("Returning *pdwEffect=%ld\n", *pdwEffect));
269#endif
270 return S_OK;
271}
272
273STDMETHODIMP VBoxDnDDropTarget::DragLeave(void)
274{
275#ifdef DEBUG_andy
276 LogFlowFunc(("cfFormat=%RI16\n", m_FormatEtc.cfFormat));
277#endif
278
279 if (m_pWndParent)
280 m_pWndParent->Hide();
281
282 return S_OK;
283}
284
285STDMETHODIMP VBoxDnDDropTarget::Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
286{
287 RT_NOREF(pt);
288 AssertPtrReturn(pDataObject, E_INVALIDARG);
289 AssertPtrReturn(pdwEffect, E_INVALIDARG);
290
291 LogFlowFunc(("mFormatEtc.cfFormat=%RI16 (%s), pDataObject=0x%p, grfKeyState=0x%x, x=%ld, y=%ld\n",
292 m_FormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(m_FormatEtc.cfFormat),
293 pDataObject, grfKeyState, pt.x, pt.y));
294
295 HRESULT hr = S_OK;
296
297 if (m_FormatEtc.cfFormat) /* Did we get a supported format yet? */
298 {
299 /* Make sure the data object's data format is still valid. */
300 hr = pDataObject->QueryGetData(&m_FormatEtc);
301 AssertMsg(SUCCEEDED(hr),
302 ("Data format changed to invalid between DragEnter() and Drop(), cfFormat=%RI16 (%s), hr=%Rhrc\n",
303 m_FormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(m_FormatEtc.cfFormat), hr));
304 }
305
306 int rc = VINF_SUCCESS;
307
308 if (SUCCEEDED(hr))
309 {
310 STGMEDIUM stgMed;
311 hr = pDataObject->GetData(&m_FormatEtc, &stgMed);
312 if (SUCCEEDED(hr))
313 {
314 /*
315 * First stage: Prepare the access to the storage medium.
316 * For now we only support HGLOBAL stuff.
317 */
318 PVOID pvData = NULL; /** @todo Put this in an own union? */
319
320 switch (m_FormatEtc.tymed)
321 {
322 case TYMED_HGLOBAL:
323 pvData = GlobalLock(stgMed.hGlobal);
324 if (!pvData)
325 {
326 LogFlowFunc(("Locking HGLOBAL storage failed with %Rrc\n",
327 RTErrConvertFromWin32(GetLastError())));
328 rc = VERR_INVALID_HANDLE;
329 hr = E_INVALIDARG; /* Set special hr for OLE. */
330 }
331 break;
332
333 default:
334 AssertMsgFailed(("Storage medium type %RI32 supported\n",
335 m_FormatEtc.tymed));
336 rc = VERR_NOT_SUPPORTED;
337 hr = DV_E_TYMED; /* Set special hr for OLE. */
338 break;
339 }
340
341 if (RT_SUCCESS(rc))
342 {
343 /*
344 * Second stage: Do the actual copying of the data object's data,
345 * based on the storage medium type.
346 */
347 switch (m_FormatEtc.cfFormat)
348 {
349 case CF_TEXT:
350 RT_FALL_THROUGH();
351 case CF_UNICODETEXT:
352 {
353 AssertPtr(pvData);
354 size_t cbSize = GlobalSize(pvData);
355
356 VBoxTrayVerbose(2, "DnD: Got %zu bytes of %s\n", cbSize,
357 m_FormatEtc.cfFormat == CF_TEXT
358 ? "ANSI text" : "Unicode text");
359 if (cbSize)
360 {
361 char *pszText = NULL;
362
363 rc = m_FormatEtc.cfFormat == CF_TEXT
364 /* ANSI codepage -> UTF-8 */
365 ? RTStrCurrentCPToUtf8(&pszText, (char *)pvData)
366 /* Unicode -> UTF-8 */
367 : RTUtf16ToUtf8((PCRTUTF16)pvData, &pszText);
368
369 if (RT_SUCCESS(rc))
370 {
371 AssertPtr(pszText);
372
373 size_t cbText = strlen(pszText) + 1; /* Include termination. */
374
375 m_pvData = RTMemDup((void *)pszText, cbText);
376 m_cbData = cbText;
377
378 RTStrFree(pszText);
379 pszText = NULL;
380 }
381 }
382
383 break;
384 }
385
386 case CF_HDROP:
387 {
388 AssertPtr(pvData);
389
390 /* Convert to a string list, separated by \r\n. */
391 DROPFILES *pDropFiles = (DROPFILES *)pvData;
392 AssertPtr(pDropFiles);
393
394 /** @todo Replace / merge the following code with VBoxShClWinDropFilesToStringList(). */
395
396 /* Do we need to do Unicode stuff? */
397 const bool fUnicode = RT_BOOL(pDropFiles->fWide);
398
399 /* Get the offset of the file list. */
400 Assert(pDropFiles->pFiles >= sizeof(DROPFILES));
401
402 /* Note: This is *not* pDropFiles->pFiles! DragQueryFile only
403 * will work with the plain storage medium pointer! */
404 HDROP hDrop = (HDROP)(pvData);
405
406 /* First, get the file count. */
407 /** @todo Does this work on Windows 2000 / NT4? */
408 char *pszFiles = NULL;
409 size_t cchFiles = 0;
410 UINT cFiles = DragQueryFile(hDrop, UINT32_MAX /* iFile */, NULL /* lpszFile */, 0 /* cchFile */);
411
412 VBoxTrayVerbose(1, "DnD: Got %RU16 file(s), fUnicode=%RTbool\n", cFiles, fUnicode);
413
414 for (UINT i = 0; i < cFiles; i++)
415 {
416 UINT cchFile = DragQueryFile(hDrop, i /* File index */, NULL /* Query size first */, 0 /* cchFile */);
417 Assert(cchFile);
418
419 if (RT_FAILURE(rc))
420 break;
421
422 char *pszFileUtf8 = NULL; /* UTF-8 version. */
423 UINT cchFileUtf8 = 0;
424 if (fUnicode)
425 {
426 /* Allocate enough space (including terminator). */
427 WCHAR *pwszFile = (WCHAR *)RTMemAlloc((cchFile + 1) * sizeof(WCHAR));
428 if (pwszFile)
429 {
430 const UINT cwcFileUtf16 = DragQueryFileW(hDrop, i /* File index */,
431 pwszFile, cchFile + 1 /* Include terminator */);
432
433 AssertMsg(cwcFileUtf16 == cchFile, ("cchFileUtf16 (%RU16) does not match cchFile (%RU16)\n",
434 cwcFileUtf16, cchFile));
435 RT_NOREF(cwcFileUtf16);
436
437 rc = RTUtf16ToUtf8(pwszFile, &pszFileUtf8);
438 if (RT_SUCCESS(rc))
439 {
440 cchFileUtf8 = (UINT)strlen(pszFileUtf8);
441 Assert(cchFileUtf8);
442 }
443
444 RTMemFree(pwszFile);
445 }
446 else
447 rc = VERR_NO_MEMORY;
448 }
449 else /* ANSI */
450 {
451 /* Allocate enough space (including terminator). */
452 pszFileUtf8 = (char *)RTMemAlloc((cchFile + 1) * sizeof(char));
453 if (pszFileUtf8)
454 {
455 cchFileUtf8 = DragQueryFileA(hDrop, i /* File index */,
456 pszFileUtf8, cchFile + 1 /* Include terminator */);
457
458 AssertMsg(cchFileUtf8 == cchFile, ("cchFileUtf8 (%RU16) does not match cchFile (%RU16)\n",
459 cchFileUtf8, cchFile));
460 }
461 else
462 rc = VERR_NO_MEMORY;
463 }
464
465 if (RT_SUCCESS(rc))
466 {
467 LogFlowFunc(("\tFile: %s (cchFile=%RU16)\n", pszFileUtf8, cchFileUtf8));
468
469 VBoxTrayVerbose(1, "DnD: Adding guest file '%s'\n", pszFileUtf8);
470
471 if (RT_SUCCESS(rc))
472 {
473 char *pszFileURI = RTUriFileCreate(pszFileUtf8);
474 if (pszFileURI)
475 {
476 const size_t cchFileURI = RTStrNLen(pszFileURI, RTPATH_MAX);
477 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileURI, cchFileURI);
478 if (RT_SUCCESS(rc))
479 cchFiles += cchFileURI;
480
481 RTStrFree(pszFileURI);
482 }
483 else
484 rc = VERR_NO_MEMORY;
485 }
486 }
487
488 if (RT_FAILURE(rc))
489 VBoxTrayError("DnD: Error handling file entry #%u, rc=%Rrc\n", i, rc);
490
491 RTStrFree(pszFileUtf8);
492
493 if (RT_SUCCESS(rc))
494 {
495 /* Add separation between filenames.
496 * Note: Also do this for the last element of the list. */
497 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, DND_PATH_SEPARATOR_STR, 2 /* Bytes */);
498 if (RT_SUCCESS(rc))
499 cchFiles += 2; /* Include \r\n */
500 }
501 }
502
503 if (RT_SUCCESS(rc))
504 {
505 cchFiles += 1; /* Add string termination. */
506
507 const size_t cbFiles = cchFiles * sizeof(char);
508
509 LogFlowFunc(("cFiles=%u, cchFiles=%zu, cbFiles=%zu, pszFiles=0x%p\n",
510 cFiles, cchFiles, cbFiles, pszFiles));
511
512 m_pvData = pszFiles;
513 m_cbData = cbFiles;
514 }
515 else
516 {
517 RTStrFree(pszFiles);
518 pszFiles = NULL;
519 }
520
521 LogFlowFunc(("Building CF_HDROP list rc=%Rrc, cFiles=%RU16, cchFiles=%RU32\n",
522 rc, cFiles, cchFiles));
523 break;
524 }
525
526 default:
527 /* Note: Should not happen due to the checks done in DragEnter(). */
528 AssertMsgFailed(("Format of type %RI16 (%s) not supported\n",
529 m_FormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(m_FormatEtc.cfFormat)));
530 hr = DV_E_CLIPFORMAT; /* Set special hr for OLE. */
531 break;
532 }
533
534 /*
535 * Third stage: Unlock + release access to the storage medium again.
536 */
537 switch (m_FormatEtc.tymed)
538 {
539 case TYMED_HGLOBAL:
540 GlobalUnlock(stgMed.hGlobal);
541 break;
542
543 default:
544 AssertMsgFailed(("Really should not happen -- see init stage!\n"));
545 break;
546 }
547 }
548
549 /* Release storage medium again. */
550 ReleaseStgMedium(&stgMed);
551
552 /* Signal waiters. */
553 m_rcDropped = rc;
554 RTSemEventSignal(m_EvtDrop);
555 }
556 }
557
558 if (RT_SUCCESS(rc))
559 {
560 /* Note: pt is not used since we don't need to differentiate within our
561 * proxy window. */
562 *pdwEffect = VBoxDnDDropTarget::GetDropEffect(grfKeyState, *pdwEffect);
563 }
564 else
565 *pdwEffect = DROPEFFECT_NONE;
566
567 if (m_pWndParent)
568 m_pWndParent->Hide();
569
570 LogFlowFunc(("Returning with hr=%Rhrc (%Rrc), mFormatEtc.cfFormat=%RI16 (%s), *pdwEffect=%RI32\n",
571 hr, rc, m_FormatEtc.cfFormat, VBoxDnDDataObject::ClipboardFormatToString(m_FormatEtc.cfFormat),
572 *pdwEffect));
573
574 return hr;
575}
576
577/**
578 * Static helper function to return a drop effect for a given key state and allowed effects.
579 *
580 * @returns Resolved drop effect.
581 * @param grfKeyState Key state to determine drop effect for.
582 * @param dwAllowedEffects Allowed drop effects to determine drop effect for.
583 */
584/* static */
585DWORD VBoxDnDDropTarget::GetDropEffect(DWORD grfKeyState, DWORD dwAllowedEffects)
586{
587 DWORD dwEffect = DROPEFFECT_NONE;
588
589 if(grfKeyState & MK_CONTROL)
590 dwEffect = dwAllowedEffects & DROPEFFECT_COPY;
591 else if(grfKeyState & MK_SHIFT)
592 dwEffect = dwAllowedEffects & DROPEFFECT_MOVE;
593
594 /* If there still was no drop effect assigned, check for the handed-in
595 * allowed effects and assign one of them.
596 *
597 * Note: A move action has precendence over a copy action! */
598 if (dwEffect == DROPEFFECT_NONE)
599 {
600 if (dwAllowedEffects & DROPEFFECT_COPY)
601 dwEffect = DROPEFFECT_COPY;
602 if (dwAllowedEffects & DROPEFFECT_MOVE)
603 dwEffect = DROPEFFECT_MOVE;
604 }
605
606#ifdef DEBUG_andy
607 LogFlowFunc(("grfKeyState=0x%x, dwAllowedEffects=0x%x, dwEffect=0x%x\n",
608 grfKeyState, dwAllowedEffects, dwEffect));
609#endif
610 return dwEffect;
611}
612
613/**
614 * Resets a drop target object.
615 */
616void VBoxDnDDropTarget::reset(void)
617{
618 LogFlowFuncEnter();
619
620 if (m_pvData)
621 {
622 RTMemFree(m_pvData);
623 m_pvData = NULL;
624 }
625
626 m_cbData = 0;
627
628 RT_ZERO(m_FormatEtc);
629 m_strFormat = "";
630}
631
632/**
633 * Returns the currently supported formats of a drop target.
634 *
635 * @returns Supported formats.
636 */
637RTCString VBoxDnDDropTarget::Formats(void) const
638{
639 return m_strFormat;
640}
641
642/**
643 * Waits for a drop event to happen.
644 *
645 * @returns VBox status code.
646 * @param msTimeout Timeout (in ms) to wait for drop event.
647 */
648int VBoxDnDDropTarget::WaitForDrop(RTMSINTERVAL msTimeout)
649{
650 LogFlowFunc(("msTimeout=%RU32\n", msTimeout));
651
652 int rc = RTSemEventWait(m_EvtDrop, msTimeout);
653 if (RT_SUCCESS(rc))
654 rc = m_rcDropped;
655
656 LogFlowFuncLeaveRC(rc);
657 return rc;
658}
659
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