VirtualBox

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

Last change on this file since 81315 was 81269, checked in by vboxsync, 6 years ago

Shared Clipboard/Transfers: Implemented transfer cancellation support for the Windows shell.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.8 KB
Line 
1/* $Id: ClipboardDataObjectImpl-win.cpp 81269 2019-10-14 18:28:34Z vboxsync $ */
2/** @file
3 * ClipboardDataObjectImpl-win.cpp - Shared Clipboard IDataObject implementation.
4 */
5
6/*
7 * Copyright (C) 2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
23#include <VBox/GuestHost/SharedClipboard-win.h>
24#include <VBox/GuestHost/SharedClipboard-transfers.h>
25
26#include <iprt/win/windows.h>
27#include <iprt/win/shlobj.h>
28#include <iprt/win/shlwapi.h>
29
30#include <iprt/asm.h>
31#include <iprt/err.h>
32#include <iprt/path.h>
33#include <iprt/semaphore.h>
34#include <iprt/uri.h>
35#include <iprt/utf16.h>
36
37#include <iprt/errcore.h>
38#include <VBox/log.h>
39
40/** @todo Also handle Unicode entries.
41 * !!! WARNING: Buggy, doesn't work yet (some memory corruption / garbage in the file name descriptions) !!! */
42//#define VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT 1
43
44SharedClipboardWinDataObject::SharedClipboardWinDataObject(PSHCLTRANSFER pTransfer,
45 LPFORMATETC pFormatEtc, LPSTGMEDIUM pStgMed, ULONG cFormats)
46 : m_enmStatus(Uninitialized)
47 , m_lRefCount(0)
48 , m_cFormats(0)
49 , m_pTransfer(pTransfer)
50 , m_pStream(NULL)
51 , m_uObjIdx(0)
52 , m_fRunning(false)
53 , m_EventListComplete(NIL_RTSEMEVENT)
54 , m_EventTransferComplete(NIL_RTSEMEVENT)
55{
56 AssertPtr(m_pTransfer);
57
58 HRESULT hr;
59
60 ULONG cFixedFormats = 3; /* CFSTR_FILEDESCRIPTORA + CFSTR_FILECONTENTS + CFSTR_PERFORMEDDROPEFFECT */
61#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
62 cFixedFormats++; /* CFSTR_FILEDESCRIPTORW */
63#endif
64 const ULONG cAllFormats = cFormats + cFixedFormats;
65
66 try
67 {
68 m_pFormatEtc = new FORMATETC[cAllFormats];
69 RT_BZERO(m_pFormatEtc, sizeof(FORMATETC) * cAllFormats);
70 m_pStgMedium = new STGMEDIUM[cAllFormats];
71 RT_BZERO(m_pStgMedium, sizeof(STGMEDIUM) * cAllFormats);
72
73 /** @todo Do we need CFSTR_FILENAME / CFSTR_SHELLIDLIST here? */
74
75 /*
76 * Register fixed formats.
77 */
78 unsigned uIdx = 0;
79
80 LogFlowFunc(("Registering CFSTR_FILEDESCRIPTORA ...\n"));
81 m_cfFileDescriptorA = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
82 registerFormat(&m_pFormatEtc[uIdx++], m_cfFileDescriptorA);
83#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
84 LogFlowFunc(("Registering CFSTR_FILEDESCRIPTORW ...\n"));
85 m_cfFileDescriptorW = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
86 registerFormat(&m_pFormatEtc[uIdx++], m_cfFileDescriptorW);
87#endif
88
89 /* IStream interface, implemented in ClipboardStreamImpl-win.cpp. */
90 LogFlowFunc(("Registering CFSTR_FILECONTENTS ...\n"));
91 m_cfFileContents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
92 registerFormat(&m_pFormatEtc[uIdx++], m_cfFileContents, TYMED_ISTREAM, 0 /* lIndex */);
93
94 /* We want to know from the target what the outcome of the operation was to react accordingly (e.g. abort a transfer). */
95 LogFlowFunc(("Registering CFSTR_PERFORMEDDROPEFFECT ...\n"));
96 m_cfPerformedDropEffect = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
97 registerFormat(&m_pFormatEtc[uIdx++], m_cfPerformedDropEffect, TYMED_HGLOBAL, -1 /* lIndex */, DVASPECT_CONTENT);
98
99 /*
100 * Registration of dynamic formats needed?
101 */
102 LogFlowFunc(("%RU32 dynamic formats\n", cFormats));
103 if (cFormats)
104 {
105 AssertPtr(pFormatEtc);
106 AssertPtr(pStgMed);
107
108 for (ULONG i = 0; i < cFormats; i++)
109 {
110 LogFlowFunc(("Format %RU32: cfFormat=%RI16, tyMed=%RU32, dwAspect=%RU32\n",
111 i, pFormatEtc[i].cfFormat, pFormatEtc[i].tymed, pFormatEtc[i].dwAspect));
112 m_pFormatEtc[cFixedFormats + i] = pFormatEtc[i];
113 m_pStgMedium[cFixedFormats + i] = pStgMed[i];
114 }
115 }
116
117 hr = S_OK;
118 }
119 catch (std::bad_alloc &)
120 {
121 hr = E_OUTOFMEMORY;
122 }
123
124 if (SUCCEEDED(hr))
125 {
126 m_cFormats = cAllFormats;
127 m_enmStatus = Initialized;
128
129 int rc2 = RTSemEventCreate(&m_EventListComplete);
130 AssertRC(rc2);
131 rc2 = RTSemEventCreate(&m_EventTransferComplete);
132 AssertRC(rc2);
133 }
134
135 LogFlowFunc(("cAllFormats=%RU32, hr=%Rhrc\n", cAllFormats, hr));
136}
137
138SharedClipboardWinDataObject::~SharedClipboardWinDataObject(void)
139{
140 LogFlowFuncEnter();
141
142 RTSemEventDestroy(m_EventListComplete);
143 m_EventListComplete = NIL_RTSEMEVENT;
144
145 RTSemEventDestroy(m_EventTransferComplete);
146 m_EventTransferComplete = NIL_RTSEMEVENT;
147
148 if (m_pStream)
149 m_pStream->Release();
150
151 if (m_pFormatEtc)
152 delete[] m_pFormatEtc;
153
154 if (m_pStgMedium)
155 delete[] m_pStgMedium;
156
157 LogFlowFunc(("mRefCount=%RI32\n", m_lRefCount));
158}
159
160/*
161 * IUnknown methods.
162 */
163
164STDMETHODIMP_(ULONG) SharedClipboardWinDataObject::AddRef(void)
165{
166 LONG lCount = InterlockedIncrement(&m_lRefCount);
167 LogFlowFunc(("lCount=%RI32\n", lCount));
168 return lCount;
169}
170
171STDMETHODIMP_(ULONG) SharedClipboardWinDataObject::Release(void)
172{
173 LONG lCount = InterlockedDecrement(&m_lRefCount);
174 LogFlowFunc(("lCount=%RI32\n", m_lRefCount));
175 if (lCount == 0)
176 {
177 delete this;
178 return 0;
179 }
180
181 return lCount;
182}
183
184STDMETHODIMP SharedClipboardWinDataObject::QueryInterface(REFIID iid, void **ppvObject)
185{
186 AssertPtrReturn(ppvObject, E_INVALIDARG);
187
188 if ( iid == IID_IDataObject
189 || iid == IID_IUnknown)
190 {
191 AddRef();
192 *ppvObject = this;
193 return S_OK;
194 }
195
196 *ppvObject = 0;
197 return E_NOINTERFACE;
198}
199
200/**
201 * Copies a chunk of data into a HGLOBAL object.
202 *
203 * @returns VBox status code.
204 * @param pvData Data to copy.
205 * @param cbData Size (in bytes) to copy.
206 * @param fFlags GlobalAlloc flags, used for allocating the HGLOBAL block.
207 * @param phGlobal Where to store the allocated HGLOBAL object.
208 */
209int SharedClipboardWinDataObject::copyToHGlobal(const void *pvData, size_t cbData, UINT fFlags, HGLOBAL *phGlobal)
210{
211 AssertPtrReturn(phGlobal, VERR_INVALID_POINTER);
212
213 HGLOBAL hGlobal = GlobalAlloc(fFlags, cbData);
214 if (!hGlobal)
215 return VERR_NO_MEMORY;
216
217 void *pvAlloc = GlobalLock(hGlobal);
218 if (pvAlloc)
219 {
220 CopyMemory(pvAlloc, pvData, cbData);
221 GlobalUnlock(hGlobal);
222
223 *phGlobal = hGlobal;
224
225 return VINF_SUCCESS;
226 }
227
228 GlobalFree(hGlobal);
229 return VERR_ACCESS_DENIED;
230}
231
232/**
233 * Reads (handles) a specific directory reursively and inserts its entry into the
234 * objects's entry list.
235 *
236 * @returns VBox status code.
237 * @param pTransfer Shared Clipboard transfer object to handle.
238 * @param strDir Directory path to handle.
239 */
240int SharedClipboardWinDataObject::readDir(PSHCLTRANSFER pTransfer, const Utf8Str &strDir)
241{
242 LogFlowFunc(("strDir=%s\n", strDir.c_str()));
243
244 SHCLLISTOPENPARMS openParmsList;
245 int rc = ShClTransferListOpenParmsInit(&openParmsList);
246 if (RT_SUCCESS(rc))
247 {
248 rc = RTStrCopy(openParmsList.pszPath, openParmsList.cbPath, strDir.c_str());
249 if (RT_SUCCESS(rc))
250 {
251 SHCLLISTHANDLE hList;
252 rc = ShClTransferListOpen(pTransfer, &openParmsList, &hList);
253 if (RT_SUCCESS(rc))
254 {
255 LogFlowFunc(("strDir=%s -> hList=%RU64\n", strDir.c_str(), hList));
256
257 SHCLLISTHDR hdrList;
258 rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList);
259 if (RT_SUCCESS(rc))
260 {
261 LogFlowFunc(("cTotalObjects=%RU64, cbTotalSize=%RU64\n\n",
262 hdrList.cTotalObjects, hdrList.cbTotalSize));
263
264 for (uint64_t o = 0; o < hdrList.cTotalObjects; o++)
265 {
266 SHCLLISTENTRY entryList;
267 rc = ShClTransferListRead(pTransfer, hList, &entryList);
268 if (RT_SUCCESS(rc))
269 {
270 PSHCLFSOBJINFO pFsObjInfo = (PSHCLFSOBJINFO)entryList.pvInfo;
271 Assert(entryList.cbInfo == sizeof(SHCLFSOBJINFO));
272
273 Utf8Str strPath = strDir + Utf8Str("\\") + Utf8Str(entryList.pszName);
274
275 LogFlowFunc(("\t%s (%RU64 bytes) -> %s\n",
276 entryList.pszName, pFsObjInfo->cbObject, strPath.c_str()));
277
278 if (RTFS_IS_DIRECTORY(pFsObjInfo->Attr.fMode))
279 {
280 FSOBJENTRY objEntry = { strPath.c_str(), *pFsObjInfo };
281
282 m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
283
284 rc = readDir(pTransfer, strPath.c_str());
285 }
286 else if (RTFS_IS_FILE(pFsObjInfo->Attr.fMode))
287 {
288 FSOBJENTRY objEntry = { strPath.c_str(), *pFsObjInfo };
289
290 m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
291 }
292
293 /** @todo Handle symlinks. */
294 }
295
296 if ( RT_FAILURE(rc)
297 && pTransfer->Thread.fStop)
298 break;
299 }
300 }
301
302 ShClTransferListClose(pTransfer, hList);
303 }
304 }
305
306 ShClTransferListOpenParmsDestroy(&openParmsList);
307 }
308
309 LogFlowFuncLeaveRC(rc);
310 return rc;
311}
312
313/**
314 * Thread for reading transfer data.
315 * The data object needs the (high level, root) transfer listing at the time of ::GetData(), so we need
316 * to block and wait until we have this data (via this thread) and continue.
317 *
318 * @returns VBox status code.
319 * @param ThreadSelf Thread handle. Unused at the moment.
320 * @param pvUser Pointer to user-provided data. Of type SharedClipboardWinDataObject.
321 */
322/* static */
323DECLCALLBACK(int) SharedClipboardWinDataObject::readThread(RTTHREAD ThreadSelf, void *pvUser)
324{
325 RT_NOREF(ThreadSelf);
326
327 LogFlowFuncEnter();
328
329 SharedClipboardWinDataObject *pThis = (SharedClipboardWinDataObject *)pvUser;
330
331 PSHCLTRANSFER pTransfer = pThis->m_pTransfer;
332 AssertPtr(pTransfer);
333
334 pTransfer->Thread.fStarted = true;
335 pTransfer->Thread.fStop = false;
336
337 RTThreadUserSignal(RTThreadSelf());
338
339 LogRel2(("Shared Clipboard: Calculating transfer ...\n"));
340
341 int rc = ShClTransferOpen(pTransfer);
342 if (RT_SUCCESS(rc))
343 {
344 PSHCLROOTLIST pRootList;
345 rc = ShClTransferRootsGet(pTransfer, &pRootList);
346 if (RT_SUCCESS(rc))
347 {
348 LogFlowFunc(("cRoots=%RU32\n\n", pRootList->Hdr.cRoots));
349
350 for (uint32_t i = 0; i < pRootList->Hdr.cRoots; i++)
351 {
352 PSHCLLISTENTRY pRootEntry = &pRootList->paEntries[i];
353 AssertPtr(pRootEntry);
354
355 Assert(pRootEntry->cbInfo == sizeof(SHCLFSOBJINFO));
356 PSHCLFSOBJINFO pFsObjInfo = (PSHCLFSOBJINFO)pRootEntry->pvInfo;
357
358 LogFlowFunc(("pszRoot=%s, fMode=0x%x\n", pRootEntry->pszName, pFsObjInfo->Attr.fMode));
359
360 if (RTFS_IS_DIRECTORY(pFsObjInfo->Attr.fMode))
361 {
362 FSOBJENTRY objEntry = { pRootEntry->pszName, *pFsObjInfo };
363
364 pThis->m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
365
366 rc = pThis->readDir(pTransfer, pRootEntry->pszName);
367 }
368 else if (RTFS_IS_FILE(pFsObjInfo->Attr.fMode))
369 {
370 FSOBJENTRY objEntry = { pRootEntry->pszName, *pFsObjInfo };
371
372 pThis->m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
373 }
374
375 if (ASMAtomicReadBool(&pTransfer->Thread.fStop))
376 {
377 LogRel2(("Shared Clipboard: Stopping transfer calculation ...\n"));
378 break;
379 }
380
381 if (RT_FAILURE(rc))
382 break;
383 }
384
385 ShClTransferRootListFree(pRootList);
386 pRootList = NULL;
387
388 if ( RT_SUCCESS(rc)
389 && !ASMAtomicReadBool(&pTransfer->Thread.fStop))
390 {
391 LogRel2(("Shared Clipboard: Transfer calculation complete (%zu root entries)\n", pThis->m_lstEntries.size()));
392
393 /*
394 * Signal the "list complete" event so that this data object can return (valid) data via ::GetData().
395 * This in turn then will create IStream instances (by the OS) for each file system object to handle.
396 */
397 int rc2 = RTSemEventSignal(pThis->m_EventListComplete);
398 AssertRC(rc2);
399
400 if (pThis->m_lstEntries.size())
401 {
402 LogRel2(("Shared Clipboard: Waiting for transfer to complete ...\n"));
403
404 LogFlowFunc(("Waiting for transfer to complete ...\n"));
405
406 /* Transferring stuff can take a while, so don't use any timeout here. */
407 rc2 = RTSemEventWait(pThis->m_EventTransferComplete, RT_INDEFINITE_WAIT);
408 AssertRC(rc2);
409
410 switch (pThis->m_enmStatus)
411 {
412 case Completed:
413 LogRel2(("Shared Clipboard: Transfer complete\n"));
414 break;
415
416 case Canceled:
417 LogRel2(("Shared Clipboard: Transfer canceled\n"));
418 break;
419
420 case Error:
421 LogRel2(("Shared Clipboard: Transfer error occurred\n"));
422 break;
423
424 default:
425 break;
426 }
427 }
428 else
429 LogRel(("Shared Clipboard: No transfer root entries found -- should not happen, please file a bug report\n"));
430 }
431 else if (RT_FAILURE(rc))
432 LogRel(("Shared Clipboard: Transfer failed with %Rrc\n", rc));
433 }
434
435 ShClTransferClose(pTransfer);
436 }
437
438 LogFlowFuncLeaveRC(rc);
439 return rc;
440}
441
442/**
443 * Creates a FILEGROUPDESCRIPTOR object from a given Shared Clipboard transfer and stores the result into an HGLOBAL object.
444 *
445 * @returns VBox status code.
446 * @param pTransfer Shared Clipboard transfer to create file grou desciprtor for.
447 * @param fUnicode Whether the FILEGROUPDESCRIPTOR object shall contain Unicode data or not.
448 * @param phGlobal Where to store the allocated HGLOBAL object on success.
449 */
450int SharedClipboardWinDataObject::createFileGroupDescriptorFromTransfer(PSHCLTRANSFER pTransfer,
451 bool fUnicode, HGLOBAL *phGlobal)
452{
453 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
454 AssertPtrReturn(phGlobal, VERR_INVALID_POINTER);
455
456 LogFlowFuncEnter();
457
458 const size_t cbFileGroupDescriptor = fUnicode ? sizeof(FILEGROUPDESCRIPTORW) : sizeof(FILEGROUPDESCRIPTORA);
459 const size_t cbFileDescriptor = fUnicode ? sizeof(FILEDESCRIPTORW) : sizeof(FILEDESCRIPTORA);
460
461 const UINT cItems = (UINT)m_lstEntries.size(); /** UINT vs. size_t. */
462 if (!cItems)
463 return VERR_NOT_FOUND;
464
465 UINT curIdx = 0; /* Current index of the handled file group descriptor (FGD). */
466
467 const size_t cbFGD = cbFileGroupDescriptor + (cbFileDescriptor * (cItems - 1));
468
469 LogFunc(("fUnicode=%RTbool, cItems=%u, cbFileDescriptor=%zu\n", fUnicode, cItems, cbFileDescriptor));
470
471 /* FILEGROUPDESCRIPTORA / FILEGROUPDESCRIPTOR matches except the cFileName member (TCHAR vs. WCHAR). */
472 FILEGROUPDESCRIPTOR *pFGD = (FILEGROUPDESCRIPTOR *)RTMemAllocZ(cbFGD);
473 if (!pFGD)
474 return VERR_NO_MEMORY;
475
476 int rc = VINF_SUCCESS;
477
478 pFGD->cItems = cItems;
479
480 char *pszFileSpec = NULL;
481
482 FsObjEntryList::const_iterator itRoot = m_lstEntries.begin();
483 while (itRoot != m_lstEntries.end())
484 {
485 FILEDESCRIPTOR *pFD = &pFGD->fgd[curIdx];
486 RT_BZERO(pFD, cbFileDescriptor);
487
488 const char *pszFile = itRoot->strPath.c_str();
489 AssertPtr(pszFile);
490
491 pszFileSpec = RTStrDup(pszFile);
492 AssertBreakStmt(pszFileSpec != NULL, rc = VERR_NO_MEMORY);
493
494 if (fUnicode)
495 {
496 PRTUTF16 pwszFileSpec;
497 rc = RTStrToUtf16(pszFileSpec, &pwszFileSpec);
498 if (RT_SUCCESS(rc))
499 {
500 rc = RTUtf16CopyEx((PRTUTF16 )pFD->cFileName, sizeof(pFD->cFileName) / sizeof(WCHAR),
501 pwszFileSpec, RTUtf16Len(pwszFileSpec));
502 RTUtf16Free(pwszFileSpec);
503
504 LogFlowFunc(("pFD->cFileNameW=%ls\n", pFD->cFileName));
505 }
506 }
507 else
508 {
509 rc = RTStrCopy(pFD->cFileName, sizeof(pFD->cFileName), pszFileSpec);
510 LogFlowFunc(("pFD->cFileNameA=%s\n", pFD->cFileName));
511 }
512
513 RTStrFree(pszFileSpec);
514 pszFileSpec = NULL;
515
516 if (RT_FAILURE(rc))
517 break;
518
519 pFD->dwFlags = FD_PROGRESSUI | FD_ATTRIBUTES;
520 if (fUnicode) /** @todo Only >= Vista. */
521 pFD->dwFlags |= FD_UNICODE;
522 pFD->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
523
524 const SHCLFSOBJINFO *pObjInfo = &itRoot->objInfo;
525
526 if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
527 {
528 pFD->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
529 }
530 else if (RTFS_IS_FILE(pObjInfo->Attr.fMode))
531 {
532 pFD->dwFlags |= FD_FILESIZE;
533
534 const uint64_t cbObjSize = pObjInfo->cbObject;
535
536 pFD->nFileSizeHigh = RT_HI_U32(cbObjSize);
537 pFD->nFileSizeLow = RT_LO_U32(cbObjSize);
538 }
539 else if (RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
540 {
541 /** @todo Implement. */
542 }
543#if 0 /** @todo Implement this. */
544 pFD->dwFlags = FD_ATTRIBUTES | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_FILESIZE;
545 pFD->dwFileAttributes =
546 pFD->ftCreationTime =
547 pFD->ftLastAccessTime =
548 pFD->ftLastWriteTime =
549#endif
550 ++curIdx;
551 ++itRoot;
552 }
553
554 if (pszFileSpec)
555 RTStrFree(pszFileSpec);
556
557 if (RT_SUCCESS(rc))
558 rc = copyToHGlobal(pFGD, cbFGD, GMEM_MOVEABLE, phGlobal);
559
560 RTMemFree(pFGD);
561
562 LogFlowFuncLeaveRC(rc);
563 return rc;
564}
565
566/**
567 * Retrieves the data stored in this object and store the result in
568 * pMedium.
569 *
570 * @return IPRT status code.
571 * @return HRESULT
572 * @param pFormatEtc
573 * @param pMedium
574 */
575STDMETHODIMP SharedClipboardWinDataObject::GetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
576{
577 AssertPtrReturn(pFormatEtc, DV_E_FORMATETC);
578 AssertPtrReturn(pMedium, DV_E_FORMATETC);
579
580 LogFlowFuncEnter();
581
582 LogFlowFunc(("lIndex=%RI32\n", pFormatEtc->lindex));
583
584 /*
585 * Initialize default values.
586 */
587 RT_BZERO(pMedium, sizeof(STGMEDIUM));
588
589 HRESULT hr = DV_E_FORMATETC; /* Play safe. */
590
591 if ( pFormatEtc->cfFormat == m_cfFileDescriptorA
592#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
593 || pFormatEtc->cfFormat == m_cfFileDescriptorW
594#endif
595 )
596 {
597 const bool fUnicode = pFormatEtc->cfFormat == m_cfFileDescriptorW;
598
599 const uint32_t enmTransferStatus = ShClTransferGetStatus(m_pTransfer);
600 RT_NOREF(enmTransferStatus);
601
602 LogFlowFunc(("FormatIndex_FileDescriptor%s, enmTransferStatus=%s, m_fRunning=%RTbool\n",
603 fUnicode ? "W" : "A", ShClTransferStatusToStr(enmTransferStatus), m_fRunning));
604
605 int rc;
606
607 /* The caller can call GetData() several times, so make sure we don't do the same transfer multiple times. */
608 if (!m_fRunning)
609 {
610 /* Start the transfer asynchronously in a separate thread. */
611 rc = ShClTransferRun(m_pTransfer, &SharedClipboardWinDataObject::readThread, this);
612 if (RT_SUCCESS(rc))
613 {
614 m_fRunning = true;
615
616 /* Don't block for too long here, as this also will screw other apps running on the OS. */
617 LogFunc(("Waiting for listing to arrive ...\n"));
618 rc = RTSemEventWait(m_EventListComplete, 30 * 1000 /* 30s timeout */);
619 if (RT_SUCCESS(rc))
620 {
621 LogFunc(("Listing complete\n"));
622 }
623 }
624 }
625 else
626 rc = VINF_SUCCESS;
627
628 if (RT_SUCCESS(rc))
629 {
630 HGLOBAL hGlobal;
631 rc = createFileGroupDescriptorFromTransfer(m_pTransfer, fUnicode, &hGlobal);
632 if (RT_SUCCESS(rc))
633 {
634 pMedium->tymed = TYMED_HGLOBAL;
635 pMedium->hGlobal = hGlobal;
636 /* Note: hGlobal now is being owned by pMedium / the caller. */
637
638 hr = S_OK;
639 }
640 else /* We can't tell any better to the caller, unfortunately. */
641 hr = E_UNEXPECTED;
642 }
643
644 if (RT_FAILURE(rc))
645 LogRel(("Shared Clipboard: Data object unable to get data, rc=%Rrc\n", rc));
646 }
647
648 if (pFormatEtc->cfFormat == m_cfFileContents)
649 {
650 if ( pFormatEtc->lindex >= 0
651 && (ULONG)pFormatEtc->lindex < m_lstEntries.size())
652 {
653 m_uObjIdx = pFormatEtc->lindex; /* lIndex of FormatEtc contains the actual index to the object being handled. */
654
655 FSOBJENTRY &fsObjEntry = m_lstEntries.at(m_uObjIdx);
656
657 LogFlowFunc(("FormatIndex_FileContents: m_uObjIdx=%u (entry '%s')\n", m_uObjIdx, fsObjEntry.strPath.c_str()));
658
659 LogRel2(("Shared Clipboard: Receiving object '%s' ...\n", fsObjEntry.strPath.c_str()));
660
661 /* Hand-in the provider so that our IStream implementation can continue working with it. */
662 hr = SharedClipboardWinStreamImpl::Create(this /* pParent */, m_pTransfer,
663 fsObjEntry.strPath.c_str()/* File name */, &fsObjEntry.objInfo /* PSHCLFSOBJINFO */,
664 &m_pStream);
665 if (SUCCEEDED(hr))
666 {
667 /* Hand over the stream to the caller. */
668 pMedium->tymed = TYMED_ISTREAM;
669 pMedium->pstm = m_pStream;
670 }
671 }
672 }
673 else if (pFormatEtc->cfFormat == m_cfPerformedDropEffect)
674 {
675 HGLOBAL hGlobal = GlobalAlloc(GHND, sizeof(DWORD));
676
677 DWORD* pdwDropEffect = (DWORD*)GlobalLock(hGlobal);
678 *pdwDropEffect = DROPEFFECT_COPY;
679
680 GlobalUnlock(hGlobal);
681
682 pMedium->tymed = TYMED_HGLOBAL;
683 pMedium->hGlobal = hGlobal;
684 pMedium->pUnkForRelease = NULL;
685 }
686
687 if ( FAILED(hr)
688 && hr != DV_E_FORMATETC) /* Can happen if the caller queries unknown / unhandled formats. */
689 {
690 LogRel(("Shared Clipboard: Error returning data from data object (%Rhrc)\n", hr));
691 }
692
693 LogFlowFunc(("hr=%Rhrc\n", hr));
694 return hr;
695}
696
697/**
698 * Only required for IStream / IStorage interfaces.
699 *
700 * @return IPRT status code.
701 * @return HRESULT
702 * @param pFormatEtc
703 * @param pMedium
704 */
705STDMETHODIMP SharedClipboardWinDataObject::GetDataHere(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
706{
707 RT_NOREF(pFormatEtc, pMedium);
708 LogFlowFunc(("\n"));
709 return E_NOTIMPL;
710}
711
712/**
713 * Query if this objects supports a specific format.
714 *
715 * @return IPRT status code.
716 * @return HRESULT
717 * @param pFormatEtc
718 */
719STDMETHODIMP SharedClipboardWinDataObject::QueryGetData(LPFORMATETC pFormatEtc)
720{
721 LogFlowFunc(("\n"));
722 return lookupFormatEtc(pFormatEtc, NULL /* puIndex */) ? S_OK : DV_E_FORMATETC;
723}
724
725STDMETHODIMP SharedClipboardWinDataObject::GetCanonicalFormatEtc(LPFORMATETC pFormatEtc, LPFORMATETC pFormatEtcOut)
726{
727 RT_NOREF(pFormatEtc);
728 LogFlowFunc(("\n"));
729
730 /* Set this to NULL in any case. */
731 pFormatEtcOut->ptd = NULL;
732 return E_NOTIMPL;
733}
734
735STDMETHODIMP SharedClipboardWinDataObject::SetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium, BOOL fRelease)
736{
737 if ( pFormatEtc == NULL
738 || pMedium == NULL)
739 return E_INVALIDARG;
740
741 if (pFormatEtc->lindex != -1)
742 return DV_E_LINDEX;
743
744 if (pFormatEtc->tymed != TYMED_HGLOBAL)
745 return DV_E_TYMED;
746
747 if (pFormatEtc->dwAspect != DVASPECT_CONTENT)
748 return DV_E_DVASPECT;
749
750 LogFlowFunc(("cfFormat=%RU16, lookupFormatEtc=%RTbool\n",
751 pFormatEtc->cfFormat, lookupFormatEtc(pFormatEtc, NULL /* puIndex */)));
752
753 /* CFSTR_PERFORMEDDROPEFFECT is used by the drop target (caller of this IDataObject) to communicate
754 * the outcome of the overall operation. */
755 if ( pFormatEtc->cfFormat == m_cfPerformedDropEffect
756 && pMedium->tymed == TYMED_HGLOBAL)
757 {
758 DWORD dwEffect = *(DWORD *)GlobalLock(pMedium->hGlobal);
759 GlobalUnlock(pMedium->hGlobal);
760
761 LogFlowFunc(("dwEffect=%RI32\n", dwEffect));
762
763 /* Did the user cancel the operation via UI (shell)? This also might happen when overwriting an existing file
764 * and the user doesn't want to allow this. */
765 if (dwEffect == DROPEFFECT_NONE)
766 {
767 LogRel2(("Shared Clipboard: Transfer canceled by user interaction\n"));
768
769 OnTransferCanceled();
770 }
771 /** @todo Detect move / overwrite actions here. */
772
773 if (fRelease)
774 ReleaseStgMedium(pMedium);
775
776 return S_OK;
777 }
778
779 return E_NOTIMPL;
780}
781
782STDMETHODIMP SharedClipboardWinDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
783{
784 LogFlowFunc(("dwDirection=%RI32, mcFormats=%RI32, mpFormatEtc=%p\n", dwDirection, m_cFormats, m_pFormatEtc));
785
786 HRESULT hr;
787 if (dwDirection == DATADIR_GET)
788 hr = SharedClipboardWinEnumFormatEtc::CreateEnumFormatEtc(m_cFormats, m_pFormatEtc, ppEnumFormatEtc);
789 else
790 hr = E_NOTIMPL;
791
792 LogFlowFunc(("hr=%Rhrc\n", hr));
793 return hr;
794}
795
796STDMETHODIMP SharedClipboardWinDataObject::DAdvise(LPFORMATETC pFormatEtc, DWORD fAdvise, IAdviseSink *pAdvSink, DWORD *pdwConnection)
797{
798 RT_NOREF(pFormatEtc, fAdvise, pAdvSink, pdwConnection);
799 return OLE_E_ADVISENOTSUPPORTED;
800}
801
802STDMETHODIMP SharedClipboardWinDataObject::DUnadvise(DWORD dwConnection)
803{
804 RT_NOREF(dwConnection);
805 return OLE_E_ADVISENOTSUPPORTED;
806}
807
808STDMETHODIMP SharedClipboardWinDataObject::EnumDAdvise(IEnumSTATDATA **ppEnumAdvise)
809{
810 RT_NOREF(ppEnumAdvise);
811 return OLE_E_ADVISENOTSUPPORTED;
812}
813
814#ifdef VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC
815/*
816 * IDataObjectAsyncCapability methods.
817 */
818
819STDMETHODIMP SharedClipboardWinDataObject::EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects)
820{
821 RT_NOREF(hResult, pbcReserved, dwEffects);
822 return E_NOTIMPL;
823}
824
825STDMETHODIMP SharedClipboardWinDataObject::GetAsyncMode(BOOL *pfIsOpAsync)
826{
827 RT_NOREF(pfIsOpAsync);
828 return E_NOTIMPL;
829}
830
831STDMETHODIMP SharedClipboardWinDataObject::InOperation(BOOL *pfInAsyncOp)
832{
833 RT_NOREF(pfInAsyncOp);
834 return E_NOTIMPL;
835}
836
837STDMETHODIMP SharedClipboardWinDataObject::SetAsyncMode(BOOL fDoOpAsync)
838{
839 RT_NOREF(fDoOpAsync);
840 return E_NOTIMPL;
841}
842
843STDMETHODIMP SharedClipboardWinDataObject::StartOperation(IBindCtx *pbcReserved)
844{
845 RT_NOREF(pbcReserved);
846 return E_NOTIMPL;
847}
848#endif /* VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC */
849
850/*
851 * Own stuff.
852 */
853
854int SharedClipboardWinDataObject::Init(void)
855{
856 LogFlowFuncLeaveRC(VINF_SUCCESS);
857 return VINF_SUCCESS;
858}
859
860void SharedClipboardWinDataObject::OnTransferComplete(int rc /* = VINF_SUCESS */)
861{
862 RT_NOREF(rc);
863
864 LogFlowFunc(("m_uObjIdx=%RU32 (total: %zu)\n", m_uObjIdx, m_lstEntries.size()));
865
866 if (RT_SUCCESS(rc))
867 {
868 const bool fComplete = m_uObjIdx == m_lstEntries.size() - 1 /* Object index is zero-based */;
869 if (fComplete)
870 {
871 m_enmStatus = Completed;
872 }
873 else
874 AssertFailed();
875 }
876 else
877 m_enmStatus = Error;
878
879 if (m_EventTransferComplete != NIL_RTSEMEVENT)
880 {
881 int rc2 = RTSemEventSignal(m_EventTransferComplete);
882 AssertRC(rc2);
883 }
884
885 LogFlowFuncLeaveRC(rc);
886}
887
888void SharedClipboardWinDataObject::OnTransferCanceled(void)
889{
890 LogFlowFuncEnter();
891
892 m_enmStatus = Canceled;
893
894 if (m_EventTransferComplete != NIL_RTSEMEVENT)
895 {
896 int rc2 = RTSemEventSignal(m_EventTransferComplete);
897 AssertRC(rc2);
898 }
899
900 LogFlowFuncLeave();
901}
902
903/* static */
904void SharedClipboardWinDataObject::logFormat(CLIPFORMAT fmt)
905{
906 char szFormat[128];
907 if (GetClipboardFormatName(fmt, szFormat, sizeof(szFormat)))
908 {
909 LogFlowFunc(("clipFormat=%RI16 -> %s\n", fmt, szFormat));
910 }
911 else
912 LogFlowFunc(("clipFormat=%RI16 is unknown\n", fmt));
913}
914
915bool SharedClipboardWinDataObject::lookupFormatEtc(LPFORMATETC pFormatEtc, ULONG *puIndex)
916{
917 AssertReturn(pFormatEtc, false);
918 /* puIndex is optional. */
919
920 for (ULONG i = 0; i < m_cFormats; i++)
921 {
922 if( (pFormatEtc->tymed & m_pFormatEtc[i].tymed)
923 && pFormatEtc->cfFormat == m_pFormatEtc[i].cfFormat)
924 /* Note: Do *not* compare dwAspect here, as this can be dynamic, depending on how the object should be represented. */
925 //&& pFormatEtc->dwAspect == m_pFormatEtc[i].dwAspect)
926 {
927 LogRel2(("Shared Clipboard: Format found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32, ulIndex=%RU32\n",
928 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect, i));
929 if (puIndex)
930 *puIndex = i;
931 return true;
932 }
933 }
934
935 LogRel2(("Shared Clipboard: Format NOT found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32\n",
936 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect));
937
938 logFormat(pFormatEtc->cfFormat);
939
940 return false;
941}
942
943void SharedClipboardWinDataObject::registerFormat(LPFORMATETC pFormatEtc, CLIPFORMAT clipFormat,
944 TYMED tyMed, LONG lIndex, DWORD dwAspect,
945 DVTARGETDEVICE *pTargetDevice)
946{
947 AssertPtr(pFormatEtc);
948
949 pFormatEtc->cfFormat = clipFormat;
950 pFormatEtc->tymed = tyMed;
951 pFormatEtc->lindex = lIndex;
952 pFormatEtc->dwAspect = dwAspect;
953 pFormatEtc->ptd = pTargetDevice;
954
955 LogFlowFunc(("Registered format=%ld\n", pFormatEtc->cfFormat));
956
957 logFormat(pFormatEtc->cfFormat);
958}
959
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette