VirtualBox

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

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

Shared Clipboard/Transfers: Various bugfixes.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use