VirtualBox

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

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

Shared Clipboard: Windows data object locking fixes. bugref:9437

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