VirtualBox

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

Last change on this file since 100620 was 100546, checked in by vboxsync, 18 months ago

Shared Clipboard: Made transfers errors more visible via balloon tooltips when VBoxTray is running verbose mode. bugref:9437

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