VirtualBox

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

Last change on this file since 100509 was 100504, checked in by vboxsync, 19 months ago

Shared Clipboard: If the Windows data object is not ready (anymore), bail out early. bugref:9437

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