VirtualBox

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

Last change on this file since 81438 was 81438, checked in by vboxsync, 5 years ago

Shared Clipboard/Transfers: Don't count directory entries as entries to process by IDataObject, as directories are not separately being processed by IStreamImpl.

  • 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 81438 2019-10-22 08:04:12Z 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 /* Note: Directories are *not* required to be part of m_lstEntries, as we only
281 * count files to transfer there. */
282
283 rc = readDir(pTransfer, strPath.c_str());
284 }
285 else if (RTFS_IS_FILE(pFsObjInfo->Attr.fMode))
286 {
287 FSOBJENTRY objEntry = { strPath.c_str(), *pFsObjInfo };
288
289 m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
290 }
291
292 /** @todo Handle symlinks. */
293 }
294
295 if ( RT_FAILURE(rc)
296 && pTransfer->Thread.fStop)
297 break;
298 }
299 }
300
301 ShClTransferListClose(pTransfer, hList);
302 }
303 }
304
305 ShClTransferListOpenParmsDestroy(&openParmsList);
306 }
307
308 LogFlowFuncLeaveRC(rc);
309 return rc;
310}
311
312/**
313 * Thread for reading transfer data.
314 * The data object needs the (high level, root) transfer listing at the time of ::GetData(), so we need
315 * to block and wait until we have this data (via this thread) and continue.
316 *
317 * @returns VBox status code.
318 * @param ThreadSelf Thread handle. Unused at the moment.
319 * @param pvUser Pointer to user-provided data. Of type SharedClipboardWinDataObject.
320 */
321/* static */
322DECLCALLBACK(int) SharedClipboardWinDataObject::readThread(RTTHREAD ThreadSelf, void *pvUser)
323{
324 RT_NOREF(ThreadSelf);
325
326 LogFlowFuncEnter();
327
328 SharedClipboardWinDataObject *pThis = (SharedClipboardWinDataObject *)pvUser;
329
330 PSHCLTRANSFER pTransfer = pThis->m_pTransfer;
331 AssertPtr(pTransfer);
332
333 pTransfer->Thread.fStarted = true;
334 pTransfer->Thread.fStop = false;
335
336 RTThreadUserSignal(RTThreadSelf());
337
338 LogRel2(("Shared Clipboard: Calculating transfer ...\n"));
339
340 int rc = ShClTransferOpen(pTransfer);
341 if (RT_SUCCESS(rc))
342 {
343 PSHCLROOTLIST pRootList;
344 rc = ShClTransferRootsGet(pTransfer, &pRootList);
345 if (RT_SUCCESS(rc))
346 {
347 LogFlowFunc(("cRoots=%RU32\n\n", pRootList->Hdr.cRoots));
348
349 for (uint32_t i = 0; i < pRootList->Hdr.cRoots; i++)
350 {
351 PSHCLLISTENTRY pRootEntry = &pRootList->paEntries[i];
352 AssertPtr(pRootEntry);
353
354 Assert(pRootEntry->cbInfo == sizeof(SHCLFSOBJINFO));
355 PSHCLFSOBJINFO pFsObjInfo = (PSHCLFSOBJINFO)pRootEntry->pvInfo;
356
357 LogFlowFunc(("pszRoot=%s, fMode=0x%x\n", pRootEntry->pszName, pFsObjInfo->Attr.fMode));
358
359 if (RTFS_IS_DIRECTORY(pFsObjInfo->Attr.fMode))
360 {
361 /* Note: Directories are *not* required to be part of m_lstEntries, as we only
362 * count files to transfer there. */
363
364 rc = pThis->readDir(pTransfer, pRootEntry->pszName);
365 }
366 else if (RTFS_IS_FILE(pFsObjInfo->Attr.fMode))
367 {
368 FSOBJENTRY objEntry = { pRootEntry->pszName, *pFsObjInfo };
369
370 pThis->m_lstEntries.push_back(objEntry); /** @todo Can this throw? */
371 }
372
373 if (ASMAtomicReadBool(&pTransfer->Thread.fStop))
374 {
375 LogRel2(("Shared Clipboard: Stopping transfer calculation ...\n"));
376 break;
377 }
378
379 if (RT_FAILURE(rc))
380 break;
381 }
382
383 ShClTransferRootListFree(pRootList);
384 pRootList = NULL;
385
386 if ( RT_SUCCESS(rc)
387 && !ASMAtomicReadBool(&pTransfer->Thread.fStop))
388 {
389 LogRel2(("Shared Clipboard: Transfer calculation complete (%zu root entries)\n", pThis->m_lstEntries.size()));
390
391 /*
392 * Signal the "list complete" event so that this data object can return (valid) data via ::GetData().
393 * This in turn then will create IStream instances (by the OS) for each file system object to handle.
394 */
395 int rc2 = RTSemEventSignal(pThis->m_EventListComplete);
396 AssertRC(rc2);
397
398 if (pThis->m_lstEntries.size())
399 {
400 LogRel2(("Shared Clipboard: Waiting for transfer to complete ...\n"));
401
402 LogFlowFunc(("Waiting for transfer to complete ...\n"));
403
404 /* Transferring stuff can take a while, so don't use any timeout here. */
405 rc2 = RTSemEventWait(pThis->m_EventTransferComplete, RT_INDEFINITE_WAIT);
406 AssertRC(rc2);
407
408 switch (pThis->m_enmStatus)
409 {
410 case Completed:
411 LogRel2(("Shared Clipboard: Transfer complete\n"));
412 break;
413
414 case Canceled:
415 LogRel2(("Shared Clipboard: Transfer canceled\n"));
416 break;
417
418 case Error:
419 LogRel2(("Shared Clipboard: Transfer error occurred\n"));
420 break;
421
422 default:
423 break;
424 }
425 }
426 else
427 LogRel(("Shared Clipboard: No transfer root entries found -- should not happen, please file a bug report\n"));
428 }
429 else if (RT_FAILURE(rc))
430 LogRel(("Shared Clipboard: Transfer failed with %Rrc\n", rc));
431 }
432
433 ShClTransferClose(pTransfer);
434 }
435
436 LogFlowFuncLeaveRC(rc);
437 return rc;
438}
439
440/**
441 * Creates a FILEGROUPDESCRIPTOR object from a given Shared Clipboard transfer and stores the result into an HGLOBAL object.
442 *
443 * @returns VBox status code.
444 * @param pTransfer Shared Clipboard transfer to create file grou desciprtor for.
445 * @param fUnicode Whether the FILEGROUPDESCRIPTOR object shall contain Unicode data or not.
446 * @param phGlobal Where to store the allocated HGLOBAL object on success.
447 */
448int SharedClipboardWinDataObject::createFileGroupDescriptorFromTransfer(PSHCLTRANSFER pTransfer,
449 bool fUnicode, HGLOBAL *phGlobal)
450{
451 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
452 AssertPtrReturn(phGlobal, VERR_INVALID_POINTER);
453
454 LogFlowFuncEnter();
455
456 const size_t cbFileGroupDescriptor = fUnicode ? sizeof(FILEGROUPDESCRIPTORW) : sizeof(FILEGROUPDESCRIPTORA);
457 const size_t cbFileDescriptor = fUnicode ? sizeof(FILEDESCRIPTORW) : sizeof(FILEDESCRIPTORA);
458
459 const UINT cItems = (UINT)m_lstEntries.size(); /** UINT vs. size_t. */
460 if (!cItems)
461 return VERR_NOT_FOUND;
462
463 UINT curIdx = 0; /* Current index of the handled file group descriptor (FGD). */
464
465 const size_t cbFGD = cbFileGroupDescriptor + (cbFileDescriptor * (cItems - 1));
466
467 LogFunc(("fUnicode=%RTbool, cItems=%u, cbFileDescriptor=%zu\n", fUnicode, cItems, cbFileDescriptor));
468
469 /* FILEGROUPDESCRIPTORA / FILEGROUPDESCRIPTOR matches except the cFileName member (TCHAR vs. WCHAR). */
470 FILEGROUPDESCRIPTOR *pFGD = (FILEGROUPDESCRIPTOR *)RTMemAllocZ(cbFGD);
471 if (!pFGD)
472 return VERR_NO_MEMORY;
473
474 int rc = VINF_SUCCESS;
475
476 pFGD->cItems = cItems;
477
478 char *pszFileSpec = NULL;
479
480 FsObjEntryList::const_iterator itRoot = m_lstEntries.begin();
481 while (itRoot != m_lstEntries.end())
482 {
483 FILEDESCRIPTOR *pFD = &pFGD->fgd[curIdx];
484 RT_BZERO(pFD, cbFileDescriptor);
485
486 const char *pszFile = itRoot->strPath.c_str();
487 AssertPtr(pszFile);
488
489 pszFileSpec = RTStrDup(pszFile);
490 AssertBreakStmt(pszFileSpec != NULL, rc = VERR_NO_MEMORY);
491
492 if (fUnicode)
493 {
494 PRTUTF16 pwszFileSpec;
495 rc = RTStrToUtf16(pszFileSpec, &pwszFileSpec);
496 if (RT_SUCCESS(rc))
497 {
498 rc = RTUtf16CopyEx((PRTUTF16 )pFD->cFileName, sizeof(pFD->cFileName) / sizeof(WCHAR),
499 pwszFileSpec, RTUtf16Len(pwszFileSpec));
500 RTUtf16Free(pwszFileSpec);
501
502 LogFlowFunc(("pFD->cFileNameW=%ls\n", pFD->cFileName));
503 }
504 }
505 else
506 {
507 rc = RTStrCopy(pFD->cFileName, sizeof(pFD->cFileName), pszFileSpec);
508 LogFlowFunc(("pFD->cFileNameA=%s\n", pFD->cFileName));
509 }
510
511 RTStrFree(pszFileSpec);
512 pszFileSpec = NULL;
513
514 if (RT_FAILURE(rc))
515 break;
516
517 pFD->dwFlags = FD_PROGRESSUI | FD_ATTRIBUTES;
518 if (fUnicode) /** @todo Only >= Vista. */
519 pFD->dwFlags |= FD_UNICODE;
520 pFD->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
521
522 const SHCLFSOBJINFO *pObjInfo = &itRoot->objInfo;
523
524 if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
525 {
526 pFD->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
527 }
528 else if (RTFS_IS_FILE(pObjInfo->Attr.fMode))
529 {
530 pFD->dwFlags |= FD_FILESIZE;
531
532 const uint64_t cbObjSize = pObjInfo->cbObject;
533
534 pFD->nFileSizeHigh = RT_HI_U32(cbObjSize);
535 pFD->nFileSizeLow = RT_LO_U32(cbObjSize);
536 }
537 else if (RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
538 {
539 /** @todo Implement. */
540 }
541#if 0 /** @todo Implement this. */
542 pFD->dwFlags = FD_ATTRIBUTES | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_FILESIZE;
543 pFD->dwFileAttributes =
544 pFD->ftCreationTime =
545 pFD->ftLastAccessTime =
546 pFD->ftLastWriteTime =
547#endif
548 ++curIdx;
549 ++itRoot;
550 }
551
552 if (pszFileSpec)
553 RTStrFree(pszFileSpec);
554
555 if (RT_SUCCESS(rc))
556 rc = copyToHGlobal(pFGD, cbFGD, GMEM_MOVEABLE, phGlobal);
557
558 RTMemFree(pFGD);
559
560 LogFlowFuncLeaveRC(rc);
561 return rc;
562}
563
564/**
565 * Retrieves the data stored in this object and store the result in
566 * pMedium.
567 *
568 * @return IPRT status code.
569 * @return HRESULT
570 * @param pFormatEtc
571 * @param pMedium
572 */
573STDMETHODIMP SharedClipboardWinDataObject::GetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
574{
575 AssertPtrReturn(pFormatEtc, DV_E_FORMATETC);
576 AssertPtrReturn(pMedium, DV_E_FORMATETC);
577
578 LogFlowFuncEnter();
579
580 LogFlowFunc(("lIndex=%RI32\n", pFormatEtc->lindex));
581
582 /*
583 * Initialize default values.
584 */
585 RT_BZERO(pMedium, sizeof(STGMEDIUM));
586
587 HRESULT hr = DV_E_FORMATETC; /* Play safe. */
588
589 if ( pFormatEtc->cfFormat == m_cfFileDescriptorA
590#ifdef VBOX_CLIPBOARD_WITH_UNICODE_SUPPORT
591 || pFormatEtc->cfFormat == m_cfFileDescriptorW
592#endif
593 )
594 {
595 const bool fUnicode = pFormatEtc->cfFormat == m_cfFileDescriptorW;
596
597 const uint32_t enmTransferStatus = ShClTransferGetStatus(m_pTransfer);
598 RT_NOREF(enmTransferStatus);
599
600 LogFlowFunc(("FormatIndex_FileDescriptor%s, enmTransferStatus=%s, m_fRunning=%RTbool\n",
601 fUnicode ? "W" : "A", ShClTransferStatusToStr(enmTransferStatus), m_fRunning));
602
603 int rc;
604
605 /* The caller can call GetData() several times, so make sure we don't do the same transfer multiple times. */
606 if (!m_fRunning)
607 {
608 /* Start the transfer asynchronously in a separate thread. */
609 rc = ShClTransferRun(m_pTransfer, &SharedClipboardWinDataObject::readThread, this);
610 if (RT_SUCCESS(rc))
611 {
612 m_fRunning = true;
613
614 /* Don't block for too long here, as this also will screw other apps running on the OS. */
615 LogFunc(("Waiting for listing to arrive ...\n"));
616 rc = RTSemEventWait(m_EventListComplete, 30 * 1000 /* 30s timeout */);
617 if (RT_SUCCESS(rc))
618 {
619 LogFunc(("Listing complete\n"));
620 }
621 }
622 }
623 else
624 rc = VINF_SUCCESS;
625
626 if (RT_SUCCESS(rc))
627 {
628 HGLOBAL hGlobal;
629 rc = createFileGroupDescriptorFromTransfer(m_pTransfer, fUnicode, &hGlobal);
630 if (RT_SUCCESS(rc))
631 {
632 pMedium->tymed = TYMED_HGLOBAL;
633 pMedium->hGlobal = hGlobal;
634 /* Note: hGlobal now is being owned by pMedium / the caller. */
635
636 hr = S_OK;
637 }
638 else /* We can't tell any better to the caller, unfortunately. */
639 hr = E_UNEXPECTED;
640 }
641
642 if (RT_FAILURE(rc))
643 LogRel(("Shared Clipboard: Data object unable to get data, rc=%Rrc\n", rc));
644 }
645
646 if (pFormatEtc->cfFormat == m_cfFileContents)
647 {
648 if ( pFormatEtc->lindex >= 0
649 && (ULONG)pFormatEtc->lindex < m_lstEntries.size())
650 {
651 m_uObjIdx = pFormatEtc->lindex; /* lIndex of FormatEtc contains the actual index to the object being handled. */
652
653 FSOBJENTRY &fsObjEntry = m_lstEntries.at(m_uObjIdx);
654
655 LogFlowFunc(("FormatIndex_FileContents: m_uObjIdx=%u (entry '%s')\n", m_uObjIdx, fsObjEntry.strPath.c_str()));
656
657 LogRel2(("Shared Clipboard: Receiving object '%s' ...\n", fsObjEntry.strPath.c_str()));
658
659 /* Hand-in the provider so that our IStream implementation can continue working with it. */
660 hr = SharedClipboardWinStreamImpl::Create(this /* pParent */, m_pTransfer,
661 fsObjEntry.strPath.c_str()/* File name */, &fsObjEntry.objInfo /* PSHCLFSOBJINFO */,
662 &m_pStream);
663 if (SUCCEEDED(hr))
664 {
665 /* Hand over the stream to the caller. */
666 pMedium->tymed = TYMED_ISTREAM;
667 pMedium->pstm = m_pStream;
668 }
669 }
670 }
671 else if (pFormatEtc->cfFormat == m_cfPerformedDropEffect)
672 {
673 HGLOBAL hGlobal = GlobalAlloc(GHND, sizeof(DWORD));
674
675 DWORD* pdwDropEffect = (DWORD*)GlobalLock(hGlobal);
676 *pdwDropEffect = DROPEFFECT_COPY;
677
678 GlobalUnlock(hGlobal);
679
680 pMedium->tymed = TYMED_HGLOBAL;
681 pMedium->hGlobal = hGlobal;
682 pMedium->pUnkForRelease = NULL;
683 }
684
685 if ( FAILED(hr)
686 && hr != DV_E_FORMATETC) /* Can happen if the caller queries unknown / unhandled formats. */
687 {
688 LogRel(("Shared Clipboard: Error returning data from data object (%Rhrc)\n", hr));
689 }
690
691 LogFlowFunc(("hr=%Rhrc\n", hr));
692 return hr;
693}
694
695/**
696 * Only required for IStream / IStorage interfaces.
697 *
698 * @return IPRT status code.
699 * @return HRESULT
700 * @param pFormatEtc
701 * @param pMedium
702 */
703STDMETHODIMP SharedClipboardWinDataObject::GetDataHere(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium)
704{
705 RT_NOREF(pFormatEtc, pMedium);
706 LogFlowFunc(("\n"));
707 return E_NOTIMPL;
708}
709
710/**
711 * Query if this objects supports a specific format.
712 *
713 * @return IPRT status code.
714 * @return HRESULT
715 * @param pFormatEtc
716 */
717STDMETHODIMP SharedClipboardWinDataObject::QueryGetData(LPFORMATETC pFormatEtc)
718{
719 LogFlowFunc(("\n"));
720 return lookupFormatEtc(pFormatEtc, NULL /* puIndex */) ? S_OK : DV_E_FORMATETC;
721}
722
723STDMETHODIMP SharedClipboardWinDataObject::GetCanonicalFormatEtc(LPFORMATETC pFormatEtc, LPFORMATETC pFormatEtcOut)
724{
725 RT_NOREF(pFormatEtc);
726 LogFlowFunc(("\n"));
727
728 /* Set this to NULL in any case. */
729 pFormatEtcOut->ptd = NULL;
730 return E_NOTIMPL;
731}
732
733STDMETHODIMP SharedClipboardWinDataObject::SetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium, BOOL fRelease)
734{
735 if ( pFormatEtc == NULL
736 || pMedium == NULL)
737 return E_INVALIDARG;
738
739 if (pFormatEtc->lindex != -1)
740 return DV_E_LINDEX;
741
742 if (pFormatEtc->tymed != TYMED_HGLOBAL)
743 return DV_E_TYMED;
744
745 if (pFormatEtc->dwAspect != DVASPECT_CONTENT)
746 return DV_E_DVASPECT;
747
748 LogFlowFunc(("cfFormat=%RU16, lookupFormatEtc=%RTbool\n",
749 pFormatEtc->cfFormat, lookupFormatEtc(pFormatEtc, NULL /* puIndex */)));
750
751 /* CFSTR_PERFORMEDDROPEFFECT is used by the drop target (caller of this IDataObject) to communicate
752 * the outcome of the overall operation. */
753 if ( pFormatEtc->cfFormat == m_cfPerformedDropEffect
754 && pMedium->tymed == TYMED_HGLOBAL)
755 {
756 DWORD dwEffect = *(DWORD *)GlobalLock(pMedium->hGlobal);
757 GlobalUnlock(pMedium->hGlobal);
758
759 LogFlowFunc(("dwEffect=%RI32\n", dwEffect));
760
761 /* Did the user cancel the operation via UI (shell)? This also might happen when overwriting an existing file
762 * and the user doesn't want to allow this. */
763 if (dwEffect == DROPEFFECT_NONE)
764 {
765 LogRel2(("Shared Clipboard: Transfer canceled by user interaction\n"));
766
767 OnTransferCanceled();
768 }
769 /** @todo Detect move / overwrite actions here. */
770
771 if (fRelease)
772 ReleaseStgMedium(pMedium);
773
774 return S_OK;
775 }
776
777 return E_NOTIMPL;
778}
779
780STDMETHODIMP SharedClipboardWinDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
781{
782 LogFlowFunc(("dwDirection=%RI32, mcFormats=%RI32, mpFormatEtc=%p\n", dwDirection, m_cFormats, m_pFormatEtc));
783
784 HRESULT hr;
785 if (dwDirection == DATADIR_GET)
786 hr = SharedClipboardWinEnumFormatEtc::CreateEnumFormatEtc(m_cFormats, m_pFormatEtc, ppEnumFormatEtc);
787 else
788 hr = E_NOTIMPL;
789
790 LogFlowFunc(("hr=%Rhrc\n", hr));
791 return hr;
792}
793
794STDMETHODIMP SharedClipboardWinDataObject::DAdvise(LPFORMATETC pFormatEtc, DWORD fAdvise, IAdviseSink *pAdvSink, DWORD *pdwConnection)
795{
796 RT_NOREF(pFormatEtc, fAdvise, pAdvSink, pdwConnection);
797 return OLE_E_ADVISENOTSUPPORTED;
798}
799
800STDMETHODIMP SharedClipboardWinDataObject::DUnadvise(DWORD dwConnection)
801{
802 RT_NOREF(dwConnection);
803 return OLE_E_ADVISENOTSUPPORTED;
804}
805
806STDMETHODIMP SharedClipboardWinDataObject::EnumDAdvise(IEnumSTATDATA **ppEnumAdvise)
807{
808 RT_NOREF(ppEnumAdvise);
809 return OLE_E_ADVISENOTSUPPORTED;
810}
811
812#ifdef VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC
813/*
814 * IDataObjectAsyncCapability methods.
815 */
816
817STDMETHODIMP SharedClipboardWinDataObject::EndOperation(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects)
818{
819 RT_NOREF(hResult, pbcReserved, dwEffects);
820 return E_NOTIMPL;
821}
822
823STDMETHODIMP SharedClipboardWinDataObject::GetAsyncMode(BOOL *pfIsOpAsync)
824{
825 RT_NOREF(pfIsOpAsync);
826 return E_NOTIMPL;
827}
828
829STDMETHODIMP SharedClipboardWinDataObject::InOperation(BOOL *pfInAsyncOp)
830{
831 RT_NOREF(pfInAsyncOp);
832 return E_NOTIMPL;
833}
834
835STDMETHODIMP SharedClipboardWinDataObject::SetAsyncMode(BOOL fDoOpAsync)
836{
837 RT_NOREF(fDoOpAsync);
838 return E_NOTIMPL;
839}
840
841STDMETHODIMP SharedClipboardWinDataObject::StartOperation(IBindCtx *pbcReserved)
842{
843 RT_NOREF(pbcReserved);
844 return E_NOTIMPL;
845}
846#endif /* VBOX_WITH_SHARED_CLIPBOARD_WIN_ASYNC */
847
848/*
849 * Own stuff.
850 */
851
852int SharedClipboardWinDataObject::Init(void)
853{
854 LogFlowFuncLeaveRC(VINF_SUCCESS);
855 return VINF_SUCCESS;
856}
857
858void SharedClipboardWinDataObject::OnTransferComplete(int rc /* = VINF_SUCESS */)
859{
860 RT_NOREF(rc);
861
862 LogFlowFunc(("m_uObjIdx=%RU32 (total: %zu)\n", m_uObjIdx, m_lstEntries.size()));
863
864 if (RT_SUCCESS(rc))
865 {
866 const bool fComplete = m_uObjIdx == m_lstEntries.size() - 1 /* Object index is zero-based */;
867 if (fComplete)
868 {
869 m_enmStatus = Completed;
870 }
871 }
872 else
873 m_enmStatus = Error;
874
875 if (m_enmStatus != Initialized)
876 {
877 if (m_EventTransferComplete != NIL_RTSEMEVENT)
878 {
879 int rc2 = RTSemEventSignal(m_EventTransferComplete);
880 AssertRC(rc2);
881 }
882 }
883
884 LogFlowFuncLeaveRC(rc);
885}
886
887void SharedClipboardWinDataObject::OnTransferCanceled(void)
888{
889 LogFlowFuncEnter();
890
891 m_enmStatus = Canceled;
892
893 if (m_EventTransferComplete != NIL_RTSEMEVENT)
894 {
895 int rc2 = RTSemEventSignal(m_EventTransferComplete);
896 AssertRC(rc2);
897 }
898
899 LogFlowFuncLeave();
900}
901
902/* static */
903void SharedClipboardWinDataObject::logFormat(CLIPFORMAT fmt)
904{
905 char szFormat[128];
906 if (GetClipboardFormatName(fmt, szFormat, sizeof(szFormat)))
907 {
908 LogFlowFunc(("clipFormat=%RI16 -> %s\n", fmt, szFormat));
909 }
910 else
911 LogFlowFunc(("clipFormat=%RI16 is unknown\n", fmt));
912}
913
914bool SharedClipboardWinDataObject::lookupFormatEtc(LPFORMATETC pFormatEtc, ULONG *puIndex)
915{
916 AssertReturn(pFormatEtc, false);
917 /* puIndex is optional. */
918
919 for (ULONG i = 0; i < m_cFormats; i++)
920 {
921 if( (pFormatEtc->tymed & m_pFormatEtc[i].tymed)
922 && pFormatEtc->cfFormat == m_pFormatEtc[i].cfFormat)
923 /* Note: Do *not* compare dwAspect here, as this can be dynamic, depending on how the object should be represented. */
924 //&& pFormatEtc->dwAspect == m_pFormatEtc[i].dwAspect)
925 {
926 LogRel2(("Shared Clipboard: Format found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32, ulIndex=%RU32\n",
927 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect, i));
928 if (puIndex)
929 *puIndex = i;
930 return true;
931 }
932 }
933
934 LogRel2(("Shared Clipboard: Format NOT found: tyMed=%RI32, cfFormat=%RI16, dwAspect=%RI32\n",
935 pFormatEtc->tymed, pFormatEtc->cfFormat, pFormatEtc->dwAspect));
936
937 logFormat(pFormatEtc->cfFormat);
938
939 return false;
940}
941
942void SharedClipboardWinDataObject::registerFormat(LPFORMATETC pFormatEtc, CLIPFORMAT clipFormat,
943 TYMED tyMed, LONG lIndex, DWORD dwAspect,
944 DVTARGETDEVICE *pTargetDevice)
945{
946 AssertPtr(pFormatEtc);
947
948 pFormatEtc->cfFormat = clipFormat;
949 pFormatEtc->tymed = tyMed;
950 pFormatEtc->lindex = lIndex;
951 pFormatEtc->dwAspect = dwAspect;
952 pFormatEtc->ptd = pTargetDevice;
953
954 LogFlowFunc(("Registered format=%ld\n", pFormatEtc->cfFormat));
955
956 logFormat(pFormatEtc->cfFormat);
957}
958
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