VirtualBox

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

Last change on this file since 106199 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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