VirtualBox

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

Last change on this file since 91933 was 87611, checked in by vboxsync, 3 years ago

Shared Clipboard/Transfers: More callback code. ​bugref:9437

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

© 2023 Oracle
ContactPrivacy policyTerms of Use