VirtualBox

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

Last change on this file since 98461 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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