VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/ClipboardDataObjectImpl-win.cpp@ 100665

Last change on this file since 100665 was 100665, checked in by vboxsync, 15 months ago

Shared Clipboard: More Windows data object refcount bugfixes. Added VBOX_SHARED_CLIPBOARD_DEBUG_OBJECT_COUNTS for helping tracking down those issues. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.9 KB
Line 
1/* $Id: ClipboardDataObjectImpl-win.cpp 100665 2023-07-20 13:20:29Z vboxsync $ */
2/** @file
3 * ClipboardDataObjectImpl-win.cpp - Shared Clipboard IDataObject implementation.
4 */
5
6/*
7 * Copyright (C) 2019-2023 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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
33#include <VBox/GuestHost/SharedClipboard-win.h>
34#include <VBox/GuestHost/SharedClipboard-transfers.h>
35
36#include <iprt/win/windows.h>
37#include <iprt/win/shlobj.h>
38#include <iprt/win/shlwapi.h>
39
40#include <iprt/thread.h> // REMOVE
41
42#include <iprt/asm.h>
43#include <iprt/errcore.h>
44#include <iprt/path.h>
45#include <iprt/semaphore.h>
46#include <iprt/uri.h>
47#include <iprt/utf16.h>
48
49#include <iprt/errcore.h>
50#include <VBox/log.h>
51
52/** Enable this to track the current counts of the data / stream / enum object we create + supply to the Windows clipboard.
53 * Helps finding refcount issues or tracking down memory leaks. */
54#ifdef VBOX_SHARED_CLIPBOARD_DEBUG_OBJECT_COUNTS
55 int g_cDbgDataObj;
56 int g_cDbgStreamObj;
57 int g_cDbgEnumFmtObj;
58#endif
59
60/** @todo Also handle Unicode entries.
61 * !!! WARNING: Buggy, doesn't work yet (some memory corruption / garbage in the file name descriptions) !!! */
62//#define VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT 1
63
64SharedClipboardWinDataObject::SharedClipboardWinDataObject(void)
65 : m_pCtx(NULL)
66 , m_enmStatus(Uninitialized)
67 , m_rcStatus(VERR_IPE_UNINITIALIZED_STATUS)
68 , m_lRefCount(0)
69 , m_cFormats(0)
70 , m_pTransfer(NULL)
71 , m_pStream(NULL)
72 , m_uObjIdx(0)
73 , m_EventListComplete(NIL_RTSEMEVENT)
74 , m_EventStatusChanged(NIL_RTSEMEVENT)
75{
76#ifdef VBOX_SHARED_CLIPBOARD_DEBUG_OBJECT_COUNTS
77 g_cDbgDataObj++;
78 LogFlowFunc(("g_cDataObj=%d, g_cStreamObj=%d, g_cEnumFmtObj=%d\n", g_cDbgDataObj, g_cDbgStreamObj, g_cDbgEnumFmtObj));
79#endif
80}
81
82SharedClipboardWinDataObject::~SharedClipboardWinDataObject(void)
83{
84 Destroy();
85
86#ifdef VBOX_SHARED_CLIPBOARD_DEBUG_OBJECT_COUNTS
87 g_cDbgDataObj--;
88 LogFlowFunc(("g_cDataObj=%d, g_cStreamObj=%d, g_cEnumFmtObj=%d\n", g_cDbgDataObj, g_cDbgStreamObj, g_cDbgEnumFmtObj));
89#endif
90
91 LogFlowFunc(("mRefCount=%RI32\n", m_lRefCount));
92}
93
94/**
95 * Initializes a data object instance.
96 *
97 * @returns VBox status code.
98 * @param pCtx Opaque Shared Clipboard context to use.
99 * @param pCallbacks Callbacks table to use.
100 * @param pFormatEtc FormatETC to use. Optional.
101 * @param pStgMed Storage medium to use. Optional.
102 * @param cFormats Number of formats in \a pFormatEtc and \a pStgMed. Optional.
103 */
104int SharedClipboardWinDataObject::Init(PSHCLCONTEXT pCtx, SharedClipboardWinDataObject::PCALLBACKS pCallbacks,
105 LPFORMATETC pFormatEtc /* = NULL */, LPSTGMEDIUM pStgMed /* = NULL */,
106 ULONG cFormats /* = 0 */)
107{
108 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
109 AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER);
110 AssertReturn(cFormats == 0 || (RT_VALID_PTR(pFormatEtc) && RT_VALID_PTR(pStgMed)), VERR_INVALID_POINTER);
111
112 int rc = VINF_SUCCESS;
113
114 m_pCtx = pCtx; /* Save opaque context. */
115
116 /*
117 * Set up callback context + table.
118 */
119 memcpy(&m_Callbacks, pCallbacks, sizeof(SharedClipboardWinDataObject::CALLBACKS));
120 m_CallbackCtx.pvUser = pCtx;
121 m_CallbackCtx.pThis = this;
122
123 /*
124 * Set up / register handled formats.
125 */
126 ULONG cFixedFormats = 3; /* CFSTR_FILEDESCRIPTORA + CFSTR_FILECONTENTS + CFSTR_PERFORMEDDROPEFFECT */
127#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
128 cFixedFormats++; /* CFSTR_FILEDESCRIPTORW */
129#endif
130 const ULONG cAllFormats = cFormats + cFixedFormats;
131
132 m_pFormatEtc = new FORMATETC[cAllFormats];
133 AssertPtrReturn(m_pFormatEtc, VERR_NO_MEMORY);
134 RT_BZERO(m_pFormatEtc, sizeof(FORMATETC) * cAllFormats);
135 m_pStgMedium = new STGMEDIUM[cAllFormats];
136 AssertPtrReturn(m_pStgMedium, VERR_NO_MEMORY);
137 RT_BZERO(m_pStgMedium, sizeof(STGMEDIUM) * cAllFormats);
138
139 /** @todo Do we need CFSTR_FILENAME / CFSTR_SHELLIDLIST here? */
140
141 /*
142 * Register fixed formats.
143 */
144 unsigned uIdx = 0;
145
146 LogFlowFunc(("Registering CFSTR_FILEDESCRIPTORA ...\n"));
147 m_cfFileDescriptorA = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
148 registerFormat(&m_pFormatEtc[uIdx++], m_cfFileDescriptorA);
149#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
150 LogFlowFunc(("Registering CFSTR_FILEDESCRIPTORW ...\n"));
151 m_cfFileDescriptorW = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
152 registerFormat(&m_pFormatEtc[uIdx++], m_cfFileDescriptorW);
153#endif
154
155 /* IStream interface, implemented in ClipboardStreamImpl-win.cpp. */
156 LogFlowFunc(("Registering CFSTR_FILECONTENTS ...\n"));
157 m_cfFileContents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
158 registerFormat(&m_pFormatEtc[uIdx++], m_cfFileContents, TYMED_ISTREAM, 0 /* lIndex */);
159
160 /* We want to know from the target what the outcome of the operation was to react accordingly (e.g. abort a transfer). */
161 LogFlowFunc(("Registering CFSTR_PERFORMEDDROPEFFECT ...\n"));
162 m_cfPerformedDropEffect = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
163 registerFormat(&m_pFormatEtc[uIdx++], m_cfPerformedDropEffect, TYMED_HGLOBAL, -1 /* lIndex */, DVASPECT_CONTENT);
164
165 /*
166 * Registration of dynamic formats needed?
167 */
168 LogFlowFunc(("%RU32 dynamic formats\n", cFormats));
169 if (cFormats)
170 {
171 for (ULONG i = 0; i < cFormats; i++)
172 {
173 LogFlowFunc(("Format %RU32: cfFormat=%RI16, tyMed=%RU32, dwAspect=%RU32\n",
174 i, pFormatEtc[i].cfFormat, pFormatEtc[i].tymed, pFormatEtc[i].dwAspect));
175 m_pFormatEtc[cFixedFormats + i] = pFormatEtc[i];
176 m_pStgMedium[cFixedFormats + i] = pStgMed[i];
177 }
178 }
179
180 if (RT_SUCCESS(rc))
181 {
182 m_cFormats = cAllFormats;
183 m_enmStatus = Initialized;
184
185 rc = RTCritSectInit(&m_CritSect);
186 if (RT_SUCCESS(rc))
187 {
188 rc = RTSemEventCreate(&m_EventListComplete);
189 if (RT_SUCCESS(rc))
190 rc = RTSemEventCreate(&m_EventStatusChanged);
191 }
192 }
193
194 LogFlowFunc(("cAllFormats=%RU32, rc=%Rrc\n", cAllFormats, rc));
195 return rc;
196}
197
198/**
199 * Uninitialized a data object instance, internal version.
200 */
201void SharedClipboardWinDataObject::uninitInternal(void)
202{
203 LogFlowFuncEnter();
204
205 lock();
206
207 if (m_enmStatus != Uninitialized)
208 {
209
210 /* Let the read thread know. */
211 setStatusLocked(Uninitialized, VINF_SUCCESS);
212
213 /* Make sure to unlock before stopping the read thread. */
214 unlock();
215
216 /* Stop the read thread. */
217 if (m_pTransfer)
218 ShClTransferStop(m_pTransfer);
219
220 lock();
221
222 /* Make sure to release the transfer. */
223 setTransferLocked(NULL);
224 }
225
226 unlock();
227}
228
229/**
230 * Uninitialized a data object instance.
231 */
232void SharedClipboardWinDataObject::Uninit(void)
233{
234 LogFlowFuncEnter();
235
236 uninitInternal();
237}
238
239/**
240 * Destroys a data object instance.
241 */
242void SharedClipboardWinDataObject::Destroy(void)
243{
244 LogFlowFuncEnter();
245
246 if (m_enmStatus == Uninitialized) /* Crit sect not available anymore. */
247 return;
248
249 uninitInternal();
250
251 int rc = RTCritSectDelete(&m_CritSect);
252 AssertRC(rc);
253
254 if (m_EventListComplete != NIL_RTSEMEVENT)
255 {
256 rc = RTSemEventDestroy(m_EventListComplete);
257 AssertRC(rc);
258 m_EventListComplete = NIL_RTSEMEVENT;
259 }
260
261 if (m_EventStatusChanged != NIL_RTSEMEVENT)
262 {
263 rc = RTSemEventDestroy(m_EventStatusChanged);
264 AssertRC(rc);
265 m_EventStatusChanged = NIL_RTSEMEVENT;
266 }
267
268 if (m_pStream)
269 m_pStream = NULL;
270
271 if (m_pFormatEtc)
272 {
273 delete[] m_pFormatEtc;
274 m_pFormatEtc = NULL;
275 }
276
277 if (m_pStgMedium)
278 {
279 delete[] m_pStgMedium;
280 m_pStgMedium = NULL;
281 }
282
283 if (m_pTransfer)
284 ShClTransferRelease(m_pTransfer);
285
286 FsObjEntryList::const_iterator itRoot = m_lstEntries.cbegin();
287 while (itRoot != m_lstEntries.end())
288 {
289 RTStrFree(itRoot->pszPath);
290 ++itRoot;
291 }
292 m_lstEntries.clear();
293}
294
295
296/*********************************************************************************************************************************
297 * IUnknown methods.
298 ********************************************************************************************************************************/
299
300STDMETHODIMP_(ULONG) SharedClipboardWinDataObject::AddRef(void)
301{
302 ULONG ulCount = InterlockedIncrement(&m_lRefCount);
303 LogFlowFunc(("lCount=%RU32\n", ulCount));
304 return ulCount;
305}
306
307STDMETHODIMP_(ULONG) SharedClipboardWinDataObject::Release(void)
308{
309 ULONG ulCount = InterlockedDecrement(&m_lRefCount);
310 LogFlowFunc(("lCount=%RU32\n", ulCount));
311 if (ulCount == 0)
312 {
313 delete this;
314 return 0;
315 }
316
317 return ulCount;
318}
319
320STDMETHODIMP SharedClipboardWinDataObject::QueryInterface(REFIID iid, void **ppvObject)
321{
322 AssertPtrReturn(ppvObject, E_INVALIDARG);
323
324 if ( iid == IID_IDataObject
325 || iid == IID_IUnknown)
326 {
327 AddRef();
328 *ppvObject = this;
329 return S_OK;
330 }
331
332 *ppvObject = 0;
333 return E_NOINTERFACE;
334}
335
336/**
337 * Copies a chunk of data into a HGLOBAL object.
338 *
339 * @returns VBox status code.
340 * @param pvData Data to copy.
341 * @param cbData Size (in bytes) to copy.
342 * @param fFlags GlobalAlloc flags, used for allocating the HGLOBAL block.
343 * @param phGlobal Where to store the allocated HGLOBAL object.
344 */
345int SharedClipboardWinDataObject::copyToHGlobal(const void *pvData, size_t cbData, UINT fFlags, HGLOBAL *phGlobal)
346{
347 AssertPtrReturn(phGlobal, VERR_INVALID_POINTER);
348
349 HGLOBAL hGlobal = GlobalAlloc(fFlags, cbData);
350 if (!hGlobal)
351 return VERR_NO_MEMORY;
352
353 void *pvAlloc = GlobalLock(hGlobal);
354 if (pvAlloc)
355 {
356 CopyMemory(pvAlloc, pvData, cbData);
357 GlobalUnlock(hGlobal);
358
359 *phGlobal = hGlobal;
360
361 return VINF_SUCCESS;
362 }
363
364 GlobalFree(hGlobal);
365 return VERR_ACCESS_DENIED;
366}
367
368inline int SharedClipboardWinDataObject::lock(void)
369{
370 int rc = RTCritSectEnter(&m_CritSect);
371 AssertRCReturn(rc, rc);
372
373 return rc;
374}
375
376inline int SharedClipboardWinDataObject::unlock(void)
377{
378 int rc = RTCritSectLeave(&m_CritSect);
379 AssertRCReturn(rc, rc);
380
381 return rc;
382}
383
384/**
385 * Reads (handles) a specific directory reursively and inserts its entry into the
386 * objects's entry list.
387 *
388 * @returns VBox status code.
389 * @param pTransfer Shared Clipboard transfer object to handle.
390 * @param strDir Directory path to handle.
391 */
392int SharedClipboardWinDataObject::readDir(PSHCLTRANSFER pTransfer, const Utf8Str &strDir)
393{
394 LogFlowFunc(("strDir=%s\n", strDir.c_str()));
395
396 SHCLLISTOPENPARMS openParmsList;
397 int rc = ShClTransferListOpenParmsInit(&openParmsList);
398 if (RT_SUCCESS(rc))
399 {
400 rc = RTStrCopy(openParmsList.pszPath, openParmsList.cbPath, strDir.c_str());
401 if (RT_SUCCESS(rc))
402 {
403 SHCLLISTHANDLE hList;
404 rc = ShClTransferListOpen(pTransfer, &openParmsList, &hList);
405 if (RT_SUCCESS(rc))
406 {
407 LogFlowFunc(("strDir=%s -> hList=%RU64\n", strDir.c_str(), hList));
408
409 SHCLLISTHDR hdrList;
410 rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList);
411 if (RT_SUCCESS(rc))
412 {
413 LogFlowFunc(("cTotalObjects=%RU64, cbTotalSize=%RU64\n\n",
414 hdrList.cEntries, hdrList.cbTotalSize));
415
416 for (uint64_t o = 0; o < hdrList.cEntries; o++)
417 {
418 SHCLLISTENTRY entryList;
419 rc = ShClTransferListEntryInit(&entryList);
420 if (RT_SUCCESS(rc))
421 {
422 rc = ShClTransferListRead(pTransfer, hList, &entryList);
423 if (RT_SUCCESS(rc))
424 {
425 if (ShClTransferListEntryIsValid(&entryList))
426 {
427 PSHCLFSOBJINFO pFsObjInfo = (PSHCLFSOBJINFO)entryList.pvInfo;
428 Assert(entryList.cbInfo == sizeof(SHCLFSOBJINFO));
429
430 Utf8Str strPath = strDir + Utf8Str("\\") + Utf8Str(entryList.pszName);
431
432 LogFlowFunc(("\t%s (%RU64 bytes) -> %s\n",
433 entryList.pszName, pFsObjInfo->cbObject, strPath.c_str()));
434
435 if ( RTFS_IS_DIRECTORY(pFsObjInfo->Attr.fMode)
436 || RTFS_IS_FILE (pFsObjInfo->Attr.fMode))
437 {
438 FSOBJENTRY objEntry;
439 objEntry.pszPath = RTStrDup(strPath.c_str());
440 AssertPtrBreakStmt(objEntry.pszPath, rc = VERR_NO_MEMORY);
441 objEntry.objInfo = *pFsObjInfo;
442
443 lock();
444 m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
445 unlock();
446 }
447 else /* Not fatal, just skip. */
448 LogRel(("Shared Clipboard: Warning: File system object '%s' of type %#x not supported, skipping\n",
449 strPath.c_str(), pFsObjInfo->Attr.fMode & RTFS_TYPE_MASK));
450
451 /** @todo Handle symlinks. */
452 }
453 else
454 rc = VERR_INVALID_PARAMETER;
455 }
456
457 ShClTransferListEntryDestroy(&entryList);
458 }
459
460 if ( RT_FAILURE(rc)
461 && pTransfer->Thread.fStop)
462 break;
463 }
464 }
465
466 ShClTransferListClose(pTransfer, hList);
467 }
468 }
469
470 ShClTransferListOpenParmsDestroy(&openParmsList);
471 }
472
473 if (RT_FAILURE(rc))
474 LogRel(("Shared Clipboard: Reading directory '%s' failed with %Rrc\n", strDir.c_str(), rc));
475
476 LogFlowFuncLeaveRC(rc);
477 return rc;
478}
479
480/**
481 * Thread for reading transfer data.
482 * The data object needs the (high level, root) transfer listing at the time of ::GetData(), so we need
483 * to block and wait until we have this data (via this thread) and continue.
484 *
485 * @returns VBox status code.
486 * @param pTransfer Pointer to transfer.
487 * @param pvUser Pointer to user-provided data. Of type SharedClipboardWinDataObject.
488 */
489/* static */
490DECLCALLBACK(int) SharedClipboardWinDataObject::readThread(PSHCLTRANSFER pTransfer, void *pvUser)
491{
492 LogFlowFuncEnter();
493
494 SharedClipboardWinDataObject *pThis = (SharedClipboardWinDataObject *)pvUser;
495
496 LogRel2(("Shared Clipboard: Calculating transfer ...\n"));
497
498 int rc = ShClTransferRootListRead(pTransfer);
499 if (RT_SUCCESS(rc))
500 {
501 uint64_t const cRoots = ShClTransferRootsCount(pTransfer);
502
503 LogFlowFunc(("cRoots=%RU64\n\n", cRoots));
504
505 for (uint32_t i = 0; i < cRoots; i++)
506 {
507 PCSHCLLISTENTRY pRootEntry = ShClTransferRootsEntryGet(pTransfer, i);
508
509 AssertBreakStmt(pRootEntry->cbInfo == sizeof(SHCLFSOBJINFO), rc = VERR_INVALID_PARAMETER);
510 PSHCLFSOBJINFO const pFsObjInfo = (PSHCLFSOBJINFO)pRootEntry->pvInfo;
511
512 LogFlowFunc(("pszRoot=%s, fMode=0x%x (type %#x)\n",
513 pRootEntry->pszName, pFsObjInfo->Attr.fMode, (pFsObjInfo->Attr.fMode & RTFS_TYPE_MASK)));
514
515 if (RTFS_IS_DIRECTORY(pFsObjInfo->Attr.fMode))
516 {
517 FSOBJENTRY objEntry;
518 objEntry.pszPath = RTStrDup(pRootEntry->pszName);
519 AssertPtrBreakStmt(objEntry.pszPath, rc = VERR_NO_MEMORY);
520 objEntry.objInfo = *pFsObjInfo;
521
522 pThis->lock();
523 pThis->m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
524 pThis->unlock();
525
526 rc = pThis->readDir(pTransfer, pRootEntry->pszName);
527 }
528 else if (RTFS_IS_FILE(pFsObjInfo->Attr.fMode))
529 {
530 FSOBJENTRY objEntry;
531 objEntry.pszPath = RTStrDup(pRootEntry->pszName);
532 AssertPtrBreakStmt(objEntry.pszPath, rc = VERR_NO_MEMORY);
533 objEntry.objInfo = *pFsObjInfo;
534
535 pThis->lock();
536 pThis->m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
537 pThis->unlock();
538 }
539 else
540 {
541 LogRel(("Shared Clipboard: Root entry '%s': File type %#x not supported\n",
542 pRootEntry->pszName, (pFsObjInfo->Attr.fMode & RTFS_TYPE_MASK)));
543 rc = VERR_NOT_SUPPORTED;
544 }
545
546 if (ASMAtomicReadBool(&pTransfer->Thread.fStop))
547 {
548 LogRel2(("Shared Clipboard: Stopping transfer calculation ...\n"));
549 break;
550 }
551
552 if (RT_FAILURE(rc))
553 break;
554 }
555
556 if ( RT_SUCCESS(rc)
557 && !ASMAtomicReadBool(&pTransfer->Thread.fStop))
558 {
559 LogRel2(("Shared Clipboard: Transfer calculation complete (%zu root entries)\n", pThis->m_lstEntries.size()));
560
561 /*
562 * Signal the "list complete" event so that this data object can return (valid) data via ::GetData().
563 * This in turn then will create IStream instances (by the OS) for each file system object to handle.
564 */
565 rc = RTSemEventSignal(pThis->m_EventListComplete);
566 if (RT_SUCCESS(rc))
567 {
568 pThis->lock();
569
570 AssertReleaseMsg(pThis->m_lstEntries.size(),
571 ("Shared Clipboard: No transfer root entries found -- should not happen, please file a bug report\n"));
572
573 LogRel2(("Shared Clipboard: Waiting for transfer to complete ...\n"));
574
575 for (;;)
576 {
577 if (ASMAtomicReadBool(&pTransfer->Thread.fStop))
578 break;
579
580 pThis->unlock();
581
582 /* Transferring stuff can take a while, so don't use any timeout here. */
583 rc = RTSemEventWait(pThis->m_EventStatusChanged, RT_INDEFINITE_WAIT);
584
585 pThis->lock();
586
587 if (RT_FAILURE(rc))
588 break;
589
590 switch (pThis->m_enmStatus)
591 {
592 case Uninitialized: /* Can happen due to transfer erros. */
593 LogRel2(("Shared Clipboard: Data object was uninitialized\n"));
594 break;
595
596 case Initialized:
597 AssertFailed(); /* State machine error -- debug this! */
598 break;
599
600 case Running:
601 continue;
602
603 case Completed:
604 LogRel2(("Shared Clipboard: Data object: Transfer complete\n"));
605 rc = ShClTransferComplete(pTransfer);
606 break;
607
608 case Canceled:
609 LogRel2(("Shared Clipboard: Data object: Transfer canceled\n"));
610 rc = ShClTransferCancel(pTransfer);
611 break;
612
613 case Error:
614 LogRel(("Shared Clipboard: Data object: Transfer error %Rrc occurred\n", pThis->m_rcStatus));
615 rc = ShClTransferError(pTransfer, pThis->m_rcStatus);
616 break;
617
618 default:
619 AssertFailed();
620 break;
621 }
622
623 pThis->unlock();
624
625 if (pThis->m_Callbacks.pfnTransferEnd)
626 {
627 int rc2 = pThis->m_Callbacks.pfnTransferEnd(&pThis->m_CallbackCtx, pTransfer, pThis->m_rcStatus);
628 if (RT_SUCCESS(rc))
629 rc = rc2;
630 }
631
632 pThis->lock();
633
634 break;
635 }
636
637 pThis->unlock();
638 }
639 }
640 }
641
642 if (RT_FAILURE(rc))
643 LogRel(("Shared Clipboard: Transfer read thread failed with %Rrc\n", rc));
644
645 LogFlowFuncLeaveRC(rc);
646 return rc;
647}
648
649/**
650 * Creates a FILEGROUPDESCRIPTOR object from a given Shared Clipboard transfer and stores the result into an HGLOBAL object.
651 *
652 * @returns VBox status code.
653 * @param pTransfer Shared Clipboard transfer to create file grou desciprtor for.
654 * @param fUnicode Whether the FILEGROUPDESCRIPTOR object shall contain Unicode data or not.
655 * @param phGlobal Where to store the allocated HGLOBAL object on success.
656 */
657int SharedClipboardWinDataObject::createFileGroupDescriptorFromTransfer(PSHCLTRANSFER pTransfer,
658 bool fUnicode, HGLOBAL *phGlobal)
659{
660 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
661 AssertPtrReturn(phGlobal, VERR_INVALID_POINTER);
662
663 LogFlowFuncEnter();
664
665 const size_t cbFileGroupDescriptor = fUnicode ? sizeof(FILEGROUPDESCRIPTORW) : sizeof(FILEGROUPDESCRIPTORA);
666 const size_t cbFileDescriptor = fUnicode ? sizeof(FILEDESCRIPTORW) : sizeof(FILEDESCRIPTORA);
667
668 const UINT cItems = (UINT)m_lstEntries.size(); /** UINT vs. size_t. */
669 if (!cItems)
670 return VERR_NOT_FOUND;
671
672 UINT curIdx = 0; /* Current index of the handled file group descriptor (FGD). */
673
674 const size_t cbFGD = cbFileGroupDescriptor + (cbFileDescriptor * (cItems - 1));
675
676 LogFunc(("fUnicode=%RTbool, cItems=%u, cbFileDescriptor=%zu\n", fUnicode, cItems, cbFileDescriptor));
677
678 /* FILEGROUPDESCRIPTORA / FILEGROUPDESCRIPTOR matches except the cFileName member (TCHAR vs. WCHAR). */
679 FILEGROUPDESCRIPTOR *pFGD = (FILEGROUPDESCRIPTOR *)RTMemAllocZ(cbFGD);
680 if (!pFGD)
681 return VERR_NO_MEMORY;
682
683 int rc = VINF_SUCCESS;
684
685 pFGD->cItems = cItems;
686
687 char *pszFileSpec = NULL;
688
689 FsObjEntryList::const_iterator itRoot = m_lstEntries.cbegin();
690 while (itRoot != m_lstEntries.end())
691 {
692 FILEDESCRIPTOR *pFD = &pFGD->fgd[curIdx];
693 RT_BZERO(pFD, cbFileDescriptor);
694
695 const char *pszFile = itRoot->pszPath;
696 AssertPtr(pszFile);
697
698 pszFileSpec = RTStrDup(pszFile);
699 AssertBreakStmt(pszFileSpec != NULL, rc = VERR_NO_MEMORY);
700
701 if (fUnicode)
702 {
703 PRTUTF16 pwszFileSpec;
704 rc = RTStrToUtf16(pszFileSpec, &pwszFileSpec);
705 if (RT_SUCCESS(rc))
706 {
707 rc = RTUtf16CopyEx((PRTUTF16 )pFD->cFileName, sizeof(pFD->cFileName) / sizeof(WCHAR),
708 pwszFileSpec, RTUtf16Len(pwszFileSpec));
709 RTUtf16Free(pwszFileSpec);
710
711 LogFlowFunc(("pFD->cFileNameW=%ls\n", pFD->cFileName));
712 }
713 }
714 else
715 {
716 rc = RTStrCopy(pFD->cFileName, sizeof(pFD->cFileName), pszFileSpec);
717 LogFlowFunc(("pFD->cFileNameA=%s\n", pFD->cFileName));
718 }
719
720 RTStrFree(pszFileSpec);
721 pszFileSpec = NULL;
722
723 if (RT_FAILURE(rc))
724 break;
725
726 pFD->dwFlags = FD_PROGRESSUI | FD_ATTRIBUTES;
727 if (fUnicode) /** @todo Only >= Vista. */
728 pFD->dwFlags |= FD_UNICODE;
729 pFD->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
730
731 const SHCLFSOBJINFO *pObjInfo = &itRoot->objInfo;
732
733 if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
734 {
735 pFD->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
736 }
737 else if (RTFS_IS_FILE(pObjInfo->Attr.fMode))
738 {
739 pFD->dwFlags |= FD_FILESIZE;
740
741 const uint64_t cbObjSize = pObjInfo->cbObject;
742
743 pFD->nFileSizeHigh = RT_HI_U32(cbObjSize);
744 pFD->nFileSizeLow = RT_LO_U32(cbObjSize);
745 }
746 else if (RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
747 {
748 /** @todo Implement. */
749 }
750#if 0 /** @todo Implement this. */
751 pFD->dwFlags = FD_ATTRIBUTES | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_FILESIZE;
752 pFD->dwFileAttributes =
753 pFD->ftCreationTime =
754 pFD->ftLastAccessTime =
755 pFD->ftLastWriteTime =
756#endif
757 ++curIdx;
758 ++itRoot;
759 }
760
761 if (pszFileSpec)
762 RTStrFree(pszFileSpec);
763
764 if (RT_SUCCESS(rc))
765 rc = copyToHGlobal(pFGD, cbFGD, GMEM_MOVEABLE, phGlobal);
766
767 RTMemFree(pFGD);
768
769 LogFlowFuncLeaveRC(rc);
770 return rc;
771}
772
773/**
774 * Retrieves the data stored in this object and store the result in pMedium.
775 *
776 * @return HRESULT
777 * @param pFormatEtc Format to retrieve.
778 * @param pMedium Where to store the data on success.
779 *
780 * @thread Windows event thread.
781 */
782STDMETHODIMP SharedClipboardWinDataObject::GetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
783{
784 AssertPtrReturn(pFormatEtc, DV_E_FORMATETC);
785 AssertPtrReturn(pMedium, DV_E_FORMATETC);
786
787 lock();
788
789 LogFlowFunc(("lIndex=%RI32, enmStatus=%#x\n", pFormatEtc->lindex, m_enmStatus));
790
791 /* If the object is not ready (anymore), bail out early. */
792 if ( m_enmStatus != Initialized
793 && m_enmStatus != Running)
794 {
795 unlock();
796 return E_UNEXPECTED;
797 }
798
799 /*
800 * Initialize default values.
801 */
802 RT_BZERO(pMedium, sizeof(STGMEDIUM));
803
804 HRESULT hr = DV_E_FORMATETC; /* Play safe. */
805
806 int rc = VINF_SUCCESS;
807
808 /* Pre-check -- see if the data object still is alive. */
809 if (m_enmStatus == Uninitialized)
810 rc = VERR_OBJECT_DESTROYED;
811
812 if ( RT_SUCCESS(rc)
813 && ( pFormatEtc->cfFormat == m_cfFileDescriptorA
814#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
815 || pFormatEtc->cfFormat == m_cfFileDescriptorW
816#endif
817 )
818 )
819 {
820 switch (m_enmStatus)
821 {
822 case Initialized:
823 {
824 LogRel2(("Shared Clipboard: Requesting data for IDataObject ...\n"));
825
826 /* Leave lock while requesting + waiting. */
827 unlock();
828
829 /* Start the transfer. */
830 AssertPtrBreak(m_Callbacks.pfnTransferBegin);
831 rc = m_Callbacks.pfnTransferBegin(&m_CallbackCtx);
832 AssertRCBreak(rc);
833
834 LogRel2(("Shared Clipboard: Waiting for IDataObject started status ...\n"));
835
836 /* Note: Keep the timeout low here (instead of using SHCL_TIMEOUT_DEFAULT_MS), as this will make
837 * Windows Explorer unresponsive (i.e. "ghost window") when waiting for too long. */
838 rc = RTSemEventWait(m_EventStatusChanged, RT_MS_10SEC);
839
840 /* Re-acquire lock. */
841 lock();
842
843 if (RT_FAILURE(rc))
844 {
845 LogRel(("Shared Clipboard: Waiting for IDataObject status status failed, rc=%Rrc\n", rc));
846 break;
847 }
848
849 if (m_enmStatus != Running)
850 {
851 LogRel(("Shared Clipboard: Received wrong IDataObject status (%#x)\n", m_enmStatus));
852 rc = VERR_WRONG_ORDER;
853 break;
854 }
855
856 /* There now must be a transfer assigned. */
857 AssertPtrBreakStmt(m_pTransfer, rc = VERR_WRONG_ORDER);
858
859 RT_FALL_THROUGH();
860 }
861
862 case Running:
863 {
864 const bool fUnicode = pFormatEtc->cfFormat == m_cfFileDescriptorW;
865
866 SHCLTRANSFERSTATUS const enmTransferStatus = ShClTransferGetStatus(m_pTransfer);
867 RT_NOREF(enmTransferStatus);
868
869 LogFlowFunc(("FormatIndex_FileDescriptor%s, enmTransferStatus=%s\n",
870 fUnicode ? "W" : "A", ShClTransferStatusToStr(enmTransferStatus)));
871
872 /* The caller can call GetData() several times, so make sure we don't do the same transfer multiple times. */
873 if (ShClTransferGetStatus(m_pTransfer) != SHCLTRANSFERSTATUS_STARTED)
874 {
875 /* Start the transfer + run it asynchronously in a separate thread. */
876 rc = ShClTransferStart(m_pTransfer);
877 if (RT_SUCCESS(rc))
878 {
879 rc = ShClTransferRun(m_pTransfer, &SharedClipboardWinDataObject::readThread, this /* pvUser */);
880 if (RT_SUCCESS(rc))
881 {
882 /* Leave lock while waiting. */
883 unlock();
884
885 /* Don't block for too long here, as this also will screw other apps running on the OS. */
886 LogRel2(("Shared Clipboard: Waiting for IDataObject listing to arrive ...\n"));
887 rc = RTSemEventWait(m_EventListComplete, RT_MS_10SEC);
888
889 /* Re-acquire lock. */
890 lock();
891
892 if ( m_pTransfer == NULL
893 || m_enmStatus != Running) /* Still in running state? */
894 {
895 rc = VERR_OBJECT_DESTROYED;
896 break;
897 }
898 }
899 }
900 }
901
902 if (RT_SUCCESS(rc))
903 {
904 HGLOBAL hGlobal;
905 rc = createFileGroupDescriptorFromTransfer(m_pTransfer, fUnicode, &hGlobal);
906 if (RT_SUCCESS(rc))
907 {
908 pMedium->tymed = TYMED_HGLOBAL;
909 pMedium->hGlobal = hGlobal;
910 /* Note: hGlobal now is being owned by pMedium / the caller. */
911
912 hr = S_OK;
913 }
914 }
915
916 break;
917 }
918
919 default:
920 AssertFailedStmt(rc = VERR_STATE_CHANGED);
921 break;
922 }
923
924 if (RT_FAILURE(rc))
925 {
926 LogRel(("Shared Clipboard: Error getting data for IDataObject, rc=%Rrc\n", rc));
927 hr = E_UNEXPECTED; /* We can't tell any better to the caller, unfortunately. */
928 }
929 }
930
931 Log2Func(("enmStatus=%#x, pTransfer=%p, rc=%Rrc\n", m_enmStatus, m_pTransfer, rc));
932
933 if (RT_SUCCESS(rc))
934 {
935 if (pFormatEtc->cfFormat == m_cfFileContents)
936 {
937 if ( pFormatEtc->lindex >= 0
938 && (ULONG)pFormatEtc->lindex < m_lstEntries.size())
939 {
940 m_uObjIdx = pFormatEtc->lindex; /* lIndex of FormatEtc contains the actual index to the object being handled. */
941
942 FSOBJENTRY &fsObjEntry = m_lstEntries.at(m_uObjIdx);
943
944 LogFlowFunc(("FormatIndex_FileContents: m_uObjIdx=%u (entry '%s')\n", m_uObjIdx, fsObjEntry.pszPath));
945
946 LogRel2(("Shared Clipboard: Receiving object '%s' ...\n", fsObjEntry.pszPath));
947
948 /* Hand-in the provider so that our IStream implementation can continue working with it. */
949 hr = SharedClipboardWinStreamImpl::Create(this /* pParent */, m_pTransfer,
950 fsObjEntry.pszPath /* File name */, &fsObjEntry.objInfo /* PSHCLFSOBJINFO */,
951 &m_pStream);
952 if (SUCCEEDED(hr))
953 {
954 /* Hand over the stream to the caller. */
955 pMedium->tymed = TYMED_ISTREAM;
956 pMedium->pstm = m_pStream;
957 }
958 }
959 }
960 else if (pFormatEtc->cfFormat == m_cfPerformedDropEffect)
961 {
962 HGLOBAL hGlobal = GlobalAlloc(GHND, sizeof(DWORD));
963
964 DWORD* pdwDropEffect = (DWORD*)GlobalLock(hGlobal);
965 *pdwDropEffect = DROPEFFECT_COPY;
966
967 GlobalUnlock(hGlobal);
968
969 pMedium->tymed = TYMED_HGLOBAL;
970 pMedium->hGlobal = hGlobal;
971 pMedium->pUnkForRelease = NULL;
972 }
973
974 if ( FAILED(hr)
975 && hr != DV_E_FORMATETC) /* Can happen if the caller queries unknown / unhandled formats. */
976 {
977 LogRel(("Shared Clipboard: Error returning data from data object (%Rhrc)\n", hr));
978 }
979 }
980
981 unlock();
982
983 LogFlowFunc(("LEAVE hr=%Rhrc\n", hr));
984 return hr;
985}
986
987/**
988 * Only required for IStream / IStorage interfaces.
989 *
990 * @return IPRT status code.
991 * @return HRESULT
992 * @param pFormatEtc
993 * @param pMedium
994 */
995STDMETHODIMP SharedClipboardWinDataObject::GetDataHere(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
996{
997 RT_NOREF(pFormatEtc, pMedium);
998 LogFlowFunc(("\n"));
999 return E_NOTIMPL;
1000}
1001
1002/**
1003 * Query if this objects supports a specific format.
1004 *
1005 * @return IPRT status code.
1006 * @return HRESULT
1007 * @param pFormatEtc
1008 */
1009STDMETHODIMP SharedClipboardWinDataObject::QueryGetData(LPFORMATETC pFormatEtc)
1010{
1011 LogFlowFunc(("\n"));
1012 return lookupFormatEtc(pFormatEtc, NULL /* puIndex */) ? S_OK : DV_E_FORMATETC;
1013}
1014
1015STDMETHODIMP SharedClipboardWinDataObject::GetCanonicalFormatEtc(LPFORMATETC pFormatEtc, LPFORMATETC pFormatEtcOut)
1016{
1017 RT_NOREF(pFormatEtc);
1018 LogFlowFunc(("\n"));
1019
1020 /* Set this to NULL in any case. */
1021 pFormatEtcOut->ptd = NULL;
1022 return E_NOTIMPL;
1023}
1024
1025STDMETHODIMP SharedClipboardWinDataObject::SetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium, BOOL fRelease)
1026{
1027 if ( pFormatEtc == NULL
1028 || pMedium == NULL)
1029 return E_INVALIDARG;
1030
1031 if (pFormatEtc->lindex != -1)
1032 return DV_E_LINDEX;
1033
1034 if (pFormatEtc->tymed != TYMED_HGLOBAL)
1035 return DV_E_TYMED;
1036
1037 if (pFormatEtc->dwAspect != DVASPECT_CONTENT)
1038 return DV_E_DVASPECT;
1039
1040 LogFlowFunc(("cfFormat=%RU16, lookupFormatEtc=%RTbool\n",
1041 pFormatEtc->cfFormat, lookupFormatEtc(pFormatEtc, NULL /* puIndex */)));
1042
1043 /* CFSTR_PERFORMEDDROPEFFECT is used by the drop target (caller of this IDataObject) to communicate
1044 * the outcome of the overall operation. */
1045 if ( pFormatEtc->cfFormat == m_cfPerformedDropEffect
1046 && pMedium->tymed == TYMED_HGLOBAL)
1047 {
1048 DWORD dwEffect = *(DWORD *)GlobalLock(pMedium->hGlobal);
1049 GlobalUnlock(pMedium->hGlobal);
1050
1051 LogFlowFunc(("dwEffect=%RI32\n", dwEffect));
1052
1053 /* Did the user cancel the operation via UI (shell)? This also might happen when overwriting an existing file
1054 * and the user doesn't want to allow this. */
1055 if (dwEffect == DROPEFFECT_NONE)
1056 {
1057 LogRel2(("Shared Clipboard: Transfer canceled by user interaction\n"));
1058
1059 SetStatus(Canceled);
1060 }
1061 /** @todo Detect move / overwrite actions here. */
1062
1063 if (fRelease)
1064 ReleaseStgMedium(pMedium);
1065
1066 return S_OK;
1067 }
1068
1069 return E_NOTIMPL;
1070}
1071
1072STDMETHODIMP SharedClipboardWinDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
1073{
1074 LogFlowFunc(("dwDirection=%RI32, mcFormats=%RI32, mpFormatEtc=%p\n", dwDirection, m_cFormats, m_pFormatEtc));
1075
1076 HRESULT hr;
1077 if (dwDirection == DATADIR_GET)
1078 hr = SharedClipboardWinEnumFormatEtc::CreateEnumFormatEtc(m_cFormats, m_pFormatEtc, ppEnumFormatEtc);
1079 else
1080 hr = E_NOTIMPL;
1081
1082 LogFlowFunc(("hr=%Rhrc\n", hr));
1083 return hr;
1084}
1085
1086STDMETHODIMP SharedClipboardWinDataObject::DAdvise(LPFORMATETC pFormatEtc, DWORD fAdvise, IAdviseSink *pAdvSink, DWORD *pdwConnection)
1087{
1088 RT_NOREF(pFormatEtc, fAdvise, pAdvSink, pdwConnection);
1089 return OLE_E_ADVISENOTSUPPORTED;
1090}
1091
1092STDMETHODIMP SharedClipboardWinDataObject::DUnadvise(DWORD dwConnection)
1093{
1094 RT_NOREF(dwConnection);
1095 return OLE_E_ADVISENOTSUPPORTED;
1096}
1097
1098STDMETHODIMP SharedClipboardWinDataObject::EnumDAdvise(IEnumSTATDATA **ppEnumAdvise)
1099{
1100 RT_NOREF(ppEnumAdvise);
1101 return OLE_E_ADVISENOTSUPPORTED;
1102}
1103
1104#ifdef VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC
1105/*
1106 * IDataObjectAsyncCapability methods.
1107 */
1108
1109STDMETHODIMP SharedClipboardWinDataObject::EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects)
1110{
1111 RT_NOREF(hResult, pbcReserved, dwEffects);
1112 return E_NOTIMPL;
1113}
1114
1115STDMETHODIMP SharedClipboardWinDataObject::GetAsyncMode(BOOL *pfIsOpAsync)
1116{
1117 RT_NOREF(pfIsOpAsync);
1118 return E_NOTIMPL;
1119}
1120
1121STDMETHODIMP SharedClipboardWinDataObject::InOperation(BOOL *pfInAsyncOp)
1122{
1123 RT_NOREF(pfInAsyncOp);
1124 return E_NOTIMPL;
1125}
1126
1127STDMETHODIMP SharedClipboardWinDataObject::SetAsyncMode(BOOL fDoOpAsync)
1128{
1129 RT_NOREF(fDoOpAsync);
1130 return E_NOTIMPL;
1131}
1132
1133STDMETHODIMP SharedClipboardWinDataObject::StartOperation(IBindCtx *pbcReserved)
1134{
1135 RT_NOREF(pbcReserved);
1136 return E_NOTIMPL;
1137}
1138#endif /* VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC */
1139
1140/*
1141 * Own stuff.
1142 */
1143
1144/**
1145 * Assigns a transfer object for the data object, internal version.
1146 *
1147 * @returns VBox status code.
1148 * @param pTransfer Transfer to assign.
1149 * Must be in INITIALIZED state.
1150 * When set to NULL, the transfer will be released from the object.
1151 */
1152int SharedClipboardWinDataObject::setTransferLocked(PSHCLTRANSFER pTransfer)
1153{
1154 AssertReturn(RTCritSectIsOwned(&m_CritSect), VERR_WRONG_ORDER);
1155
1156 LogFlowFunc(("pTransfer=%p\n", pTransfer));
1157
1158 int rc = VINF_SUCCESS;
1159
1160 if (pTransfer) /* Set */
1161 {
1162 Assert(m_pTransfer == NULL); /* Transfer already set? */
1163
1164 if (m_enmStatus == Initialized)
1165 {
1166 SHCLTRANSFERSTATUS const enmSts = ShClTransferGetStatus(pTransfer);
1167 AssertMsgStmt(enmSts == SHCLTRANSFERSTATUS_INITIALIZED, /* Transfer must not be started yet. */
1168 ("Transfer has wrong status (%#x)\n", enmSts), rc = VERR_WRONG_ORDER);
1169 if (RT_SUCCESS(rc))
1170 {
1171 m_pTransfer = pTransfer;
1172
1173 SharedClipboardWinTransferCtx *pWinURITransferCtx = (SharedClipboardWinTransferCtx *)pTransfer->pvUser;
1174 AssertPtr(pWinURITransferCtx);
1175
1176 pWinURITransferCtx->pDataObj = this; /* Save a backref to this object. */
1177
1178 ShClTransferAcquire(pTransfer);
1179 }
1180 }
1181 else
1182 AssertFailedStmt(rc = VERR_WRONG_ORDER);
1183 }
1184 else /* Unset */
1185 {
1186 if (m_pTransfer)
1187 {
1188 SharedClipboardWinTransferCtx *pWinURITransferCtx = (SharedClipboardWinTransferCtx *)m_pTransfer->pvUser;
1189 AssertPtr(pWinURITransferCtx);
1190
1191 pWinURITransferCtx->pDataObj = NULL; /* Release backref to this object. */
1192
1193 ShClTransferRelease(m_pTransfer);
1194 m_pTransfer = NULL;
1195
1196 /* Make sure to notify any waiters. */
1197 rc = RTSemEventSignal(m_EventListComplete);
1198 AssertRC(rc);
1199 }
1200 }
1201
1202 return rc;
1203}
1204
1205/**
1206 * Assigns a transfer object for the data object.
1207 *
1208 * @returns VBox status code.
1209 * @param pTransfer Transfer to assign.
1210 * Must be in INITIALIZED state.
1211 * When set to NULL, the transfer will be released from the object.
1212 */
1213int SharedClipboardWinDataObject::SetTransfer(PSHCLTRANSFER pTransfer)
1214{
1215 lock();
1216
1217 int rc = setTransferLocked(pTransfer);
1218
1219 unlock();
1220
1221 return rc;
1222}
1223
1224/**
1225 * Sets a new status to the data object and signals its waiter.
1226 *
1227 * @returns VBox status code.
1228 * @param enmStatus New status to signal.
1229 * @param rcSts Result code. Optional.
1230 *
1231 * @note Called by the main clipboard thread + SharedClipboardWinStreamImpl.
1232 */
1233int SharedClipboardWinDataObject::SetStatus(Status enmStatus, int rcSts /* = VINF_SUCCESS */)
1234{
1235 lock();
1236
1237 int rc = setStatusLocked(enmStatus, rcSts);
1238
1239 unlock();
1240 return rc;
1241}
1242
1243/* static */
1244void SharedClipboardWinDataObject::logFormat(CLIPFORMAT fmt)
1245{
1246 char szFormat[128];
1247 if (GetClipboardFormatName(fmt, szFormat, sizeof(szFormat)))
1248 {
1249 LogFlowFunc(("clipFormat=%RI16 -> %s\n", fmt, szFormat));
1250 }
1251 else
1252 LogFlowFunc(("clipFormat=%RI16 is unknown\n", fmt));
1253}
1254
1255bool SharedClipboardWinDataObject::lookupFormatEtc(LPFORMATETC pFormatEtc, ULONG *puIndex)
1256{
1257 AssertReturn(pFormatEtc, false);
1258 /* puIndex is optional. */
1259
1260 for (ULONG i = 0; i < m_cFormats; i++)
1261 {
1262 if( (pFormatEtc->tymed & m_pFormatEtc[i].tymed)
1263 && pFormatEtc->cfFormat == m_pFormatEtc[i].cfFormat)
1264 /* Note: Do *not* compare dwAspect here, as this can be dynamic, depending on how the object should be represented. */
1265 //&& pFormatEtc->dwAspect == m_pFormatEtc[i].dwAspect)
1266 {
1267 LogRel2(("Shared Clipboard: Format found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32, ulIndex=%RU32\n",
1268 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect, i));
1269 if (puIndex)
1270 *puIndex = i;
1271 return true;
1272 }
1273 }
1274
1275 LogRel2(("Shared Clipboard: Format NOT found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32\n",
1276 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect));
1277
1278 logFormat(pFormatEtc->cfFormat);
1279
1280 return false;
1281}
1282
1283void SharedClipboardWinDataObject::registerFormat(LPFORMATETC pFormatEtc, CLIPFORMAT clipFormat,
1284 TYMED tyMed, LONG lIndex, DWORD dwAspect,
1285 DVTARGETDEVICE *pTargetDevice)
1286{
1287 AssertPtr(pFormatEtc);
1288
1289 pFormatEtc->cfFormat = clipFormat;
1290 pFormatEtc->tymed = tyMed;
1291 pFormatEtc->lindex = lIndex;
1292 pFormatEtc->dwAspect = dwAspect;
1293 pFormatEtc->ptd = pTargetDevice;
1294
1295 LogFlowFunc(("Registered format=%ld\n", pFormatEtc->cfFormat));
1296
1297 logFormat(pFormatEtc->cfFormat);
1298}
1299
1300/**
1301 * Sets a new status to the data object and signals its waiter.
1302 *
1303 * @returns VBox status code.
1304 * @param enmStatus New status to signal.
1305 * @param rc Result code. Optional.
1306 * Errors only accepted when status also is 'Error'.
1307 *
1308 * @note Caller must have taken the critical section.
1309 */
1310int SharedClipboardWinDataObject::setStatusLocked(Status enmStatus, int rc /* = VINF_SUCCESS */)
1311{
1312 AssertReturn(enmStatus == Error || RT_SUCCESS(rc), VERR_INVALID_PARAMETER);
1313 AssertReturn(RTCritSectIsOwned(&m_CritSect), VERR_WRONG_ORDER);
1314
1315 LogFlowFunc(("enmStatus=%#x, rc=%Rrc (current is: %#x)\n", enmStatus, rc, m_enmStatus));
1316
1317 int rc2 = VINF_SUCCESS;
1318
1319 m_rcStatus = rc;
1320
1321 switch (enmStatus)
1322 {
1323 case Completed:
1324 {
1325 LogFlowFunc(("m_uObjIdx=%RU32 (total: %zu)\n", m_uObjIdx, m_lstEntries.size()));
1326
1327 const bool fComplete = m_uObjIdx == m_lstEntries.size() - 1 /* Object index is zero-based */;
1328 if (fComplete)
1329 m_enmStatus = Completed;
1330 break;
1331 }
1332
1333 default:
1334 {
1335 m_enmStatus = enmStatus;
1336 break;
1337 }
1338 }
1339
1340 if (RT_FAILURE(rc))
1341 LogRel(("Shared Clipboard: Data object received error %Rrc (status %#x)\n", rc, enmStatus));
1342
1343 rc2 = RTSemEventSignal(m_EventStatusChanged);
1344
1345 LogFlowFuncLeaveRC(rc2);
1346 return rc2;
1347}
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