VirtualBox

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

Last change on this file since 103068 was 100665, checked in by vboxsync, 14 months ago

Shared Clipboard: More Windows data object refcount bugfixes. Added VBOX_SHARED_CLIPBOARD_DEBUG_OBJECT_COUNTS for helping tracking down those issues. bugref:9437

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

© 2024 Oracle
ContactPrivacy/Do Not Sell My InfoTerms of Use