VirtualBox

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

Last change on this file since 107380 was 107185, checked in by vboxsync, 7 weeks ago

SharedClipboard/ClipboardDataObjectImpl-win.cpp: Fixed a warning found by Parfait.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.6 KB
Line 
1/* $Id: ClipboardDataObjectImpl-win.cpp 107185 2024-11-29 13:09:26Z 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 AssertPtrBreakStmt(pRootEntry, rc = VERR_INVALID_POINTER);
506
507 AssertBreakStmt(pRootEntry->cbInfo == sizeof(SHCLFSOBJINFO), rc = VERR_INVALID_PARAMETER);
508 PSHCLFSOBJINFO const pFsObjInfo = (PSHCLFSOBJINFO)pRootEntry->pvInfo;
509
510 LogFlowFunc(("pszRoot=%s, fMode=0x%x (type %#x)\n",
511 pRootEntry->pszName, pFsObjInfo->Attr.fMode, (pFsObjInfo->Attr.fMode & RTFS_TYPE_MASK)));
512
513 if (RTFS_IS_DIRECTORY(pFsObjInfo->Attr.fMode))
514 {
515 FSOBJENTRY objEntry;
516 objEntry.pszPath = RTStrDup(pRootEntry->pszName);
517 AssertPtrBreakStmt(objEntry.pszPath, rc = VERR_NO_MEMORY);
518 objEntry.objInfo = *pFsObjInfo;
519
520 pThis->lock();
521 pThis->m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
522 pThis->unlock();
523
524 rc = pThis->readDir(pTransfer, pRootEntry->pszName);
525 }
526 else if (RTFS_IS_FILE(pFsObjInfo->Attr.fMode))
527 {
528 FSOBJENTRY objEntry;
529 objEntry.pszPath = RTStrDup(pRootEntry->pszName);
530 AssertPtrBreakStmt(objEntry.pszPath, rc = VERR_NO_MEMORY);
531 objEntry.objInfo = *pFsObjInfo;
532
533 pThis->lock();
534 pThis->m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
535 pThis->unlock();
536 }
537 else
538 {
539 LogRel(("Shared Clipboard: Root entry '%s': File type %#x not supported\n",
540 pRootEntry->pszName, (pFsObjInfo->Attr.fMode & RTFS_TYPE_MASK)));
541 rc = VERR_NOT_SUPPORTED;
542 }
543
544 if (ASMAtomicReadBool(&pTransfer->Thread.fStop))
545 {
546 LogRel2(("Shared Clipboard: Stopping transfer calculation ...\n"));
547 break;
548 }
549
550 if (RT_FAILURE(rc))
551 break;
552 }
553
554 if ( RT_SUCCESS(rc)
555 && !ASMAtomicReadBool(&pTransfer->Thread.fStop))
556 {
557 LogRel2(("Shared Clipboard: Transfer calculation complete (%zu root entries)\n", pThis->m_lstEntries.size()));
558
559 /*
560 * Signal the "list complete" event so that this data object can return (valid) data via ::GetData().
561 * This in turn then will create IStream instances (by the OS) for each file system object to handle.
562 */
563 rc = RTSemEventSignal(pThis->m_EventListComplete);
564 if (RT_SUCCESS(rc))
565 {
566 AssertReleaseMsg(pThis->m_lstEntries.size(),
567 ("Shared Clipboard: No transfer root entries found -- should not happen, please file a bug report\n"));
568
569 LogRel2(("Shared Clipboard: Waiting for transfer to complete ...\n"));
570
571 for (;;)
572 {
573 if (ASMAtomicReadBool(&pTransfer->Thread.fStop))
574 break;
575
576 /* Transferring stuff can take a while, so don't use any timeout here. */
577 rc = RTSemEventWait(pThis->m_EventStatusChanged, RT_INDEFINITE_WAIT);
578 if (RT_FAILURE(rc))
579 break;
580
581 switch (pThis->m_enmStatus)
582 {
583 case Uninitialized: /* Can happen due to transfer erros. */
584 LogRel2(("Shared Clipboard: Data object was uninitialized\n"));
585 break;
586
587 case Initialized:
588 AssertFailed(); /* State machine error -- debug this! */
589 break;
590
591 case Running:
592 continue;
593
594 case Completed:
595 LogRel2(("Shared Clipboard: Data object: Transfer complete\n"));
596 rc = ShClTransferComplete(pTransfer);
597 break;
598
599 case Canceled:
600 LogRel2(("Shared Clipboard: Data object: Transfer canceled\n"));
601 rc = ShClTransferCancel(pTransfer);
602 break;
603
604 case Error:
605 LogRel(("Shared Clipboard: Data object: Transfer error %Rrc occurred\n", pThis->m_rcStatus));
606 rc = ShClTransferError(pTransfer, pThis->m_rcStatus);
607 break;
608
609 default:
610 AssertFailed();
611 break;
612 }
613
614 if (pThis->m_Callbacks.pfnTransferEnd)
615 {
616 int rc2 = pThis->m_Callbacks.pfnTransferEnd(&pThis->m_CallbackCtx, pTransfer, pThis->m_rcStatus);
617 if (RT_SUCCESS(rc))
618 rc = rc2;
619 }
620
621 break;
622 } /* for */
623 }
624 }
625 }
626
627 if (RT_FAILURE(rc))
628 LogRel(("Shared Clipboard: Transfer read thread failed with %Rrc\n", rc));
629
630 LogFlowFuncLeaveRC(rc);
631 return rc;
632}
633
634/**
635 * Creates a FILEGROUPDESCRIPTOR object from a given Shared Clipboard transfer and stores the result into an HGLOBAL object.
636 *
637 * @returns VBox status code.
638 * @param pTransfer Shared Clipboard transfer to create file grou desciprtor for.
639 * @param fUnicode Whether the FILEGROUPDESCRIPTOR object shall contain Unicode data or not.
640 * @param phGlobal Where to store the allocated HGLOBAL object on success.
641 */
642int ShClWinDataObject::createFileGroupDescriptorFromTransfer(PSHCLTRANSFER pTransfer,
643 bool fUnicode, HGLOBAL *phGlobal)
644{
645 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
646 AssertPtrReturn(phGlobal, VERR_INVALID_POINTER);
647
648 LogFlowFuncEnter();
649
650 const size_t cbFileGroupDescriptor = fUnicode ? sizeof(FILEGROUPDESCRIPTORW) : sizeof(FILEGROUPDESCRIPTORA);
651 const size_t cbFileDescriptor = fUnicode ? sizeof(FILEDESCRIPTORW) : sizeof(FILEDESCRIPTORA);
652
653 const UINT cItems = (UINT)m_lstEntries.size(); /** UINT vs. size_t. */
654 if (!cItems)
655 return VERR_NOT_FOUND;
656
657 UINT curIdx = 0; /* Current index of the handled file group descriptor (FGD). */
658
659 const size_t cbFGD = cbFileGroupDescriptor + (cbFileDescriptor * (cItems - 1));
660
661 LogFunc(("fUnicode=%RTbool, cItems=%u, cbFileDescriptor=%zu\n", fUnicode, cItems, cbFileDescriptor));
662
663 /* FILEGROUPDESCRIPTORA / FILEGROUPDESCRIPTOR matches except the cFileName member (TCHAR vs. WCHAR). */
664 FILEGROUPDESCRIPTOR *pFGD = (FILEGROUPDESCRIPTOR *)RTMemAllocZ(cbFGD);
665 if (!pFGD)
666 return VERR_NO_MEMORY;
667
668 int rc = VINF_SUCCESS;
669
670 pFGD->cItems = cItems;
671
672 char *pszFileSpec = NULL;
673
674 FsObjEntryList::const_iterator itRoot = m_lstEntries.cbegin();
675 while (itRoot != m_lstEntries.end())
676 {
677 FILEDESCRIPTOR *pFD = &pFGD->fgd[curIdx];
678 RT_BZERO(pFD, cbFileDescriptor);
679
680 const char *pszFile = itRoot->pszPath;
681 AssertPtr(pszFile);
682
683 pszFileSpec = RTStrDup(pszFile);
684 AssertBreakStmt(pszFileSpec != NULL, rc = VERR_NO_MEMORY);
685
686 if (fUnicode)
687 {
688 PRTUTF16 pwszFileSpec;
689 rc = RTStrToUtf16(pszFileSpec, &pwszFileSpec);
690 if (RT_SUCCESS(rc))
691 {
692 rc = RTUtf16CopyEx((PRTUTF16 )pFD->cFileName, sizeof(pFD->cFileName) / sizeof(WCHAR),
693 pwszFileSpec, RTUtf16Len(pwszFileSpec));
694 RTUtf16Free(pwszFileSpec);
695
696 LogFlowFunc(("pFD->cFileNameW=%ls\n", pFD->cFileName));
697 }
698 }
699 else
700 {
701 rc = RTStrCopy(pFD->cFileName, sizeof(pFD->cFileName), pszFileSpec);
702 LogFlowFunc(("pFD->cFileNameA=%s\n", pFD->cFileName));
703 }
704
705 RTStrFree(pszFileSpec);
706 pszFileSpec = NULL;
707
708 if (RT_FAILURE(rc))
709 break;
710
711 pFD->dwFlags = FD_PROGRESSUI | FD_ATTRIBUTES;
712 if (fUnicode) /** @todo Only >= Vista. */
713 pFD->dwFlags |= FD_UNICODE;
714 pFD->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
715
716 const SHCLFSOBJINFO *pObjInfo = &itRoot->objInfo;
717
718 if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
719 {
720 pFD->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
721 }
722 else if (RTFS_IS_FILE(pObjInfo->Attr.fMode))
723 {
724 pFD->dwFlags |= FD_FILESIZE;
725
726 const uint64_t cbObjSize = pObjInfo->cbObject;
727
728 pFD->nFileSizeHigh = RT_HI_U32(cbObjSize);
729 pFD->nFileSizeLow = RT_LO_U32(cbObjSize);
730 }
731 else if (RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
732 {
733 /** @todo Implement. */
734 }
735#if 0 /** @todo Implement this. */
736 pFD->dwFlags = FD_ATTRIBUTES | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_FILESIZE;
737 pFD->dwFileAttributes =
738 pFD->ftCreationTime =
739 pFD->ftLastAccessTime =
740 pFD->ftLastWriteTime =
741#endif
742 ++curIdx;
743 ++itRoot;
744 }
745
746 if (pszFileSpec)
747 RTStrFree(pszFileSpec);
748
749 if (RT_SUCCESS(rc))
750 rc = copyToHGlobal(pFGD, cbFGD, GMEM_MOVEABLE, phGlobal);
751
752 RTMemFree(pFGD);
753
754 LogFlowFuncLeaveRC(rc);
755 return rc;
756}
757
758/**
759 * Retrieves the data stored in this object and store the result in pMedium.
760 *
761 * @return HRESULT
762 * @param pFormatEtc Format to retrieve.
763 * @param pMedium Where to store the data on success.
764 *
765 * @thread Windows event thread.
766 */
767STDMETHODIMP ShClWinDataObject::GetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
768{
769 AssertPtrReturn(pFormatEtc, DV_E_FORMATETC);
770 AssertPtrReturn(pMedium, DV_E_FORMATETC);
771
772 lock();
773
774 LogFlowFunc(("lIndex=%RI32, enmStatus=%#x\n", pFormatEtc->lindex, m_enmStatus));
775
776 /* If the object is not ready (anymore), bail out early. */
777 if ( m_enmStatus != Initialized
778 && m_enmStatus != Running)
779 {
780 unlock();
781 return E_UNEXPECTED;
782 }
783
784 /*
785 * Initialize default values.
786 */
787 RT_BZERO(pMedium, sizeof(STGMEDIUM));
788
789 HRESULT hr = DV_E_FORMATETC; /* Play safe. */
790
791 int rc = VINF_SUCCESS;
792
793 if ( RT_SUCCESS(rc)
794 && ( pFormatEtc->cfFormat == m_cfFileDescriptorA
795#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
796 || pFormatEtc->cfFormat == m_cfFileDescriptorW
797#endif
798 )
799 )
800 {
801 switch (m_enmStatus)
802 {
803 case Initialized:
804 {
805 LogRel2(("Shared Clipboard: Requesting data for IDataObject ...\n"));
806
807 /* Leave lock while requesting + waiting. */
808 unlock();
809
810 /* Start the transfer. */
811 AssertPtrBreak(m_Callbacks.pfnTransferBegin);
812 rc = m_Callbacks.pfnTransferBegin(&m_CallbackCtx);
813 AssertRCBreak(rc);
814
815 LogRel2(("Shared Clipboard: Waiting for IDataObject started status ...\n"));
816
817 /* Note: Keep the timeout low here (instead of using SHCL_TIMEOUT_DEFAULT_MS), as this will make
818 * Windows Explorer unresponsive (i.e. "ghost window") when waiting for too long. */
819 rc = RTSemEventWait(m_EventStatusChanged, RT_MS_10SEC);
820
821 /* Re-acquire lock. */
822 lock();
823
824 LogFunc(("Wait resulted in %Rrc and status %#x\n", rc, m_enmStatus));
825
826 if (RT_FAILURE(rc))
827 {
828 LogRel(("Shared Clipboard: Waiting for IDataObject status failed, rc=%Rrc\n", rc));
829 break;
830 }
831
832 if (m_enmStatus != Running)
833 {
834 LogRel(("Shared Clipboard: Received wrong IDataObject status (%#x)\n", m_enmStatus));
835 rc = VERR_WRONG_ORDER;
836 break;
837 }
838
839 /* There now must be a transfer assigned. */
840 AssertPtrBreakStmt(m_pTransfer, rc = VERR_WRONG_ORDER);
841
842 RT_FALL_THROUGH();
843 }
844
845 case Running:
846 {
847 const bool fUnicode = pFormatEtc->cfFormat == m_cfFileDescriptorW;
848
849 /* Leave lock while waiting. */
850 unlock();
851
852 SHCLTRANSFERSTATUS const enmTransferStatus = ShClTransferGetStatus(m_pTransfer);
853
854 LogFlowFunc(("FormatIndex_FileDescriptor%s, enmTransferStatus=%s\n",
855 fUnicode ? "W" : "A", ShClTransferStatusToStr(enmTransferStatus)));
856
857 /* The caller can call GetData() several times, so make sure we don't do the same transfer multiple times. */
858 if (enmTransferStatus != SHCLTRANSFERSTATUS_STARTED)
859 {
860 /* Start the transfer + run it asynchronously in a separate thread. */
861 rc = ShClTransferStart(m_pTransfer);
862 if (RT_SUCCESS(rc))
863 {
864 rc = ShClTransferRun(m_pTransfer, &ShClWinDataObject::readThread, this /* pvUser */);
865 if (RT_SUCCESS(rc))
866 {
867 /* Don't block for too long here, as this also will screw other apps running on the OS. */
868 LogRel2(("Shared Clipboard: Waiting for IDataObject listing to arrive ...\n"));
869 rc = RTSemEventWait(m_EventListComplete, RT_MS_10SEC);
870
871 /* Re-acquire lock. */
872 lock();
873
874 if ( m_pTransfer == NULL
875 || m_enmStatus != Running) /* Still in running state? */
876 {
877 rc = VERR_SHCLPB_NO_DATA;
878 break;
879 }
880
881 unlock();
882 }
883 }
884 }
885
886 lock();
887
888 if (RT_SUCCESS(rc))
889 {
890 HGLOBAL hGlobal;
891 rc = createFileGroupDescriptorFromTransfer(m_pTransfer, fUnicode, &hGlobal);
892 if (RT_SUCCESS(rc))
893 {
894 pMedium->tymed = TYMED_HGLOBAL;
895 pMedium->hGlobal = hGlobal;
896 /* Note: hGlobal now is being owned by pMedium / the caller. */
897
898 hr = S_OK;
899 }
900 }
901
902 break;
903 }
904
905 default:
906 AssertFailedStmt(rc = VERR_STATE_CHANGED);
907 break;
908 }
909
910 if (RT_FAILURE(rc))
911 {
912 LogRel(("Shared Clipboard: Error getting data for IDataObject, rc=%Rrc\n", rc));
913 hr = E_UNEXPECTED; /* We can't tell any better to the caller, unfortunately. */
914 }
915 }
916
917 Log2Func(("enmStatus=%#x, pTransfer=%p, rc=%Rrc\n", m_enmStatus, m_pTransfer, rc));
918
919 if (RT_SUCCESS(rc))
920 {
921 if (pFormatEtc->cfFormat == m_cfFileContents)
922 {
923 if ( pFormatEtc->lindex >= 0
924 && (ULONG)pFormatEtc->lindex < m_lstEntries.size())
925 {
926 m_uObjIdx = pFormatEtc->lindex; /* lIndex of FormatEtc contains the actual index to the object being handled. */
927
928 FSOBJENTRY &fsObjEntry = m_lstEntries.at(m_uObjIdx);
929
930 LogFlowFunc(("FormatIndex_FileContents: m_uObjIdx=%u (entry '%s')\n", m_uObjIdx, fsObjEntry.pszPath));
931
932 LogRel2(("Shared Clipboard: Receiving object '%s' ...\n", fsObjEntry.pszPath));
933
934 /* Hand-in the provider so that our IStream implementation can continue working with it. */
935 hr = ShClWinStreamImpl::Create(this /* pParent */, m_pTransfer,
936 fsObjEntry.pszPath /* File name */, &fsObjEntry.objInfo /* PSHCLFSOBJINFO */,
937 &m_pStream);
938 if (SUCCEEDED(hr))
939 {
940 /* Hand over the stream to the caller. */
941 pMedium->tymed = TYMED_ISTREAM;
942 pMedium->pstm = m_pStream;
943 }
944 }
945 }
946 else if (pFormatEtc->cfFormat == m_cfPerformedDropEffect)
947 {
948 HGLOBAL hGlobal = GlobalAlloc(GHND, sizeof(DWORD));
949
950 DWORD* pdwDropEffect = (DWORD*)GlobalLock(hGlobal);
951 *pdwDropEffect = DROPEFFECT_COPY;
952
953 GlobalUnlock(hGlobal);
954
955 pMedium->tymed = TYMED_HGLOBAL;
956 pMedium->hGlobal = hGlobal;
957 pMedium->pUnkForRelease = NULL;
958 }
959
960 if ( FAILED(hr)
961 && hr != DV_E_FORMATETC) /* Can happen if the caller queries unknown / unhandled formats. */
962 {
963 LogRel(("Shared Clipboard: Error returning data from data object (%Rhrc)\n", hr));
964 }
965 }
966
967 unlock();
968
969 LogFlowFunc(("LEAVE hr=%Rhrc\n", hr));
970 return hr;
971}
972
973/**
974 * Only required for IStream / IStorage interfaces.
975 *
976 * @return IPRT status code.
977 * @return HRESULT
978 * @param pFormatEtc
979 * @param pMedium
980 */
981STDMETHODIMP ShClWinDataObject::GetDataHere(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
982{
983 RT_NOREF(pFormatEtc, pMedium);
984 LogFlowFunc(("\n"));
985 return E_NOTIMPL;
986}
987
988/**
989 * Query if this objects supports a specific format.
990 *
991 * @return IPRT status code.
992 * @return HRESULT
993 * @param pFormatEtc
994 */
995STDMETHODIMP ShClWinDataObject::QueryGetData(LPFORMATETC pFormatEtc)
996{
997 LogFlowFunc(("\n"));
998 return lookupFormatEtc(pFormatEtc, NULL /* puIndex */) ? S_OK : DV_E_FORMATETC;
999}
1000
1001STDMETHODIMP ShClWinDataObject::GetCanonicalFormatEtc(LPFORMATETC pFormatEtc, LPFORMATETC pFormatEtcOut)
1002{
1003 RT_NOREF(pFormatEtc);
1004 LogFlowFunc(("\n"));
1005
1006 /* Set this to NULL in any case. */
1007 pFormatEtcOut->ptd = NULL;
1008 return E_NOTIMPL;
1009}
1010
1011STDMETHODIMP ShClWinDataObject::SetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium, BOOL fRelease)
1012{
1013 if ( pFormatEtc == NULL
1014 || pMedium == NULL)
1015 return E_INVALIDARG;
1016
1017 if (pFormatEtc->lindex != -1)
1018 return DV_E_LINDEX;
1019
1020 if (pFormatEtc->tymed != TYMED_HGLOBAL)
1021 return DV_E_TYMED;
1022
1023 if (pFormatEtc->dwAspect != DVASPECT_CONTENT)
1024 return DV_E_DVASPECT;
1025
1026 LogFlowFunc(("cfFormat=%RU16, lookupFormatEtc=%RTbool\n",
1027 pFormatEtc->cfFormat, lookupFormatEtc(pFormatEtc, NULL /* puIndex */)));
1028
1029 /* CFSTR_PERFORMEDDROPEFFECT is used by the drop target (caller of this IDataObject) to communicate
1030 * the outcome of the overall operation. */
1031 if ( pFormatEtc->cfFormat == m_cfPerformedDropEffect
1032 && pMedium->tymed == TYMED_HGLOBAL)
1033 {
1034 DWORD dwEffect = *(DWORD *)GlobalLock(pMedium->hGlobal);
1035 GlobalUnlock(pMedium->hGlobal);
1036
1037 LogFlowFunc(("dwEffect=%RI32\n", dwEffect));
1038
1039 /* Did the user cancel the operation via UI (shell)? This also might happen when overwriting an existing file
1040 * and the user doesn't want to allow this. */
1041 if (dwEffect == DROPEFFECT_NONE)
1042 {
1043 LogRel2(("Shared Clipboard: Transfer canceled by user interaction\n"));
1044
1045 SetStatus(Canceled);
1046 }
1047 /** @todo Detect move / overwrite actions here. */
1048
1049 if (fRelease)
1050 ReleaseStgMedium(pMedium);
1051
1052 return S_OK;
1053 }
1054
1055 return E_NOTIMPL;
1056}
1057
1058STDMETHODIMP ShClWinDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
1059{
1060 LogFlowFunc(("dwDirection=%RI32, mcFormats=%RI32, mpFormatEtc=%p\n", dwDirection, m_cFormats, m_pFormatEtc));
1061
1062 HRESULT hr;
1063 if (dwDirection == DATADIR_GET)
1064 hr = ShClWinEnumFormatEtc::CreateEnumFormatEtc(m_cFormats, m_pFormatEtc, ppEnumFormatEtc);
1065 else
1066 hr = E_NOTIMPL;
1067
1068 LogFlowFunc(("hr=%Rhrc\n", hr));
1069 return hr;
1070}
1071
1072STDMETHODIMP ShClWinDataObject::DAdvise(LPFORMATETC pFormatEtc, DWORD fAdvise, IAdviseSink *pAdvSink, DWORD *pdwConnection)
1073{
1074 RT_NOREF(pFormatEtc, fAdvise, pAdvSink, pdwConnection);
1075 return OLE_E_ADVISENOTSUPPORTED;
1076}
1077
1078STDMETHODIMP ShClWinDataObject::DUnadvise(DWORD dwConnection)
1079{
1080 RT_NOREF(dwConnection);
1081 return OLE_E_ADVISENOTSUPPORTED;
1082}
1083
1084STDMETHODIMP ShClWinDataObject::EnumDAdvise(IEnumSTATDATA **ppEnumAdvise)
1085{
1086 RT_NOREF(ppEnumAdvise);
1087 return OLE_E_ADVISENOTSUPPORTED;
1088}
1089
1090#ifdef VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC
1091/*
1092 * IDataObjectAsyncCapability methods.
1093 */
1094
1095STDMETHODIMP ShClWinDataObject::EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects)
1096{
1097 RT_NOREF(hResult, pbcReserved, dwEffects);
1098 return E_NOTIMPL;
1099}
1100
1101STDMETHODIMP ShClWinDataObject::GetAsyncMode(BOOL *pfIsOpAsync)
1102{
1103 RT_NOREF(pfIsOpAsync);
1104 return E_NOTIMPL;
1105}
1106
1107STDMETHODIMP ShClWinDataObject::InOperation(BOOL *pfInAsyncOp)
1108{
1109 RT_NOREF(pfInAsyncOp);
1110 return E_NOTIMPL;
1111}
1112
1113STDMETHODIMP ShClWinDataObject::SetAsyncMode(BOOL fDoOpAsync)
1114{
1115 RT_NOREF(fDoOpAsync);
1116 return E_NOTIMPL;
1117}
1118
1119STDMETHODIMP ShClWinDataObject::StartOperation(IBindCtx *pbcReserved)
1120{
1121 RT_NOREF(pbcReserved);
1122 return E_NOTIMPL;
1123}
1124#endif /* VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC */
1125
1126/*
1127 * Own stuff.
1128 */
1129
1130/**
1131 * Assigns a transfer object for the data object, internal version.
1132 *
1133 * @returns VBox status code.
1134 * @param pTransfer Transfer to assign.
1135 * When set to NULL, the transfer will be released from the object.
1136 */
1137int ShClWinDataObject::setTransferLocked(PSHCLTRANSFER pTransfer)
1138{
1139 AssertReturn(RTCritSectIsOwned(&m_CritSect), VERR_WRONG_ORDER);
1140
1141 LogFunc(("pTransfer=%p\n", pTransfer));
1142
1143 int rc = VINF_SUCCESS;
1144
1145 if (pTransfer) /* Set */
1146 {
1147 Assert(m_pTransfer == NULL); /* Transfer already set? */
1148
1149 if (m_enmStatus == Initialized)
1150 {
1151 m_pTransfer = pTransfer;
1152
1153 ShClWinTransferCtx *pWinURITransferCtx = (ShClWinTransferCtx *)pTransfer->pvUser;
1154 AssertPtr(pWinURITransferCtx);
1155
1156 pWinURITransferCtx->pDataObj = this; /* Save a backref to this object. */
1157
1158 ShClTransferAcquire(pTransfer);
1159 }
1160 else
1161 AssertFailedStmt(rc = VERR_WRONG_ORDER);
1162 }
1163 else /* Unset */
1164 {
1165 if (m_pTransfer)
1166 {
1167 ShClWinTransferCtx *pWinURITransferCtx = (ShClWinTransferCtx *)m_pTransfer->pvUser;
1168 AssertPtr(pWinURITransferCtx);
1169
1170 pWinURITransferCtx->pDataObj = NULL; /* Release backref to this object. */
1171
1172 ShClTransferRelease(m_pTransfer);
1173 m_pTransfer = NULL;
1174
1175 /* Make sure to notify any waiters. */
1176 rc = RTSemEventSignal(m_EventListComplete);
1177 AssertRC(rc);
1178 }
1179 }
1180
1181 return rc;
1182}
1183
1184/**
1185 * Assigns a transfer object for the data object.
1186 *
1187 * @returns VBox status code.
1188 * @param pTransfer Transfer to assign.
1189 * When set to NULL, the transfer will be released from the object.
1190 */
1191int ShClWinDataObject::SetTransfer(PSHCLTRANSFER pTransfer)
1192{
1193 lock();
1194
1195 int rc = setTransferLocked(pTransfer);
1196
1197 unlock();
1198
1199 return rc;
1200}
1201
1202/**
1203 * Sets a new status to the data object and signals its waiter.
1204 *
1205 * @returns VBox status code.
1206 * @param enmStatus New status to signal.
1207 * @param rcSts Result code. Optional.
1208 *
1209 * @note Called by the main clipboard thread + ShClWinStreamImpl.
1210 */
1211int ShClWinDataObject::SetStatus(Status enmStatus, int rcSts /* = VINF_SUCCESS */)
1212{
1213 lock();
1214
1215 int rc = setStatusLocked(enmStatus, rcSts);
1216
1217 unlock();
1218 return rc;
1219}
1220
1221/* static */
1222void ShClWinDataObject::logFormat(CLIPFORMAT fmt)
1223{
1224 char szFormat[128];
1225 if (GetClipboardFormatName(fmt, szFormat, sizeof(szFormat)))
1226 {
1227 LogFlowFunc(("clipFormat=%RI16 -> %s\n", fmt, szFormat));
1228 }
1229 else
1230 LogFlowFunc(("clipFormat=%RI16 is unknown\n", fmt));
1231}
1232
1233bool ShClWinDataObject::lookupFormatEtc(LPFORMATETC pFormatEtc, ULONG *puIndex)
1234{
1235 AssertReturn(pFormatEtc, false);
1236 /* puIndex is optional. */
1237
1238 for (ULONG i = 0; i < m_cFormats; i++)
1239 {
1240 if( (pFormatEtc->tymed & m_pFormatEtc[i].tymed)
1241 && pFormatEtc->cfFormat == m_pFormatEtc[i].cfFormat)
1242 /* Note: Do *not* compare dwAspect here, as this can be dynamic, depending on how the object should be represented. */
1243 //&& pFormatEtc->dwAspect == m_pFormatEtc[i].dwAspect)
1244 {
1245 LogRel2(("Shared Clipboard: Format found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32, ulIndex=%RU32\n",
1246 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect, i));
1247 if (puIndex)
1248 *puIndex = i;
1249 return true;
1250 }
1251 }
1252
1253 LogRel2(("Shared Clipboard: Format NOT found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32\n",
1254 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect));
1255
1256 logFormat(pFormatEtc->cfFormat);
1257
1258 return false;
1259}
1260
1261void ShClWinDataObject::registerFormat(LPFORMATETC pFormatEtc, CLIPFORMAT clipFormat,
1262 TYMED tyMed, LONG lIndex, DWORD dwAspect,
1263 DVTARGETDEVICE *pTargetDevice)
1264{
1265 AssertPtr(pFormatEtc);
1266
1267 pFormatEtc->cfFormat = clipFormat;
1268 pFormatEtc->tymed = tyMed;
1269 pFormatEtc->lindex = lIndex;
1270 pFormatEtc->dwAspect = dwAspect;
1271 pFormatEtc->ptd = pTargetDevice;
1272
1273 LogFlowFunc(("Registered format=%ld\n", pFormatEtc->cfFormat));
1274
1275 logFormat(pFormatEtc->cfFormat);
1276}
1277
1278/**
1279 * Sets a new status to the data object and signals its waiter.
1280 *
1281 * @returns VBox status code.
1282 * @param enmStatus New status to signal.
1283 * @param rc Result code. Optional.
1284 * Errors only accepted when status also is 'Error'.
1285 *
1286 * @note Caller must have taken the critical section.
1287 */
1288int ShClWinDataObject::setStatusLocked(Status enmStatus, int rc /* = VINF_SUCCESS */)
1289{
1290 AssertReturn(enmStatus == Error || RT_SUCCESS(rc), VERR_INVALID_PARAMETER);
1291 AssertReturn(RTCritSectIsOwned(&m_CritSect), VERR_WRONG_ORDER);
1292
1293 LogFlowFunc(("enmStatus=%#x, rc=%Rrc (current is: %#x)\n", enmStatus, rc, m_enmStatus));
1294
1295 int rc2 = VINF_SUCCESS;
1296
1297 m_rcStatus = rc;
1298
1299 switch (enmStatus)
1300 {
1301 case Completed:
1302 {
1303 LogFlowFunc(("m_uObjIdx=%RU32 (total: %zu)\n", m_uObjIdx, m_lstEntries.size()));
1304
1305 const bool fComplete = m_uObjIdx == m_lstEntries.size() - 1 /* Object index is zero-based */;
1306 if (fComplete)
1307 m_enmStatus = Completed;
1308 break;
1309 }
1310
1311 default:
1312 {
1313 m_enmStatus = enmStatus;
1314 break;
1315 }
1316 }
1317
1318 if (RT_FAILURE(rc))
1319 LogRel(("Shared Clipboard: Data object received error %Rrc (status %#x)\n", rc, enmStatus));
1320
1321 rc2 = RTSemEventSignal(m_EventStatusChanged);
1322
1323 LogFlowFuncLeaveRC(rc2);
1324 return rc2;
1325}
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