VirtualBox

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

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

Shared Clipboard/Transfers: Update.

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