VirtualBox

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

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

Shared Clipboard: Renaming (SHAREDCLIPBOARD -> SHCL and VBOXCLIPBOARD -> SHCL).

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